1 #include "common/bios_utils.hpp" 2 #include "common/test/mocked_utils.hpp" 3 #include "libpldmresponder/bios_config.hpp" 4 #include "libpldmresponder/bios_string_attribute.hpp" 5 #include "libpldmresponder/oem_handler.hpp" 6 #include "libpldmresponder/platform_config.hpp" 7 #include "mocked_bios.hpp" 8 9 #include <nlohmann/json.hpp> 10 11 #include <fstream> 12 #include <memory> 13 14 #include <gmock/gmock.h> 15 #include <gtest/gtest.h> 16 17 using namespace pldm::bios::utils; 18 using namespace pldm::responder::bios; 19 using namespace pldm::utils; 20 21 using ::testing::_; 22 using ::testing::ElementsAreArray; 23 using ::testing::Return; 24 using ::testing::StrEq; 25 using ::testing::Throw; 26 27 class TestBIOSConfig : public ::testing::Test 28 { 29 public: 30 static void SetUpTestCase() // will execute once at the begining of all 31 // TestBIOSConfig objects 32 { 33 char tmpdir[] = "/tmp/BIOSTables.XXXXXX"; 34 tableDir = fs::path(mkdtemp(tmpdir)); 35 36 std::vector<fs::path> paths = { 37 "./bios_jsons/string_attrs.json", 38 "./bios_jsons/integer_attrs.json", 39 "./bios_jsons/enum_attrs.json", 40 }; 41 42 for (auto& path : paths) 43 { 44 std::ifstream file; 45 file.open(path); 46 auto j = Json::parse(file); 47 jsons.emplace_back(j); 48 } 49 } 50 51 std::optional<Json> findJsonEntry(const std::string& name) 52 { 53 for (auto& json : jsons) 54 { 55 auto entries = json.at("entries"); 56 for (auto& entry : entries) 57 { 58 auto n = entry.at("attribute_name").get<std::string>(); 59 if (n == name) 60 { 61 return entry; 62 } 63 } 64 } 65 return std::nullopt; 66 } 67 68 static void TearDownTestCase() // will be executed once at th end of all 69 // TestBIOSConfig objects 70 { 71 fs::remove_all(tableDir); 72 } 73 74 static fs::path tableDir; 75 static std::vector<Json> jsons; 76 }; 77 78 fs::path TestBIOSConfig::tableDir; 79 std::vector<Json> TestBIOSConfig::jsons; 80 81 class MockSystemConfig : public pldm::responder::platform_config::Handler 82 { 83 public: 84 MockSystemConfig() {} 85 MOCK_METHOD(void, ibmCompatibleAddedCallback, (sdbusplus::message_t&), ()); 86 MOCK_METHOD(std::optional<std::filesystem::path>, getPlatformName, ()); 87 }; 88 89 TEST_F(TestBIOSConfig, buildTablesTest) 90 { 91 MockdBusHandler dbusHandler; 92 MockSystemConfig mockSystemConfig; 93 94 EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return("")); 95 ON_CALL(dbusHandler, getDbusPropertyVariant(_, _, _)) 96 .WillByDefault(Throw(std::exception())); 97 98 BIOSConfig biosConfig("./bios_jsons", tableDir.c_str(), &dbusHandler, 0, 0, 99 nullptr, nullptr, &mockSystemConfig); 100 biosConfig.buildTables(); 101 auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE); 102 auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE); 103 auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE); 104 105 EXPECT_TRUE(stringTable); 106 EXPECT_TRUE(attrTable); 107 EXPECT_TRUE(attrValueTable); 108 109 std::set<std::string> expectedStrings = {"HMCManagedState", 110 "On", 111 "Off", 112 "FWBootSide", 113 "Perm", 114 "Temp", 115 "InbandCodeUpdate", 116 "Allowed", 117 "Allowed", 118 "NotAllowed", 119 "CodeUpdatePolicy", 120 "Concurrent", 121 "Disruptive", 122 "VDD_AVSBUS_RAIL", 123 "SBE_IMAGE_MINIMUM_VALID_ECS", 124 "INTEGER_INVALID_CASE", 125 "str_example1", 126 "str_example2", 127 "str_example3"}; 128 std::set<std::string> strings; 129 for (auto entry : BIOSTableIter<PLDM_BIOS_STRING_TABLE>( 130 stringTable->data(), stringTable->size())) 131 { 132 auto str = table::string::decodeString(entry); 133 strings.emplace(str); 134 } 135 136 EXPECT_EQ(strings, expectedStrings); 137 138 BIOSStringTable biosStringTable(*stringTable); 139 140 for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(), 141 attrTable->size())) 142 { 143 auto header = table::attribute::decodeHeader(entry); 144 auto attrName = biosStringTable.findString(header.stringHandle); 145 auto jsonEntry = findJsonEntry(attrName); 146 EXPECT_TRUE(jsonEntry); 147 switch (header.attrType) 148 { 149 case PLDM_BIOS_STRING: 150 case PLDM_BIOS_STRING_READ_ONLY: 151 { 152 auto stringField = table::attribute::decodeStringEntry(entry); 153 auto stringType = BIOSStringAttribute::strTypeMap.at( 154 jsonEntry->at("string_type").get<std::string>()); 155 EXPECT_EQ(stringField.stringType, 156 static_cast<uint8_t>(stringType)); 157 158 EXPECT_EQ( 159 stringField.minLength, 160 jsonEntry->at("minimum_string_length").get<uint16_t>()); 161 EXPECT_EQ( 162 stringField.maxLength, 163 jsonEntry->at("maximum_string_length").get<uint16_t>()); 164 EXPECT_EQ( 165 stringField.defLength, 166 jsonEntry->at("default_string_length").get<uint16_t>()); 167 EXPECT_EQ(stringField.defString, 168 jsonEntry->at("default_string").get<std::string>()); 169 break; 170 } 171 case PLDM_BIOS_INTEGER: 172 case PLDM_BIOS_INTEGER_READ_ONLY: 173 { 174 auto integerField = table::attribute::decodeIntegerEntry(entry); 175 EXPECT_EQ(integerField.lowerBound, 176 jsonEntry->at("lower_bound").get<uint64_t>()); 177 EXPECT_EQ(integerField.upperBound, 178 jsonEntry->at("upper_bound").get<uint64_t>()); 179 EXPECT_EQ(integerField.scalarIncrement, 180 jsonEntry->at("scalar_increment").get<uint32_t>()); 181 EXPECT_EQ(integerField.defaultValue, 182 jsonEntry->at("default_value").get<uint64_t>()); 183 break; 184 } 185 case PLDM_BIOS_ENUMERATION: 186 case PLDM_BIOS_ENUMERATION_READ_ONLY: 187 { 188 auto [pvHdls, 189 defInds] = table::attribute::decodeEnumEntry(entry); 190 auto possibleValues = jsonEntry->at("possible_values") 191 .get<std::vector<std::string>>(); 192 std::vector<std::string> strings; 193 for (auto pv : pvHdls) 194 { 195 auto s = biosStringTable.findString(pv); 196 strings.emplace_back(s); 197 } 198 EXPECT_EQ(strings, possibleValues); 199 EXPECT_EQ(defInds.size(), 1); 200 201 auto defValue = biosStringTable.findString(pvHdls[defInds[0]]); 202 auto defaultValues = jsonEntry->at("default_values") 203 .get<std::vector<std::string>>(); 204 EXPECT_EQ(defValue, defaultValues[0]); 205 206 break; 207 } 208 default: 209 EXPECT_TRUE(false); 210 break; 211 } 212 } 213 214 for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_VAL_TABLE>( 215 attrValueTable->data(), attrValueTable->size())) 216 { 217 auto header = table::attribute_value::decodeHeader(entry); 218 auto attrEntry = table::attribute::findByHandle(*attrTable, 219 header.attrHandle); 220 auto attrHeader = table::attribute::decodeHeader(attrEntry); 221 auto attrName = biosStringTable.findString(attrHeader.stringHandle); 222 auto jsonEntry = findJsonEntry(attrName); 223 EXPECT_TRUE(jsonEntry); 224 switch (header.attrType) 225 { 226 case PLDM_BIOS_STRING: 227 case PLDM_BIOS_STRING_READ_ONLY: 228 { 229 auto value = table::attribute_value::decodeStringEntry(entry); 230 auto defValue = 231 jsonEntry->at("default_string").get<std::string>(); 232 EXPECT_EQ(value, defValue); 233 break; 234 } 235 case PLDM_BIOS_INTEGER: 236 case PLDM_BIOS_INTEGER_READ_ONLY: 237 { 238 auto value = table::attribute_value::decodeIntegerEntry(entry); 239 auto defValue = jsonEntry->at("default_value").get<uint64_t>(); 240 EXPECT_EQ(value, defValue); 241 break; 242 } 243 case PLDM_BIOS_ENUMERATION: 244 case PLDM_BIOS_ENUMERATION_READ_ONLY: 245 { 246 auto indices = table::attribute_value::decodeEnumEntry(entry); 247 EXPECT_EQ(indices.size(), 1); 248 auto possibleValues = jsonEntry->at("possible_values") 249 .get<std::vector<std::string>>(); 250 251 auto defValues = jsonEntry->at("default_values") 252 .get<std::vector<std::string>>(); 253 EXPECT_EQ(possibleValues[indices[0]], defValues[0]); 254 break; 255 } 256 default: 257 EXPECT_TRUE(false); 258 break; 259 } 260 } 261 } 262 263 TEST_F(TestBIOSConfig, buildTablesSystemSpecificTest) 264 { 265 MockdBusHandler dbusHandler; 266 MockSystemConfig mockSystemConfig; 267 268 EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return("")); 269 ON_CALL(dbusHandler, getDbusPropertyVariant(_, _, _)) 270 .WillByDefault(Throw(std::exception())); 271 272 BIOSConfig biosConfig("./system_type1/bios_jsons", tableDir.c_str(), 273 &dbusHandler, 0, 0, nullptr, nullptr, 274 &mockSystemConfig); 275 276 biosConfig.buildTables(); 277 278 auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE); 279 auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE); 280 auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE); 281 282 EXPECT_TRUE(stringTable); 283 EXPECT_TRUE(attrTable); 284 EXPECT_TRUE(attrValueTable); 285 286 BIOSStringTable biosStringTable(*stringTable); 287 288 for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(), 289 attrTable->size())) 290 { 291 auto header = table::attribute::decodeHeader(entry); 292 auto attrName = biosStringTable.findString(header.stringHandle); 293 auto jsonEntry = findJsonEntry(attrName); 294 EXPECT_TRUE(jsonEntry); 295 switch (header.attrType) 296 { 297 case PLDM_BIOS_STRING: 298 case PLDM_BIOS_STRING_READ_ONLY: 299 { 300 if (attrName == "str_example2") 301 { 302 auto stringField = 303 table::attribute::decodeStringEntry(entry); 304 EXPECT_EQ(stringField.maxLength, 200); 305 } 306 307 break; 308 } 309 case PLDM_BIOS_INTEGER: 310 case PLDM_BIOS_INTEGER_READ_ONLY: 311 { 312 if (attrName == "SBE_IMAGE_MINIMUM_VALID_ECS") 313 { 314 auto integerField = 315 table::attribute::decodeIntegerEntry(entry); 316 EXPECT_EQ(integerField.upperBound, 30); 317 } 318 break; 319 } 320 case PLDM_BIOS_ENUMERATION: 321 case PLDM_BIOS_ENUMERATION_READ_ONLY: 322 { 323 if (attrName == "FWBootSide") 324 { 325 auto [pvHdls, 326 defInds] = table::attribute::decodeEnumEntry(entry); 327 auto defValue = 328 biosStringTable.findString(pvHdls[defInds[0]]); 329 EXPECT_EQ(defValue, "Temp"); 330 } 331 } 332 } 333 } 334 } 335 336 TEST_F(TestBIOSConfig, setAttrValue) 337 { 338 MockdBusHandler dbusHandler; 339 MockSystemConfig mockSystemConfig; 340 341 EXPECT_CALL(mockSystemConfig, getPlatformName()).WillOnce(Return("")); 342 BIOSConfig biosConfig("./bios_jsons", tableDir.c_str(), &dbusHandler, 0, 0, 343 nullptr, nullptr, &mockSystemConfig); 344 biosConfig.removeTables(); 345 biosConfig.buildTables(); 346 347 auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE); 348 auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE); 349 350 BIOSStringTable biosStringTable(*stringTable); 351 BIOSTableIter<PLDM_BIOS_ATTR_TABLE> attrTableIter(attrTable->data(), 352 attrTable->size()); 353 auto stringHandle = biosStringTable.findHandle("str_example1"); 354 uint16_t attrHandle{}; 355 356 for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(), 357 attrTable->size())) 358 { 359 auto header = table::attribute::decodeHeader(entry); 360 if (header.stringHandle == stringHandle) 361 { 362 attrHandle = header.attrHandle; 363 break; 364 } 365 } 366 367 EXPECT_NE(attrHandle, 0); 368 369 std::vector<uint8_t> attrValueEntry{ 370 0, 0, /* attr handle */ 371 1, /* attr type string read-write */ 372 4, 0, /* current string length */ 373 'a', 'b', 'c', 'd', /* defaut value string handle index */ 374 }; 375 376 attrValueEntry[0] = attrHandle & 0xff; 377 attrValueEntry[1] = (attrHandle >> 8) & 0xff; 378 379 DBusMapping dbusMapping{"/xyz/abc/def", 380 "xyz.openbmc_project.str_example1.value", 381 "Str_example1", "string"}; 382 PropertyValue value = std::string("abcd"); 383 EXPECT_CALL(dbusHandler, setDbusProperty(dbusMapping, value)).Times(1); 384 385 auto rc = biosConfig.setAttrValue(attrValueEntry.data(), 386 attrValueEntry.size(), false); 387 EXPECT_EQ(rc, PLDM_SUCCESS); 388 389 auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE); 390 auto findEntry = 391 [&attrValueTable]( 392 uint16_t handle) -> const pldm_bios_attr_val_table_entry* { 393 for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_VAL_TABLE>( 394 attrValueTable->data(), attrValueTable->size())) 395 { 396 auto [attrHandle, _] = table::attribute_value::decodeHeader(entry); 397 if (attrHandle == handle) 398 return entry; 399 } 400 return nullptr; 401 }; 402 403 auto entry = findEntry(attrHandle); 404 EXPECT_NE(entry, nullptr); 405 406 auto p = reinterpret_cast<const uint8_t*>(entry); 407 EXPECT_THAT(std::vector<uint8_t>(p, p + attrValueEntry.size()), 408 ElementsAreArray(attrValueEntry)); 409 } 410