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