bios: Implement BIOSConfig

Load the parsed json configs into memory.
And provid APIs to get/set on bios tables.

Signed-off-by: John Wang <wangzqbj@inspur.com>
Change-Id: Ida1fedc923d31afc61dd2d4aec70d81bb6a90ae9
diff --git a/libpldmresponder/bios_config.cpp b/libpldmresponder/bios_config.cpp
new file mode 100644
index 0000000..d5c39ba
--- /dev/null
+++ b/libpldmresponder/bios_config.cpp
@@ -0,0 +1,260 @@
+#include "bios_config.hpp"
+
+#include "bios_string_attribute.hpp"
+
+#include <fstream>
+#include <iostream>
+
+namespace pldm
+{
+namespace responder
+{
+namespace bios
+{
+namespace
+{
+
+constexpr auto enumJsonFile = "enum_attrs.json";
+constexpr auto stringJsonFile = "string_attrs.json";
+constexpr auto integerJsonFile = "integer_attrs.json";
+
+constexpr auto stringTableFile = "stringTable";
+constexpr auto attrTableFile = "attributeTable";
+constexpr auto attrValueTableFile = "attributeValueTable";
+
+} // namespace
+
+BIOSConfig::BIOSConfig(const char* jsonDir, const char* tableDir,
+                       DBusHandler* const dbusHandler) :
+    jsonDir(jsonDir),
+    tableDir(tableDir), dbusHandler(dbusHandler)
+{
+    constructAttributes();
+}
+
+void BIOSConfig::buildTables()
+{
+    fs::create_directory(tableDir);
+    auto stringTable = buildAndStoreStringTable();
+    if (stringTable)
+    {
+        buildAndStoreAttrTables(*stringTable);
+    }
+}
+
+std::optional<Table> BIOSConfig::getBIOSTable(pldm_bios_table_types tableType)
+{
+    fs::path tablePath;
+    switch (tableType)
+    {
+        case PLDM_BIOS_STRING_TABLE:
+            tablePath = tableDir / stringTableFile;
+            break;
+        case PLDM_BIOS_ATTR_TABLE:
+            tablePath = tableDir / attrTableFile;
+            break;
+        case PLDM_BIOS_ATTR_VAL_TABLE:
+            tablePath = tableDir / attrValueTableFile;
+            break;
+    }
+    return loadTable(tablePath);
+}
+
+void BIOSConfig::constructAttributes()
+{
+    load(jsonDir / stringJsonFile, [this](const Json& entry) {
+        constructAttribute<BIOSStringAttribute>(entry);
+    });
+}
+
+void BIOSConfig::buildAndStoreAttrTables(const Table& stringTable)
+{
+    BIOSStringTable biosStringTable(stringTable);
+
+    if (biosAttributes.empty())
+    {
+        return;
+    }
+
+    Table attrTable, attrValueTable;
+
+    for (auto& attr : biosAttributes)
+    {
+        try
+        {
+            attr->constructEntry(biosStringTable, attrTable, attrValueTable);
+        }
+        catch (const std::exception& e)
+        {
+            std::cerr << "Construct Table Entry Error, AttributeName = "
+                      << attr->name << std::endl;
+        }
+    }
+
+    table::appendPadAndChecksum(attrTable);
+    table::appendPadAndChecksum(attrValueTable);
+
+    storeTable(tableDir / attrTableFile, attrTable);
+    storeTable(tableDir / attrValueTableFile, attrValueTable);
+}
+
+std::optional<Table> BIOSConfig::buildAndStoreStringTable()
+{
+    std::set<std::string> strings;
+    auto handler = [&strings](const Json& entry) {
+        strings.emplace(entry.at("attribute_name"));
+    };
+
+    load(jsonDir / stringJsonFile, handler);
+    load(jsonDir / integerJsonFile, handler);
+    load(jsonDir / enumJsonFile, [&strings](const Json& entry) {
+        strings.emplace(entry.at("attribute_name"));
+        auto possibleValues = entry.at("possible_values");
+        for (auto& pv : possibleValues)
+        {
+            strings.emplace(pv);
+        }
+    });
+
+    if (strings.empty())
+    {
+        return std::nullopt;
+    }
+
+    Table table;
+    for (const auto& elem : strings)
+    {
+        table::string::constructEntry(table, elem);
+    }
+
+    table::appendPadAndChecksum(table);
+    storeTable(tableDir / stringTableFile, table);
+    return table;
+}
+
+void BIOSConfig::storeTable(const fs::path& path, const Table& table)
+{
+    BIOSTable biosTable(path.c_str());
+    biosTable.store(table);
+}
+
+std::optional<Table> BIOSConfig::loadTable(const fs::path& path)
+{
+    BIOSTable biosTable(path.c_str());
+    if (biosTable.isEmpty())
+    {
+        return std::nullopt;
+    }
+
+    Table table;
+    biosTable.load(table);
+    return table;
+}
+
+void BIOSConfig::load(const fs::path& filePath, ParseHandler handler)
+{
+    std::ifstream file;
+    Json jsonConf;
+    if (fs::exists(filePath))
+    {
+        try
+        {
+            file.open(filePath);
+            jsonConf = Json::parse(file);
+            auto entries = jsonConf.at("entries");
+            for (auto& entry : entries)
+            {
+                try
+                {
+                    handler(entry);
+                }
+                catch (const std::exception& e)
+                {
+                    std::cerr
+                        << "Failed to parse JSON config file(entry handler) : "
+                        << filePath.c_str() << ", " << e.what() << std::endl;
+                }
+            }
+        }
+        catch (const std::exception& e)
+        {
+            std::cerr << "Failed to parse JSON config file : "
+                      << filePath.c_str() << std::endl;
+        }
+    }
+}
+
+int BIOSConfig::setAttrValue(const void* entry, size_t size)
+{
+    auto attrValueTable = getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
+    auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE);
+    auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE);
+    if (!attrValueTable || !attrTable || !stringTable)
+    {
+        return PLDM_BIOS_TABLE_UNAVAILABLE;
+    }
+
+    auto destTable =
+        table::attribute_value::updateTable(*attrValueTable, entry, size);
+
+    if (!destTable)
+    {
+        return PLDM_ERROR;
+    }
+    auto attrValueEntry =
+        reinterpret_cast<const pldm_bios_attr_val_table_entry*>(entry);
+
+    auto attrValHeader = table::attribute_value::decodeHeader(attrValueEntry);
+
+    auto attrEntry =
+        table::attribute::findByHandle(*attrTable, attrValHeader.attrHandle);
+    if (!attrEntry)
+    {
+        return PLDM_ERROR;
+    }
+
+    try
+    {
+        auto attrHeader = table::attribute::decodeHeader(attrEntry);
+
+        BIOSStringTable biosStringTable(*stringTable);
+        auto attrName = biosStringTable.findString(attrHeader.stringHandle);
+
+        auto iter = std::find_if(
+            biosAttributes.begin(), biosAttributes.end(),
+            [&attrName](const auto& attr) { return attr->name == attrName; });
+
+        if (iter == biosAttributes.end())
+        {
+            return PLDM_ERROR;
+        }
+        (*iter)->setAttrValueOnDbus(attrValueEntry, attrEntry, biosStringTable);
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << "Set attribute value error: " << e.what() << std::endl;
+        return PLDM_ERROR;
+    }
+
+    BIOSTable biosAttrValueTable((tableDir / attrValueTableFile).c_str());
+    biosAttrValueTable.store(*destTable);
+    return PLDM_SUCCESS;
+}
+
+void BIOSConfig::removeTables()
+{
+    try
+    {
+        fs::remove(tableDir / stringTableFile);
+        fs::remove(tableDir / attrTableFile);
+        fs::remove(tableDir / attrValueTableFile);
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << "Remove the tables error: " << e.what() << std::endl;
+    }
+}
+
+} // namespace bios
+} // namespace responder
+} // namespace pldm
diff --git a/libpldmresponder/bios_config.hpp b/libpldmresponder/bios_config.hpp
new file mode 100644
index 0000000..dd892b1
--- /dev/null
+++ b/libpldmresponder/bios_config.hpp
@@ -0,0 +1,127 @@
+#pragma once
+
+#include "bios_attribute.hpp"
+#include "bios_table.hpp"
+
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <nlohmann/json.hpp>
+#include <optional>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "bios_table.h"
+
+namespace pldm
+{
+namespace responder
+{
+namespace bios
+{
+
+/** @class BIOSConfig
+ *  @brief Manager BIOS Attributes
+ */
+class BIOSConfig
+{
+  public:
+    BIOSConfig() = delete;
+    BIOSConfig(const BIOSConfig&) = delete;
+    BIOSConfig(BIOSConfig&&) = delete;
+    BIOSConfig& operator=(const BIOSConfig&) = delete;
+    BIOSConfig& operator=(BIOSConfig&&) = delete;
+    ~BIOSConfig() = default;
+
+    /** @brief Construct BIOSConfig
+     *  @param[in] jsonDir - The directory where json file exists
+     *  @param[in] tableDir - The directory where the persistent table is placed
+     *  @param[in] dbusHandler - Dbus Handler
+     */
+    explicit BIOSConfig(const char* jsonDir, const char* tableDir,
+                        DBusHandler* const dbusHandler);
+
+    /** @brief Set attribute value on dbus and attribute value table
+     *  @param[in] entry - attribute value entry
+     *  @param[in] size - size of the attribute value entry
+     *  @return pldm_completion_codes
+     */
+    int setAttrValue(const void* entry, size_t size);
+
+    /** @brief Remove the persistent tables */
+    void removeTables();
+
+    /** @brief Build bios tables(string,attribute,attribute value table)*/
+    void buildTables();
+
+    /** @brief Get BIOS table of specified type
+     *  @param[in] tableType - The table type
+     *  @return The bios table, std::nullopt if the table is unaviliable
+     */
+    std::optional<Table> getBIOSTable(pldm_bios_table_types tableType);
+
+  private:
+    const fs::path jsonDir;
+    const fs::path tableDir;
+    DBusHandler* const dbusHandler;
+
+    // vector persists all attributes
+    using BIOSAttributes = std::vector<std::unique_ptr<BIOSAttribute>>;
+    BIOSAttributes biosAttributes;
+
+    /** @brief Construct an attribute and persist it
+     *  @tparam T - attribute type
+     *  @param[in] entry - json entry
+     */
+    template <typename T>
+    void constructAttribute(const Json& entry)
+    {
+        try
+        {
+            biosAttributes.push_back(std::make_unique<T>(entry, dbusHandler));
+        }
+        catch (const std::exception& e)
+        {
+            std::cerr << "Constructs Attribute Error, " << e.what()
+                      << std::endl;
+        }
+    }
+
+    /** Construct attributes and persist them */
+    void constructAttributes();
+
+    using ParseHandler = std::function<void(const Json& entry)>;
+
+    /** @brief Helper function to parse json
+     *  @param[in] filePath - Path of json file
+     *  @param[in] handler - Handler to process each entry in the json
+     */
+    void load(const fs::path& filePath, ParseHandler handler);
+
+    /** @brief Build String Table and persist it
+     *  @return The built string table, std::nullopt if it fails.
+     */
+    std::optional<Table> buildAndStoreStringTable();
+
+    /** @brief Build attr table and attr value table and persist them
+     *  @param[in] stringTable - The string Table
+     */
+    void buildAndStoreAttrTables(const Table& stringTable);
+
+    /** @brief Persist the table
+     *  @param[in] path - Path to persist the table
+     *  @param[in] table - The table
+     */
+    void storeTable(const fs::path& path, const Table& table);
+
+    /** @brief Load bios table to ram
+     *  @param[in] path - Path of the table
+     *  @return The table, std::nullopt if loading fails
+     */
+    std::optional<Table> loadTable(const fs::path& path);
+};
+
+} // namespace bios
+} // namespace responder
+} // namespace pldm
\ No newline at end of file
diff --git a/libpldmresponder/bios_table.cpp b/libpldmresponder/bios_table.cpp
index e68884b..4a190f0 100644
--- a/libpldmresponder/bios_table.cpp
+++ b/libpldmresponder/bios_table.cpp
@@ -82,6 +82,16 @@
 namespace table
 {
 
+void appendPadAndChecksum(Table& table)
+{
+    auto sizeWithoutPad = table.size();
+    auto padAndChecksumSize = pldm_bios_table_pad_checksum_size(sizeWithoutPad);
+    table.resize(table.size() + padAndChecksumSize);
+
+    pldm_bios_table_append_pad_checksum(table.data(), table.size(),
+                                        sizeWithoutPad);
+}
+
 namespace string
 {
 
@@ -98,6 +108,17 @@
                                                buffer.size());
     return std::string(buffer.data(), buffer.data() + strLength);
 }
+const pldm_bios_string_table_entry* constructEntry(Table& table,
+                                                   const std::string& str)
+{
+    auto tableSize = table.size();
+    auto entryLength = pldm_bios_table_string_entry_encode_length(str.length());
+    table.resize(tableSize + entryLength);
+    pldm_bios_table_string_entry_encode(table.data() + tableSize, entryLength,
+                                        str.c_str(), str.length());
+    return reinterpret_cast<pldm_bios_string_table_entry*>(table.data() +
+                                                           tableSize);
+}
 
 } // namespace string
 
@@ -112,6 +133,13 @@
     return {attrHandle, attrType, stringHandle};
 }
 
+const pldm_bios_attr_table_entry* findByHandle(const Table& table,
+                                               uint16_t handle)
+{
+    return pldm_bios_table_attr_find_by_handle(table.data(), table.size(),
+                                               handle);
+}
+
 const pldm_bios_attr_table_entry*
     constructStringEntry(Table& table,
                          pldm_bios_table_attr_entry_string_info* info)
@@ -179,6 +207,30 @@
     return reinterpret_cast<pldm_bios_attr_val_table_entry*>(table.data() +
                                                              tableSize);
 }
+std::optional<Table> updateTable(const Table& table, const void* entry,
+                                 size_t size)
+{
+    // Replace the old attribute with the new attribute, the size of table will
+    // change:
+    //   sizeof(newTableBuffer) = srcTableSize + sizeof(newAttribute) -
+    //                      sizeof(oldAttribute) + pad(4-byte alignment, max =
+    //                      3)
+    // For simplicity, we use
+    //   sizeof(newTableBuffer) = srcTableSize + sizeof(newAttribute) + 3
+    size_t destBufferLength = table.size() + size + 3;
+    Table destTable(destBufferLength);
+
+    auto rc = pldm_bios_table_attr_value_copy_and_update(
+        table.data(), table.size(), destTable.data(), &destBufferLength, entry,
+        size);
+    if (rc != PLDM_SUCCESS)
+    {
+        return std::nullopt;
+    }
+    destTable.resize(destBufferLength);
+
+    return destTable;
+}
 
 } // namespace attribute_value
 
diff --git a/libpldmresponder/bios_table.hpp b/libpldmresponder/bios_table.hpp
index d1dc7c9..4a482db 100644
--- a/libpldmresponder/bios_table.hpp
+++ b/libpldmresponder/bios_table.hpp
@@ -3,6 +3,7 @@
 #include <stdint.h>
 
 #include <filesystem>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -137,6 +138,12 @@
 namespace table
 {
 
+/** @brief Append Pad and Checksum
+ *
+ *  @param[in,out] table - table to be appended with pad and checksum
+ */
+void appendPadAndChecksum(Table& table);
+
 namespace string
 {
 
@@ -152,6 +159,15 @@
  */
 std::string decodeString(const pldm_bios_string_table_entry* entry);
 
+/** @brief construct entry of string table at the end of the given
+ *         table
+ *  @param[in,out] table - The given table
+ *  @param[in] str - string itself
+ *  @return pointer to the constructed entry
+ */
+const pldm_bios_string_table_entry* constructEntry(Table& table,
+                                                   const std::string& str);
+
 } // namespace string
 
 namespace attribute
@@ -173,6 +189,14 @@
  */
 TableHeader decodeHeader(const pldm_bios_attr_table_entry* entry);
 
+/** @brief Find attribute entry by handle
+ *  @param[in] table - attribute table
+ *  @param[in] handle - attribute handle
+ *  @return Pointer to the attribute table entry
+ */
+const pldm_bios_attr_table_entry* findByHandle(const Table& table,
+                                               uint16_t handle);
+
 /** @struct StringField
  *  @brief String field of attribute table
  */
@@ -239,6 +263,15 @@
     constructStringEntry(Table& table, uint16_t attrHandle, uint8_t attrType,
                          const std::string& str);
 
+/** @brief construct a table with an new entry
+ *  @param[in] table - the table need to be updated
+ *  @param[in] entry - the new attribute value entry
+ *  @param[in] size - size of the new entry
+ *  @return newly constructed table, std::nullopt if failed
+ */
+std::optional<Table> updateTable(const Table& table, const void* entry,
+                                 size_t size);
+
 } // namespace attribute_value
 
 } // namespace table
diff --git a/libpldmresponder/meson.build b/libpldmresponder/meson.build
index 372cae0..0a5bfa2 100644
--- a/libpldmresponder/meson.build
+++ b/libpldmresponder/meson.build
@@ -12,6 +12,7 @@
   'bios_parser.cpp',
   'bios_attribute.cpp',
   'bios_string_attribute.cpp',
+  'bios_config.cpp',
   'pdr_utils.cpp',
   'pdr.cpp',
   'platform.cpp',