#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 #include #include #include #include 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 beginning of all // TestSystemSpecificBIOSConfig objects { char tmpdir[] = "/tmp/BIOSTables.XXXXXX"; tableDir = fs::path(mkdtemp(tmpdir)); std::vector 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 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(); 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 jsons; }; fs::path TestSystemSpecificBIOSConfig::tableDir; std::vector TestSystemSpecificBIOSConfig::jsons; class MockSystemConfig : public pldm::responder::platform_config::Handler { public: MockSystemConfig() {} MOCK_METHOD(void, ibmCompatibleAddedCallback, (sdbusplus::message_t&), ()); MOCK_METHOD(std::optional, 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 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 strings; for (auto entry : BIOSTableIter( stringTable->data(), stringTable->size())) { auto str = table::string::decodeString(entry); strings.emplace(str); } EXPECT_EQ(strings, expectedStrings); BIOSStringTable biosStringTable(*stringTable); for (auto entry : BIOSTableIter(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()); EXPECT_EQ(stringField.stringType, static_cast(stringType)); EXPECT_EQ( stringField.minLength, jsonEntry->at("minimum_string_length").get()); EXPECT_EQ( stringField.maxLength, jsonEntry->at("maximum_string_length").get()); EXPECT_EQ(stringField.defString, jsonEntry->at("default_string").get()); EXPECT_EQ(stringField.defLength, (stringField.defString).length()); 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()); EXPECT_EQ(integerField.upperBound, jsonEntry->at("upper_bound").get()); EXPECT_EQ(integerField.scalarIncrement, jsonEntry->at("scalar_increment").get()); EXPECT_EQ(integerField.defaultValue, jsonEntry->at("default_value").get()); 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 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>(); EXPECT_EQ(defValue, defaultValues[0]); break; } default: EXPECT_TRUE(false); break; } } for (auto entry : BIOSTableIter( 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(); 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(); 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>(); auto defValues = jsonEntry->at("default_values") .get>(); 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(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 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 attrValueEntry{ 0, 0, /* attr handle */ 1, /* attr type string read-write */ 4, 0, /* current string length */ 'a', 'b', 'c', 'd', /* default 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 setting 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 attrTableIter(attrTable->data(), attrTable->size()); auto stringHandle = biosStringTable.findHandle("str_example1"); uint16_t attrHandle{}; for (auto entry : BIOSTableIter(attrTable->data(), attrTable->size())) { auto header = table::attribute::decodeHeader(entry); if (header.stringHandle == stringHandle) { attrHandle = header.attrHandle; break; } } EXPECT_NE(attrHandle, 0); std::vector attrValueEntry{ 0, 0, /* attr handle */ 1, /* attr type string read-write */ 4, 0, /* current string length */ 'a', 'b', 'c', 'd', /* default 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( 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(entry); EXPECT_THAT(std::vector(p, p + attrValueEntry.size()), ElementsAreArray(attrValueEntry)); }