pldm: Meson option for system specific bios attributes

PLDM users who want to use bios attributes based on the system type
can enable this meson option.
With disabled option default bios json files are picked to build
bios attribute tables.

Added meson option "system-specific-bios-json" to add the support
for system specific bios attributes.
Below is the pre-requisite to support system specific bios
attributes:
1. Entity Manager service is active
2. Entity Manager should have Decorator.Compatible interface
   and system type in Names property under this interface
3. BIOS Attribute json files are added into the folder(Named on
   the System Type property value) and should be installed
   under /usr/share/pldm/bios/

With disabled "system-specific-bios-json" option default bios
attributes are populated.

Tested:
Poweron system-specific-bios-json enabled/disabled

Change-Id: I95a953cdb12c344d22f487b83040356a1b5fa937
Signed-off-by: Archana Kakani <archana.kakani@ibm.com>
diff --git a/libpldmresponder/test/libpldmresponder_systemspecific_bios_test.cpp b/libpldmresponder/test/libpldmresponder_systemspecific_bios_test.cpp
new file mode 100644
index 0000000..6d80b9d
--- /dev/null
+++ b/libpldmresponder/test/libpldmresponder_systemspecific_bios_test.cpp
@@ -0,0 +1,476 @@
+#include "common/bios_utils.hpp"
+#include "common/test/mocked_utils.hpp"
+#include "libpldmresponder/bios_config.hpp"
+#include "libpldmresponder/bios_string_attribute.hpp"
+#include "libpldmresponder/oem_handler.hpp"
+#include "libpldmresponder/platform_config.hpp"
+#include "mocked_bios.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <fstream>
+#include <memory>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace pldm::bios::utils;
+using namespace pldm::responder::bios;
+using namespace pldm::utils;
+
+using ::testing::_;
+using ::testing::ElementsAreArray;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::Throw;
+
+class TestSystemSpecificBIOSConfig : public ::testing::Test
+{
+  public:
+    static void SetUpTestCase() // will execute once at the begining of all
+                                // TestSystemSpecificBIOSConfig objects
+    {
+        char tmpdir[] = "/tmp/BIOSTables.XXXXXX";
+        tableDir = fs::path(mkdtemp(tmpdir));
+
+        std::vector<fs::path> paths = {
+            "./bios_jsons/string_attrs.json",
+            "./bios_jsons/integer_attrs.json",
+            "./bios_jsons/enum_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
+                                   // TestSystemSpecificBIOSConfig objects
+    {
+        fs::remove_all(tableDir);
+    }
+
+    static fs::path tableDir;
+    static std::vector<Json> jsons;
+};
+
+fs::path TestSystemSpecificBIOSConfig::tableDir;
+std::vector<Json> TestSystemSpecificBIOSConfig::jsons;
+
+class MockSystemConfig : public pldm::responder::platform_config::Handler
+{
+  public:
+    MockSystemConfig() {}
+    MOCK_METHOD(void, ibmCompatibleAddedCallback, (sdbusplus::message_t&), ());
+    MOCK_METHOD(std::optional<std::filesystem::path>, getPlatformName, ());
+};
+
+TEST_F(TestSystemSpecificBIOSConfig, buildTablesTest)
+{
+    MockdBusHandler dbusHandler;
+    MockSystemConfig mockSystemConfig;
+    std::string biosFilePath("./");
+
+    EXPECT_CALL(mockSystemConfig, getPlatformName())
+        .WillOnce(Return(std::filesystem::path("bios_jsons")));
+
+    BIOSConfig biosConfig(biosFilePath.c_str(), tableDir.c_str(), &dbusHandler,
+                          0, 0, nullptr, nullptr, &mockSystemConfig, []() {});
+    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",
+                                             "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;
+            }
+            case PLDM_BIOS_INTEGER:
+            case PLDM_BIOS_INTEGER_READ_ONLY:
+            {
+                auto integerField = table::attribute::decodeIntegerEntry(entry);
+                EXPECT_EQ(integerField.lowerBound,
+                          jsonEntry->at("lower_bound").get<uint64_t>());
+                EXPECT_EQ(integerField.upperBound,
+                          jsonEntry->at("upper_bound").get<uint64_t>());
+                EXPECT_EQ(integerField.scalarIncrement,
+                          jsonEntry->at("scalar_increment").get<uint32_t>());
+                EXPECT_EQ(integerField.defaultValue,
+                          jsonEntry->at("default_value").get<uint64_t>());
+                break;
+            }
+            case PLDM_BIOS_ENUMERATION:
+            case PLDM_BIOS_ENUMERATION_READ_ONLY:
+            {
+                auto [pvHdls,
+                      defInds] = table::attribute::decodeEnumEntry(entry);
+                auto possibleValues = jsonEntry->at("possible_values")
+                                          .get<std::vector<std::string>>();
+                std::vector<std::string> strings;
+                for (auto pv : pvHdls)
+                {
+                    auto s = biosStringTable.findString(pv);
+                    strings.emplace_back(s);
+                }
+                EXPECT_EQ(strings, possibleValues);
+                EXPECT_EQ(defInds.size(), 1);
+
+                auto defValue = biosStringTable.findString(pvHdls[defInds[0]]);
+                auto defaultValues = jsonEntry->at("default_values")
+                                         .get<std::vector<std::string>>();
+                EXPECT_EQ(defValue, defaultValues[0]);
+
+                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;
+            }
+            case PLDM_BIOS_INTEGER:
+            case PLDM_BIOS_INTEGER_READ_ONLY:
+            {
+                auto value = table::attribute_value::decodeIntegerEntry(entry);
+                auto defValue = jsonEntry->at("default_value").get<uint64_t>();
+                EXPECT_EQ(value, defValue);
+                break;
+            }
+            case PLDM_BIOS_ENUMERATION:
+            case PLDM_BIOS_ENUMERATION_READ_ONLY:
+            {
+                auto indices = table::attribute_value::decodeEnumEntry(entry);
+                EXPECT_EQ(indices.size(), 1);
+                auto possibleValues = jsonEntry->at("possible_values")
+                                          .get<std::vector<std::string>>();
+
+                auto defValues = jsonEntry->at("default_values")
+                                     .get<std::vector<std::string>>();
+                EXPECT_EQ(possibleValues[indices[0]], defValues[0]);
+                break;
+            }
+            default:
+                EXPECT_TRUE(false);
+                break;
+        }
+    }
+}
+
+TEST_F(TestSystemSpecificBIOSConfig, buildTablesSystemSpecificTest)
+{
+    MockdBusHandler dbusHandler;
+    MockSystemConfig mockSystemConfig;
+
+    EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return(""));
+    ON_CALL(dbusHandler, getDbusPropertyVariant(_, _, _))
+        .WillByDefault(Throw(std::exception()));
+
+    BIOSConfig biosConfig("./system_type1/bios_jsons", tableDir.c_str(),
+                          &dbusHandler, 0, 0, nullptr, nullptr,
+                          &mockSystemConfig, []() {});
+
+    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);
+
+    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:
+            {
+                if (attrName == "str_example2")
+                {
+                    auto stringField =
+                        table::attribute::decodeStringEntry(entry);
+                    EXPECT_EQ(stringField.maxLength, 200);
+                }
+
+                break;
+            }
+            case PLDM_BIOS_INTEGER:
+            case PLDM_BIOS_INTEGER_READ_ONLY:
+            {
+                if (attrName == "SBE_IMAGE_MINIMUM_VALID_ECS")
+                {
+                    auto integerField =
+                        table::attribute::decodeIntegerEntry(entry);
+                    EXPECT_EQ(integerField.upperBound, 30);
+                }
+                break;
+            }
+            case PLDM_BIOS_ENUMERATION:
+            case PLDM_BIOS_ENUMERATION_READ_ONLY:
+            {
+                if (attrName == "FWBootSide")
+                {
+                    auto [pvHdls,
+                          defInds] = table::attribute::decodeEnumEntry(entry);
+                    auto defValue =
+                        biosStringTable.findString(pvHdls[defInds[0]]);
+                    EXPECT_EQ(defValue, "Temp");
+                }
+            }
+        }
+    }
+}
+
+TEST_F(TestSystemSpecificBIOSConfig, setBIOSTable)
+{
+    MockdBusHandler dbusHandler;
+    MockSystemConfig mockSystemConfig;
+
+    EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return("jsons"));
+    BIOSConfig biosConfig("./", tableDir.c_str(), &dbusHandler, 0, 0, nullptr,
+                          nullptr, &mockSystemConfig, []() {});
+
+    std::set<std::string> strings{"pvm_system_name", "pvm_stop_at_standby",
+                                  "fw_boot_side", "fw_next_boot_side"};
+
+    Table table;
+    for (const auto& elem : strings)
+    {
+        table::string::constructEntry(table, elem);
+    }
+
+    table::appendPadAndChecksum(table);
+    auto rc = biosConfig.setBIOSTable(PLDM_BIOS_STRING_TABLE, table);
+    EXPECT_EQ(rc, PLDM_SUCCESS);
+
+    auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE);
+    EXPECT_TRUE(stringTable);
+}
+
+TEST_F(TestSystemSpecificBIOSConfig, getBIOSTableFailure)
+{
+    MockdBusHandler dbusHandler;
+    MockSystemConfig mockSystemConfig;
+
+    EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return("jsons"));
+    BIOSConfig biosConfig("./", tableDir.c_str(), &dbusHandler, 0, 0, nullptr,
+                          nullptr, &mockSystemConfig, []() {});
+
+    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_FALSE(stringTable);
+    EXPECT_FALSE(attrTable);
+    EXPECT_FALSE(attrValueTable);
+}
+
+TEST_F(TestSystemSpecificBIOSConfig, setAttrValueFailure)
+{
+    MockdBusHandler dbusHandler;
+    MockSystemConfig mockSystemConfig;
+
+    EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return("jsons"));
+    BIOSConfig biosConfig("./", tableDir.c_str(), &dbusHandler, 0, 0, nullptr,
+                          nullptr, &mockSystemConfig, []() {});
+
+    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 */
+    };
+
+    uint16_t attrHandle{10};
+    attrValueEntry[0] = attrHandle & 0xff;
+    attrValueEntry[1] = (attrHandle >> 8) & 0xff;
+
+    auto rc = biosConfig.setAttrValue(attrValueEntry.data(),
+                                      attrValueEntry.size(), false);
+    std::cout << "Error in settig Attribute " << rc << std::endl;
+    EXPECT_EQ(rc, PLDM_BIOS_TABLE_UNAVAILABLE);
+}
+
+TEST_F(TestSystemSpecificBIOSConfig, setAttrValue)
+{
+    MockdBusHandler dbusHandler;
+    MockSystemConfig mockSystemConfig;
+
+    EXPECT_CALL(mockSystemConfig, getPlatformName())
+        .WillOnce(Return(std::filesystem::path("")));
+
+    BIOSConfig biosConfig("./bios_jsons", tableDir.c_str(), &dbusHandler, 0, 0,
+                          nullptr, nullptr, &mockSystemConfig, []() {});
+
+    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(), false);
+    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));
+}