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