libpldmresponder: Implement SetBIOSTable

Register the setBIOSTable method and set separately string table,
attribute table, and attribute value table.
When the attribute value table is set, the corresponding D-Bus
property value needs to updated.

Tested:
test with JSON:
https://gist.github.com/lxwinspur/2afffff2e445fddf90dfed6eceef4014

~# pldmtool raw -d 0x80 0x03 0x02 0x00 0x00 0x00 0x00 0x05 0x00 0x00 0x00 0x03 0x00 0x4c 0x65 0x64 0x01 0x00 0x03 0x00 0x4f 0x66 0x66 0x02 0x00 0x02 0x00 0x4f 0x6e 0x23 0x4d 0x50 0x09
Request Message:
08 01 80 03 02 00 00 00 00 05 00 00 00 03 00 4c 65 64 01 00 03 00 4f 66 66 02 00 02 00 4f 6e 23 4d 50 09
Response Message:
08 01 00 03 02 00 00 00 00 00

~# pldmtool raw -d 0x80 0x03 0x02 0x00 0x00 0x00 0x00 0x05 0x01 0x00 0x00 0x00 0x00 0x00 0x02 0x02 0x00 0x01 0x00 0x01 0x01 0xff 0x10 0x22 0x77
Request Message:
08 01 80 03 02 00 00 00 00 05 01 00 00 00 00 00 02 02 00 01 00 01 01 ff 10 22 77
Response Message:
08 01 00 03 02 00 00 00 00 00

~# pldmtool raw -d 0x80 0x03 0x02 0x00 0x00 0x00 0x00 0x05 0x02 0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0xbc 0x91 0xfe 0xe0
Request Message:
08 01 80 03 02 00 00 00 00 05 02 00 00 00 01 01 00 00 00 bc 91 fe e0
Response Message:
08 01 00 03 02 00 00 00 00 00

~# pldmtool bios GetBIOSTable -t 0
PLDM StringTable:
BIOSStringHandle : BIOSString
0 : Led
1 : Off
2 : On

~# pldmtool bios GetBIOSTable -t 1
PLDM AttributeTable:
AttributeHandle: 0, AttributeNameHandle: 0(Led)
	AttributeType: BIOSEnumeration
	NumberOfPossibleValues: 2
		PossibleValueStringHandle[0] = 2(On)
		PossibleValueStringHandle[1] = 1(Off)
	NumberOfDefaultValues: 1
		DefaultValueStringHandleIndex[0] = 1, StringHandle = 1(Off)

~# pldmtool bios GetBIOSTable -t 2
PLDM AttributeValueTable:
AttributeHandle: 0
	AttributeType: BIOSEnumeration
	NumberOfCurrentValues: 1
	CurrentValueStringHandleIndex[0] = 1, StringHandle = Off

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I0ff88961afdfe0835c9fd0b56e37f7ed75f2fb45
diff --git a/libpldmresponder/bios_config.cpp b/libpldmresponder/bios_config.cpp
index c1810de..805b172 100644
--- a/libpldmresponder/bios_config.cpp
+++ b/libpldmresponder/bios_config.cpp
@@ -4,6 +4,7 @@
 #include "bios_integer_attribute.hpp"
 #include "bios_string_attribute.hpp"
 #include "bios_table.hpp"
+#include "common/bios_utils.hpp"
 
 #include <fstream>
 #include <iostream>
@@ -30,7 +31,7 @@
 BIOSConfig::BIOSConfig(const char* jsonDir, const char* tableDir,
                        DBusHandler* const dbusHandler) :
     jsonDir(jsonDir),
-    tableDir(tableDir), dbusHandler(dbusHandler)
+    tableDir(tableDir), dbusHandler(dbusHandler), isUpdateProperty(false)
 {
     fs::create_directories(tableDir);
     constructAttributes();
@@ -63,6 +64,378 @@
     return loadTable(tablePath);
 }
 
+int BIOSConfig::setBIOSTable(uint8_t tableType, const Table& table)
+{
+    fs::path stringTablePath(tableDir / stringTableFile);
+    fs::path attrTablePath(tableDir / attrTableFile);
+    fs::path attrValueTablePath(tableDir / attrValueTableFile);
+
+    if (!pldm_bios_table_checksum(table.data(), table.size()))
+    {
+        return PLDM_INVALID_BIOS_TABLE_DATA_INTEGRITY_CHECK;
+    }
+
+    if (tableType == PLDM_BIOS_STRING_TABLE)
+    {
+        storeTable(stringTablePath, table);
+    }
+    else if (tableType == PLDM_BIOS_ATTR_TABLE)
+    {
+        BIOSTable biosStringTable(stringTablePath.c_str());
+        if (biosStringTable.isEmpty())
+        {
+            return PLDM_INVALID_BIOS_TABLE_TYPE;
+        }
+
+        auto rc = checkAttributeTable(table);
+        if (rc != PLDM_SUCCESS)
+        {
+            return rc;
+        }
+
+        storeTable(attrTablePath, table);
+    }
+    else if (tableType == PLDM_BIOS_ATTR_VAL_TABLE)
+    {
+        BIOSTable biosStringTable(stringTablePath.c_str());
+        BIOSTable biosStringValueTable(attrTablePath.c_str());
+        if (biosStringTable.isEmpty() || biosStringValueTable.isEmpty())
+        {
+            return PLDM_INVALID_BIOS_TABLE_TYPE;
+        }
+
+        auto rc = checkAttributeValueTable(table);
+        if (rc != PLDM_SUCCESS)
+        {
+            return rc;
+        }
+
+        storeTable(attrValueTablePath, table);
+        isUpdateProperty = true;
+    }
+    else
+    {
+        return PLDM_INVALID_BIOS_TABLE_TYPE;
+    }
+
+    if (isUpdateProperty)
+    {
+        isUpdateProperty = false;
+        updateBaseBIOSTableProperty();
+    }
+
+    return PLDM_SUCCESS;
+}
+
+int BIOSConfig::checkAttributeTable(const Table& table)
+{
+    using namespace pldm::bios::utils;
+    auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
+    for (auto entry :
+         BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(table.data(), table.size()))
+    {
+        auto attrNameHandle =
+            pldm_bios_table_attr_entry_decode_string_handle(entry);
+
+        auto stringEnty = pldm_bios_table_string_find_by_handle(
+            stringTable->data(), stringTable->size(), attrNameHandle);
+        if (stringEnty == nullptr)
+        {
+            return PLDM_INVALID_BIOS_ATTR_HANDLE;
+        }
+
+        auto attrType = static_cast<pldm_bios_attribute_type>(
+            pldm_bios_table_attr_entry_decode_attribute_type(entry));
+
+        switch (attrType)
+        {
+            case PLDM_BIOS_ENUMERATION:
+            case PLDM_BIOS_ENUMERATION_READ_ONLY:
+            {
+                auto pvNum =
+                    pldm_bios_table_attr_entry_enum_decode_pv_num(entry);
+                std::vector<uint16_t> pvHandls(pvNum);
+                pldm_bios_table_attr_entry_enum_decode_pv_hdls(
+                    entry, pvHandls.data(), pvHandls.size());
+                auto defNum =
+                    pldm_bios_table_attr_entry_enum_decode_def_num(entry);
+                std::vector<uint8_t> defIndices(defNum);
+                pldm_bios_table_attr_entry_enum_decode_def_indices(
+                    entry, defIndices.data(), defIndices.size());
+
+                for (size_t i = 0; i < pvHandls.size(); i++)
+                {
+                    auto stringEntry = pldm_bios_table_string_find_by_handle(
+                        stringTable->data(), stringTable->size(), pvHandls[i]);
+                    if (stringEntry == nullptr)
+                    {
+                        return PLDM_INVALID_BIOS_ATTR_HANDLE;
+                    }
+                }
+
+                for (size_t i = 0; i < defIndices.size(); i++)
+                {
+                    auto stringEntry = pldm_bios_table_string_find_by_handle(
+                        stringTable->data(), stringTable->size(),
+                        pvHandls[defIndices[i]]);
+                    if (stringEntry == nullptr)
+                    {
+                        return PLDM_INVALID_BIOS_ATTR_HANDLE;
+                    }
+                }
+                break;
+            }
+            case PLDM_BIOS_INTEGER:
+            case PLDM_BIOS_INTEGER_READ_ONLY:
+            case PLDM_BIOS_STRING:
+            case PLDM_BIOS_STRING_READ_ONLY:
+            case PLDM_BIOS_PASSWORD:
+            case PLDM_BIOS_PASSWORD_READ_ONLY:
+                break;
+            default:
+                return PLDM_INVALID_BIOS_ATTR_HANDLE;
+        }
+    }
+
+    return PLDM_SUCCESS;
+}
+
+int BIOSConfig::checkAttributeValueTable(const Table& table)
+{
+    using namespace pldm::bios::utils;
+    auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
+    auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
+
+    baseBIOSTableMaps.clear();
+
+    for (auto tableEntry :
+         BIOSTableIter<PLDM_BIOS_ATTR_VAL_TABLE>(table.data(), table.size()))
+    {
+        AttributeName attributeName{};
+        AttributeType attributeType{};
+        ReadonlyStatus readonlyStatus{};
+        DisplayName displayName{};
+        Description description{};
+        MenuPath menuPath{};
+        CurrentValue currentValue{};
+        DefaultValue defaultValue{};
+        Option options{};
+
+        auto attrValueHandle =
+            pldm_bios_table_attr_value_entry_decode_attribute_handle(
+                tableEntry);
+        auto attrType = static_cast<pldm_bios_attribute_type>(
+            pldm_bios_table_attr_value_entry_decode_attribute_type(tableEntry));
+
+        auto attrEntry = pldm_bios_table_attr_find_by_handle(
+            attrTable->data(), attrTable->size(), attrValueHandle);
+        if (attrEntry == nullptr)
+        {
+            return PLDM_INVALID_BIOS_ATTR_HANDLE;
+        }
+        auto attrHandle =
+            pldm_bios_table_attr_entry_decode_attribute_handle(attrEntry);
+        auto attrNameHandle =
+            pldm_bios_table_attr_entry_decode_string_handle(attrEntry);
+
+        auto stringEntry = pldm_bios_table_string_find_by_handle(
+            stringTable->data(), stringTable->size(), attrNameHandle);
+        if (stringEntry == nullptr)
+        {
+            return PLDM_INVALID_BIOS_ATTR_HANDLE;
+        }
+        auto strLength =
+            pldm_bios_table_string_entry_decode_string_length(stringEntry);
+        std::vector<char> buffer(strLength + 1 /* sizeof '\0' */);
+        pldm_bios_table_string_entry_decode_string(stringEntry, buffer.data(),
+                                                   buffer.size());
+        attributeName = std::string(buffer.data(), buffer.data() + strLength);
+
+        if (!biosAttributes.empty())
+        {
+            readonlyStatus =
+                biosAttributes[attrHandle / biosAttributes.size()]->readOnly;
+        }
+
+        switch (attrType)
+        {
+            case PLDM_BIOS_ENUMERATION:
+            case PLDM_BIOS_ENUMERATION_READ_ONLY:
+            {
+                auto getValue = [](uint16_t handle,
+                                   const Table& table) -> std::string {
+                    auto stringEntry = pldm_bios_table_string_find_by_handle(
+                        table.data(), table.size(), handle);
+
+                    auto strLength =
+                        pldm_bios_table_string_entry_decode_string_length(
+                            stringEntry);
+                    std::vector<char> buffer(strLength + 1 /* sizeof '\0' */);
+                    pldm_bios_table_string_entry_decode_string(
+                        stringEntry, buffer.data(), buffer.size());
+
+                    return std::string(buffer.data(),
+                                       buffer.data() + strLength);
+                };
+
+                attributeType = "xyz.openbmc_project.BIOSConfig.Manager."
+                                "AttributeType.Enumeration";
+
+                auto pvNum =
+                    pldm_bios_table_attr_entry_enum_decode_pv_num(attrEntry);
+                std::vector<uint16_t> pvHandls(pvNum);
+                pldm_bios_table_attr_entry_enum_decode_pv_hdls(
+                    attrEntry, pvHandls.data(), pvHandls.size());
+
+                // get possible_value
+                for (size_t i = 0; i < pvHandls.size(); i++)
+                {
+                    options.push_back(
+                        std::make_tuple("xyz.openbmc_project.BIOSConfig."
+                                        "Manager.BoundType.OneOf",
+                                        getValue(pvHandls[i], *stringTable)));
+                }
+
+                auto count =
+                    pldm_bios_table_attr_value_entry_enum_decode_number(
+                        tableEntry);
+                std::vector<uint8_t> handles(count);
+                pldm_bios_table_attr_value_entry_enum_decode_handles(
+                    tableEntry, handles.data(), handles.size());
+
+                // get current_value
+                for (size_t i = 0; i < handles.size(); i++)
+                {
+                    currentValue = getValue(pvHandls[handles[i]], *stringTable);
+                }
+
+                auto defNum =
+                    pldm_bios_table_attr_entry_enum_decode_def_num(attrEntry);
+                std::vector<uint8_t> defIndices(defNum);
+                pldm_bios_table_attr_entry_enum_decode_def_indices(
+                    attrEntry, defIndices.data(), defIndices.size());
+
+                // get default_value
+                for (size_t i = 0; i < defIndices.size(); i++)
+                {
+                    defaultValue =
+                        getValue(pvHandls[defIndices[i]], *stringTable);
+                }
+
+                break;
+            }
+            case PLDM_BIOS_INTEGER:
+            case PLDM_BIOS_INTEGER_READ_ONLY:
+            {
+                attributeType = "xyz.openbmc_project.BIOSConfig.Manager."
+                                "AttributeType.Integer";
+                currentValue = static_cast<int64_t>(
+                    pldm_bios_table_attr_value_entry_integer_decode_cv(
+                        tableEntry));
+
+                uint64_t lower, upper, def;
+                uint32_t scalar;
+                pldm_bios_table_attr_entry_integer_decode(
+                    attrEntry, &lower, &upper, &scalar, &def);
+                options.push_back(
+                    std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
+                                    "BoundType.LowerBound",
+                                    static_cast<int64_t>(lower)));
+                options.push_back(
+                    std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
+                                    "BoundType.UpperBound",
+                                    static_cast<int64_t>(upper)));
+                options.push_back(
+                    std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
+                                    "BoundType.ScalarIncrement",
+                                    static_cast<int64_t>(scalar)));
+                defaultValue = static_cast<int64_t>(def);
+                break;
+            }
+            case PLDM_BIOS_STRING:
+            case PLDM_BIOS_STRING_READ_ONLY:
+            {
+                attributeType = "xyz.openbmc_project.BIOSConfig.Manager."
+                                "AttributeType.String";
+                variable_field currentString;
+                pldm_bios_table_attr_value_entry_string_decode_string(
+                    tableEntry, &currentString);
+                currentValue = std::string(
+                    reinterpret_cast<const char*>(currentString.ptr),
+                    currentString.length);
+                auto min = pldm_bios_table_attr_entry_string_decode_min_length(
+                    attrEntry);
+                auto max = pldm_bios_table_attr_entry_string_decode_max_length(
+                    attrEntry);
+                auto def =
+                    pldm_bios_table_attr_entry_string_decode_def_string_length(
+                        attrEntry);
+                std::vector<char> defString(def + 1);
+                pldm_bios_table_attr_entry_string_decode_def_string(
+                    attrEntry, defString.data(), defString.size());
+                options.push_back(
+                    std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
+                                    "BoundType.MinStringLength",
+                                    static_cast<int64_t>(min)));
+                options.push_back(
+                    std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
+                                    "BoundType.MaxStringLength",
+                                    static_cast<int64_t>(max)));
+                defaultValue = defString.data();
+                break;
+            }
+            case PLDM_BIOS_PASSWORD:
+            case PLDM_BIOS_PASSWORD_READ_ONLY:
+            {
+                attributeType = "xyz.openbmc_project.BIOSConfig.Manager."
+                                "AttributeType.Password";
+                break;
+            }
+            default:
+                return PLDM_INVALID_BIOS_ATTR_HANDLE;
+        }
+        baseBIOSTableMaps.emplace(
+            std::move(attributeName),
+            std::make_tuple(attributeType, readonlyStatus, displayName,
+                            description, menuPath, currentValue, defaultValue,
+                            std::move(options)));
+    }
+
+    return PLDM_SUCCESS;
+}
+
+void BIOSConfig::updateBaseBIOSTableProperty()
+{
+    constexpr static auto biosConfigPath =
+        "/xyz/openbmc_project/bios_config/manager";
+    constexpr static auto biosConfigInterface =
+        "xyz.openbmc_project.BIOSConfig.Manager";
+    constexpr static auto biosConfigPropertyName = "BaseBIOSTable";
+    constexpr static auto dbusProperties = "org.freedesktop.DBus.Properties";
+
+    if (baseBIOSTableMaps.empty())
+    {
+        return;
+    }
+
+    try
+    {
+        auto& bus = dbusHandler->getBus();
+        auto service =
+            dbusHandler->getService(biosConfigPath, biosConfigInterface);
+        auto method = bus.new_method_call(service.c_str(), biosConfigPath,
+                                          dbusProperties, "Set");
+        std::variant<BaseBIOSTable> value = baseBIOSTableMaps;
+        method.append(biosConfigInterface, biosConfigPropertyName, value);
+        bus.call_noreply(method);
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << "failed to update BaseBIOSTable property, ERROR="
+                  << e.what() << "\n";
+    }
+}
+
 void BIOSConfig::constructAttributes()
 {
     load(jsonDir / stringJsonFile, [this](const Json& entry) {
@@ -102,9 +475,8 @@
 
     table::appendPadAndChecksum(attrTable);
     table::appendPadAndChecksum(attrValueTable);
-
-    storeTable(tableDir / attrTableFile, attrTable);
-    storeTable(tableDir / attrValueTableFile, attrValueTable);
+    setBIOSTable(PLDM_BIOS_ATTR_TABLE, attrTable);
+    setBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE, attrValueTable);
 }
 
 std::optional<Table> BIOSConfig::buildAndStoreStringTable()
@@ -137,7 +509,7 @@
     }
 
     table::appendPadAndChecksum(table);
-    storeTable(tableDir / stringTableFile, table);
+    setBIOSTable(PLDM_BIOS_STRING_TABLE, table);
     return table;
 }