biosxml: run dos2unix
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: Ie3977e37d069f676a2f84b498c49cda5bb9e00da
diff --git a/include/biosxml.hpp b/include/biosxml.hpp
index 573115d..39af77c 100644
--- a/include/biosxml.hpp
+++ b/include/biosxml.hpp
@@ -1,878 +1,878 @@
-#pragma once
-
-#include "tinyxml2.h"
-
-#include <phosphor-logging/elog-errors.hpp>
-#include <phosphor-logging/log.hpp>
-
-#include <map>
-#include <sstream>
-#include <stack>
-#include <string>
-#include <variant>
-#include <vector>
-
-namespace bios
-{
-/* Can hold one 'option'
- * For example
- * <option text="TIS" value="0x0"/>
- */
-using OptionType = std::tuple<std::string, std::variant<int64_t, std::string>>;
-
-/* Can hold one 'options'
- * For example
- * <options>
- * <option text="TIS" value="0x0"/>
- * <option text="PTP FIFO" value="0x1"/>
- * <option text="PTP CRB" value="0x2"/>
- * </options>
- */
-using OptionTypeVector = std::vector<OptionType>;
-
-/* Can hold one 'knob'
- * For example
- * <knob type="scalar" setupType="oneof" name="TpmDeviceInterfaceAttempt"
- * varstoreIndex="14" prompt="Attempt PTP TPM Device Interface"
- * description="Attempt PTP TPM Device Interface: PTP FIFO, PTP CRB" size="1"
- * offset="0x0005" depex="Sif( _LIST_ TpmDevice _EQU_ 0 1 ) _AND_ Sif(
- * TpmDeviceInterfacePtpFifoSupported _EQU_ 0 OR
- * TpmDeviceInterfacePtpCrbSupported _EQU_ 0 )" default="0x00"
- *CurrentVal="0x00"> <options> <option text="TIS" value="0x0"/> <option
- *text="PTP FIFO" value="0x1"/> <option text="PTP CRB" value="0x2"/>
- * </options>
- * </knob>
- */
-using BiosBaseTableTypeEntry =
- std::tuple<std::string, bool, std::string, std::string, std::string,
- std::variant<int64_t, std::string>,
- std::variant<int64_t, std::string>, OptionTypeVector>;
-
-/* Can hold one 'biosknobs'
- * biosknobs has array of 'knob' */
-using BiosBaseTableType = std::map<std::string, BiosBaseTableTypeEntry>;
-
-namespace knob
-{
-/* These are the operators we support in a 'depex' expression
- * Note: We also support '_LIST_', 'Sif', 'Gif', 'Dif', and 'NOT'. But they are
- * handeled sepeartely. */
-enum class DepexOperators
-{
- unknown = 0,
- OR,
- AND,
- GT,
- GTE,
- LTE,
- LT,
- EQU,
- NEQ,
- MODULO
-};
-
-namespace option
-{
-/* Can hold one 'option' */
-struct option
-{
- option(std::string text, std::string value) :
- text(std::move(text)), value(std::move(value))
- {}
-
- std::string text;
- std::string value;
-};
-} // namespace option
-
-/* Can hold one 'knob' */
-struct knob
-{
- knob(std::string nameStr, std::string currentValStr, int currentVal,
- std::string descriptionStr, std::string defaultStr,
- std::string promptStr, std::string depexStr,
- std::string& setupTypeStr) :
- nameStr(std::move(nameStr)),
- currentValStr(std::move(currentValStr)), currentVal(currentVal),
- descriptionStr(std::move(descriptionStr)),
- defaultStr(std::move(defaultStr)), promptStr(std::move(promptStr)),
- depexStr(std::move(depexStr)), depex(false),
- readOnly(("ReadOnly" == setupTypeStr) ? true : false)
- {}
-
- bool depex;
- bool readOnly;
- int currentVal;
-
- std::string nameStr;
- std::string currentValStr;
- std::string descriptionStr;
- std::string defaultStr;
- std::string promptStr;
- std::string depexStr;
-
- /* Can hold one 'options' */
- std::vector<option::option> options;
-};
-} // namespace knob
-
-/* Class capable of computing 'depex' expression. */
-class Depex
-{
- public:
- Depex(std::vector<knob::knob>& knobs) : mKnobs(knobs)
- {}
-
- /* Compute 'depex' expression of all knobs in 'biosknobs'. */
- void compute()
- {
- mError.clear();
-
- for (auto& knob : mKnobs)
- {
- /* if 'depex' == "TRUE" no need to execute expression. */
- if ("TRUE" == knob.depexStr)
- {
- knob.depex = true;
- }
- else if (!knob.readOnly)
- {
- int value = 0;
-
- if (!evaluateExpression(knob.depexStr, value))
- {
- mError.emplace_back("bad depex: " + knob.depexStr +
- " in knob: " + knob.nameStr);
- }
- else
- {
- if (value)
- {
- knob.depex = true;
- }
- }
- }
- }
- }
-
- /* Returns the number of 'knob's which have a bad 'depex' expression. */
- size_t getErrorCount()
- {
- return mError.size();
- }
-
- /* Prints all the 'knob's which have a bad 'depex' expression. */
- void printError()
- {
- for (auto& error : mError)
- {
- phosphor::logging::log<phosphor::logging::level::ERR>(
- error.c_str());
- }
- }
-
- private:
- /* Returns 'true' if the argument string is a number. */
- bool isNumber(const std::string& s)
- {
- return !s.empty() &&
- std::find_if(s.begin(), s.end(), [](unsigned char c) {
- return !std::isdigit(c);
- }) == s.end();
- }
-
- /* Returns 'true' if the argument string is hex representation of a number.
- */
- bool isHexNotation(std::string const& s)
- {
- return s.compare(0, 2, "0x") == 0 && s.size() > 2 &&
- s.find_first_not_of("0123456789abcdefABCDEF", 2) ==
- std::string::npos;
- }
-
- /* Function to find current value of a 'knob'
- * search is done using 'knob' attribute 'name' */
- bool getValue(std::string& variableName, int& value)
- {
- for (auto& knob : mKnobs)
- {
- if (knob.nameStr == variableName)
- {
- value = knob.currentVal;
- return true;
- }
- }
-
- std::string error =
- "Unable to find knob: " + variableName + " in knob list\n";
- phosphor::logging::log<phosphor::logging::level::ERR>(error.c_str());
-
- return false;
- }
-
- /* Get the expression enclosed within brackets, i.e., between '(' and ')' */
- bool getSubExpression(const std::string& expression,
- std::string& subExpression, size_t& i)
- {
- int level = 1;
- subExpression.clear();
-
- for (; i < expression.length(); i++)
- {
- if (expression[i] == '(')
- {
- ++level;
- }
- else if (expression[i] == ')')
- {
- --level;
- if (level == 0)
- {
- break;
- }
- }
-
- subExpression.push_back(expression[i]);
- }
-
- if (!subExpression.empty())
- {
- return true;
- }
-
- return false;
- }
-
- /* Function to handle operator '_LIST_'
- * Convert a '_LIST_' expression to a normal expression
- * Example "_LIST_ VariableA _EQU_ 0 1" is converted to "VariableA _EQU_ 0
- * OR VariableA _EQU_ 1" */
- bool getListExpression(const std::string& expression,
- std::string& subExpression, size_t& i)
- {
- subExpression.clear();
-
- int cnt = 0;
- std::string variableStr;
- std::string operatorStr;
-
- for (; i < expression.length(); i++)
- {
- if (expression[i] == '(')
- {
- return false;
- }
- else if (expression[i] == ')')
- {
- break;
- }
- else if (expression[i] == ' ')
- {
- /* whitespace */
- continue;
- }
- else
- {
- std::string word;
-
- /* Get the next word in expression string */
- while ((i < expression.length()) && (expression[i] != ' '))
- {
- word.push_back(expression[i++]);
- }
-
- if (word == "_OR_" || word == "OR" || word == "_AND_" ||
- word == "AND" || word == "NOT")
- {
- i = i - word.length();
- break;
- }
-
- ++cnt;
-
- if (cnt == 1)
- {
- variableStr = word;
- }
- else if (cnt == 2)
- {
- operatorStr = word;
- }
- else
- {
- if (cnt > 3)
- {
- subExpression += " OR ";
- }
-
- subExpression += "( ";
- subExpression += variableStr;
- subExpression += " ";
- subExpression += operatorStr;
- subExpression += " ";
- subExpression += word;
- subExpression += " )";
- }
- }
- }
-
- if (!subExpression.empty())
- {
- return true;
- }
-
- return false;
- }
-
- /* Function to handle operator 'NOT'
- * 1) Find the variable
- * 2) apply NOT on the variable */
- bool getNotValue(const std::string& expression, size_t& i, int& value)
- {
- std::string word;
-
- for (; i < expression.length(); i++)
- {
- if (expression[i] == ' ')
- {
- /* whitespace */
- continue;
- }
- else
- {
- /* Get the next word in expression string */
- while ((i < expression.length()) && (expression[i] != ' '))
- {
- word.push_back(expression[i++]);
- }
-
- break;
- }
- }
-
- if (!word.empty())
- {
- if (getValue(word, value))
- {
- value = !value;
- return true;
- }
- }
-
- return false;
- }
-
- /* 1) Pop one operator from operator stack, example 'OR'
- * 2) Pop two variable from variable stack, example VarA and VarB
- * 3) Push back result of 'VarA OR VarB' to variable stack
- * 4) Repeat till operator stack is empty
- *
- * The last variable in variable stack is the output of the expression. */
- bool evaluateExprStack(std::stack<int>& values,
- std::stack<knob::DepexOperators>& operators,
- int& output)
- {
- if (values.size() != (operators.size() + 1))
- {
- return false;
- }
-
- while (!operators.empty())
- {
- int b = values.top();
- values.pop();
-
- int a = values.top();
- values.pop();
-
- switch (operators.top())
- {
- case knob::DepexOperators::OR:
- values.emplace(a | b);
- break;
-
- case knob::DepexOperators::AND:
- values.emplace(a & b);
- break;
-
- case knob::DepexOperators::EQU:
- if (a == b)
- {
- values.emplace(1);
- break;
- }
-
- values.emplace(0);
- break;
-
- case knob::DepexOperators::NEQ:
- if (a != b)
- {
- values.emplace(1);
- break;
- }
-
- values.emplace(0);
- break;
-
- case knob::DepexOperators::LTE:
- if (a <= b)
- {
- values.emplace(1);
- break;
- }
-
- values.emplace(0);
- break;
-
- case knob::DepexOperators::LT:
- if (a < b)
- {
- values.emplace(1);
- break;
- }
-
- values.emplace(0);
- break;
-
- case knob::DepexOperators::GTE:
- if (a >= b)
- {
- values.emplace(1);
- break;
- }
-
- values.emplace(0);
- break;
-
- case knob::DepexOperators::GT:
- if (a > b)
- {
- values.emplace(1);
- break;
- }
-
- values.emplace(0);
- break;
-
- case knob::DepexOperators::MODULO:
- if (b == 0)
- {
- return false;
- }
- values.emplace(a % b);
- break;
-
- default:
- return false;
- }
-
- operators.pop();
- }
-
- if (values.size() == 1)
- {
- output = values.top();
- values.pop();
-
- return true;
- }
-
- return false;
- }
-
- /* Evaluvate one 'depex' expression
- * 1) Find a word in expression string
- * 2) If word is a variable push to variable stack
- * 3) If word is a operator push to operator stack
- *
- * Execute the stack at end to get the result of expression. */
- bool evaluateExpression(const std::string& expression, int& output)
- {
- if (expression.empty())
- {
- return false;
- }
-
- size_t i;
- int value;
- std::stack<int> values;
- std::stack<knob::DepexOperators> operators;
- std::string subExpression;
-
- for (i = 0; i < expression.length(); i++)
- {
- if (expression[i] == ' ')
- {
- /* whitespace */
- continue;
- }
- else
- {
- std::string word;
-
- /* Get the next word in expression string */
- while ((i < expression.length()) && (expression[i] != ' '))
- {
- word.push_back(expression[i++]);
- }
-
- if (word == "_OR_" || word == "OR")
- {
- /* OR and AND has more precedence than other operators
- * To handle statements like "a != b or c != d"
- * we need to execute, for above example, both '!=' before
- * 'or' */
- if (!operators.empty())
- {
- if (!evaluateExprStack(values, operators, value))
- {
- return false;
- }
-
- values.emplace(value);
- }
-
- operators.emplace(knob::DepexOperators::OR);
- }
- else if (word == "_AND_" || word == "AND")
- {
- /* OR and AND has more precedence than other operators
- * To handle statements like "a == b and c == d"
- * we need to execute, for above example, both '==' before
- * 'and' */
- if (!operators.empty())
- {
- if (!evaluateExprStack(values, operators, value))
- {
- return false;
- }
-
- values.emplace(value);
- }
-
- operators.emplace(knob::DepexOperators::AND);
- }
- else if (word == "_LTE_")
- {
- operators.emplace(knob::DepexOperators::LTE);
- }
- else if (word == "_LT_")
- {
- operators.emplace(knob::DepexOperators::LT);
- }
- else if (word == "_GTE_")
- {
- operators.emplace(knob::DepexOperators::GTE);
- }
- else if (word == "_GT_")
- {
- operators.emplace(knob::DepexOperators::GT);
- }
- else if (word == "_NEQ_")
- {
- operators.emplace(knob::DepexOperators::NEQ);
- }
- else if (word == "_EQU_")
- {
- operators.emplace(knob::DepexOperators::EQU);
- }
- else if (word == "%")
- {
- operators.emplace(knob::DepexOperators::MODULO);
- }
- else
- {
- /* Handle 'Sif(', 'Gif(', 'Dif(' and '('
- * by taking the inner/sub expression and evaluating it */
- if (word.back() == '(')
- {
- if (!getSubExpression(expression, subExpression, i))
- break;
-
- if (!evaluateExpression(subExpression, value))
- break;
- }
- else if (word == "_LIST_")
- {
- if (!getListExpression(expression, subExpression, i))
- break;
-
- --i;
-
- if (!evaluateExpression(subExpression, value))
- break;
- }
- else if (word == "NOT")
- {
- if (!getNotValue(expression, i, value))
- break;
- }
- else if (isNumber(word) || isHexNotation(word))
- {
- try
- {
- value = std::stoi(word);
- }
- catch (std::exception& ex)
- {
- phosphor::logging::log<
- phosphor::logging::level::ERR>(ex.what());
- return false;
- }
- }
- else
- {
- if (!getValue(word, value))
- break;
- }
-
- values.emplace(value);
- }
- }
- }
-
- if (i == expression.length())
- {
- if (evaluateExprStack(values, operators, output))
- {
- return true;
- }
- }
-
- return false;
- }
-
- private:
- /* To store all 'knob's in 'biosknobs' */
- std::vector<knob::knob>& mKnobs;
-
- /* To store all bad 'depex' expression */
- std::vector<std::string> mError;
-};
-
-class Xml
-{
- public:
- Xml(const char* filePath) : mDepex(std::make_unique<Depex>(mKnobs))
- {
- if (!getKnobs(filePath))
- {
- std::string error =
- "Unable to get knobs in file: " + std::string(filePath);
- throw std::runtime_error(error);
- }
- }
-
- /* Fill Bios table with all 'knob's which have output of 'depex' expression
- * as 'true' */
- bool getBaseTable(bios::BiosBaseTableType& baseTable)
- {
- baseTable.clear();
-
- for (auto& knob : mKnobs)
- {
- if (knob.depex)
- {
- std::string text =
- "xyz.openbmc_project.BIOSConfig.Manager.BoundType.OneOf";
- bios::OptionTypeVector options;
-
- for (auto& option : knob.options)
- {
- options.emplace_back(text, option.value);
- }
-
- bios::BiosBaseTableTypeEntry baseTableEntry = std::make_tuple(
- "xyz.openbmc_project.BIOSConfig.Manager.AttributeType."
- "String",
- false, knob.nameStr, knob.descriptionStr, "./",
- knob.currentValStr, knob.defaultStr, options);
-
- baseTable.emplace(knob.nameStr, baseTableEntry);
- }
- }
-
- if (!baseTable.empty())
- {
- return true;
- }
-
- return false;
- }
-
- /* Execute all 'depex' expression */
- bool doDepexCompute()
- {
- mDepex->compute();
-
- if (mDepex->getErrorCount())
- {
- mDepex->printError();
- return false;
- }
-
- return true;
- }
-
- private:
- /* Get 'option' */
- void getOption(tinyxml2::XMLElement* pOption)
- {
- if (pOption)
- {
- std::string valueStr;
- std::string textStr;
-
- if (pOption->Attribute("text"))
- valueStr = pOption->Attribute("text");
-
- if (pOption->Attribute("value"))
- textStr = pOption->Attribute("value");
-
- mKnobs.back().options.emplace_back(pOption->Attribute("text"),
- pOption->Attribute("value"));
- }
- }
-
- /* Get 'options' */
- void getOptions(tinyxml2::XMLElement* pKnob)
- {
- uint16_t reserveCnt = 0;
-
- /* Get node options inside knob */
- tinyxml2::XMLElement* pOptions = pKnob->FirstChildElement("options");
-
- if (pOptions)
- {
- for (tinyxml2::XMLElement* pOption =
- pOptions->FirstChildElement("option");
- pOption; pOption = pOption->NextSiblingElement("option"))
- {
- ++reserveCnt;
- }
-
- mKnobs.back().options.reserve(reserveCnt);
-
- /* Loop through all option inside options */
- for (tinyxml2::XMLElement* pOption =
- pOptions->FirstChildElement("option");
- pOption; pOption = pOption->NextSiblingElement("option"))
- {
- getOption(pOption);
- }
- }
- }
-
- /* Get 'knob' */
- void getKnob(tinyxml2::XMLElement* pKnob)
- {
- if (pKnob)
- {
- int currentVal = 0;
- std::string nameStr;
- std::string currentValStr;
- std::string descriptionStr;
- std::string defaultStr;
- std::string depexStr;
- std::string promptStr;
- std::string setupTypeStr;
-
- if (!pKnob->Attribute("name") || !pKnob->Attribute("CurrentVal"))
- {
- return;
- }
-
- nameStr = pKnob->Attribute("name");
- currentValStr = pKnob->Attribute("CurrentVal");
-
- try
- {
- currentVal = std::stoi(currentValStr);
- }
- catch (std::exception& ex)
- {
- phosphor::logging::log<phosphor::logging::level::ERR>(
- ex.what());
- return;
- }
-
- if (pKnob->Attribute("description"))
- descriptionStr = pKnob->Attribute("description");
-
- if (pKnob->Attribute("default"))
- defaultStr = pKnob->Attribute("default");
-
- if (pKnob->Attribute("depex"))
- depexStr = pKnob->Attribute("depex");
-
- if (pKnob->Attribute("prompt"))
- promptStr = pKnob->Attribute("prompt");
-
- if (pKnob->Attribute("setupType"))
- setupTypeStr = pKnob->Attribute("setupType");
-
- mKnobs.emplace_back(nameStr, currentValStr, currentVal,
- descriptionStr, defaultStr, promptStr, depexStr,
- setupTypeStr);
-
- getOptions(pKnob);
- }
- }
-
- /* Get 'biosknobs' */
- bool getKnobs(const char* biosXmlFilePath)
- {
- uint16_t reserveCnt = 0;
-
- mKnobs.clear();
-
- tinyxml2::XMLDocument biosXml;
-
- /* Load the XML file into the Doc instance */
- biosXml.LoadFile(biosXmlFilePath);
-
- /* Get 'SYSTEM' */
- tinyxml2::XMLElement* pRootElement = biosXml.RootElement();
- if (pRootElement)
- {
- /* Get 'biosknobs' inside 'SYSTEM' */
- tinyxml2::XMLElement* pBiosknobs =
- pRootElement->FirstChildElement("biosknobs");
- if (pBiosknobs)
- {
- for (tinyxml2::XMLElement* pKnob =
- pBiosknobs->FirstChildElement("knob");
- pKnob; pKnob = pKnob->NextSiblingElement("knob"))
- {
- ++reserveCnt;
- }
-
- /* reserve before emplace_back will avoids realloc(s) */
- mKnobs.reserve(reserveCnt);
-
- for (tinyxml2::XMLElement* pKnob =
- pBiosknobs->FirstChildElement("knob");
- pKnob; pKnob = pKnob->NextSiblingElement("knob"))
- {
- getKnob(pKnob);
- }
- }
- }
-
- if (!mKnobs.empty())
- {
- return true;
- }
-
- return false;
- }
-
- private:
- /* To store all 'knob's in 'biosknobs' */
- std::vector<knob::knob> mKnobs;
-
- /* Object of Depex class to compute 'depex' expression */
- std::unique_ptr<Depex> mDepex;
-};
-} // namespace bios
+#pragma once
+
+#include "tinyxml2.h"
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+
+#include <map>
+#include <sstream>
+#include <stack>
+#include <string>
+#include <variant>
+#include <vector>
+
+namespace bios
+{
+/* Can hold one 'option'
+ * For example
+ * <option text="TIS" value="0x0"/>
+ */
+using OptionType = std::tuple<std::string, std::variant<int64_t, std::string>>;
+
+/* Can hold one 'options'
+ * For example
+ * <options>
+ * <option text="TIS" value="0x0"/>
+ * <option text="PTP FIFO" value="0x1"/>
+ * <option text="PTP CRB" value="0x2"/>
+ * </options>
+ */
+using OptionTypeVector = std::vector<OptionType>;
+
+/* Can hold one 'knob'
+ * For example
+ * <knob type="scalar" setupType="oneof" name="TpmDeviceInterfaceAttempt"
+ * varstoreIndex="14" prompt="Attempt PTP TPM Device Interface"
+ * description="Attempt PTP TPM Device Interface: PTP FIFO, PTP CRB" size="1"
+ * offset="0x0005" depex="Sif( _LIST_ TpmDevice _EQU_ 0 1 ) _AND_ Sif(
+ * TpmDeviceInterfacePtpFifoSupported _EQU_ 0 OR
+ * TpmDeviceInterfacePtpCrbSupported _EQU_ 0 )" default="0x00"
+ *CurrentVal="0x00"> <options> <option text="TIS" value="0x0"/> <option
+ *text="PTP FIFO" value="0x1"/> <option text="PTP CRB" value="0x2"/>
+ * </options>
+ * </knob>
+ */
+using BiosBaseTableTypeEntry =
+ std::tuple<std::string, bool, std::string, std::string, std::string,
+ std::variant<int64_t, std::string>,
+ std::variant<int64_t, std::string>, OptionTypeVector>;
+
+/* Can hold one 'biosknobs'
+ * biosknobs has array of 'knob' */
+using BiosBaseTableType = std::map<std::string, BiosBaseTableTypeEntry>;
+
+namespace knob
+{
+/* These are the operators we support in a 'depex' expression
+ * Note: We also support '_LIST_', 'Sif', 'Gif', 'Dif', and 'NOT'. But they are
+ * handeled sepeartely. */
+enum class DepexOperators
+{
+ unknown = 0,
+ OR,
+ AND,
+ GT,
+ GTE,
+ LTE,
+ LT,
+ EQU,
+ NEQ,
+ MODULO
+};
+
+namespace option
+{
+/* Can hold one 'option' */
+struct option
+{
+ option(std::string text, std::string value) :
+ text(std::move(text)), value(std::move(value))
+ {}
+
+ std::string text;
+ std::string value;
+};
+} // namespace option
+
+/* Can hold one 'knob' */
+struct knob
+{
+ knob(std::string nameStr, std::string currentValStr, int currentVal,
+ std::string descriptionStr, std::string defaultStr,
+ std::string promptStr, std::string depexStr,
+ std::string& setupTypeStr) :
+ nameStr(std::move(nameStr)),
+ currentValStr(std::move(currentValStr)), currentVal(currentVal),
+ descriptionStr(std::move(descriptionStr)),
+ defaultStr(std::move(defaultStr)), promptStr(std::move(promptStr)),
+ depexStr(std::move(depexStr)), depex(false),
+ readOnly(("ReadOnly" == setupTypeStr) ? true : false)
+ {}
+
+ bool depex;
+ bool readOnly;
+ int currentVal;
+
+ std::string nameStr;
+ std::string currentValStr;
+ std::string descriptionStr;
+ std::string defaultStr;
+ std::string promptStr;
+ std::string depexStr;
+
+ /* Can hold one 'options' */
+ std::vector<option::option> options;
+};
+} // namespace knob
+
+/* Class capable of computing 'depex' expression. */
+class Depex
+{
+ public:
+ Depex(std::vector<knob::knob>& knobs) : mKnobs(knobs)
+ {}
+
+ /* Compute 'depex' expression of all knobs in 'biosknobs'. */
+ void compute()
+ {
+ mError.clear();
+
+ for (auto& knob : mKnobs)
+ {
+ /* if 'depex' == "TRUE" no need to execute expression. */
+ if ("TRUE" == knob.depexStr)
+ {
+ knob.depex = true;
+ }
+ else if (!knob.readOnly)
+ {
+ int value = 0;
+
+ if (!evaluateExpression(knob.depexStr, value))
+ {
+ mError.emplace_back("bad depex: " + knob.depexStr +
+ " in knob: " + knob.nameStr);
+ }
+ else
+ {
+ if (value)
+ {
+ knob.depex = true;
+ }
+ }
+ }
+ }
+ }
+
+ /* Returns the number of 'knob's which have a bad 'depex' expression. */
+ size_t getErrorCount()
+ {
+ return mError.size();
+ }
+
+ /* Prints all the 'knob's which have a bad 'depex' expression. */
+ void printError()
+ {
+ for (auto& error : mError)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ error.c_str());
+ }
+ }
+
+ private:
+ /* Returns 'true' if the argument string is a number. */
+ bool isNumber(const std::string& s)
+ {
+ return !s.empty() &&
+ std::find_if(s.begin(), s.end(), [](unsigned char c) {
+ return !std::isdigit(c);
+ }) == s.end();
+ }
+
+ /* Returns 'true' if the argument string is hex representation of a number.
+ */
+ bool isHexNotation(std::string const& s)
+ {
+ return s.compare(0, 2, "0x") == 0 && s.size() > 2 &&
+ s.find_first_not_of("0123456789abcdefABCDEF", 2) ==
+ std::string::npos;
+ }
+
+ /* Function to find current value of a 'knob'
+ * search is done using 'knob' attribute 'name' */
+ bool getValue(std::string& variableName, int& value)
+ {
+ for (auto& knob : mKnobs)
+ {
+ if (knob.nameStr == variableName)
+ {
+ value = knob.currentVal;
+ return true;
+ }
+ }
+
+ std::string error =
+ "Unable to find knob: " + variableName + " in knob list\n";
+ phosphor::logging::log<phosphor::logging::level::ERR>(error.c_str());
+
+ return false;
+ }
+
+ /* Get the expression enclosed within brackets, i.e., between '(' and ')' */
+ bool getSubExpression(const std::string& expression,
+ std::string& subExpression, size_t& i)
+ {
+ int level = 1;
+ subExpression.clear();
+
+ for (; i < expression.length(); i++)
+ {
+ if (expression[i] == '(')
+ {
+ ++level;
+ }
+ else if (expression[i] == ')')
+ {
+ --level;
+ if (level == 0)
+ {
+ break;
+ }
+ }
+
+ subExpression.push_back(expression[i]);
+ }
+
+ if (!subExpression.empty())
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /* Function to handle operator '_LIST_'
+ * Convert a '_LIST_' expression to a normal expression
+ * Example "_LIST_ VariableA _EQU_ 0 1" is converted to "VariableA _EQU_ 0
+ * OR VariableA _EQU_ 1" */
+ bool getListExpression(const std::string& expression,
+ std::string& subExpression, size_t& i)
+ {
+ subExpression.clear();
+
+ int cnt = 0;
+ std::string variableStr;
+ std::string operatorStr;
+
+ for (; i < expression.length(); i++)
+ {
+ if (expression[i] == '(')
+ {
+ return false;
+ }
+ else if (expression[i] == ')')
+ {
+ break;
+ }
+ else if (expression[i] == ' ')
+ {
+ /* whitespace */
+ continue;
+ }
+ else
+ {
+ std::string word;
+
+ /* Get the next word in expression string */
+ while ((i < expression.length()) && (expression[i] != ' '))
+ {
+ word.push_back(expression[i++]);
+ }
+
+ if (word == "_OR_" || word == "OR" || word == "_AND_" ||
+ word == "AND" || word == "NOT")
+ {
+ i = i - word.length();
+ break;
+ }
+
+ ++cnt;
+
+ if (cnt == 1)
+ {
+ variableStr = word;
+ }
+ else if (cnt == 2)
+ {
+ operatorStr = word;
+ }
+ else
+ {
+ if (cnt > 3)
+ {
+ subExpression += " OR ";
+ }
+
+ subExpression += "( ";
+ subExpression += variableStr;
+ subExpression += " ";
+ subExpression += operatorStr;
+ subExpression += " ";
+ subExpression += word;
+ subExpression += " )";
+ }
+ }
+ }
+
+ if (!subExpression.empty())
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /* Function to handle operator 'NOT'
+ * 1) Find the variable
+ * 2) apply NOT on the variable */
+ bool getNotValue(const std::string& expression, size_t& i, int& value)
+ {
+ std::string word;
+
+ for (; i < expression.length(); i++)
+ {
+ if (expression[i] == ' ')
+ {
+ /* whitespace */
+ continue;
+ }
+ else
+ {
+ /* Get the next word in expression string */
+ while ((i < expression.length()) && (expression[i] != ' '))
+ {
+ word.push_back(expression[i++]);
+ }
+
+ break;
+ }
+ }
+
+ if (!word.empty())
+ {
+ if (getValue(word, value))
+ {
+ value = !value;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /* 1) Pop one operator from operator stack, example 'OR'
+ * 2) Pop two variable from variable stack, example VarA and VarB
+ * 3) Push back result of 'VarA OR VarB' to variable stack
+ * 4) Repeat till operator stack is empty
+ *
+ * The last variable in variable stack is the output of the expression. */
+ bool evaluateExprStack(std::stack<int>& values,
+ std::stack<knob::DepexOperators>& operators,
+ int& output)
+ {
+ if (values.size() != (operators.size() + 1))
+ {
+ return false;
+ }
+
+ while (!operators.empty())
+ {
+ int b = values.top();
+ values.pop();
+
+ int a = values.top();
+ values.pop();
+
+ switch (operators.top())
+ {
+ case knob::DepexOperators::OR:
+ values.emplace(a | b);
+ break;
+
+ case knob::DepexOperators::AND:
+ values.emplace(a & b);
+ break;
+
+ case knob::DepexOperators::EQU:
+ if (a == b)
+ {
+ values.emplace(1);
+ break;
+ }
+
+ values.emplace(0);
+ break;
+
+ case knob::DepexOperators::NEQ:
+ if (a != b)
+ {
+ values.emplace(1);
+ break;
+ }
+
+ values.emplace(0);
+ break;
+
+ case knob::DepexOperators::LTE:
+ if (a <= b)
+ {
+ values.emplace(1);
+ break;
+ }
+
+ values.emplace(0);
+ break;
+
+ case knob::DepexOperators::LT:
+ if (a < b)
+ {
+ values.emplace(1);
+ break;
+ }
+
+ values.emplace(0);
+ break;
+
+ case knob::DepexOperators::GTE:
+ if (a >= b)
+ {
+ values.emplace(1);
+ break;
+ }
+
+ values.emplace(0);
+ break;
+
+ case knob::DepexOperators::GT:
+ if (a > b)
+ {
+ values.emplace(1);
+ break;
+ }
+
+ values.emplace(0);
+ break;
+
+ case knob::DepexOperators::MODULO:
+ if (b == 0)
+ {
+ return false;
+ }
+ values.emplace(a % b);
+ break;
+
+ default:
+ return false;
+ }
+
+ operators.pop();
+ }
+
+ if (values.size() == 1)
+ {
+ output = values.top();
+ values.pop();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /* Evaluvate one 'depex' expression
+ * 1) Find a word in expression string
+ * 2) If word is a variable push to variable stack
+ * 3) If word is a operator push to operator stack
+ *
+ * Execute the stack at end to get the result of expression. */
+ bool evaluateExpression(const std::string& expression, int& output)
+ {
+ if (expression.empty())
+ {
+ return false;
+ }
+
+ size_t i;
+ int value;
+ std::stack<int> values;
+ std::stack<knob::DepexOperators> operators;
+ std::string subExpression;
+
+ for (i = 0; i < expression.length(); i++)
+ {
+ if (expression[i] == ' ')
+ {
+ /* whitespace */
+ continue;
+ }
+ else
+ {
+ std::string word;
+
+ /* Get the next word in expression string */
+ while ((i < expression.length()) && (expression[i] != ' '))
+ {
+ word.push_back(expression[i++]);
+ }
+
+ if (word == "_OR_" || word == "OR")
+ {
+ /* OR and AND has more precedence than other operators
+ * To handle statements like "a != b or c != d"
+ * we need to execute, for above example, both '!=' before
+ * 'or' */
+ if (!operators.empty())
+ {
+ if (!evaluateExprStack(values, operators, value))
+ {
+ return false;
+ }
+
+ values.emplace(value);
+ }
+
+ operators.emplace(knob::DepexOperators::OR);
+ }
+ else if (word == "_AND_" || word == "AND")
+ {
+ /* OR and AND has more precedence than other operators
+ * To handle statements like "a == b and c == d"
+ * we need to execute, for above example, both '==' before
+ * 'and' */
+ if (!operators.empty())
+ {
+ if (!evaluateExprStack(values, operators, value))
+ {
+ return false;
+ }
+
+ values.emplace(value);
+ }
+
+ operators.emplace(knob::DepexOperators::AND);
+ }
+ else if (word == "_LTE_")
+ {
+ operators.emplace(knob::DepexOperators::LTE);
+ }
+ else if (word == "_LT_")
+ {
+ operators.emplace(knob::DepexOperators::LT);
+ }
+ else if (word == "_GTE_")
+ {
+ operators.emplace(knob::DepexOperators::GTE);
+ }
+ else if (word == "_GT_")
+ {
+ operators.emplace(knob::DepexOperators::GT);
+ }
+ else if (word == "_NEQ_")
+ {
+ operators.emplace(knob::DepexOperators::NEQ);
+ }
+ else if (word == "_EQU_")
+ {
+ operators.emplace(knob::DepexOperators::EQU);
+ }
+ else if (word == "%")
+ {
+ operators.emplace(knob::DepexOperators::MODULO);
+ }
+ else
+ {
+ /* Handle 'Sif(', 'Gif(', 'Dif(' and '('
+ * by taking the inner/sub expression and evaluating it */
+ if (word.back() == '(')
+ {
+ if (!getSubExpression(expression, subExpression, i))
+ break;
+
+ if (!evaluateExpression(subExpression, value))
+ break;
+ }
+ else if (word == "_LIST_")
+ {
+ if (!getListExpression(expression, subExpression, i))
+ break;
+
+ --i;
+
+ if (!evaluateExpression(subExpression, value))
+ break;
+ }
+ else if (word == "NOT")
+ {
+ if (!getNotValue(expression, i, value))
+ break;
+ }
+ else if (isNumber(word) || isHexNotation(word))
+ {
+ try
+ {
+ value = std::stoi(word);
+ }
+ catch (std::exception& ex)
+ {
+ phosphor::logging::log<
+ phosphor::logging::level::ERR>(ex.what());
+ return false;
+ }
+ }
+ else
+ {
+ if (!getValue(word, value))
+ break;
+ }
+
+ values.emplace(value);
+ }
+ }
+ }
+
+ if (i == expression.length())
+ {
+ if (evaluateExprStack(values, operators, output))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private:
+ /* To store all 'knob's in 'biosknobs' */
+ std::vector<knob::knob>& mKnobs;
+
+ /* To store all bad 'depex' expression */
+ std::vector<std::string> mError;
+};
+
+class Xml
+{
+ public:
+ Xml(const char* filePath) : mDepex(std::make_unique<Depex>(mKnobs))
+ {
+ if (!getKnobs(filePath))
+ {
+ std::string error =
+ "Unable to get knobs in file: " + std::string(filePath);
+ throw std::runtime_error(error);
+ }
+ }
+
+ /* Fill Bios table with all 'knob's which have output of 'depex' expression
+ * as 'true' */
+ bool getBaseTable(bios::BiosBaseTableType& baseTable)
+ {
+ baseTable.clear();
+
+ for (auto& knob : mKnobs)
+ {
+ if (knob.depex)
+ {
+ std::string text =
+ "xyz.openbmc_project.BIOSConfig.Manager.BoundType.OneOf";
+ bios::OptionTypeVector options;
+
+ for (auto& option : knob.options)
+ {
+ options.emplace_back(text, option.value);
+ }
+
+ bios::BiosBaseTableTypeEntry baseTableEntry = std::make_tuple(
+ "xyz.openbmc_project.BIOSConfig.Manager.AttributeType."
+ "String",
+ false, knob.nameStr, knob.descriptionStr, "./",
+ knob.currentValStr, knob.defaultStr, options);
+
+ baseTable.emplace(knob.nameStr, baseTableEntry);
+ }
+ }
+
+ if (!baseTable.empty())
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /* Execute all 'depex' expression */
+ bool doDepexCompute()
+ {
+ mDepex->compute();
+
+ if (mDepex->getErrorCount())
+ {
+ mDepex->printError();
+ return false;
+ }
+
+ return true;
+ }
+
+ private:
+ /* Get 'option' */
+ void getOption(tinyxml2::XMLElement* pOption)
+ {
+ if (pOption)
+ {
+ std::string valueStr;
+ std::string textStr;
+
+ if (pOption->Attribute("text"))
+ valueStr = pOption->Attribute("text");
+
+ if (pOption->Attribute("value"))
+ textStr = pOption->Attribute("value");
+
+ mKnobs.back().options.emplace_back(pOption->Attribute("text"),
+ pOption->Attribute("value"));
+ }
+ }
+
+ /* Get 'options' */
+ void getOptions(tinyxml2::XMLElement* pKnob)
+ {
+ uint16_t reserveCnt = 0;
+
+ /* Get node options inside knob */
+ tinyxml2::XMLElement* pOptions = pKnob->FirstChildElement("options");
+
+ if (pOptions)
+ {
+ for (tinyxml2::XMLElement* pOption =
+ pOptions->FirstChildElement("option");
+ pOption; pOption = pOption->NextSiblingElement("option"))
+ {
+ ++reserveCnt;
+ }
+
+ mKnobs.back().options.reserve(reserveCnt);
+
+ /* Loop through all option inside options */
+ for (tinyxml2::XMLElement* pOption =
+ pOptions->FirstChildElement("option");
+ pOption; pOption = pOption->NextSiblingElement("option"))
+ {
+ getOption(pOption);
+ }
+ }
+ }
+
+ /* Get 'knob' */
+ void getKnob(tinyxml2::XMLElement* pKnob)
+ {
+ if (pKnob)
+ {
+ int currentVal = 0;
+ std::string nameStr;
+ std::string currentValStr;
+ std::string descriptionStr;
+ std::string defaultStr;
+ std::string depexStr;
+ std::string promptStr;
+ std::string setupTypeStr;
+
+ if (!pKnob->Attribute("name") || !pKnob->Attribute("CurrentVal"))
+ {
+ return;
+ }
+
+ nameStr = pKnob->Attribute("name");
+ currentValStr = pKnob->Attribute("CurrentVal");
+
+ try
+ {
+ currentVal = std::stoi(currentValStr);
+ }
+ catch (std::exception& ex)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ ex.what());
+ return;
+ }
+
+ if (pKnob->Attribute("description"))
+ descriptionStr = pKnob->Attribute("description");
+
+ if (pKnob->Attribute("default"))
+ defaultStr = pKnob->Attribute("default");
+
+ if (pKnob->Attribute("depex"))
+ depexStr = pKnob->Attribute("depex");
+
+ if (pKnob->Attribute("prompt"))
+ promptStr = pKnob->Attribute("prompt");
+
+ if (pKnob->Attribute("setupType"))
+ setupTypeStr = pKnob->Attribute("setupType");
+
+ mKnobs.emplace_back(nameStr, currentValStr, currentVal,
+ descriptionStr, defaultStr, promptStr, depexStr,
+ setupTypeStr);
+
+ getOptions(pKnob);
+ }
+ }
+
+ /* Get 'biosknobs' */
+ bool getKnobs(const char* biosXmlFilePath)
+ {
+ uint16_t reserveCnt = 0;
+
+ mKnobs.clear();
+
+ tinyxml2::XMLDocument biosXml;
+
+ /* Load the XML file into the Doc instance */
+ biosXml.LoadFile(biosXmlFilePath);
+
+ /* Get 'SYSTEM' */
+ tinyxml2::XMLElement* pRootElement = biosXml.RootElement();
+ if (pRootElement)
+ {
+ /* Get 'biosknobs' inside 'SYSTEM' */
+ tinyxml2::XMLElement* pBiosknobs =
+ pRootElement->FirstChildElement("biosknobs");
+ if (pBiosknobs)
+ {
+ for (tinyxml2::XMLElement* pKnob =
+ pBiosknobs->FirstChildElement("knob");
+ pKnob; pKnob = pKnob->NextSiblingElement("knob"))
+ {
+ ++reserveCnt;
+ }
+
+ /* reserve before emplace_back will avoids realloc(s) */
+ mKnobs.reserve(reserveCnt);
+
+ for (tinyxml2::XMLElement* pKnob =
+ pBiosknobs->FirstChildElement("knob");
+ pKnob; pKnob = pKnob->NextSiblingElement("knob"))
+ {
+ getKnob(pKnob);
+ }
+ }
+ }
+
+ if (!mKnobs.empty())
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ /* To store all 'knob's in 'biosknobs' */
+ std::vector<knob::knob> mKnobs;
+
+ /* Object of Depex class to compute 'depex' expression */
+ std::unique_ptr<Depex> mDepex;
+};
+} // namespace bios