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_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