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