1 #include "bios_config.hpp" 2 3 #include "bios_enum_attribute.hpp" 4 #include "bios_integer_attribute.hpp" 5 #include "bios_string_attribute.hpp" 6 #include "bios_table.hpp" 7 #include "common/bios_utils.hpp" 8 9 #include <phosphor-logging/lg2.hpp> 10 #include <xyz/openbmc_project/BIOSConfig/Manager/server.hpp> 11 12 #include <fstream> 13 #include <iostream> 14 15 #ifdef OEM_IBM 16 #include "oem/ibm/libpldmresponder/platform_oem_ibm.hpp" 17 #endif 18 19 PHOSPHOR_LOG2_USING; 20 21 using namespace pldm::utils; 22 23 namespace pldm 24 { 25 namespace responder 26 { 27 namespace bios 28 { 29 namespace 30 { 31 using BIOSConfigManager = 32 sdbusplus::xyz::openbmc_project::BIOSConfig::server::Manager; 33 34 constexpr auto enumJsonFile = "enum_attrs.json"; 35 constexpr auto stringJsonFile = "string_attrs.json"; 36 constexpr auto integerJsonFile = "integer_attrs.json"; 37 38 constexpr auto stringTableFile = "stringTable"; 39 constexpr auto attrTableFile = "attributeTable"; 40 constexpr auto attrValueTableFile = "attributeValueTable"; 41 42 } // namespace 43 44 BIOSConfig::BIOSConfig( 45 const char* jsonDir, const char* tableDir, DBusHandler* const dbusHandler, 46 int fd, uint8_t eid, pldm::InstanceIdDb* instanceIdDb, 47 pldm::requester::Handler<pldm::requester::Request>* handler, 48 pldm::responder::platform_config::Handler* platformConfigHandler) : 49 jsonDir(jsonDir), 50 tableDir(tableDir), dbusHandler(dbusHandler), fd(fd), eid(eid), 51 instanceIdDb(instanceIdDb), handler(handler), 52 platformConfigHandler(platformConfigHandler) 53 54 { 55 if (platformConfigHandler) 56 { 57 auto systemType = platformConfigHandler->getPlatformName(); 58 if (systemType.has_value()) 59 { 60 sysType = systemType.value(); 61 } 62 } 63 fs::create_directories(tableDir); 64 constructAttributes(); 65 listenPendingAttributes(); 66 } 67 68 void BIOSConfig::buildTables() 69 { 70 auto stringTable = buildAndStoreStringTable(); 71 if (stringTable) 72 { 73 buildAndStoreAttrTables(*stringTable); 74 } 75 } 76 77 std::optional<Table> BIOSConfig::getBIOSTable(pldm_bios_table_types tableType) 78 { 79 fs::path tablePath; 80 switch (tableType) 81 { 82 case PLDM_BIOS_STRING_TABLE: 83 tablePath = tableDir / stringTableFile; 84 break; 85 case PLDM_BIOS_ATTR_TABLE: 86 tablePath = tableDir / attrTableFile; 87 break; 88 case PLDM_BIOS_ATTR_VAL_TABLE: 89 tablePath = tableDir / attrValueTableFile; 90 break; 91 } 92 return loadTable(tablePath); 93 } 94 95 int BIOSConfig::setBIOSTable(uint8_t tableType, const Table& table, 96 bool updateBaseBIOSTable) 97 { 98 fs::path stringTablePath(tableDir / stringTableFile); 99 fs::path attrTablePath(tableDir / attrTableFile); 100 fs::path attrValueTablePath(tableDir / attrValueTableFile); 101 102 if (!pldm_bios_table_checksum(table.data(), table.size())) 103 { 104 return PLDM_INVALID_BIOS_TABLE_DATA_INTEGRITY_CHECK; 105 } 106 107 if (tableType == PLDM_BIOS_STRING_TABLE) 108 { 109 storeTable(stringTablePath, table); 110 } 111 else if (tableType == PLDM_BIOS_ATTR_TABLE) 112 { 113 BIOSTable biosStringTable(stringTablePath.c_str()); 114 if (biosStringTable.isEmpty()) 115 { 116 return PLDM_INVALID_BIOS_TABLE_TYPE; 117 } 118 119 auto rc = checkAttributeTable(table); 120 if (rc != PLDM_SUCCESS) 121 { 122 return rc; 123 } 124 125 storeTable(attrTablePath, table); 126 } 127 else if (tableType == PLDM_BIOS_ATTR_VAL_TABLE) 128 { 129 BIOSTable biosStringTable(stringTablePath.c_str()); 130 BIOSTable biosStringValueTable(attrTablePath.c_str()); 131 if (biosStringTable.isEmpty() || biosStringValueTable.isEmpty()) 132 { 133 return PLDM_INVALID_BIOS_TABLE_TYPE; 134 } 135 136 auto rc = checkAttributeValueTable(table); 137 if (rc != PLDM_SUCCESS) 138 { 139 return rc; 140 } 141 142 storeTable(attrValueTablePath, table); 143 } 144 else 145 { 146 return PLDM_INVALID_BIOS_TABLE_TYPE; 147 } 148 149 if ((tableType == PLDM_BIOS_ATTR_VAL_TABLE) && updateBaseBIOSTable) 150 { 151 updateBaseBIOSTableProperty(); 152 } 153 154 return PLDM_SUCCESS; 155 } 156 157 int BIOSConfig::checkAttributeTable(const Table& table) 158 { 159 using namespace pldm::bios::utils; 160 auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE); 161 for (auto entry : 162 BIOSTableIter<PLDM_BIOS_ATTR_TABLE>(table.data(), table.size())) 163 { 164 auto attrNameHandle = 165 pldm_bios_table_attr_entry_decode_string_handle(entry); 166 167 auto stringEnty = pldm_bios_table_string_find_by_handle( 168 stringTable->data(), stringTable->size(), attrNameHandle); 169 if (stringEnty == nullptr) 170 { 171 return PLDM_INVALID_BIOS_ATTR_HANDLE; 172 } 173 174 auto attrType = static_cast<pldm_bios_attribute_type>( 175 pldm_bios_table_attr_entry_decode_attribute_type(entry)); 176 177 switch (attrType) 178 { 179 case PLDM_BIOS_ENUMERATION: 180 case PLDM_BIOS_ENUMERATION_READ_ONLY: 181 { 182 uint8_t pvNum; 183 // Preconditions are upheld therefore no error check necessary 184 pldm_bios_table_attr_entry_enum_decode_pv_num_check(entry, 185 &pvNum); 186 std::vector<uint16_t> pvHandls(pvNum); 187 // Preconditions are upheld therefore no error check necessary 188 pldm_bios_table_attr_entry_enum_decode_pv_hdls_check( 189 entry, pvHandls.data(), pvHandls.size()); 190 uint8_t defNum; 191 pldm_bios_table_attr_entry_enum_decode_def_num_check(entry, 192 &defNum); 193 std::vector<uint8_t> defIndices(defNum); 194 pldm_bios_table_attr_entry_enum_decode_def_indices( 195 entry, defIndices.data(), defIndices.size()); 196 197 for (size_t i = 0; i < pvHandls.size(); i++) 198 { 199 auto stringEntry = pldm_bios_table_string_find_by_handle( 200 stringTable->data(), stringTable->size(), pvHandls[i]); 201 if (stringEntry == nullptr) 202 { 203 return PLDM_INVALID_BIOS_ATTR_HANDLE; 204 } 205 } 206 207 for (size_t i = 0; i < defIndices.size(); i++) 208 { 209 auto stringEntry = pldm_bios_table_string_find_by_handle( 210 stringTable->data(), stringTable->size(), 211 pvHandls[defIndices[i]]); 212 if (stringEntry == nullptr) 213 { 214 return PLDM_INVALID_BIOS_ATTR_HANDLE; 215 } 216 } 217 break; 218 } 219 case PLDM_BIOS_INTEGER: 220 case PLDM_BIOS_INTEGER_READ_ONLY: 221 case PLDM_BIOS_STRING: 222 case PLDM_BIOS_STRING_READ_ONLY: 223 case PLDM_BIOS_PASSWORD: 224 case PLDM_BIOS_PASSWORD_READ_ONLY: 225 break; 226 default: 227 return PLDM_INVALID_BIOS_ATTR_HANDLE; 228 } 229 } 230 231 return PLDM_SUCCESS; 232 } 233 234 int BIOSConfig::checkAttributeValueTable(const Table& table) 235 { 236 using namespace pldm::bios::utils; 237 auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE); 238 auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE); 239 240 baseBIOSTableMaps.clear(); 241 242 for (auto tableEntry : 243 BIOSTableIter<PLDM_BIOS_ATTR_VAL_TABLE>(table.data(), table.size())) 244 { 245 AttributeName attributeName{}; 246 AttributeType attributeType{}; 247 ReadonlyStatus readonlyStatus{}; 248 DisplayName displayName{}; 249 Description description{}; 250 MenuPath menuPath{}; 251 CurrentValue currentValue{}; 252 DefaultValue defaultValue{}; 253 std::vector<ValueDisplayName> valueDisplayNames; 254 std::map<uint16_t, std::vector<std::string>> valueDisplayNamesMap; 255 Option options{}; 256 257 auto attrValueHandle = 258 pldm_bios_table_attr_value_entry_decode_attribute_handle( 259 tableEntry); 260 auto attrType = static_cast<pldm_bios_attribute_type>( 261 pldm_bios_table_attr_value_entry_decode_attribute_type(tableEntry)); 262 263 auto attrEntry = pldm_bios_table_attr_find_by_handle( 264 attrTable->data(), attrTable->size(), attrValueHandle); 265 if (attrEntry == nullptr) 266 { 267 return PLDM_INVALID_BIOS_ATTR_HANDLE; 268 } 269 auto attrHandle = 270 pldm_bios_table_attr_entry_decode_attribute_handle(attrEntry); 271 auto attrNameHandle = 272 pldm_bios_table_attr_entry_decode_string_handle(attrEntry); 273 274 auto stringEntry = pldm_bios_table_string_find_by_handle( 275 stringTable->data(), stringTable->size(), attrNameHandle); 276 if (stringEntry == nullptr) 277 { 278 return PLDM_INVALID_BIOS_ATTR_HANDLE; 279 } 280 auto strLength = 281 pldm_bios_table_string_entry_decode_string_length(stringEntry); 282 std::vector<char> buffer(strLength + 1 /* sizeof '\0' */); 283 // Preconditions are upheld therefore no error check necessary 284 pldm_bios_table_string_entry_decode_string_check( 285 stringEntry, buffer.data(), buffer.size()); 286 287 attributeName = std::string(buffer.data(), buffer.data() + strLength); 288 289 if (!biosAttributes.empty()) 290 { 291 readonlyStatus = 292 biosAttributes[attrHandle % biosAttributes.size()]->readOnly; 293 description = 294 biosAttributes[attrHandle % biosAttributes.size()]->helpText; 295 displayName = 296 biosAttributes[attrHandle % biosAttributes.size()]->displayName; 297 valueDisplayNamesMap = 298 biosAttributes[attrHandle % biosAttributes.size()] 299 ->valueDisplayNamesMap; 300 } 301 302 switch (attrType) 303 { 304 case PLDM_BIOS_ENUMERATION: 305 case PLDM_BIOS_ENUMERATION_READ_ONLY: 306 { 307 if (valueDisplayNamesMap.contains(attrHandle)) 308 { 309 const std::vector<ValueDisplayName>& vdn = 310 valueDisplayNamesMap[attrHandle]; 311 valueDisplayNames.insert(valueDisplayNames.end(), 312 vdn.begin(), vdn.end()); 313 } 314 auto getValue = [](uint16_t handle, 315 const Table& table) -> std::string { 316 auto stringEntry = pldm_bios_table_string_find_by_handle( 317 table.data(), table.size(), handle); 318 319 auto strLength = 320 pldm_bios_table_string_entry_decode_string_length( 321 stringEntry); 322 std::vector<char> buffer(strLength + 1 /* sizeof '\0' */); 323 // Preconditions are upheld therefore no error check 324 // necessary 325 pldm_bios_table_string_entry_decode_string_check( 326 stringEntry, buffer.data(), buffer.size()); 327 328 return std::string(buffer.data(), 329 buffer.data() + strLength); 330 }; 331 332 attributeType = "xyz.openbmc_project.BIOSConfig.Manager." 333 "AttributeType.Enumeration"; 334 335 uint8_t pvNum; 336 // Preconditions are upheld therefore no error check necessary 337 pldm_bios_table_attr_entry_enum_decode_pv_num_check(attrEntry, 338 &pvNum); 339 std::vector<uint16_t> pvHandls(pvNum); 340 // Preconditions are upheld therefore no error check necessary 341 pldm_bios_table_attr_entry_enum_decode_pv_hdls_check( 342 attrEntry, pvHandls.data(), pvHandls.size()); 343 344 // get possible_value 345 for (size_t i = 0; i < pvHandls.size(); i++) 346 { 347 options.push_back( 348 std::make_tuple("xyz.openbmc_project.BIOSConfig." 349 "Manager.BoundType.OneOf", 350 getValue(pvHandls[i], *stringTable), 351 valueDisplayNames[i])); 352 } 353 354 auto count = 355 pldm_bios_table_attr_value_entry_enum_decode_number( 356 tableEntry); 357 std::vector<uint8_t> handles(count); 358 pldm_bios_table_attr_value_entry_enum_decode_handles( 359 tableEntry, handles.data(), handles.size()); 360 361 // get current_value 362 for (size_t i = 0; i < handles.size(); i++) 363 { 364 currentValue = getValue(pvHandls[handles[i]], *stringTable); 365 } 366 367 uint8_t defNum; 368 // Preconditions are upheld therefore no error check necessary 369 pldm_bios_table_attr_entry_enum_decode_def_num_check(attrEntry, 370 &defNum); 371 std::vector<uint8_t> defIndices(defNum); 372 pldm_bios_table_attr_entry_enum_decode_def_indices( 373 attrEntry, defIndices.data(), defIndices.size()); 374 375 // get default_value 376 for (size_t i = 0; i < defIndices.size(); i++) 377 { 378 defaultValue = getValue(pvHandls[defIndices[i]], 379 *stringTable); 380 } 381 382 break; 383 } 384 case PLDM_BIOS_INTEGER: 385 case PLDM_BIOS_INTEGER_READ_ONLY: 386 { 387 attributeType = "xyz.openbmc_project.BIOSConfig.Manager." 388 "AttributeType.Integer"; 389 currentValue = static_cast<int64_t>( 390 pldm_bios_table_attr_value_entry_integer_decode_cv( 391 tableEntry)); 392 393 uint64_t lower, upper, def; 394 uint32_t scalar; 395 pldm_bios_table_attr_entry_integer_decode( 396 attrEntry, &lower, &upper, &scalar, &def); 397 options.push_back(std::make_tuple( 398 "xyz.openbmc_project.BIOSConfig.Manager." 399 "BoundType.LowerBound", 400 static_cast<int64_t>(lower), attributeName)); 401 options.push_back(std::make_tuple( 402 "xyz.openbmc_project.BIOSConfig.Manager." 403 "BoundType.UpperBound", 404 static_cast<int64_t>(upper), attributeName)); 405 options.push_back(std::make_tuple( 406 "xyz.openbmc_project.BIOSConfig.Manager." 407 "BoundType.ScalarIncrement", 408 static_cast<int64_t>(scalar), attributeName)); 409 defaultValue = static_cast<int64_t>(def); 410 break; 411 } 412 case PLDM_BIOS_STRING: 413 case PLDM_BIOS_STRING_READ_ONLY: 414 { 415 attributeType = "xyz.openbmc_project.BIOSConfig.Manager." 416 "AttributeType.String"; 417 variable_field currentString; 418 pldm_bios_table_attr_value_entry_string_decode_string( 419 tableEntry, ¤tString); 420 currentValue = std::string( 421 reinterpret_cast<const char*>(currentString.ptr), 422 currentString.length); 423 auto min = pldm_bios_table_attr_entry_string_decode_min_length( 424 attrEntry); 425 auto max = pldm_bios_table_attr_entry_string_decode_max_length( 426 attrEntry); 427 uint16_t def; 428 // Preconditions are upheld therefore no error check necessary 429 pldm_bios_table_attr_entry_string_decode_def_string_length_check( 430 attrEntry, &def); 431 std::vector<char> defString(def + 1); 432 pldm_bios_table_attr_entry_string_decode_def_string( 433 attrEntry, defString.data(), defString.size()); 434 options.push_back( 435 std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager." 436 "BoundType.MinStringLength", 437 static_cast<int64_t>(min), attributeName)); 438 options.push_back( 439 std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager." 440 "BoundType.MaxStringLength", 441 static_cast<int64_t>(max), attributeName)); 442 defaultValue = defString.data(); 443 break; 444 } 445 case PLDM_BIOS_PASSWORD: 446 case PLDM_BIOS_PASSWORD_READ_ONLY: 447 { 448 attributeType = "xyz.openbmc_project.BIOSConfig.Manager." 449 "AttributeType.Password"; 450 break; 451 } 452 default: 453 return PLDM_INVALID_BIOS_ATTR_HANDLE; 454 } 455 baseBIOSTableMaps.emplace( 456 std::move(attributeName), 457 std::make_tuple(attributeType, readonlyStatus, displayName, 458 description, menuPath, currentValue, defaultValue, 459 std::move(options))); 460 } 461 462 return PLDM_SUCCESS; 463 } 464 465 void BIOSConfig::updateBaseBIOSTableProperty() 466 { 467 constexpr static auto biosConfigPath = 468 "/xyz/openbmc_project/bios_config/manager"; 469 constexpr static auto biosConfigInterface = 470 "xyz.openbmc_project.BIOSConfig.Manager"; 471 constexpr static auto biosConfigPropertyName = "BaseBIOSTable"; 472 constexpr static auto dbusProperties = "org.freedesktop.DBus.Properties"; 473 474 if (baseBIOSTableMaps.empty()) 475 { 476 return; 477 } 478 479 try 480 { 481 auto& bus = dbusHandler->getBus(); 482 auto service = dbusHandler->getService(biosConfigPath, 483 biosConfigInterface); 484 auto method = bus.new_method_call(service.c_str(), biosConfigPath, 485 dbusProperties, "Set"); 486 std::variant<BaseBIOSTable> value = baseBIOSTableMaps; 487 method.append(biosConfigInterface, biosConfigPropertyName, value); 488 bus.call_noreply(method, dbusTimeout); 489 } 490 catch (const std::exception& e) 491 { 492 error("failed to update BaseBIOSTable property, ERROR={ERR_EXCEP}", 493 "ERR_EXCEP", e.what()); 494 } 495 } 496 497 void BIOSConfig::constructAttributes() 498 { 499 load(jsonDir / sysType / stringJsonFile, [this](const Json& entry) { 500 constructAttribute<BIOSStringAttribute>(entry); 501 }); 502 load(jsonDir / sysType / integerJsonFile, [this](const Json& entry) { 503 constructAttribute<BIOSIntegerAttribute>(entry); 504 }); 505 load(jsonDir / sysType / enumJsonFile, [this](const Json& entry) { 506 constructAttribute<BIOSEnumAttribute>(entry); 507 }); 508 } 509 510 void BIOSConfig::buildAndStoreAttrTables(const Table& stringTable) 511 { 512 BIOSStringTable biosStringTable(stringTable); 513 514 if (biosAttributes.empty()) 515 { 516 return; 517 } 518 519 BaseBIOSTable biosTable{}; 520 constexpr auto biosObjPath = "/xyz/openbmc_project/bios_config/manager"; 521 constexpr auto biosInterface = "xyz.openbmc_project.BIOSConfig.Manager"; 522 523 try 524 { 525 auto& bus = dbusHandler->getBus(); 526 auto service = dbusHandler->getService(biosObjPath, biosInterface); 527 auto method = bus.new_method_call(service.c_str(), biosObjPath, 528 "org.freedesktop.DBus.Properties", 529 "Get"); 530 method.append(biosInterface, "BaseBIOSTable"); 531 auto reply = bus.call(method, dbusTimeout); 532 std::variant<BaseBIOSTable> varBiosTable{}; 533 reply.read(varBiosTable); 534 biosTable = std::get<BaseBIOSTable>(varBiosTable); 535 } 536 // Failed to read the BaseBIOSTable, so update the BaseBIOSTable with the 537 // default values populated from the BIOS JSONs to keep PLDM and 538 // bios-settings-manager in sync 539 catch (const std::exception& e) 540 { 541 error("Failed to read BaseBIOSTable property, ERROR={ERR_EXCEP}", 542 "ERR_EXCEP", e.what()); 543 } 544 545 Table attrTable, attrValueTable; 546 547 for (auto& attr : biosAttributes) 548 { 549 try 550 { 551 auto iter = biosTable.find(attr->name); 552 if (iter == biosTable.end()) 553 { 554 attr->constructEntry(biosStringTable, attrTable, attrValueTable, 555 std::nullopt); 556 } 557 else 558 { 559 attr->constructEntry( 560 biosStringTable, attrTable, attrValueTable, 561 std::get<static_cast<uint8_t>(Index::currentValue)>( 562 iter->second)); 563 } 564 } 565 catch (const std::exception& e) 566 { 567 error("Error constructing table entry for '{ATTR}': {ERROR}", 568 "ATTR", attr->name, "ERROR", e); 569 } 570 } 571 572 table::appendPadAndChecksum(attrTable); 573 table::appendPadAndChecksum(attrValueTable); 574 setBIOSTable(PLDM_BIOS_ATTR_TABLE, attrTable); 575 setBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE, attrValueTable); 576 } 577 578 std::optional<Table> BIOSConfig::buildAndStoreStringTable() 579 { 580 std::set<std::string> strings; 581 auto handler = [&strings](const Json& entry) { 582 strings.emplace(entry.at("attribute_name")); 583 }; 584 585 load(jsonDir / sysType / stringJsonFile, handler); 586 load(jsonDir / sysType / integerJsonFile, handler); 587 load(jsonDir / sysType / enumJsonFile, [&strings](const Json& entry) { 588 strings.emplace(entry.at("attribute_name")); 589 auto possibleValues = entry.at("possible_values"); 590 for (auto& pv : possibleValues) 591 { 592 strings.emplace(pv); 593 } 594 }); 595 596 if (strings.empty()) 597 { 598 return std::nullopt; 599 } 600 601 Table table; 602 for (const auto& elem : strings) 603 { 604 table::string::constructEntry(table, elem); 605 } 606 607 table::appendPadAndChecksum(table); 608 setBIOSTable(PLDM_BIOS_STRING_TABLE, table); 609 return table; 610 } 611 612 void BIOSConfig::storeTable(const fs::path& path, const Table& table) 613 { 614 BIOSTable biosTable(path.c_str()); 615 biosTable.store(table); 616 } 617 618 std::optional<Table> BIOSConfig::loadTable(const fs::path& path) 619 { 620 BIOSTable biosTable(path.c_str()); 621 if (biosTable.isEmpty()) 622 { 623 return std::nullopt; 624 } 625 626 Table table; 627 biosTable.load(table); 628 return table; 629 } 630 631 void BIOSConfig::load(const fs::path& filePath, ParseHandler handler) 632 { 633 std::ifstream file; 634 Json jsonConf; 635 if (fs::exists(filePath)) 636 { 637 try 638 { 639 file.open(filePath); 640 jsonConf = Json::parse(file); 641 auto entries = jsonConf.at("entries"); 642 for (auto& entry : entries) 643 { 644 try 645 { 646 handler(entry); 647 } 648 catch (const std::exception& e) 649 { 650 error( 651 "Failed to parse JSON config file(entry handler) : {JSON_PATH}, {ERR_EXCEP}", 652 "JSON_PATH", filePath.c_str(), "ERR_EXCEP", e.what()); 653 } 654 } 655 } 656 catch (const std::exception& e) 657 { 658 error("Failed to parse JSON config at '{PATH}': {ERROR}", "PATH", 659 filePath.c_str(), "ERROR", e); 660 } 661 } 662 } 663 664 std::string BIOSConfig::decodeStringFromStringEntry( 665 const pldm_bios_string_table_entry* stringEntry) 666 { 667 auto strLength = 668 pldm_bios_table_string_entry_decode_string_length(stringEntry); 669 std::vector<char> buffer(strLength + 1 /* sizeof '\0' */); 670 // Preconditions are upheld therefore no error check necessary 671 pldm_bios_table_string_entry_decode_string_check(stringEntry, buffer.data(), 672 buffer.size()); 673 return std::string(buffer.data(), buffer.data() + strLength); 674 } 675 676 std::string 677 BIOSConfig::displayStringHandle(uint16_t handle, uint8_t index, 678 const std::optional<Table>& attrTable, 679 const std::optional<Table>& stringTable) 680 { 681 auto attrEntry = pldm_bios_table_attr_find_by_handle( 682 attrTable->data(), attrTable->size(), handle); 683 uint8_t pvNum; 684 int rc = pldm_bios_table_attr_entry_enum_decode_pv_num_check(attrEntry, 685 &pvNum); 686 if (rc != PLDM_SUCCESS) 687 { 688 error( 689 "Failed to decode BIOS table possible values for attribute entry: {LIPBLDM_ERROR}", 690 "LIBPLDM_ERROR", rc); 691 throw std::runtime_error( 692 "Failed to decode BIOS table possible values for attribute entry"); 693 } 694 695 std::vector<uint16_t> pvHandls(pvNum); 696 // Preconditions are upheld therefore no error check necessary 697 pldm_bios_table_attr_entry_enum_decode_pv_hdls_check( 698 attrEntry, pvHandls.data(), pvHandls.size()); 699 700 std::string displayString = std::to_string(pvHandls[index]); 701 702 auto stringEntry = pldm_bios_table_string_find_by_handle( 703 stringTable->data(), stringTable->size(), pvHandls[index]); 704 705 auto decodedStr = decodeStringFromStringEntry(stringEntry); 706 707 return decodedStr + "(" + displayString + ")"; 708 } 709 710 void BIOSConfig::traceBIOSUpdate( 711 const pldm_bios_attr_val_table_entry* attrValueEntry, 712 const pldm_bios_attr_table_entry* attrEntry, bool isBMC) 713 { 714 auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE); 715 auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE); 716 717 auto [attrHandle, 718 attrType] = table::attribute_value::decodeHeader(attrValueEntry); 719 720 auto attrHeader = table::attribute::decodeHeader(attrEntry); 721 BIOSStringTable biosStringTable(*stringTable); 722 auto attrName = biosStringTable.findString(attrHeader.stringHandle); 723 724 switch (attrType) 725 { 726 case PLDM_BIOS_ENUMERATION: 727 case PLDM_BIOS_ENUMERATION_READ_ONLY: 728 { 729 auto count = pldm_bios_table_attr_value_entry_enum_decode_number( 730 attrValueEntry); 731 std::vector<uint8_t> handles(count); 732 pldm_bios_table_attr_value_entry_enum_decode_handles( 733 attrValueEntry, handles.data(), handles.size()); 734 735 for (uint8_t handle : handles) 736 { 737 auto nwVal = displayStringHandle(attrHandle, handle, attrTable, 738 stringTable); 739 auto chkBMC = isBMC ? "true" : "false"; 740 info( 741 "BIOS:{ATTR_NAME}, updated to value: {NEW_VAL}, by BMC: {CHK_BMC} ", 742 "ATTR_NAME", attrName, "NEW_VAL", nwVal, "CHK_BMC", chkBMC); 743 } 744 break; 745 } 746 case PLDM_BIOS_INTEGER: 747 case PLDM_BIOS_INTEGER_READ_ONLY: 748 { 749 auto value = 750 table::attribute_value::decodeIntegerEntry(attrValueEntry); 751 auto chkBMC = isBMC ? "true" : "false"; 752 info( 753 "BIOS: {ATTR_NAME}, updated to value: {UPDATED_VAL}, by BMC: {CHK_BMC}", 754 "ATTR_NAME", attrName, "UPDATED_VAL", value, "CHK_BMC", chkBMC); 755 break; 756 } 757 case PLDM_BIOS_STRING: 758 case PLDM_BIOS_STRING_READ_ONLY: 759 { 760 auto value = 761 table::attribute_value::decodeStringEntry(attrValueEntry); 762 auto chkBMC = isBMC ? "true" : "false"; 763 info( 764 "BIOS: {ATTR_NAME}, updated to value: {UPDATED_VAL}, by BMC: {CHK_BMC}", 765 "ATTR_NAME", attrName, "UPDATED_VAL", value, "CHK_BMC", chkBMC); 766 break; 767 } 768 default: 769 break; 770 }; 771 } 772 773 int BIOSConfig::checkAttrValueToUpdate( 774 const pldm_bios_attr_val_table_entry* attrValueEntry, 775 const pldm_bios_attr_table_entry* attrEntry, Table&) 776 777 { 778 auto [attrHandle, 779 attrType] = table::attribute_value::decodeHeader(attrValueEntry); 780 781 switch (attrType) 782 { 783 case PLDM_BIOS_ENUMERATION: 784 case PLDM_BIOS_ENUMERATION_READ_ONLY: 785 { 786 auto value = 787 table::attribute_value::decodeEnumEntry(attrValueEntry); 788 auto [pvHdls, 789 defIndex] = table::attribute::decodeEnumEntry(attrEntry); 790 if (!(value.size() == 1)) 791 { 792 return PLDM_ERROR_INVALID_LENGTH; 793 } 794 if (value[0] >= pvHdls.size()) 795 { 796 error("Enum: Illgeal index, Index = {ATTR_INDEX}", "ATTR_INDEX", 797 (int)value[0]); 798 return PLDM_ERROR_INVALID_DATA; 799 } 800 return PLDM_SUCCESS; 801 } 802 case PLDM_BIOS_INTEGER: 803 case PLDM_BIOS_INTEGER_READ_ONLY: 804 { 805 auto value = 806 table::attribute_value::decodeIntegerEntry(attrValueEntry); 807 auto [lower, upper, scalar, 808 def] = table::attribute::decodeIntegerEntry(attrEntry); 809 810 if (value < lower || value > upper) 811 { 812 error("Integer: out of bound, value = {ATTR_VALUE}", 813 "ATTR_VALUE", value); 814 return PLDM_ERROR_INVALID_DATA; 815 } 816 return PLDM_SUCCESS; 817 } 818 case PLDM_BIOS_STRING: 819 case PLDM_BIOS_STRING_READ_ONLY: 820 { 821 auto stringConf = table::attribute::decodeStringEntry(attrEntry); 822 auto value = 823 table::attribute_value::decodeStringEntry(attrValueEntry); 824 if (value.size() < stringConf.minLength || 825 value.size() > stringConf.maxLength) 826 { 827 error( 828 "String: Length error, string = {ATTR_VALUE} length {LEN}", 829 "ATTR_VALUE", value, "LEN", value.size()); 830 return PLDM_ERROR_INVALID_LENGTH; 831 } 832 return PLDM_SUCCESS; 833 } 834 default: 835 error("ReadOnly or Unspported type, type = {ATTR_TYPE}", 836 "ATTR_TYPE", attrType); 837 return PLDM_ERROR; 838 }; 839 } 840 841 int BIOSConfig::setAttrValue(const void* entry, size_t size, bool isBMC, 842 bool updateDBus, bool updateBaseBIOSTable) 843 { 844 auto attrValueTable = getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE); 845 auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE); 846 auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE); 847 if (!attrValueTable || !attrTable || !stringTable) 848 { 849 return PLDM_BIOS_TABLE_UNAVAILABLE; 850 } 851 852 auto attrValueEntry = 853 reinterpret_cast<const pldm_bios_attr_val_table_entry*>(entry); 854 855 auto attrValHeader = table::attribute_value::decodeHeader(attrValueEntry); 856 857 auto attrEntry = table::attribute::findByHandle(*attrTable, 858 attrValHeader.attrHandle); 859 if (!attrEntry) 860 { 861 return PLDM_ERROR; 862 } 863 864 auto rc = checkAttrValueToUpdate(attrValueEntry, attrEntry, *stringTable); 865 if (rc != PLDM_SUCCESS) 866 { 867 return rc; 868 } 869 870 auto destTable = table::attribute_value::updateTable(*attrValueTable, entry, 871 size); 872 873 if (!destTable) 874 { 875 return PLDM_ERROR; 876 } 877 878 try 879 { 880 auto attrHeader = table::attribute::decodeHeader(attrEntry); 881 882 BIOSStringTable biosStringTable(*stringTable); 883 auto attrName = biosStringTable.findString(attrHeader.stringHandle); 884 auto iter = std::find_if( 885 biosAttributes.begin(), biosAttributes.end(), 886 [&attrName](const auto& attr) { return attr->name == attrName; }); 887 888 if (iter == biosAttributes.end()) 889 { 890 return PLDM_ERROR; 891 } 892 if (updateDBus) 893 { 894 (*iter)->setAttrValueOnDbus(attrValueEntry, attrEntry, 895 biosStringTable); 896 } 897 } 898 catch (const std::exception& e) 899 { 900 error("Set attribute value error: {ERR_EXCEP}", "ERR_EXCEP", e.what()); 901 return PLDM_ERROR; 902 } 903 904 setBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE, *destTable, updateBaseBIOSTable); 905 906 traceBIOSUpdate(attrValueEntry, attrEntry, isBMC); 907 908 return PLDM_SUCCESS; 909 } 910 911 void BIOSConfig::removeTables() 912 { 913 try 914 { 915 fs::remove(tableDir / stringTableFile); 916 fs::remove(tableDir / attrTableFile); 917 fs::remove(tableDir / attrValueTableFile); 918 } 919 catch (const std::exception& e) 920 { 921 error("Remove the tables error: {ERR_EXCEP}", "ERR_EXCEP", e.what()); 922 } 923 } 924 925 void BIOSConfig::processBiosAttrChangeNotification( 926 const DbusChObjProperties& chProperties, uint32_t biosAttrIndex) 927 { 928 const auto& dBusMap = biosAttributes[biosAttrIndex]->getDBusMap(); 929 const auto& propertyName = dBusMap->propertyName; 930 const auto& attrName = biosAttributes[biosAttrIndex]->name; 931 932 const auto it = chProperties.find(propertyName); 933 if (it == chProperties.end()) 934 { 935 return; 936 } 937 938 PropertyValue newPropVal = it->second; 939 auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE); 940 if (!stringTable.has_value()) 941 { 942 error("BIOS string table unavailable"); 943 return; 944 } 945 BIOSStringTable biosStringTable(*stringTable); 946 uint16_t attrNameHdl{}; 947 try 948 { 949 attrNameHdl = biosStringTable.findHandle(attrName); 950 } 951 catch (const std::invalid_argument& e) 952 { 953 error("Missing handle for '{ATTR}': {ERROR}", "ATTR", attrName, "ERROR", 954 e); 955 return; 956 } 957 958 auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE); 959 if (!attrTable.has_value()) 960 { 961 error("Attribute table not present"); 962 return; 963 } 964 const struct pldm_bios_attr_table_entry* tableEntry = 965 table::attribute::findByStringHandle(*attrTable, attrNameHdl); 966 if (tableEntry == nullptr) 967 { 968 error( 969 "Attribute not found in attribute table, name= {ATTR_NAME} name handle={ATTR_HANDLE}", 970 "ATTR_NAME", attrName.c_str(), "ATTR_HANDLE", attrNameHdl); 971 return; 972 } 973 974 auto [attrHdl, attrType, 975 stringHdl] = table::attribute::decodeHeader(tableEntry); 976 977 auto attrValueSrcTable = getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE); 978 979 if (!attrValueSrcTable.has_value()) 980 { 981 error("Attribute value table not present"); 982 return; 983 } 984 985 Table newValue; 986 auto rc = biosAttributes[biosAttrIndex]->updateAttrVal( 987 newValue, attrHdl, attrType, newPropVal); 988 if (rc != PLDM_SUCCESS) 989 { 990 error( 991 "Could not update the attribute value table for attribute handle={ATTR_HANDLE} and type={ATTR_TYPE}", 992 "ATTR_HANDLE", attrHdl, "ATTR_TYPE", (uint32_t)attrType); 993 return; 994 } 995 auto destTable = table::attribute_value::updateTable( 996 *attrValueSrcTable, newValue.data(), newValue.size()); 997 if (destTable.has_value()) 998 { 999 storeTable(tableDir / attrValueTableFile, *destTable); 1000 } 1001 1002 rc = setAttrValue(newValue.data(), newValue.size(), true, false); 1003 if (rc != PLDM_SUCCESS) 1004 { 1005 error("could not setAttrValue on base bios table and dbus, rc = {RC}", 1006 "RC", rc); 1007 } 1008 } 1009 1010 uint16_t BIOSConfig::findAttrHandle(const std::string& attrName) 1011 { 1012 auto stringTable = getBIOSTable(PLDM_BIOS_STRING_TABLE); 1013 auto attrTable = getBIOSTable(PLDM_BIOS_ATTR_TABLE); 1014 1015 BIOSStringTable biosStringTable(*stringTable); 1016 pldm::bios::utils::BIOSTableIter<PLDM_BIOS_ATTR_TABLE> attrTableIter( 1017 attrTable->data(), attrTable->size()); 1018 auto stringHandle = biosStringTable.findHandle(attrName); 1019 1020 for (auto entry : pldm::bios::utils::BIOSTableIter<PLDM_BIOS_ATTR_TABLE>( 1021 attrTable->data(), attrTable->size())) 1022 { 1023 auto header = table::attribute::decodeHeader(entry); 1024 if (header.stringHandle == stringHandle) 1025 { 1026 return header.attrHandle; 1027 } 1028 } 1029 1030 throw std::invalid_argument("Unknow attribute Name"); 1031 } 1032 1033 void BIOSConfig::constructPendingAttribute( 1034 const PendingAttributes& pendingAttributes) 1035 { 1036 std::vector<uint16_t> listOfHandles{}; 1037 1038 for (auto& attribute : pendingAttributes) 1039 { 1040 std::string attributeName = attribute.first; 1041 auto& [attributeType, attributevalue] = attribute.second; 1042 1043 auto iter = std::find_if(biosAttributes.begin(), biosAttributes.end(), 1044 [&attributeName](const auto& attr) { 1045 return attr->name == attributeName; 1046 }); 1047 1048 if (iter == biosAttributes.end()) 1049 { 1050 error("Wrong attribute name, attributeName = {ATTR_NAME}", 1051 "ATTR_NAME", attributeName); 1052 continue; 1053 } 1054 1055 Table attrValueEntry(sizeof(pldm_bios_attr_val_table_entry), 0); 1056 auto entry = reinterpret_cast<pldm_bios_attr_val_table_entry*>( 1057 attrValueEntry.data()); 1058 1059 auto handler = findAttrHandle(attributeName); 1060 auto type = 1061 BIOSConfigManager::convertAttributeTypeFromString(attributeType); 1062 1063 if (type != BIOSConfigManager::AttributeType::Enumeration && 1064 type != BIOSConfigManager::AttributeType::String && 1065 type != BIOSConfigManager::AttributeType::Integer) 1066 { 1067 error("Attribute type not supported, attributeType = {ATTR_TYPE}", 1068 "ATTR_TYPE", attributeType); 1069 continue; 1070 } 1071 1072 const auto [attrType, readonlyStatus, displayName, description, 1073 menuPath, currentValue, defaultValue, 1074 option] = baseBIOSTableMaps.at(attributeName); 1075 1076 entry->attr_handle = htole16(handler); 1077 1078 // Need to verify that the current value has really changed 1079 if (attributeType == attrType && attributevalue != currentValue) 1080 { 1081 listOfHandles.emplace_back(htole16(handler)); 1082 } 1083 1084 (*iter)->generateAttributeEntry(attributevalue, attrValueEntry); 1085 1086 setAttrValue(attrValueEntry.data(), attrValueEntry.size(), true); 1087 } 1088 1089 if (listOfHandles.size()) 1090 { 1091 #ifdef OEM_IBM 1092 auto rc = pldm::responder::platform::sendBiosAttributeUpdateEvent( 1093 eid, instanceIdDb, listOfHandles, handler); 1094 if (rc != PLDM_SUCCESS) 1095 { 1096 return; 1097 } 1098 #endif 1099 } 1100 } 1101 1102 void BIOSConfig::listenPendingAttributes() 1103 { 1104 constexpr auto objPath = "/xyz/openbmc_project/bios_config/manager"; 1105 constexpr auto objInterface = "xyz.openbmc_project.BIOSConfig.Manager"; 1106 1107 using namespace sdbusplus::bus::match::rules; 1108 auto updateBIOSMatch = std::make_unique<sdbusplus::bus::match_t>( 1109 pldm::utils::DBusHandler::getBus(), 1110 propertiesChanged(objPath, objInterface), 1111 [this](sdbusplus::message_t& msg) { 1112 constexpr auto propertyName = "PendingAttributes"; 1113 1114 using Value = 1115 std::variant<std::string, PendingAttributes, BaseBIOSTable>; 1116 using Properties = std::map<DbusProp, Value>; 1117 1118 Properties props{}; 1119 std::string intf; 1120 msg.read(intf, props); 1121 1122 auto valPropMap = props.find(propertyName); 1123 if (valPropMap == props.end()) 1124 { 1125 return; 1126 } 1127 1128 PendingAttributes pendingAttributes = 1129 std::get<PendingAttributes>(valPropMap->second); 1130 this->constructPendingAttribute(pendingAttributes); 1131 }); 1132 1133 biosAttrMatch.emplace_back(std::move(updateBIOSMatch)); 1134 } 1135 1136 } // namespace bios 1137 } // namespace responder 1138 } // namespace pldm 1139