Add depex support in bios.xml parsing.

Added logic to execute depex expression for every knob in bios.xml.
knob is added to BiosAttributeRegistry only if depex evaluation result
is TRUE.

Tested:
By giving GET Request to
'redfish/v1/Registries/BiosAttributeRegistry/BiosAttributeRegistry'.
Output sample showing few knobs added becuase depex evaluation result
was TRUE:

{
    "AttributeName": "AEPErrorInjEn",
    "CurrentValue": "0x00",
    "DefaultValue": "0x00",
    "DisplayName": "AEPErrorInjEn",
    "HelpText": "Enable/Disable PMem Error Injection",
    "MenuPath": "./",
    "ReadOnly": false,
    "Type": "String",
    "Value": [
        {
            "OneOf": "0x0"
        },
        {
            "OneOf": "0x1"
        }
    ]
}
{
    "AttributeName": "wrVrefCenter",
    "CurrentValue": "0x01",
    "DefaultValue": "0x01",
    "DisplayName": "wrVrefCenter",
    "HelpText": "Write Vref Centering Disable/Enable",
    "MenuPath": "./",
    "ReadOnly": false,
    "Type": "String",
    "Value": [
        {
            "OneOf": "0x0"
        },
        {
            "OneOf": "0x1"
        }
    ]
}

Following are few knobs rejected becuase depex evaluation
result was FALSE:

<knob  type="scalar" setupType="oneof" name="PfrProvision"
varstoreIndex="04" prompt="PFR Provision"
description="Selectable if PFR is not locked." size="1"
offset="0x0126"
depex="Sif( PfrSupported _EQU_ 0 ) _AND_ Gif( PfrLockStatus _EQU_ 1 OR
PfrProvisionStatus _EQU_ 1 )"
default="0x00" CurrentVal="0x00">
    <options>
        <option text="Disable" value="0x0"/>
        <option text="Enable" value="0x1"/>
    </options>
</knob>

<knob  type="scalar" setupType="oneof" name="SataHotPlugController0_0"
varstoreIndex="01" prompt="
Hot Plug" description="Designates this port as Hot Pluggable." size="1"
offset="0x0119"
depex="Sif( PchSata_0 _EQU_ 0 ) _AND_
Sif( SataExternalController0_0 _EQU_ 1 )" default="0x00"
CurrentVal="0x00">
    <options>
        <option text="Disabled" value="0x0"/>
        <option text="Enabled" value="0x1"/>
    </options>
</knob>

Signed-off-by: Arun Lal K M <arun.lal@intel.com>
Change-Id: Iff1ea01fb06200e21cc7f2a2e0fdfdff70376581
diff --git a/include/biosxml.hpp b/include/biosxml.hpp
new file mode 100644
index 0000000..c5be53c
--- /dev/null
+++ b/include/biosxml.hpp
@@ -0,0 +1,884 @@
+#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>

+

+std::string mapAttrTypeToRedfish(const std::string_view typeDbus)

+{

+    std::string ret;

+    if (typeDbus == "xyz.openbmc_project.BIOSConfig.Manager."

+                    "AttributeType.Enumeration")

+    {

+        ret = "Enumeration";

+    }

+    else if (typeDbus == "xyz.openbmc_project.BIOSConfig."

+                         "Manager.AttributeType.String")

+    {

+        ret = "String";

+    }

+    else if (typeDbus == "xyz.openbmc_project.BIOSConfig."

+                         "Manager.AttributeType.Password")

+    {

+        ret = "Password";

+    }

+    else if (typeDbus == "xyz.openbmc_project.BIOSConfig."

+                         "Manager.AttributeType.Integer")

+    {

+        ret = "Integer";

+    }

+    else if (typeDbus == "xyz.openbmc_project.BIOSConfig."

+                         "Manager.AttributeType.Boolean")

+    {

+        ret = "Boolean";

+    }

+    else

+    {

+        ret = "UNKNOWN";

+    }

+

+    return ret;

+}

+

+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,

+    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::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 == "_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