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, (sdbusplus::message_t&), ()); 87 MOCK_METHOD(std::optional<std::string>, getPlatformName, ()); 88 }; 89 90 TEST_F(TestBIOSConfig, buildTablesTest) 91 { 92 MockdBusHandler dbusHandler; 93 94 MockBiosSystemConfig mockBiosSystemConfig(&dbusHandler); 95 96 ON_CALL(dbusHandler, getDbusPropertyVariant(_, _, _)) 97 .WillByDefault(Throw(std::exception())); 98 99 BIOSConfig biosConfig("./bios_jsons", tableDir.c_str(), &dbusHandler, 0, 0, 100 nullptr, nullptr, &mockBiosSystemConfig); 101 biosConfig.buildTables(); 102 103 auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE); 104 auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE); 105 auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE); 106 107 EXPECT_TRUE(stringTable); 108 EXPECT_TRUE(attrTable); 109 EXPECT_TRUE(attrValueTable); 110 111 std::set<std::string> expectedStrings = {"HMCManagedState", 112 "On", 113 "Off", 114 "FWBootSide", 115 "Perm", 116 "Temp", 117 "InbandCodeUpdate", 118 "Allowed", 119 "Allowed", 120 "NotAllowed", 121 "CodeUpdatePolicy", 122 "Concurrent", 123 "Disruptive", 124 "VDD_AVSBUS_RAIL", 125 "SBE_IMAGE_MINIMUM_VALID_ECS", 126 "INTEGER_INVALID_CASE", 127 "str_example1", 128 "str_example2", 129 "str_example3"}; 130 std::set<std::string> strings; 131 for (auto entry : BIOSTableIter<PLDM_BIOS_STRING_TABLE>( 132 stringTable->data(), stringTable->size())) 133 { 134 auto str = table::string::decodeString(entry); 135 strings.emplace(str); 136 } 137 138 EXPECT_EQ(strings, expectedStrings); 139 140 BIOSStringTable biosStringTable(*stringTable); 141 142 for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(), 143 attrTable->size())) 144 { 145 auto header = table::attribute::decodeHeader(entry); 146 auto attrName = biosStringTable.findString(header.stringHandle); 147 auto jsonEntry = findJsonEntry(attrName); 148 EXPECT_TRUE(jsonEntry); 149 switch (header.attrType) 150 { 151 case PLDM_BIOS_STRING: 152 case PLDM_BIOS_STRING_READ_ONLY: 153 { 154 auto stringField = table::attribute::decodeStringEntry(entry); 155 auto stringType = BIOSStringAttribute::strTypeMap.at( 156 jsonEntry->at("string_type").get<std::string>()); 157 EXPECT_EQ(stringField.stringType, 158 static_cast<uint8_t>(stringType)); 159 160 EXPECT_EQ( 161 stringField.minLength, 162 jsonEntry->at("minimum_string_length").get<uint16_t>()); 163 EXPECT_EQ( 164 stringField.maxLength, 165 jsonEntry->at("maximum_string_length").get<uint16_t>()); 166 EXPECT_EQ( 167 stringField.defLength, 168 jsonEntry->at("default_string_length").get<uint16_t>()); 169 EXPECT_EQ(stringField.defString, 170 jsonEntry->at("default_string").get<std::string>()); 171 break; 172 } 173 case PLDM_BIOS_INTEGER: 174 case PLDM_BIOS_INTEGER_READ_ONLY: 175 { 176 auto integerField = table::attribute::decodeIntegerEntry(entry); 177 EXPECT_EQ(integerField.lowerBound, 178 jsonEntry->at("lower_bound").get<uint64_t>()); 179 EXPECT_EQ(integerField.upperBound, 180 jsonEntry->at("upper_bound").get<uint64_t>()); 181 EXPECT_EQ(integerField.scalarIncrement, 182 jsonEntry->at("scalar_increment").get<uint32_t>()); 183 EXPECT_EQ(integerField.defaultValue, 184 jsonEntry->at("default_value").get<uint64_t>()); 185 break; 186 } 187 case PLDM_BIOS_ENUMERATION: 188 case PLDM_BIOS_ENUMERATION_READ_ONLY: 189 { 190 auto [pvHdls, 191 defInds] = table::attribute::decodeEnumEntry(entry); 192 auto possibleValues = jsonEntry->at("possible_values") 193 .get<std::vector<std::string>>(); 194 std::vector<std::string> strings; 195 for (auto pv : pvHdls) 196 { 197 auto s = biosStringTable.findString(pv); 198 strings.emplace_back(s); 199 } 200 EXPECT_EQ(strings, possibleValues); 201 EXPECT_EQ(defInds.size(), 1); 202 203 auto defValue = biosStringTable.findString(pvHdls[defInds[0]]); 204 auto defaultValues = jsonEntry->at("default_values") 205 .get<std::vector<std::string>>(); 206 EXPECT_EQ(defValue, defaultValues[0]); 207 208 break; 209 } 210 default: 211 EXPECT_TRUE(false); 212 break; 213 } 214 } 215 216 for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_VAL_TABLE>( 217 attrValueTable->data(), attrValueTable->size())) 218 { 219 auto header = table::attribute_value::decodeHeader(entry); 220 auto attrEntry = table::attribute::findByHandle(*attrTable, 221 header.attrHandle); 222 auto attrHeader = table::attribute::decodeHeader(attrEntry); 223 auto attrName = biosStringTable.findString(attrHeader.stringHandle); 224 auto jsonEntry = findJsonEntry(attrName); 225 EXPECT_TRUE(jsonEntry); 226 switch (header.attrType) 227 { 228 case PLDM_BIOS_STRING: 229 case PLDM_BIOS_STRING_READ_ONLY: 230 { 231 auto value = table::attribute_value::decodeStringEntry(entry); 232 auto defValue = 233 jsonEntry->at("default_string").get<std::string>(); 234 EXPECT_EQ(value, defValue); 235 break; 236 } 237 case PLDM_BIOS_INTEGER: 238 case PLDM_BIOS_INTEGER_READ_ONLY: 239 { 240 auto value = table::attribute_value::decodeIntegerEntry(entry); 241 auto defValue = jsonEntry->at("default_value").get<uint64_t>(); 242 EXPECT_EQ(value, defValue); 243 break; 244 } 245 case PLDM_BIOS_ENUMERATION: 246 case PLDM_BIOS_ENUMERATION_READ_ONLY: 247 { 248 auto indices = table::attribute_value::decodeEnumEntry(entry); 249 EXPECT_EQ(indices.size(), 1); 250 auto possibleValues = jsonEntry->at("possible_values") 251 .get<std::vector<std::string>>(); 252 253 auto defValues = jsonEntry->at("default_values") 254 .get<std::vector<std::string>>(); 255 EXPECT_EQ(possibleValues[indices[0]], defValues[0]); 256 break; 257 } 258 default: 259 EXPECT_TRUE(false); 260 break; 261 } 262 } 263 } 264 265 TEST_F(TestBIOSConfig, buildTablesSystemSpecificTest) 266 { 267 MockdBusHandler dbusHandler; 268 269 MockBiosSystemConfig mockBiosSystemConfig(&dbusHandler); 270 271 ON_CALL(dbusHandler, getDbusPropertyVariant(_, _, _)) 272 .WillByDefault(Throw(std::exception())); 273 274 BIOSConfig biosConfig("./system_type1/bios_jsons", tableDir.c_str(), 275 &dbusHandler, 0, 0, nullptr, nullptr, 276 &mockBiosSystemConfig); 277 278 biosConfig.buildTables(); 279 280 auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE); 281 auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE); 282 auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE); 283 284 EXPECT_TRUE(stringTable); 285 EXPECT_TRUE(attrTable); 286 EXPECT_TRUE(attrValueTable); 287 288 BIOSStringTable biosStringTable(*stringTable); 289 290 for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(), 291 attrTable->size())) 292 { 293 auto header = table::attribute::decodeHeader(entry); 294 auto attrName = biosStringTable.findString(header.stringHandle); 295 auto jsonEntry = findJsonEntry(attrName); 296 EXPECT_TRUE(jsonEntry); 297 switch (header.attrType) 298 { 299 case PLDM_BIOS_STRING: 300 case PLDM_BIOS_STRING_READ_ONLY: 301 { 302 if (attrName == "str_example2") 303 { 304 auto stringField = 305 table::attribute::decodeStringEntry(entry); 306 EXPECT_EQ(stringField.maxLength, 200); 307 } 308 309 break; 310 } 311 case PLDM_BIOS_INTEGER: 312 case PLDM_BIOS_INTEGER_READ_ONLY: 313 { 314 if (attrName == "SBE_IMAGE_MINIMUM_VALID_ECS") 315 { 316 auto integerField = 317 table::attribute::decodeIntegerEntry(entry); 318 EXPECT_EQ(integerField.upperBound, 30); 319 } 320 break; 321 } 322 case PLDM_BIOS_ENUMERATION: 323 case PLDM_BIOS_ENUMERATION_READ_ONLY: 324 { 325 if (attrName == "FWBootSide") 326 { 327 auto [pvHdls, 328 defInds] = table::attribute::decodeEnumEntry(entry); 329 auto defValue = 330 biosStringTable.findString(pvHdls[defInds[0]]); 331 EXPECT_EQ(defValue, "Temp"); 332 } 333 } 334 } 335 } 336 } 337 338 TEST_F(TestBIOSConfig, setAttrValue) 339 { 340 MockdBusHandler dbusHandler; 341 342 MockBiosSystemConfig mockBiosSystemConfig(&dbusHandler); 343 344 BIOSConfig biosConfig("./bios_jsons", tableDir.c_str(), &dbusHandler, 0, 0, 345 nullptr, nullptr, &mockBiosSystemConfig); 346 biosConfig.removeTables(); 347 biosConfig.buildTables(); 348 349 auto stringTable = biosConfig.getBIOSTable(PLDM_BIOS_STRING_TABLE); 350 auto attrTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_TABLE); 351 352 BIOSStringTable biosStringTable(*stringTable); 353 BIOSTableIter<PLDM_BIOS_ATTR_TABLE> attrTableIter(attrTable->data(), 354 attrTable->size()); 355 auto stringHandle = biosStringTable.findHandle("str_example1"); 356 uint16_t attrHandle{}; 357 358 for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(attrTable->data(), 359 attrTable->size())) 360 { 361 auto header = table::attribute::decodeHeader(entry); 362 if (header.stringHandle == stringHandle) 363 { 364 attrHandle = header.attrHandle; 365 break; 366 } 367 } 368 369 EXPECT_NE(attrHandle, 0); 370 371 std::vector<uint8_t> attrValueEntry{ 372 0, 0, /* attr handle */ 373 1, /* attr type string read-write */ 374 4, 0, /* current string length */ 375 'a', 'b', 'c', 'd', /* defaut value string handle index */ 376 }; 377 378 attrValueEntry[0] = attrHandle & 0xff; 379 attrValueEntry[1] = (attrHandle >> 8) & 0xff; 380 381 DBusMapping dbusMapping{"/xyz/abc/def", 382 "xyz.openbmc_project.str_example1.value", 383 "Str_example1", "string"}; 384 PropertyValue value = std::string("abcd"); 385 EXPECT_CALL(dbusHandler, setDbusProperty(dbusMapping, value)).Times(1); 386 387 auto rc = biosConfig.setAttrValue(attrValueEntry.data(), 388 attrValueEntry.size(), false); 389 EXPECT_EQ(rc, PLDM_SUCCESS); 390 391 auto attrValueTable = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE); 392 auto findEntry = 393 [&attrValueTable]( 394 uint16_t handle) -> const pldm_bios_attr_val_table_entry* { 395 for (auto entry : BIOSTableIter<PLDM_BIOS_ATTR_VAL_TABLE>( 396 attrValueTable->data(), attrValueTable->size())) 397 { 398 auto [attrHandle, _] = table::attribute_value::decodeHeader(entry); 399 if (attrHandle == handle) 400 return entry; 401 } 402 return nullptr; 403 }; 404 405 auto entry = findEntry(attrHandle); 406 EXPECT_NE(entry, nullptr); 407 408 auto p = reinterpret_cast<const uint8_t*>(entry); 409 EXPECT_THAT(std::vector<uint8_t>(p, p + attrValueEntry.size()), 410 ElementsAreArray(attrValueEntry)); 411 } 412