support runtime update of BIOS attributes

Register the BIOSConfig/PendingAttributes signal and when the out of band
redfish user updates the BIOS attributes, the pldm daemon should process and
check the PendingAttributes porperty, and then update the
BIOSConfig/BaseBIOSTable property and the BIOS table.

Tested: test JSON with
        https://gist.github.com/lxwinspur/2afffff2e445fddf90dfed6eceef4014
Type.Enumeration:
~#: busctl set-property xyz.openbmc_project.BIOSConfigManager
    /xyz/openbmc_project/bios_config/manager
	xyz.openbmc_project.BIOSConfig.Manager PendingAttributes a\{s\(sv\)\} 1
	"Led" "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Enumeration" s
	"xyz.openbmc_project.Led.Physical.Action.On"
~#: pldmtool bios getbiostable -t 2
    PLDM AttributeValueTable:
    ... ...
    AttributeHandle: 3
 	    AttributeType: BIOSEnumeration
	    NumberOfCurrentValues: 1
	    CurrentValueStringHandleIndex[0] = 0, StringHandle = On
    ... ...

Type.String
~#: busctl set-property xyz.openbmc_project.BIOSConfigManager
    /xyz/openbmc_project/bios_config/manager
	xyz.openbmc_project.BIOSConfig.Manager PendingAttributes a\{s\(sv\)\} 1
	"Model" "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String" s
	"testModel"
~#: pldmtool bios getbiostable -t 2
    PLDM AttributeValueTable:
    AttributeHandle: 0
	    AttributeType: BIOSString
	    CurrentStringLength: 10
	    CurrentString: testModel
	... ...

Type.Integer
~#: busctl set-property xyz.openbmc_project.BIOSConfigManager
    /xyz/openbmc_project/bios_config/manager
	xyz.openbmc_project.BIOSConfig.Manager PendingAttributes a\{s\(sv\)\} 1
	"OUTLET" "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Integer" x
	1000
~#: pldmtool bios getbiostable -t 2
    ... ...
	AttributeHandle: 2
	    AttributeType: BIOSInteger
	    CurrentValue: 1000
	... ...

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I4d4d2941e6c9bfa7f6fccb664db1580f2ffc9c9f
diff --git a/libpldmresponder/bios_attribute.hpp b/libpldmresponder/bios_attribute.hpp
index 7b4116a..7a15c09 100644
--- a/libpldmresponder/bios_attribute.hpp
+++ b/libpldmresponder/bios_attribute.hpp
@@ -73,6 +73,15 @@
                               uint8_t attrType,
                               const PropertyValue& newPropVal) = 0;
 
+    /** @brief Generate attribute entry by the spec DSP0247_1.0.0 Table 14
+     *  @param[in] attributevalue - attribute value(Enumeration, String and
+     *             Integer)
+     *  @param[in,out] attrValueEntry - attribute entry
+     */
+    virtual void generateAttributeEntry(
+        const std::variant<int64_t, std::string>& attributevalue,
+        Table& attrValueEntry) = 0;
+
     /** @brief Method to return the D-Bus map */
     std::optional<DBusMapping> getDBusMap();
 
diff --git a/libpldmresponder/bios_config.cpp b/libpldmresponder/bios_config.cpp
index ee634c9..e3d89a6 100644
--- a/libpldmresponder/bios_config.cpp
+++ b/libpldmresponder/bios_config.cpp
@@ -6,6 +6,8 @@
 #include "bios_table.hpp"
 #include "common/bios_utils.hpp"
 
+#include <xyz/openbmc_project/BIOSConfig/Manager/server.hpp>
+
 #include <fstream>
 #include <iostream>
 
@@ -18,6 +20,9 @@
 namespace
 {
 
+using BIOSConfigManager =
+    sdbusplus::xyz::openbmc_project::BIOSConfig::server::Manager;
+
 constexpr auto enumJsonFile = "enum_attrs.json";
 constexpr auto stringJsonFile = "string_attrs.json";
 constexpr auto integerJsonFile = "integer_attrs.json";
@@ -35,6 +40,7 @@
 {
     fs::create_directories(tableDir);
     constructAttributes();
+    listenPendingAttributes();
 }
 
 void BIOSConfig::buildTables()
@@ -782,6 +788,107 @@
     }
 }
 
+uint16_t BIOSConfig::findAttrHandle(const std::string& attrName)
+{
+    auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
+    auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
+
+    BIOSStringTable biosStringTable(*stringTable);
+    pldm::bios::utils::BIOSTableIter<PLDM_BIOS_ATTR_TABLE> attrTableIter(
+        attrTable->data(), attrTable->size());
+    auto stringHandle = biosStringTable.findHandle(attrName);
+
+    for (auto entry : pldm::bios::utils::BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(
+             attrTable->data(), attrTable->size()))
+    {
+        auto header = table::attribute::decodeHeader(entry);
+        if (header.stringHandle == stringHandle)
+        {
+            return header.attrHandle;
+        }
+    }
+
+    throw std::invalid_argument("Unknow attribute Name");
+}
+
+void BIOSConfig::constructPendingAttribute(
+    const PendingAttributes& pendingAttributes)
+{
+    for (auto& attribute : pendingAttributes)
+    {
+        std::string attributeName = attribute.first;
+        auto& [attributeType, attributevalue] = attribute.second;
+
+        auto iter = std::find_if(biosAttributes.begin(), biosAttributes.end(),
+                                 [&attributeName](const auto& attr) {
+                                     return attr->name == attributeName;
+                                 });
+
+        if (iter == biosAttributes.end())
+        {
+            std::cerr << "Wrong attribute name, attributeName = "
+                      << attributeName << std::endl;
+            continue;
+        }
+
+        Table attrValueEntry(sizeof(pldm_bios_attr_val_table_entry), 0);
+        auto entry = reinterpret_cast<pldm_bios_attr_val_table_entry*>(
+            attrValueEntry.data());
+
+        auto handler = findAttrHandle(attributeName);
+        auto type =
+            BIOSConfigManager::convertAttributeTypeFromString(attributeType);
+
+        if (type != BIOSConfigManager::AttributeType::Enumeration &&
+            type != BIOSConfigManager::AttributeType::String &&
+            type != BIOSConfigManager::AttributeType::Integer)
+        {
+            std::cerr << "Attribute type not supported, attributeType = "
+                      << attributeType << std::endl;
+            continue;
+        }
+
+        entry->attr_handle = htole16(handler);
+        (*iter)->generateAttributeEntry(attributevalue, attrValueEntry);
+
+        setAttrValue(attrValueEntry.data(), attrValueEntry.size());
+    }
+}
+
+void BIOSConfig::listenPendingAttributes()
+{
+    constexpr auto objPath = "/xyz/openbmc_project/bios_config/manager";
+    constexpr auto objInterface = "xyz.openbmc_project.BIOSConfig.Manager";
+
+    using namespace sdbusplus::bus::match::rules;
+    auto updateBIOSMatch = std::make_unique<sdbusplus::bus::match::match>(
+        pldm::utils::DBusHandler::getBus(),
+        propertiesChanged(objPath, objInterface),
+        [this](sdbusplus::message::message& msg) {
+            constexpr auto propertyName = "PendingAttributes";
+
+            using Value =
+                std::variant<std::string, PendingAttributes, BaseBIOSTable>;
+            using Properties = std::map<DbusProp, Value>;
+
+            Properties props{};
+            std::string intf;
+            msg.read(intf, props);
+
+            auto valPropMap = props.find(propertyName);
+            if (valPropMap == props.end())
+            {
+                return;
+            }
+
+            PendingAttributes pendingAttributes =
+                std::get<PendingAttributes>(valPropMap->second);
+            this->constructPendingAttribute(pendingAttributes);
+        });
+
+    biosAttrMatch.emplace_back(std::move(updateBIOSMatch));
+}
+
 } // namespace bios
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/bios_config.hpp b/libpldmresponder/bios_config.hpp
index 8782640..b10ca78 100644
--- a/libpldmresponder/bios_config.hpp
+++ b/libpldmresponder/bios_config.hpp
@@ -48,6 +48,9 @@
                MenuPath, CurrentValue, DefaultValue, Option>;
 using BaseBIOSTable = std::map<AttributeName, BIOSTableObj>;
 
+using PendingObj = std::tuple<AttributeType, CurrentValue>;
+using PendingAttributes = std::map<AttributeName, PendingObj>;
+
 /** @class BIOSConfig
  *  @brief Manager BIOS Attributes
  */
@@ -220,6 +223,23 @@
     /** @brief Update the BaseBIOSTable property of the D-Bus interface
      */
     void updateBaseBIOSTableProperty();
+
+    /** @brief Listen the PendingAttributes property of the D-Bus interface and
+     *         update BaseBIOSTable
+     */
+    void listenPendingAttributes();
+
+    /** @brief Find attribute handle from bios attribute table
+     *  @param[in] attrName - attribute name
+     *  @return attribute handle
+     */
+    uint16_t findAttrHandle(const std::string& attrName);
+
+    /** @brief Listen the PendingAttributes property of the D-Bus interface
+     * and update BaseBIOSTable
+     *  @param[in] msg - Data associated with subscribed signal
+     */
+    void constructPendingAttribute(const PendingAttributes& pendingAttributes);
 };
 
 } // namespace bios
diff --git a/libpldmresponder/bios_enum_attribute.cpp b/libpldmresponder/bios_enum_attribute.cpp
index ecbb8f5..e799cee 100644
--- a/libpldmresponder/bios_enum_attribute.cpp
+++ b/libpldmresponder/bios_enum_attribute.cpp
@@ -147,6 +147,30 @@
     }
 }
 
+uint8_t BIOSEnumAttribute::getAttrValueIndex(const PropertyValue& propValue)
+{
+    auto defaultValueIndex = getValueIndex(defaultValue, possibleValues);
+    if (readOnly)
+    {
+        return defaultValueIndex;
+    }
+
+    try
+    {
+        auto iter = valMap.find(propValue);
+        if (iter == valMap.end())
+        {
+            return defaultValueIndex;
+        }
+        auto currentValue = iter->second;
+        return getValueIndex(currentValue, possibleValues);
+    }
+    catch (const std::exception& e)
+    {
+        return defaultValueIndex;
+    }
+}
+
 void BIOSEnumAttribute::setAttrValueOnDbus(
     const pldm_bios_attr_val_table_entry* attrValueEntry,
     const pldm_bios_attr_table_entry* attrEntry,
@@ -220,6 +244,21 @@
     return PLDM_SUCCESS;
 }
 
+void BIOSEnumAttribute::generateAttributeEntry(
+    const std::variant<int64_t, std::string>& attributevalue,
+    Table& attrValueEntry)
+{
+    attrValueEntry.resize(sizeof(pldm_bios_attr_val_table_entry) + 1);
+
+    auto entry = reinterpret_cast<pldm_bios_attr_val_table_entry*>(
+        attrValueEntry.data());
+
+    std::string value = std::get<std::string>(attributevalue);
+    entry->attr_type = 0;
+    entry->value[0] = 1; // number of current values, default 1
+    entry->value[1] = getAttrValueIndex(value);
+}
+
 } // namespace bios
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/bios_enum_attribute.hpp b/libpldmresponder/bios_enum_attribute.hpp
index c86d9d7..e69d76d 100644
--- a/libpldmresponder/bios_enum_attribute.hpp
+++ b/libpldmresponder/bios_enum_attribute.hpp
@@ -51,6 +51,15 @@
     void constructEntry(const BIOSStringTable& stringTable, Table& attrTable,
                         Table& attrValueTable) override;
 
+    /** @brief Generate attribute entry by the spec DSP0247_1.0.0 Table 14
+     *  @param[in] attributevalue - attribute value(Enumeration, String and
+     *             Integer)
+     *  @param[in,out] attrValueEntry - attribute entry
+     */
+    void generateAttributeEntry(
+        const std::variant<int64_t, std::string>& attributevalue,
+        Table& attrValueEntry) override;
+
     int updateAttrVal(Table& newValue, uint16_t attrHdl, uint8_t attrType,
                       const PropertyValue& newPropVal);
 
@@ -89,6 +98,13 @@
      *  @return The index of the current value in possible values
      */
     uint8_t getAttrValueIndex();
+
+    /** @brief Get index of the property value in possible values
+     *  @param[in] propValue - property values
+     *
+     *  @return The index of the property value in possible values
+     */
+    uint8_t getAttrValueIndex(const PropertyValue& propValue);
 };
 
 } // namespace bios
diff --git a/libpldmresponder/bios_integer_attribute.cpp b/libpldmresponder/bios_integer_attribute.cpp
index d6d1214..a4bc14b 100644
--- a/libpldmresponder/bios_integer_attribute.cpp
+++ b/libpldmresponder/bios_integer_attribute.cpp
@@ -191,6 +191,21 @@
     return PLDM_SUCCESS;
 }
 
+void BIOSIntegerAttribute::generateAttributeEntry(
+    const std::variant<int64_t, std::string>& attributevalue,
+    Table& attrValueEntry)
+{
+    attrValueEntry.resize(sizeof(pldm_bios_attr_val_table_entry) +
+                          sizeof(int64_t) - 1);
+
+    auto entry = reinterpret_cast<pldm_bios_attr_val_table_entry*>(
+        attrValueEntry.data());
+
+    int64_t value = std::get<int64_t>(attributevalue);
+    entry->attr_type = 3;
+    memcpy(entry->value, &value, sizeof(int64_t));
+}
+
 } // namespace bios
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/bios_integer_attribute.hpp b/libpldmresponder/bios_integer_attribute.hpp
index e180066..5f2773a 100644
--- a/libpldmresponder/bios_integer_attribute.hpp
+++ b/libpldmresponder/bios_integer_attribute.hpp
@@ -47,6 +47,15 @@
     void constructEntry(const BIOSStringTable& stringTable, Table& attrTable,
                         Table& attrValueTable) override;
 
+    /** @brief Generate attribute entry by the spec DSP0247_1.0.0 Table 14
+     *  @param[in] attributevalue - attribute value(Enumeration, String and
+     *             Integer)
+     *  @param[in,out] attrValueEntry - attribute entry
+     */
+    void generateAttributeEntry(
+        const std::variant<int64_t, std::string>& attributevalue,
+        Table& attrValueEntry) override;
+
     int updateAttrVal(Table& newValue, uint16_t attrHdl, uint8_t attrType,
                       const PropertyValue& newPropVal);
 
diff --git a/libpldmresponder/bios_string_attribute.cpp b/libpldmresponder/bios_string_attribute.cpp
index 1453644..19819c9 100644
--- a/libpldmresponder/bios_string_attribute.cpp
+++ b/libpldmresponder/bios_string_attribute.cpp
@@ -129,6 +129,24 @@
     return PLDM_SUCCESS;
 }
 
+void BIOSStringAttribute::generateAttributeEntry(
+    const std::variant<int64_t, std::string>& attributevalue,
+    Table& attrValueEntry)
+{
+    std::string value = std::get<std::string>(attributevalue);
+    uint16_t len = value.size();
+
+    attrValueEntry.resize(sizeof(pldm_bios_attr_val_table_entry) +
+                          sizeof(uint16_t) + len - 1);
+
+    auto entry = reinterpret_cast<pldm_bios_attr_val_table_entry*>(
+        attrValueEntry.data());
+
+    entry->attr_type = 1;
+    memcpy(entry->value, &len, sizeof(uint16_t));
+    memcpy(entry->value + sizeof(uint16_t), value.c_str(), value.size());
+}
+
 } // namespace bios
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/bios_string_attribute.hpp b/libpldmresponder/bios_string_attribute.hpp
index 3b29dc1..b079401 100644
--- a/libpldmresponder/bios_string_attribute.hpp
+++ b/libpldmresponder/bios_string_attribute.hpp
@@ -70,6 +70,15 @@
     void constructEntry(const BIOSStringTable& stringTable, Table& attrTable,
                         Table& attrValueTable) override;
 
+    /** @brief Generate attribute entry by the spec DSP0247_1.0.0 Table 14
+     *  @param[in] attributevalue - attribute value(Enumeration, String and
+     *             Integer)
+     *  @param[in,out] attrValueEntry - attribute entry
+     */
+    void generateAttributeEntry(
+        const std::variant<int64_t, std::string>& attributevalue,
+        Table& attrValueEntry) override;
+
     int updateAttrVal(Table& newValue, uint16_t attrHdl, uint8_t attrType,
                       const PropertyValue& newPropVal);
 
diff --git a/test/libpldmresponder_bios_attribute_test.cpp b/test/libpldmresponder_bios_attribute_test.cpp
index b4200d3..8abaac7 100644
--- a/test/libpldmresponder_bios_attribute_test.cpp
+++ b/test/libpldmresponder_bios_attribute_test.cpp
@@ -32,6 +32,11 @@
     {
         return PLDM_SUCCESS;
     }
+
+    void generateAttributeEntry(
+        const std::variant<int64_t, std::string>& /*attributevalue*/,
+        Table& /*attrValueEntry*/)
+    {}
 };
 
 TEST(BIOSAttribute, CtorTest)