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',
diff --git a/test/libpldmresponder_bios_config_test.cpp b/test/libpldmresponder_bios_config_test.cpp
new file mode 100644
index 0000000..2a2b72f
--- /dev/null
+++ b/test/libpldmresponder_bios_config_test.cpp
@@ -0,0 +1,255 @@
+#include "bios_utils.hpp"
+#include "libpldmresponder/bios_config.hpp"
+#include "libpldmresponder/bios_string_attribute.hpp"
+#include "mocked_bios.hpp"
+#include "mocked_utils.hpp"
+
+#include <fstream>
+#include <memory>
+#include <nlohmann/json.hpp>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace pldm::bios::utils;
+
+using ::testing::_;
+using ::testing::ElementsAreArray;
+using ::testing::Throw;
+
+class TestBIOSConfig : public ::testing::Test
+{
+  public:
+    static void SetUpTestCase() // will execute once at the begining of all
+                                // TestBIOSConfig objects
+    {
+        char tmpdir[] = "/tmp/BIOSTables.XXXXXX";
+        tableDir = fs::path(mkdtemp(tmpdir));
+
+        std::vector<fs::path> paths = {
+            "./bios_jsons/string_attrs.json",
+        };
+
+        for (auto& path : paths)
+        {
+            std::ifstream file;
+            file.open(path);
+            auto j = Json::parse(file);
+            jsons.emplace_back(j);
+        }
+    }
+
+    std::optional<Json> findJsonEntry(const std::string& name)
+    {
+        for (auto& json : jsons)
+        {
+            auto entries = json.at("entries");
+            for (auto& entry : entries)
+            {
+                auto n = entry.at("attribute_name").get<std::string>();
+                if (n == name)
+                {
+                    return entry;
+                }
+            }
+        }
+        return std::nullopt;
+    }
+
+    static void TearDownTestCase() // will be executed once at th end of all
+                                   // TestBIOSConfig objects
+    {
+        fs::remove_all(tableDir);
+    }
+
+    static fs::path tableDir;
+    static std::vector<Json> jsons;
+};
+
+fs::path TestBIOSConfig::tableDir;
+std::vector<Json> TestBIOSConfig::jsons;
+
+TEST_F(TestBIOSConfig, buildTablesTest)
+{
+    MockdBusHandler dbusHandler;
+
+    ON_CALL(dbusHandler, getDbusPropertyVariant(_, _, _))
+        .WillByDefault(Throw(std::exception()));
+
+    BIOSConfig biosConfig("./bios_jsons", tableDir.c_str(), &dbusHandler);
+    biosConfig.buildTables();
+
+    auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE);
+    auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE);
+    auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
+
+    EXPECT_TRUE(stringTable);
+    EXPECT_TRUE(attrTable);
+    EXPECT_TRUE(attrValueTable);
+
+    std::set<std::string> expectedStrings = {"HMCManagedState",
+                                             "On",
+                                             "Off",
+                                             "FWBootSide",
+                                             "Perm",
+                                             "Temp",
+                                             "InbandCodeUpdate",
+                                             "Allowed",
+                                             "NotAllowed",
+                                             "CodeUpdatePolicy",
+                                             "Concurrent",
+                                             "Disruptive",
+                                             "VDD_AVSBUS_RAIL",
+                                             "SBE_IMAGE_MINIMUM_VALID_ECS",
+                                             "INTEGER_INVALID_CASE",
+                                             "str_example1",
+                                             "str_example2",
+                                             "str_example3"};
+    std::set<std::string> strings;
+    for (auto entry : BIOSTableIter<PLDM_BIOS_STRING_TABLE>(
+             stringTable->data(), stringTable->size()))
+    {
+        auto str = table::string::decodeString(entry);
+        strings.emplace(str);
+    }
+
+    EXPECT_EQ(strings, expectedStrings);
+
+    BIOSStringTable biosStringTable(*stringTable);
+
+    for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(),
+                                                          attrTable->size()))
+    {
+        auto header = table::attribute::decodeHeader(entry);
+        auto attrName = biosStringTable.findString(header.stringHandle);
+        auto jsonEntry = findJsonEntry(attrName);
+        EXPECT_TRUE(jsonEntry);
+        switch (header.attrType)
+        {
+            case PLDM_BIOS_STRING:
+            case PLDM_BIOS_STRING_READ_ONLY:
+            {
+                auto stringField = table::attribute::decodeStringEntry(entry);
+                auto stringType = BIOSStringAttribute::strTypeMap.at(
+                    jsonEntry->at("string_type").get<std::string>());
+                EXPECT_EQ(stringField.stringType,
+                          static_cast<uint8_t>(stringType));
+
+                EXPECT_EQ(
+                    stringField.minLength,
+                    jsonEntry->at("minimum_string_length").get<uint16_t>());
+                EXPECT_EQ(
+                    stringField.maxLength,
+                    jsonEntry->at("maximum_string_length").get<uint16_t>());
+                EXPECT_EQ(
+                    stringField.defLength,
+                    jsonEntry->at("default_string_length").get<uint16_t>());
+                EXPECT_EQ(stringField.defString,
+                          jsonEntry->at("default_string").get<std::string>());
+                break;
+            }
+            default:
+                EXPECT_TRUE(false);
+                break;
+        }
+    }
+
+    for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_VAL_TABLE>(
+             attrValueTable->data(), attrValueTable->size()))
+    {
+        auto header = table::attribute_value::decodeHeader(entry);
+        auto attrEntry =
+            table::attribute::findByHandle(*attrTable, header.attrHandle);
+        auto attrHeader = table::attribute::decodeHeader(attrEntry);
+        auto attrName = biosStringTable.findString(attrHeader.stringHandle);
+        auto jsonEntry = findJsonEntry(attrName);
+        EXPECT_TRUE(jsonEntry);
+        switch (header.attrType)
+        {
+            case PLDM_BIOS_STRING:
+            case PLDM_BIOS_STRING_READ_ONLY:
+            {
+                auto value = table::attribute_value::decodeStringEntry(entry);
+                auto defValue =
+                    jsonEntry->at("default_string").get<std::string>();
+                EXPECT_EQ(value, defValue);
+                break;
+            }
+            default:
+                EXPECT_TRUE(false);
+                break;
+        }
+    }
+}
+
+TEST_F(TestBIOSConfig, setAttrValue)
+{
+    MockdBusHandler dbusHandler;
+
+    BIOSConfig biosConfig("./bios_jsons", tableDir.c_str(), &dbusHandler);
+    biosConfig.removeTables();
+    biosConfig.buildTables();
+
+    auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE);
+    auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE);
+
+    BIOSStringTable biosStringTable(*stringTable);
+    BIOSTableIter<PLDM_BIOS_ATTR_TABLE> attrTableIter(attrTable->data(),
+                                                      attrTable->size());
+    auto stringHandle = biosStringTable.findHandle("str_example1");
+    uint16_t attrHandle{};
+
+    for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(),
+                                                          attrTable->size()))
+    {
+        auto header = table::attribute::decodeHeader(entry);
+        if (header.stringHandle == stringHandle)
+        {
+            attrHandle = header.attrHandle;
+            break;
+        }
+    }
+
+    EXPECT_NE(attrHandle, 0);
+
+    std::vector<uint8_t> attrValueEntry{
+        0,   0,             /* attr handle */
+        1,                  /* attr type string read-write */
+        4,   0,             /* current string length */
+        'a', 'b', 'c', 'd', /* defaut value string handle index */
+    };
+
+    attrValueEntry[0] = attrHandle & 0xff;
+    attrValueEntry[1] = (attrHandle >> 8) & 0xff;
+
+    DBusMapping dbusMapping{"/xyz/abc/def",
+                            "xyz.openbmc_project.str_example1.value",
+                            "Str_example1", "string"};
+    PropertyValue value = std::string("abcd");
+    EXPECT_CALL(dbusHandler, setDbusProperty(dbusMapping, value)).Times(1);
+
+    auto rc =
+        biosConfig.setAttrValue(attrValueEntry.data(), attrValueEntry.size());
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
+    auto findEntry =
+        [&attrValueTable](
+            uint16_t handle) -> const pldm_bios_attr_val_table_entry* {
+        for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_VAL_TABLE>(
+                 attrValueTable->data(), attrValueTable->size()))
+        {
+            auto [attrHandle, _] = table::attribute_value::decodeHeader(entry);
+            if (attrHandle == handle)
+                return entry;
+        }
+        return nullptr;
+    };
+
+    auto entry = findEntry(attrHandle);
+    EXPECT_NE(entry, nullptr);
+
+    auto p = reinterpret_cast<const uint8_t*>(entry);
+    EXPECT_THAT(std::vector<uint8_t>(p, p + attrValueEntry.size()),
+                ElementsAreArray(attrValueEntry));
+}
diff --git a/test/meson.build b/test/meson.build
index 827f9fc..5c03866 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -21,6 +21,7 @@
   'libpldmresponder_bios_test',
   'libpldmresponder_bios_attribute_test',
   'libpldmresponder_bios_string_attribute_test',
+  'libpldmresponder_bios_config_test',
   'libpldmresponder_pdr_state_effecter_test',
   'libpldmresponder_bios_table_test',
   'libpldmresponder_platform_test',