1 #pragma once
2 
3 #include "tinyxml2.h"
4 
5 #include <phosphor-logging/elog-errors.hpp>
6 #include <phosphor-logging/log.hpp>
7 #include <types.hpp>
8 
9 #include <map>
10 #include <sstream>
11 #include <stack>
12 #include <string>
13 #include <variant>
14 #include <vector>
15 
16 namespace bios
17 {
18 /* Can hold one 'option'
19  * For example
20  *  <option text="TIS" value="0x0"/>
21  */
22 using OptionType = std::tuple<std::string, ipmi::DbusVariant, std::string>;
23 
24 /* Can hold one 'options'
25  * For example
26  *  <options>
27  *		<option text="TIS" value="0x0"/>
28  *		<option text="PTP FIFO" value="0x1"/>
29  *		<option text="PTP CRB" value="0x2"/>
30  *	</options>
31  */
32 using OptionTypeVector = std::vector<OptionType>;
33 
34 /* Can hold one 'knob'
35  * For example
36  *  <knob  type="scalar" setupType="oneof" name="TpmDeviceInterfaceAttempt"
37  *  varstoreIndex="14" prompt="Attempt PTP TPM Device Interface"
38  *  description="Attempt PTP TPM Device Interface: PTP FIFO, PTP CRB" size="1"
39  *  offset="0x0005" depex="Sif( _LIST_ TpmDevice _EQU_ 0 1 ) _AND_ Sif(
40  *  TpmDeviceInterfacePtpFifoSupported _EQU_ 0 OR
41  *  TpmDeviceInterfacePtpCrbSupported _EQU_ 0 )" default="0x00"
42  *CurrentVal="0x00"> <options> <option text="TIS" value="0x0"/> <option
43  *text="PTP FIFO" value="0x1"/> <option text="PTP CRB" value="0x2"/>
44  *		</options>
45  *	</knob>
46  */
47 using BiosBaseTableTypeEntry =
48     std::tuple<std::string, bool, std::string, std::string, std::string,
49                ipmi::DbusVariant, ipmi::DbusVariant, OptionTypeVector>;
50 
51 /* Can hold one 'biosknobs'
52  * biosknobs has array of 'knob' */
53 using BiosBaseTableType = std::map<std::string, BiosBaseTableTypeEntry>;
54 
55 namespace knob
56 {
57 /* These are the operators we support in a 'depex' expression
58  * Note: We also support '_LIST_', 'Sif', 'Gif', 'Dif', and 'NOT'. But they are
59  * handeled sepeartely. */
60 enum class DepexOperators
61 {
62     unknown = 0,
63     OR,
64     AND,
65     GT,
66     GTE,
67     LTE,
68     LT,
69     EQU,
70     NEQ,
71     MODULO
72 };
73 
74 namespace option
75 {
76 /* Can hold one 'option' */
77 struct option
78 {
optionbios::knob::option::option79     option(std::string text, std::string value) :
80         text(std::move(text)), value(std::move(value))
81     {}
82 
83     std::string text;
84     std::string value;
85 };
86 } // namespace option
87 
88 /* Can hold one 'knob' */
89 struct knob
90 {
knobbios::knob::knob91     knob(std::string nameStr, std::string currentValStr, int currentVal,
92          std::string descriptionStr, std::string defaultStr,
93          std::string promptStr, std::string depexStr,
94          std::string& setupTypeStr) :
95         depex(false),
96         readOnly(("ReadOnly" == setupTypeStr) ? true : false),
97         currentVal(currentVal), nameStr(std::move(nameStr)),
98         currentValStr(std::move(currentValStr)),
99         descriptionStr(std::move(descriptionStr)),
100         defaultStr(std::move(defaultStr)), promptStr(std::move(promptStr)),
101         depexStr(std::move(depexStr))
102     {}
103 
104     bool depex;
105     bool readOnly;
106     int currentVal;
107 
108     std::string nameStr;
109     std::string currentValStr;
110     std::string descriptionStr;
111     std::string defaultStr;
112     std::string promptStr;
113     std::string depexStr;
114 
115     /* Can hold one 'options' */
116     std::vector<option::option> options;
117 };
118 } // namespace knob
119 
120 /* Class capable of computing 'depex' expression. */
121 class Depex
122 {
123   public:
Depex(std::vector<knob::knob> & knobs)124     Depex(std::vector<knob::knob>& knobs) : mKnobs(knobs) {}
125 
126     /* Compute 'depex' expression of all knobs in 'biosknobs'. */
compute()127     void compute()
128     {
129         mError.clear();
130 
131         for (auto& knob : mKnobs)
132         {
133             /* if 'depex' == "TRUE" no need to execute expression. */
134             if ("TRUE" == knob.depexStr)
135             {
136                 knob.depex = true;
137             }
138             else if (!knob.readOnly)
139             {
140                 int value = 0;
141 
142                 if (!evaluateExpression(knob.depexStr, value))
143                 {
144                     mError.emplace_back("bad depex: " + knob.depexStr +
145                                         " in knob: " + knob.nameStr);
146                 }
147                 else
148                 {
149                     if (value)
150                     {
151                         knob.depex = true;
152                     }
153                 }
154             }
155         }
156     }
157 
158     /* Returns the number of 'knob's which have a bad 'depex' expression. */
getErrorCount()159     size_t getErrorCount()
160     {
161         return mError.size();
162     }
163 
164     /* Prints all the 'knob's which have a bad 'depex' expression. */
printError()165     void printError()
166     {
167         for (auto& error : mError)
168         {
169             phosphor::logging::log<phosphor::logging::level::ERR>(
170                 error.c_str());
171         }
172     }
173 
174   private:
175     /* Returns 'true' if the argument string is a number. */
isNumber(const std::string & s)176     bool isNumber(const std::string& s)
177     {
178         return !s.empty() &&
179                std::find_if(s.begin(), s.end(), [](unsigned char c) {
180             return !std::isdigit(c);
181         }) == s.end();
182     }
183 
184     /* Returns 'true' if the argument string is hex representation of a number.
185      */
isHexNotation(const std::string & s)186     bool isHexNotation(const std::string& s)
187     {
188         return s.compare(0, 2, "0x") == 0 && s.size() > 2 &&
189                s.find_first_not_of("0123456789abcdefABCDEF", 2) ==
190                    std::string::npos;
191     }
192 
193     /* Function to find current value of a 'knob'
194      * search is done using 'knob' attribute 'name' */
getValue(std::string & variableName,int & value)195     bool getValue(std::string& variableName, int& value)
196     {
197         for (auto& knob : mKnobs)
198         {
199             if (knob.nameStr == variableName)
200             {
201                 value = knob.currentVal;
202                 return true;
203             }
204         }
205 
206         std::string error = "Unable to find knob: " + variableName +
207                             " in knob list\n";
208         phosphor::logging::log<phosphor::logging::level::ERR>(error.c_str());
209 
210         return false;
211     }
212 
213     /* Get the expression enclosed within brackets, i.e., between '(' and ')' */
getSubExpression(const std::string & expression,std::string & subExpression,size_t & i)214     bool getSubExpression(const std::string& expression,
215                           std::string& subExpression, size_t& i)
216     {
217         int level = 1;
218         subExpression.clear();
219 
220         for (; i < expression.length(); i++)
221         {
222             if (expression[i] == '(')
223             {
224                 ++level;
225             }
226             else if (expression[i] == ')')
227             {
228                 --level;
229                 if (level == 0)
230                 {
231                     break;
232                 }
233             }
234 
235             subExpression.push_back(expression[i]);
236         }
237 
238         if (!subExpression.empty())
239         {
240             return true;
241         }
242 
243         return false;
244     }
245 
246     /* Function to handle operator '_LIST_'
247      * Convert a '_LIST_' expression to a normal expression
248      * Example "_LIST_ VariableA _EQU_ 0 1" is converted to "VariableA _EQU_ 0
249      * OR VariableA _EQU_ 1" */
getListExpression(const std::string & expression,std::string & subExpression,size_t & i)250     bool getListExpression(const std::string& expression,
251                            std::string& subExpression, size_t& i)
252     {
253         subExpression.clear();
254 
255         int cnt = 0;
256         std::string variableStr;
257         std::string operatorStr;
258 
259         for (; i < expression.length(); i++)
260         {
261             if (expression[i] == '(')
262             {
263                 return false;
264             }
265             else if (expression[i] == ')')
266             {
267                 break;
268             }
269             else if (expression[i] == ' ')
270             {
271                 /* whitespace */
272                 continue;
273             }
274             else
275             {
276                 std::string word;
277 
278                 /* Get the next word in expression string */
279                 while ((i < expression.length()) && (expression[i] != ' '))
280                 {
281                     word.push_back(expression[i++]);
282                 }
283 
284                 if (word == "_OR_" || word == "OR" || word == "_AND_" ||
285                     word == "AND" || word == "NOT")
286                 {
287                     i = i - word.length();
288                     break;
289                 }
290 
291                 ++cnt;
292 
293                 if (cnt == 1)
294                 {
295                     variableStr = word;
296                 }
297                 else if (cnt == 2)
298                 {
299                     operatorStr = word;
300                 }
301                 else
302                 {
303                     if (cnt > 3)
304                     {
305                         if (operatorStr == "_EQU_" || operatorStr == "EQU")
306                         {
307                             subExpression += " OR ";
308                         }
309                         if (operatorStr == "_NEQ_" || operatorStr == "NEQ")
310                         {
311                             subExpression += " AND ";
312                         }
313                     }
314 
315                     subExpression += "( ";
316                     subExpression += variableStr;
317                     subExpression += " ";
318                     subExpression += operatorStr;
319                     subExpression += " ";
320                     subExpression += word;
321                     subExpression += " )";
322                 }
323             }
324         }
325 
326         if (!subExpression.empty())
327         {
328             return true;
329         }
330 
331         return false;
332     }
333 
334     /* Function to handle operator 'NOT'
335      * 1) Find the variable
336      * 2) apply NOT on the variable */
getNotValue(const std::string & expression,size_t & i,int & value)337     bool getNotValue(const std::string& expression, size_t& i, int& value)
338     {
339         std::string word;
340 
341         for (; i < expression.length(); i++)
342         {
343             if (expression[i] == ' ')
344             {
345                 /* whitespace */
346                 continue;
347             }
348             else
349             {
350                 /* Get the next word in expression string */
351                 while ((i < expression.length()) && (expression[i] != ' '))
352                 {
353                     word.push_back(expression[i++]);
354                 }
355 
356                 break;
357             }
358         }
359 
360         if (!word.empty())
361         {
362             if (getValue(word, value))
363             {
364                 value = !value;
365                 return true;
366             }
367         }
368 
369         return false;
370     }
371 
372     /* 1) Pop one operator from operator stack, example 'OR'
373      * 2) Pop two variable from variable stack, example VarA and VarB
374      * 3) Push back result of 'VarA OR VarB' to variable stack
375      * 4) Repeat till operator stack is empty
376      *
377      * The last variable in variable stack is the output of the expression. */
evaluateExprStack(std::stack<int> & values,std::stack<knob::DepexOperators> & operators,int & output)378     bool evaluateExprStack(std::stack<int>& values,
379                            std::stack<knob::DepexOperators>& operators,
380                            int& output)
381     {
382         if (values.size() != (operators.size() + 1))
383         {
384             return false;
385         }
386 
387         while (!operators.empty())
388         {
389             int b = values.top();
390             values.pop();
391 
392             int a = values.top();
393             values.pop();
394 
395             switch (operators.top())
396             {
397                 case knob::DepexOperators::OR:
398                     values.emplace(a | b);
399                     break;
400 
401                 case knob::DepexOperators::AND:
402                     values.emplace(a & b);
403                     break;
404 
405                 case knob::DepexOperators::EQU:
406                     if (a == b)
407                     {
408                         values.emplace(1);
409                         break;
410                     }
411 
412                     values.emplace(0);
413                     break;
414 
415                 case knob::DepexOperators::NEQ:
416                     if (a != b)
417                     {
418                         values.emplace(1);
419                         break;
420                     }
421 
422                     values.emplace(0);
423                     break;
424 
425                 case knob::DepexOperators::LTE:
426                     if (a <= b)
427                     {
428                         values.emplace(1);
429                         break;
430                     }
431 
432                     values.emplace(0);
433                     break;
434 
435                 case knob::DepexOperators::LT:
436                     if (a < b)
437                     {
438                         values.emplace(1);
439                         break;
440                     }
441 
442                     values.emplace(0);
443                     break;
444 
445                 case knob::DepexOperators::GTE:
446                     if (a >= b)
447                     {
448                         values.emplace(1);
449                         break;
450                     }
451 
452                     values.emplace(0);
453                     break;
454 
455                 case knob::DepexOperators::GT:
456                     if (a > b)
457                     {
458                         values.emplace(1);
459                         break;
460                     }
461 
462                     values.emplace(0);
463                     break;
464 
465                 case knob::DepexOperators::MODULO:
466                     if (b == 0)
467                     {
468                         return false;
469                     }
470                     values.emplace(a % b);
471                     break;
472 
473                 default:
474                     return false;
475             }
476 
477             operators.pop();
478         }
479 
480         if (values.size() == 1)
481         {
482             output = values.top();
483             values.pop();
484 
485             return true;
486         }
487 
488         return false;
489     }
490 
491     /* Evaluvate one 'depex' expression
492      * 1) Find a word in expression string
493      * 2) If word is a variable push to variable stack
494      * 3) If word is a operator push to operator stack
495      *
496      * Execute the stack at end to get the result of expression. */
evaluateExpression(const std::string & expression,int & output)497     bool evaluateExpression(const std::string& expression, int& output)
498     {
499         if (expression.empty())
500         {
501             return false;
502         }
503 
504         size_t i;
505         int value;
506         bool ifFormSetOperator = false;
507         std::stack<int> values;
508         std::stack<knob::DepexOperators> operators;
509         std::string subExpression;
510 
511         for (i = 0; i < expression.length(); i++)
512         {
513             if (expression[i] == ' ')
514             {
515                 /* whitespace */
516                 continue;
517             }
518             else
519             {
520                 std::string word;
521 
522                 /* Get the next word in expression string */
523                 while ((i < expression.length()) && (expression[i] != ' '))
524                 {
525                     word.push_back(expression[i++]);
526                 }
527 
528                 if (word == "_OR_" || word == "OR")
529                 {
530                     /* OR and AND has more precedence than other operators
531                      * To handle statements like "a != b or c != d"
532                      * we need to execute, for above example, both '!=' before
533                      * 'or' */
534                     if (!operators.empty())
535                     {
536                         if (!evaluateExprStack(values, operators, value))
537                         {
538                             return false;
539                         }
540 
541                         values.emplace(value);
542                     }
543 
544                     operators.emplace(knob::DepexOperators::OR);
545                 }
546                 else if (word == "_AND_" || word == "AND")
547                 {
548                     /* OR and AND has more precedence than other operators
549                      * To handle statements like "a == b and c == d"
550                      * we need to execute, for above example, both '==' before
551                      * 'and' */
552                     if (!operators.empty())
553                     {
554                         if (!evaluateExprStack(values, operators, value))
555                         {
556                             return false;
557                         }
558 
559                         values.emplace(value);
560                     }
561 
562                     operators.emplace(knob::DepexOperators::AND);
563                 }
564                 else if (word == "_LTE_")
565                 {
566                     operators.emplace(knob::DepexOperators::LTE);
567                 }
568                 else if (word == "_LT_")
569                 {
570                     operators.emplace(knob::DepexOperators::LT);
571                 }
572                 else if (word == "_GTE_")
573                 {
574                     operators.emplace(knob::DepexOperators::GTE);
575                 }
576                 else if (word == "_GT_")
577                 {
578                     operators.emplace(knob::DepexOperators::GT);
579                 }
580                 else if (word == "_NEQ_")
581                 {
582                     operators.emplace(knob::DepexOperators::NEQ);
583                 }
584                 else if (word == "_EQU_")
585                 {
586                     operators.emplace(knob::DepexOperators::EQU);
587                 }
588                 else if (word == "%")
589                 {
590                     operators.emplace(knob::DepexOperators::MODULO);
591                 }
592                 else
593                 {
594                     /* Handle 'Sif(', 'Gif(', 'Dif(' and '('
595                      * by taking the inner/sub expression and evaluating it */
596                     if (word.back() == '(')
597                     {
598                         if (word == "Sif(" || word == "Gif(" || word == "Dif(")
599                         {
600                             ifFormSetOperator = true;
601                         }
602                         if (!getSubExpression(expression, subExpression, i))
603                             break;
604 
605                         if (!evaluateExpression(subExpression, value))
606                             break;
607                     }
608                     else if (word == "_LIST_")
609                     {
610                         if (!getListExpression(expression, subExpression, i))
611                             break;
612 
613                         --i;
614 
615                         if (!evaluateExpression(subExpression, value))
616                             break;
617                     }
618                     else if (word == "NOT")
619                     {
620                         if (!getNotValue(expression, i, value))
621                             break;
622                     }
623                     else if (isNumber(word) || isHexNotation(word))
624                     {
625                         try
626                         {
627                             value = std::stoi(word, nullptr, 0);
628                         }
629                         catch (const std::exception& ex)
630                         {
631                             phosphor::logging::log<
632                                 phosphor::logging::level::ERR>(ex.what());
633                             return false;
634                         }
635                     }
636                     else
637                     {
638                         if (!getValue(word, value))
639                             break;
640                     }
641 
642                     /* 'Sif(', 'Gif(', 'Dif( == IF NOT,
643                     we have to negate the vaule */
644                     if (ifFormSetOperator == true)
645                     {
646                         value = !value;
647                     }
648                     values.emplace(value);
649                 }
650             }
651         }
652 
653         if (i == expression.length())
654         {
655             if (evaluateExprStack(values, operators, output))
656             {
657                 return true;
658             }
659         }
660 
661         return false;
662     }
663 
664   private:
665     /* To store all 'knob's in 'biosknobs' */
666     std::vector<knob::knob>& mKnobs;
667 
668     /* To store all bad 'depex' expression */
669     std::vector<std::string> mError;
670 };
671 
672 class Xml
673 {
674   public:
Xml(const char * filePath)675     Xml(const char* filePath) : mDepex(std::make_unique<Depex>(mKnobs))
676     {
677         if (!getKnobs(filePath))
678         {
679             std::string error = "Unable to get knobs in file: " +
680                                 std::string(filePath);
681             throw std::runtime_error(error);
682         }
683     }
684 
685     /* Fill Bios table with all 'knob's which have output of 'depex' expression
686      * as 'true' */
getBaseTable(bios::BiosBaseTableType & baseTable)687     bool getBaseTable(bios::BiosBaseTableType& baseTable)
688     {
689         baseTable.clear();
690 
691         for (auto& knob : mKnobs)
692         {
693             if (knob.depex)
694             {
695                 std::string text =
696                     "xyz.openbmc_project.BIOSConfig.Manager.BoundType.OneOf";
697                 bios::OptionTypeVector options;
698 
699                 for (auto& option : knob.options)
700                 {
701                     options.emplace_back(text, option.value, option.text);
702                 }
703 
704                 bios::BiosBaseTableTypeEntry baseTableEntry = std::make_tuple(
705                     "xyz.openbmc_project.BIOSConfig.Manager.AttributeType."
706                     "Enumeration",
707                     false, knob.promptStr, knob.descriptionStr, "./",
708                     knob.currentValStr, knob.defaultStr, options);
709 
710                 baseTable.emplace(knob.nameStr, baseTableEntry);
711             }
712         }
713 
714         if (!baseTable.empty())
715         {
716             return true;
717         }
718 
719         return false;
720     }
721 
722     /* Execute all 'depex' expression */
doDepexCompute()723     bool doDepexCompute()
724     {
725         mDepex->compute();
726 
727         if (mDepex->getErrorCount())
728         {
729             mDepex->printError();
730             return false;
731         }
732 
733         return true;
734     }
735 
736   private:
737     /* Get 'option' */
getOption(tinyxml2::XMLElement * pOption)738     void getOption(tinyxml2::XMLElement* pOption)
739     {
740         if (pOption)
741         {
742             std::string valueStr;
743             std::string textStr;
744 
745             if (pOption->Attribute("text"))
746                 valueStr = pOption->Attribute("text");
747 
748             if (pOption->Attribute("value"))
749                 textStr = pOption->Attribute("value");
750 
751             mKnobs.back().options.emplace_back(pOption->Attribute("text"),
752                                                pOption->Attribute("value"));
753         }
754     }
755 
756     /* Get 'options' */
getOptions(tinyxml2::XMLElement * pKnob)757     void getOptions(tinyxml2::XMLElement* pKnob)
758     {
759         uint16_t reserveCnt = 0;
760 
761         /* Get node options inside knob */
762         tinyxml2::XMLElement* pOptions = pKnob->FirstChildElement("options");
763 
764         if (pOptions)
765         {
766             for (tinyxml2::XMLElement* pOption =
767                      pOptions->FirstChildElement("option");
768                  pOption; pOption = pOption->NextSiblingElement("option"))
769             {
770                 ++reserveCnt;
771             }
772 
773             mKnobs.back().options.reserve(reserveCnt);
774 
775             /* Loop through all option inside options */
776             for (tinyxml2::XMLElement* pOption =
777                      pOptions->FirstChildElement("option");
778                  pOption; pOption = pOption->NextSiblingElement("option"))
779             {
780                 getOption(pOption);
781             }
782         }
783     }
784 
785     /* Get 'knob' */
getKnob(tinyxml2::XMLElement * pKnob)786     void getKnob(tinyxml2::XMLElement* pKnob)
787     {
788         if (pKnob)
789         {
790             int currentVal = 0;
791             std::string nameStr;
792             std::string currentValStr;
793             std::string descriptionStr;
794             std::string defaultStr;
795             std::string depexStr;
796             std::string promptStr;
797             std::string setupTypeStr;
798 
799             if (!pKnob->Attribute("name") || !pKnob->Attribute("CurrentVal"))
800             {
801                 return;
802             }
803 
804             nameStr = pKnob->Attribute("name");
805             currentValStr = pKnob->Attribute("CurrentVal");
806             std::stringstream ss;
807             ss << std::hex << currentValStr;
808             if (ss.good())
809             {
810                 ss >> currentVal;
811             }
812             else
813             {
814                 std::string error = "Invalid hex value input " + currentValStr +
815                                     " for " + nameStr + "\n";
816                 phosphor::logging::log<phosphor::logging::level::ERR>(
817                     error.c_str());
818                 return;
819             }
820             if (pKnob->Attribute("description"))
821                 descriptionStr = pKnob->Attribute("description");
822 
823             if (pKnob->Attribute("default"))
824                 defaultStr = pKnob->Attribute("default");
825 
826             if (pKnob->Attribute("depex"))
827                 depexStr = pKnob->Attribute("depex");
828 
829             if (pKnob->Attribute("prompt"))
830                 promptStr = pKnob->Attribute("prompt");
831 
832             if (pKnob->Attribute("setupType"))
833                 setupTypeStr = pKnob->Attribute("setupType");
834 
835             mKnobs.emplace_back(nameStr, currentValStr, currentVal,
836                                 descriptionStr, defaultStr, promptStr, depexStr,
837                                 setupTypeStr);
838 
839             getOptions(pKnob);
840         }
841     }
842 
843     /* Get 'biosknobs' */
getKnobs(const char * biosXmlFilePath)844     bool getKnobs(const char* biosXmlFilePath)
845     {
846         uint16_t reserveCnt = 0;
847 
848         mKnobs.clear();
849 
850         tinyxml2::XMLDocument biosXml;
851 
852         /* Load the XML file into the Doc instance */
853         biosXml.LoadFile(biosXmlFilePath);
854 
855         /* Get 'SYSTEM' */
856         tinyxml2::XMLElement* pRootElement = biosXml.RootElement();
857         if (pRootElement)
858         {
859             /* Get 'biosknobs' inside 'SYSTEM' */
860             tinyxml2::XMLElement* pBiosknobs =
861                 pRootElement->FirstChildElement("biosknobs");
862             if (pBiosknobs)
863             {
864                 for (tinyxml2::XMLElement* pKnob =
865                          pBiosknobs->FirstChildElement("knob");
866                      pKnob; pKnob = pKnob->NextSiblingElement("knob"))
867                 {
868                     ++reserveCnt;
869                 }
870 
871                 /* reserve before emplace_back will avoids realloc(s) */
872                 mKnobs.reserve(reserveCnt);
873 
874                 for (tinyxml2::XMLElement* pKnob =
875                          pBiosknobs->FirstChildElement("knob");
876                      pKnob; pKnob = pKnob->NextSiblingElement("knob"))
877                 {
878                     getKnob(pKnob);
879                 }
880             }
881         }
882 
883         if (!mKnobs.empty())
884         {
885             return true;
886         }
887 
888         return false;
889     }
890 
891   private:
892     /* To store all 'knob's in 'biosknobs' */
893     std::vector<knob::knob> mKnobs;
894 
895     /* Object of Depex class to compute 'depex' expression */
896     std::unique_ptr<Depex> mDepex;
897 };
898 } // namespace bios
899