1 #include "bios.hpp" 2 3 #include "libpldmresponder/utils.hpp" 4 #include "registration.hpp" 5 #include "xyz/openbmc_project/Common/error.hpp" 6 7 #include <array> 8 #include <boost/crc.hpp> 9 #include <chrono> 10 #include <ctime> 11 #include <iostream> 12 #include <memory> 13 #include <numeric> 14 #include <phosphor-logging/elog-errors.hpp> 15 #include <phosphor-logging/log.hpp> 16 #include <stdexcept> 17 #include <string> 18 #include <variant> 19 #include <vector> 20 21 using namespace pldm::responder::bios; 22 using namespace bios_parser; 23 24 namespace pldm 25 { 26 27 using namespace phosphor::logging; 28 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 29 using EpochTimeUS = uint64_t; 30 using BIOSTableRow = std::vector<uint8_t>; 31 using BIOSJsonName = std::string; 32 33 constexpr auto dbusProperties = "org.freedesktop.DBus.Properties"; 34 constexpr auto padChksumMax = 7; 35 36 namespace responder 37 { 38 39 namespace utils 40 { 41 42 void epochToBCDTime(uint64_t timeSec, uint8_t& seconds, uint8_t& minutes, 43 uint8_t& hours, uint8_t& day, uint8_t& month, 44 uint16_t& year) 45 { 46 auto t = time_t(timeSec); 47 auto time = localtime(&t); 48 49 seconds = decimalToBcd(time->tm_sec); 50 minutes = decimalToBcd(time->tm_min); 51 hours = decimalToBcd(time->tm_hour); 52 day = decimalToBcd(time->tm_mday); 53 month = 54 decimalToBcd(time->tm_mon + 1); // The number of months in the range 55 // 0 to 11.PLDM expects range 1 to 12 56 year = decimalToBcd(time->tm_year + 1900); // The number of years since 1900 57 } 58 59 size_t getTableTotalsize(size_t sizeWithoutPad) 60 { 61 auto padSize = getNumPadBytes(sizeWithoutPad); 62 return sizeWithoutPad + padSize + sizeof(uint32_t) /* checksum */; 63 } 64 65 void padAndChecksum(Table& table) 66 { 67 auto padSize = getNumPadBytes(table.size()); 68 table.insert(table.end(), padSize, 0); 69 70 boost::crc_32_type result; 71 size_t size = table.size(); 72 result.process_bytes(table.data(), size); 73 uint32_t checkSum = result.checksum(); 74 uint8_t* checkSumPtr = reinterpret_cast<uint8_t*>(&checkSum); 75 table.insert(table.end(), checkSumPtr, checkSumPtr + sizeof(checkSum)); 76 } 77 78 } // namespace utils 79 80 Response getDateTime(const pldm_msg* request, size_t /*payloadLength*/) 81 { 82 uint8_t seconds = 0; 83 uint8_t minutes = 0; 84 uint8_t hours = 0; 85 uint8_t day = 0; 86 uint8_t month = 0; 87 uint16_t year = 0; 88 89 constexpr auto timeInterface = "xyz.openbmc_project.Time.EpochTime"; 90 constexpr auto hostTimePath = "/xyz/openbmc_project/time/host"; 91 Response response(sizeof(pldm_msg_hdr) + PLDM_GET_DATE_TIME_RESP_BYTES, 0); 92 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 93 std::variant<EpochTimeUS> value; 94 95 auto bus = sdbusplus::bus::new_default(); 96 try 97 { 98 auto service = getService(bus, hostTimePath, timeInterface); 99 100 auto method = bus.new_method_call(service.c_str(), hostTimePath, 101 dbusProperties, "Get"); 102 method.append(timeInterface, "Elapsed"); 103 104 auto reply = bus.call(method); 105 reply.read(value); 106 } 107 108 catch (std::exception& e) 109 { 110 log<level::ERR>("Error getting time", entry("PATH=%s", hostTimePath), 111 entry("TIME INTERACE=%s", timeInterface)); 112 113 encode_get_date_time_resp(request->hdr.instance_id, PLDM_ERROR, seconds, 114 minutes, hours, day, month, year, 115 responsePtr); 116 return response; 117 } 118 119 uint64_t timeUsec = std::get<EpochTimeUS>(value); 120 121 uint64_t timeSec = std::chrono::duration_cast<std::chrono::seconds>( 122 std::chrono::microseconds(timeUsec)) 123 .count(); 124 125 utils::epochToBCDTime(timeSec, seconds, minutes, hours, day, month, year); 126 127 encode_get_date_time_resp(request->hdr.instance_id, PLDM_SUCCESS, seconds, 128 minutes, hours, day, month, year, responsePtr); 129 return response; 130 } 131 132 /** @brief Generate the next attribute handle 133 * 134 * @return - uint16_t - next attribute handle 135 */ 136 AttributeHandle nextAttributeHandle() 137 { 138 static AttributeHandle attrHdl = 0; 139 return attrHdl++; 140 } 141 142 /** @brief Generate the next string handle 143 * * 144 * @return - uint16_t - next string handle 145 */ 146 StringHandle nextStringHandle() 147 { 148 static StringHandle strHdl = 0; 149 return strHdl++; 150 } 151 152 /** @brief Construct the BIOS string table 153 * 154 * @param[in] BIOSStringTable - the string table 155 * @param[in] transferHandle - transfer handle to identify part of transfer 156 * @param[in] transferOpFlag - flag to indicate which part of data being 157 * transferred 158 * @param[in] instanceID - instance ID to identify the command 159 * @param[in] biosJsonDir - path where the BIOS json files are present 160 */ 161 Response getBIOSStringTable(BIOSTable& BIOSStringTable, 162 uint32_t /*transferHandle*/, 163 uint8_t /*transferOpFlag*/, uint8_t instanceID, 164 const char* biosJsonDir) 165 { 166 Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES, 167 0); 168 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 169 170 if (BIOSStringTable.isEmpty()) 171 { // no persisted table, constructing fresh table and file 172 auto biosStrings = bios_parser::getStrings(biosJsonDir); 173 if (biosStrings.empty()) 174 { 175 encode_get_bios_table_resp(instanceID, PLDM_BIOS_TABLE_UNAVAILABLE, 176 0, PLDM_START_AND_END, nullptr, 177 response.size(), responsePtr); 178 return response; 179 } 180 std::sort(biosStrings.begin(), biosStrings.end()); 181 // remove all duplicate strings received from bios json 182 biosStrings.erase(std::unique(biosStrings.begin(), biosStrings.end()), 183 biosStrings.end()); 184 size_t allStringsLen = 185 std::accumulate(biosStrings.begin(), biosStrings.end(), 0, 186 [](size_t sum, const std::string& elem) { 187 return sum + elem.size(); 188 }); 189 size_t sizeWithoutPad = 190 allStringsLen + 191 (biosStrings.size() * (sizeof(pldm_bios_string_table_entry) - 1)); 192 Table stringTable; 193 stringTable.reserve(utils::getTableTotalsize(sizeWithoutPad)); 194 195 stringTable.resize(sizeWithoutPad); 196 auto tablePtr = stringTable.data(); 197 for (const auto& elem : biosStrings) 198 { 199 auto stringPtr = 200 reinterpret_cast<struct pldm_bios_string_table_entry*>( 201 tablePtr); 202 203 stringPtr->string_handle = nextStringHandle(); 204 stringPtr->string_length = elem.length(); 205 memcpy(stringPtr->name, elem.c_str(), elem.length()); 206 tablePtr += sizeof(stringPtr->string_handle) + 207 sizeof(stringPtr->string_length); 208 tablePtr += elem.length(); 209 } 210 211 utils::padAndChecksum(stringTable); 212 BIOSStringTable.store(stringTable); 213 response.resize(sizeof(pldm_msg_hdr) + 214 PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + 215 stringTable.size(), 216 0); 217 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 218 size_t respPayloadLength = response.size(); 219 uint32_t nxtTransferHandle = 0; 220 uint8_t transferFlag = PLDM_START_AND_END; 221 encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle, 222 transferFlag, stringTable.data(), 223 respPayloadLength, responsePtr); 224 } 225 else 226 { // persisted table present, constructing response 227 size_t respPayloadLength = response.size(); 228 uint32_t nxtTransferHandle = 0; 229 uint8_t transferFlag = PLDM_START_AND_END; 230 encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle, 231 transferFlag, nullptr, respPayloadLength, 232 responsePtr); // filling up the header here 233 BIOSStringTable.load(response); 234 } 235 236 return response; 237 } 238 239 /** @brief Find the string handle from the BIOS string table given the name 240 * 241 * @param[in] name - name of the BIOS string 242 * @param[in] BIOSStringTable - the string table 243 * @return - uint16_t - handle of the string 244 */ 245 StringHandle findStringHandle(const std::string& name, 246 const BIOSTable& BIOSStringTable) 247 { 248 StringHandle hdl{}; 249 Response response; 250 BIOSStringTable.load(response); 251 252 auto tableData = response.data(); 253 size_t tableLen = response.size(); 254 auto tableEntry = 255 reinterpret_cast<struct pldm_bios_string_table_entry*>(response.data()); 256 while (1) 257 { 258 hdl = tableEntry->string_handle; 259 uint16_t len = tableEntry->string_length; 260 if (name.compare(0, name.length(), tableEntry->name, len) == 0) 261 { 262 break; 263 } 264 tableData += (sizeof(struct pldm_bios_string_table_entry) - 1) + len; 265 266 if (std::distance(tableData, response.data() + tableLen) <= 267 padChksumMax) 268 { 269 log<level::ERR>("Reached end of BIOS string table,did not find the " 270 "handle for the string", 271 entry("STRING=%s", name.c_str())); 272 elog<InternalFailure>(); 273 break; 274 } 275 276 tableEntry = 277 reinterpret_cast<struct pldm_bios_string_table_entry*>(tableData); 278 } 279 return hdl; 280 } 281 282 /** @brief Find the string name from the BIOS string table for a string handle 283 * 284 * @param[in] stringHdl - string handle 285 * @param[in] BIOSStringTable - the string table 286 * 287 * @return - std::string - name of the corresponding BIOS string 288 */ 289 std::string findStringName(StringHandle stringHdl, 290 const BIOSTable& BIOSStringTable) 291 { 292 std::string name; 293 Response response; 294 BIOSStringTable.load(response); 295 296 auto tableData = response.data(); 297 size_t tableLen = response.size(); 298 auto tableEntry = 299 reinterpret_cast<struct pldm_bios_string_table_entry*>(response.data()); 300 while (1) 301 { 302 StringHandle currHdl = tableEntry->string_handle; 303 uint16_t len = tableEntry->string_length; 304 if (currHdl == stringHdl) 305 { 306 name.resize(len); 307 memcpy(name.data(), tableEntry->name, len); 308 break; 309 } 310 tableData += (sizeof(struct pldm_bios_string_table_entry) - 1) + len; 311 312 if (std::distance(tableData, response.data() + tableLen) <= 313 padChksumMax) 314 { 315 log<level::ERR>("Reached end of BIOS string table,did not find " 316 "string name for handle", 317 entry("STRING_HANDLE=%d", stringHdl)); 318 break; 319 } 320 321 tableEntry = 322 reinterpret_cast<struct pldm_bios_string_table_entry*>(tableData); 323 } 324 return name; 325 } 326 327 namespace bios_type_enum 328 { 329 330 using namespace bios_parser::bios_enum; 331 332 /** @brief Find the indices into the array of the possible values of string 333 * handles for the current values.This is used in attribute value table 334 * 335 * @param[in] possiVals - vector of string handles comprising all the possible 336 * values for an attribute 337 * @param[in] currVals - vector of strings comprising all current values 338 * for an attribute 339 * @param[in] BIOSStringTable - the string table 340 * 341 * @return - std::vector<uint8_t> - indices into the array of the possible 342 * values of string handles 343 */ 344 std::vector<uint8_t> findStrIndices(PossibleValuesByHandle possiVals, 345 CurrentValues currVals, 346 const BIOSTable& BIOSStringTable) 347 { 348 std::vector<uint8_t> stringIndices; 349 350 for (const auto& currVal : currVals) 351 { 352 StringHandle curHdl; 353 try 354 { 355 curHdl = findStringHandle(currVal, BIOSStringTable); 356 } 357 catch (InternalFailure& e) 358 { 359 log<level::ERR>("Exception fetching handle for the string", 360 entry("STRING=%s", currVal.c_str())); 361 continue; 362 } 363 364 uint8_t i = 0; 365 for (auto possiHdl : possiVals) 366 { 367 if (possiHdl == curHdl) 368 { 369 stringIndices.push_back(i); 370 break; 371 } 372 i++; 373 } 374 } 375 return stringIndices; 376 } 377 378 /** @brief Find the indices into the array of the possible values of string 379 * handles for the default values. This is used in attribute table 380 * 381 * @param[in] possiVals - vector of strings comprising all the possible values 382 * for an attribute 383 * @param[in] defVals - vector of strings comprising all the default values 384 * for an attribute 385 * @return - std::vector<uint8_t> - indices into the array of the possible 386 * values of string 387 */ 388 std::vector<uint8_t> findDefaultValHandle(const PossibleValues& possiVals, 389 const DefaultValues& defVals) 390 { 391 std::vector<uint8_t> defHdls; 392 for (const auto& defs : defVals) 393 { 394 auto index = std::lower_bound(possiVals.begin(), possiVals.end(), defs); 395 if (index != possiVals.end()) 396 { 397 defHdls.push_back(index - possiVals.begin()); 398 } 399 } 400 401 return defHdls; 402 } 403 404 /** @brief Construct the attibute table for BIOS type Enumeration and 405 * Enumeration ReadOnly 406 * @param[in] BIOSStringTable - the string table 407 * @param[in] biosJsonDir - path where the BIOS json files are present 408 * @param[in,out] attributeTable - the attribute table 409 * 410 */ 411 void constructAttrTable(const BIOSTable& BIOSStringTable, 412 const char* biosJsonDir, Table& attributeTable) 413 { 414 setupValueLookup(biosJsonDir); 415 const auto& attributeMap = getValues(); 416 StringHandle strHandle; 417 418 for (const auto& [key, value] : attributeMap) 419 { 420 try 421 { 422 strHandle = findStringHandle(key, BIOSStringTable); 423 } 424 catch (InternalFailure& e) 425 { 426 log<level::ERR>("Could not find handle for BIOS string", 427 entry("ATTRIBUTE=%s", key.c_str())); 428 continue; 429 } 430 uint8_t typeOfAttr = (std::get<0>(value)) 431 ? PLDM_BIOS_ENUMERATION_READ_ONLY 432 : PLDM_BIOS_ENUMERATION; 433 PossibleValues possiVals = std::get<1>(value); 434 DefaultValues defVals = std::get<2>(value); 435 // both the possible and default values are stored in sorted manner to 436 // ease in fetching back/comparison 437 std::sort(possiVals.begin(), possiVals.end()); 438 std::sort(defVals.begin(), defVals.end()); 439 440 std::vector<StringHandle> possiValsByHdl; 441 for (const auto& elem : possiVals) 442 { 443 try 444 { 445 auto hdl = findStringHandle(elem, BIOSStringTable); 446 possiValsByHdl.push_back(std::move(hdl)); 447 } 448 catch (InternalFailure& e) 449 { 450 log<level::ERR>("Could not find handle for BIOS string", 451 entry("STRING=%s", elem.c_str())); 452 continue; 453 } 454 } 455 auto defValsByHdl = findDefaultValHandle(possiVals, defVals); 456 457 BIOSTableRow enumAttrTable( 458 (sizeof(struct pldm_bios_attr_table_entry) - 1) + sizeof(uint8_t) + 459 possiValsByHdl.size() * sizeof(uint16_t) + sizeof(uint8_t) + 460 defValsByHdl.size() * sizeof(uint8_t), 461 0); 462 BIOSTableRow::iterator it = enumAttrTable.begin(); 463 auto attrPtr = reinterpret_cast<struct pldm_bios_attr_table_entry*>( 464 enumAttrTable.data()); 465 attrPtr->attr_handle = nextAttributeHandle(); 466 attrPtr->attr_type = typeOfAttr; 467 attrPtr->string_handle = std::move(strHandle); 468 std::advance(it, (sizeof(struct pldm_bios_attr_table_entry) - 1)); 469 uint8_t numPossibleVals = possiValsByHdl.size(); 470 std::copy_n(&numPossibleVals, sizeof(numPossibleVals), it); 471 std::advance(it, sizeof(numPossibleVals)); 472 std::copy_n(reinterpret_cast<uint8_t*>(possiValsByHdl.data()), 473 sizeof(uint16_t) * possiValsByHdl.size(), it); 474 std::advance( 475 it, sizeof(uint16_t) * 476 possiValsByHdl.size()); // possible val handle is uint16_t 477 uint8_t numDefaultVals = defValsByHdl.size(); 478 std::copy_n(&numDefaultVals, sizeof(numDefaultVals), it); 479 std::advance(it, sizeof(numDefaultVals)); 480 std::copy(defValsByHdl.begin(), defValsByHdl.end(), it); 481 std::advance(it, defValsByHdl.size()); 482 483 std::move(enumAttrTable.begin(), enumAttrTable.end(), 484 std::back_inserter(attributeTable)); 485 } 486 } 487 488 /** @brief Construct the attibute value table for BIOS type Enumeration and 489 * Enumeration ReadOnly 490 * 491 * @param[in] BIOSAttributeTable - the attribute table 492 * @param[in] BIOSStringTable - the string table 493 * @param[in, out] attributeValueTable - the attribute value table 494 * 495 */ 496 void constructAttrValueTable(const BIOSTable& BIOSAttributeTable, 497 const BIOSTable& BIOSStringTable, 498 Table& attributeValueTable) 499 { 500 Response response; 501 BIOSAttributeTable.load(response); 502 503 auto tableData = response.data(); 504 size_t tableLen = response.size(); 505 auto attrPtr = 506 reinterpret_cast<struct pldm_bios_attr_table_entry*>(response.data()); 507 508 while (1) 509 { 510 uint16_t attrHdl = attrPtr->attr_handle; 511 uint8_t attrType = attrPtr->attr_type; 512 uint16_t stringHdl = attrPtr->string_handle; 513 tableData += (sizeof(struct pldm_bios_attr_table_entry) - 1); 514 uint8_t numPossiVals = *tableData; 515 tableData++; // pass number of possible values 516 PossibleValuesByHandle possiValsByHdl(numPossiVals, 0); 517 memcpy(possiValsByHdl.data(), tableData, 518 sizeof(uint16_t) * numPossiVals); 519 tableData += sizeof(uint16_t) * numPossiVals; 520 uint8_t numDefVals = *tableData; 521 tableData++; // pass number of def vals 522 tableData += numDefVals; // pass all the def val indices 523 524 auto attrName = findStringName(stringHdl, BIOSStringTable); 525 if (attrName.empty()) 526 { 527 if (std::distance(tableData, response.data() + tableLen) <= 528 padChksumMax) 529 { 530 log<level::ERR>("Did not find string name for handle", 531 entry("STRING_HANDLE=%d", stringHdl)); 532 return; 533 } 534 attrPtr = 535 reinterpret_cast<struct pldm_bios_attr_table_entry*>(tableData); 536 continue; 537 } 538 CurrentValues currVals; 539 try 540 { 541 currVals = getAttrValue(attrName); 542 } 543 catch (const std::exception& e) 544 { 545 log<level::ERR>( 546 "constructAttrValueTable returned error for attribute", 547 entry("NAME=%s", attrName.c_str()), 548 entry("ERROR=%s", e.what())); 549 if (std::distance(tableData, response.data() + tableLen) <= 550 padChksumMax) 551 { 552 return; 553 } 554 555 attrPtr = 556 reinterpret_cast<struct pldm_bios_attr_table_entry*>(tableData); 557 continue; 558 } 559 // sorting since the possible values are stored in sorted way 560 std::sort(currVals.begin(), currVals.end()); 561 auto currValStrIndices = 562 findStrIndices(possiValsByHdl, currVals, BIOSStringTable); 563 // number of current values equals to the number of string handles 564 // received not the number of strings received from getAttrValue 565 uint8_t numCurrVals = currValStrIndices.size(); 566 567 BIOSTableRow enumAttrValTable( 568 (sizeof(struct pldm_bios_attr_val_table_entry) - 1) + 569 sizeof(uint8_t) + numCurrVals * sizeof(uint8_t), 570 0); 571 BIOSTableRow::iterator it = enumAttrValTable.begin(); 572 auto attrValPtr = 573 reinterpret_cast<struct pldm_bios_attr_val_table_entry*>( 574 enumAttrValTable.data()); 575 attrValPtr->attr_handle = attrHdl; 576 attrValPtr->attr_type = attrType; 577 std::advance(it, (sizeof(pldm_bios_attr_val_table_entry) - 1)); 578 std::copy_n(&numCurrVals, sizeof(numCurrVals), it); 579 std::advance(it, sizeof(numCurrVals)); 580 if (numCurrVals) 581 { 582 std::copy(currValStrIndices.begin(), currValStrIndices.end(), it); 583 std::advance(it, currValStrIndices.size()); 584 } 585 std::move(enumAttrValTable.begin(), enumAttrValTable.end(), 586 std::back_inserter(attributeValueTable)); 587 588 if (std::distance(tableData, response.data() + tableLen) <= 589 padChksumMax) 590 { 591 break; 592 } 593 594 attrPtr = 595 reinterpret_cast<struct pldm_bios_attr_table_entry*>(tableData); 596 } 597 } 598 599 } // end namespace bios_type_enum 600 601 namespace bios_type_string 602 { 603 604 using namespace bios_parser::bios_string; 605 606 /** @brief Construct the attibute table for BIOS type String and 607 * String ReadOnly 608 * @param[in] BIOSStringTable - the string table 609 * @param[in] biosJsonDir - path where the BIOS json files are present 610 * @param[in,out] attributeTable - the attribute table 611 * 612 */ 613 void constructAttrTable(const BIOSTable& BIOSStringTable, 614 const char* biosJsonDir, Table& attributeTable) 615 { 616 auto rc = setupValueLookup(biosJsonDir); 617 if (rc == -1) 618 { 619 log<level::ERR>("Failed to parse entries in Json file"); 620 return; 621 } 622 const auto& attributeMap = getValues(); 623 StringHandle strHandle; 624 625 for (const auto& [key, value] : attributeMap) 626 { 627 try 628 { 629 strHandle = findStringHandle(key, BIOSStringTable); 630 } 631 catch (InternalFailure& e) 632 { 633 log<level::ERR>("Could not find handle for BIOS string", 634 entry("ATTRIBUTE=%s", key.c_str())); 635 continue; 636 } 637 638 const auto& [type, strType, minStrLen, maxStrLen, defaultStrLen, 639 defaultStr] = value; 640 uint8_t typeOfAttr = 641 type ? PLDM_BIOS_STRING_READ_ONLY : PLDM_BIOS_STRING; 642 643 BIOSTableRow stringAttrTable(bios_parser::bios_string::attrTableSize + 644 defaultStr.size()); 645 BIOSTableRow::iterator it = stringAttrTable.begin(); 646 auto attrPtr = reinterpret_cast<struct pldm_bios_attr_table_entry*>( 647 stringAttrTable.data()); 648 attrPtr->attr_handle = nextAttributeHandle(); 649 attrPtr->attr_type = typeOfAttr; 650 attrPtr->string_handle = strHandle; 651 652 std::advance(it, (sizeof(struct pldm_bios_attr_table_entry) - 1)); 653 std::copy_n(&strType, sizeof(uint8_t), it); 654 std::advance(it, sizeof(uint8_t)); 655 std::copy_n(reinterpret_cast<const uint8_t*>(&minStrLen), 656 sizeof(uint16_t), it); 657 std::advance(it, sizeof(uint16_t)); 658 std::copy_n(reinterpret_cast<const uint8_t*>(&maxStrLen), 659 sizeof(uint16_t), it); 660 std::advance(it, sizeof(uint16_t)); 661 std::copy_n(reinterpret_cast<const uint8_t*>(&defaultStrLen), 662 sizeof(uint16_t), it); 663 std::advance(it, sizeof(uint16_t)); 664 std::copy_n(defaultStr.data(), defaultStr.size(), it); 665 std::advance(it, defaultStr.size()); 666 667 attributeTable.insert(attributeTable.end(), stringAttrTable.begin(), 668 stringAttrTable.end()); 669 } 670 } 671 672 /** @brief Construct the attibute value table for BIOS type String and 673 * String ReadOnly 674 * 675 * @param[in] BIOSAttributeTable - the attribute table 676 * @param[in] BIOSStringTable - the string table 677 * @param[in, out] attributeValueTable - the attribute value table 678 * 679 */ 680 void constructAttrValueTable(const BIOSTable& BIOSAttributeTable, 681 const BIOSTable& BIOSStringTable, 682 Table& attributeValueTable) 683 { 684 Response response; 685 BIOSAttributeTable.load(response); 686 687 auto dataPtr = response.data(); 688 size_t tableLen = response.size(); 689 690 while (true) 691 { 692 auto attrPtr = 693 reinterpret_cast<struct pldm_bios_attr_table_entry*>(dataPtr); 694 uint16_t attrHdl = attrPtr->attr_handle; 695 uint8_t attrType = attrPtr->attr_type; 696 uint16_t stringHdl = attrPtr->string_handle; 697 dataPtr += (sizeof(struct pldm_bios_attr_table_entry) - 1); 698 // pass number of StringType, MinimumStringLength, MaximumStringLength 699 dataPtr += sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint16_t); 700 auto sizeDefaultStr = *(reinterpret_cast<uint16_t*>(dataPtr)); 701 // pass number of DefaultStringLength, DefaultString 702 dataPtr += sizeof(uint16_t) + sizeDefaultStr; 703 704 auto attrName = findStringName(stringHdl, BIOSStringTable); 705 if (attrName.empty()) 706 { 707 if (std::distance(dataPtr, response.data() + tableLen) <= 708 padChksumMax) 709 { 710 log<level::ERR>("Did not find string name for handle", 711 entry("STRING_HANDLE=%d", stringHdl)); 712 return; 713 } 714 continue; 715 } 716 717 uint16_t currStrLen = 0; 718 std::string currStr; 719 try 720 { 721 currStr = getAttrValue(attrName); 722 currStrLen = currStr.size(); 723 } 724 catch (const std::exception& e) 725 { 726 log<level::ERR>("getAttrValue returned error for attribute", 727 entry("NAME=%s", attrName.c_str()), 728 entry("ERROR=%s", e.what())); 729 if (std::distance(dataPtr, response.data() + tableLen) <= 730 padChksumMax) 731 { 732 return; 733 } 734 continue; 735 } 736 737 BIOSTableRow strAttrValTable( 738 bios_parser::bios_string::attrValueTableSize + currStrLen, 0); 739 BIOSTableRow::iterator it = strAttrValTable.begin(); 740 auto attrValPtr = 741 reinterpret_cast<struct pldm_bios_attr_val_table_entry*>( 742 strAttrValTable.data()); 743 attrValPtr->attr_handle = attrHdl; 744 attrValPtr->attr_type = attrType; 745 std::advance(it, (sizeof(pldm_bios_attr_val_table_entry) - 1)); 746 std::copy_n(reinterpret_cast<uint8_t*>(&currStrLen), sizeof(uint16_t), 747 it); 748 std::advance(it, sizeof(uint16_t)); 749 if (currStrLen) 750 { 751 std::copy_n(currStr.cbegin(), currStrLen, it); 752 std::advance(it, currStrLen); 753 } 754 755 attributeValueTable.insert(attributeValueTable.end(), 756 strAttrValTable.begin(), 757 strAttrValTable.end()); 758 759 if (std::distance(dataPtr, response.data() + tableLen) <= padChksumMax) 760 { 761 break; 762 } 763 } 764 } 765 766 } // end namespace bios_type_string 767 768 void traverseBIOSAttrTable(const Table& biosAttrTable, 769 AttrTableEntryHandler handler) 770 { 771 std::unique_ptr<pldm_bios_table_iter, decltype(&pldm_bios_table_iter_free)> 772 iter(pldm_bios_table_iter_create(biosAttrTable.data(), 773 biosAttrTable.size(), 774 PLDM_BIOS_ATTR_TABLE), 775 pldm_bios_table_iter_free); 776 while (!pldm_bios_table_iter_is_end(iter.get())) 777 { 778 auto table_entry = pldm_bios_table_iter_attr_entry_value(iter.get()); 779 try 780 { 781 handler(table_entry); 782 } 783 catch (const std::exception& e) 784 { 785 log<level::ERR>("handler fails when traversing BIOSAttrTable", 786 entry("ERROR=%s", e.what())); 787 } 788 pldm_bios_table_iter_next(iter.get()); 789 } 790 } 791 792 using typeHandler = 793 std::function<void(const BIOSTable& BIOSStringTable, 794 const char* biosJsonDir, Table& attributeTable)>; 795 std::map<BIOSJsonName, typeHandler> attrTypeHandlers{ 796 {bios_parser::bIOSEnumJson, bios_type_enum::constructAttrTable}, 797 {bios_parser::bIOSStrJson, bios_type_string::constructAttrTable}}; 798 799 using valueHandler = std::function<void(const BIOSTable& BIOSAttributeTable, 800 801 const BIOSTable& BIOSStringTable, 802 803 Table& attributeTable)>; 804 805 std::map<BIOSJsonName, valueHandler> attrValueHandlers{ 806 {bios_parser::bIOSEnumJson, bios_type_enum::constructAttrValueTable}, 807 {bios_parser::bIOSStrJson, bios_type_string::constructAttrValueTable}}; 808 809 /** @brief Construct the BIOS attribute table 810 * 811 * @param[in] BIOSAttributeTable - the attribute table 812 * @param[in] BIOSStringTable - the string table 813 * @param[in] transferHandle - transfer handle to identify part of transfer 814 * @param[in] transferOpFlag - flag to indicate which part of data being 815 * transferred 816 * @param[in] instanceID - instance ID to identify the command 817 * @param[in] biosJsonDir - path where the BIOS json files are present 818 */ 819 Response getBIOSAttributeTable(BIOSTable& BIOSAttributeTable, 820 const BIOSTable& BIOSStringTable, 821 uint32_t /*transferHandle*/, 822 uint8_t /*transferOpFlag*/, uint8_t instanceID, 823 const char* biosJsonDir) 824 { 825 Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES, 826 0); 827 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 828 uint32_t nxtTransferHandle = 0; 829 uint8_t transferFlag = PLDM_START_AND_END; 830 831 if (BIOSAttributeTable.isEmpty()) 832 { // no persisted table, constructing fresh table and response 833 Table attributeTable; 834 fs::path dir(biosJsonDir); 835 836 for (auto it = attrTypeHandlers.begin(); it != attrTypeHandlers.end(); 837 it++) 838 { 839 fs::path file = dir / it->first; 840 if (fs::exists(file)) 841 { 842 it->second(BIOSStringTable, biosJsonDir, attributeTable); 843 } 844 } 845 846 if (attributeTable.empty()) 847 { // no available json file is found 848 encode_get_bios_table_resp(instanceID, PLDM_BIOS_TABLE_UNAVAILABLE, 849 nxtTransferHandle, transferFlag, nullptr, 850 response.size(), responsePtr); 851 return response; 852 } 853 utils::padAndChecksum(attributeTable); 854 BIOSAttributeTable.store(attributeTable); 855 response.resize(sizeof(pldm_msg_hdr) + 856 PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + 857 attributeTable.size()); 858 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 859 encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle, 860 transferFlag, attributeTable.data(), 861 response.size(), responsePtr); 862 } 863 else 864 { // persisted table present, constructing response 865 encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle, 866 transferFlag, nullptr, response.size(), 867 responsePtr); // filling up the header here 868 BIOSAttributeTable.load(response); 869 } 870 871 return response; 872 } 873 874 /** @brief Construct the BIOS attribute value table 875 * 876 * @param[in] BIOSAttributeValueTable - the attribute value table 877 * @param[in] BIOSAttributeTable - the attribute table 878 * @param[in] BIOSStringTable - the string table 879 * @param[in] transferHandle - transfer handle to identify part of transfer 880 * @param[in] transferOpFlag - flag to indicate which part of data being 881 * transferred 882 * @param[in] instanceID - instance ID to identify the command 883 */ 884 Response getBIOSAttributeValueTable(BIOSTable& BIOSAttributeValueTable, 885 const BIOSTable& BIOSAttributeTable, 886 const BIOSTable& BIOSStringTable, 887 uint32_t& /*transferHandle*/, 888 uint8_t& /*transferOpFlag*/, 889 uint8_t instanceID, const char* biosJsonDir) 890 { 891 Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES, 892 0); 893 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 894 uint32_t nxtTransferHandle = 0; 895 uint8_t transferFlag = PLDM_START_AND_END; 896 size_t respPayloadLength{}; 897 898 if (BIOSAttributeValueTable.isEmpty()) 899 { // no persisted table, constructing fresh table and data 900 Table attributeValueTable; 901 fs::path dir(biosJsonDir); 902 903 for (auto it = attrValueHandlers.begin(); it != attrValueHandlers.end(); 904 it++) 905 { 906 fs::path file = dir / it->first; 907 if (fs::exists(file)) 908 { 909 it->second(BIOSAttributeTable, BIOSStringTable, 910 attributeValueTable); 911 } 912 } 913 914 if (attributeValueTable.empty()) 915 { // no available json file is found 916 encode_get_bios_table_resp(instanceID, PLDM_BIOS_TABLE_UNAVAILABLE, 917 nxtTransferHandle, transferFlag, nullptr, 918 response.size(), responsePtr); 919 return response; 920 } 921 // calculate pad 922 uint8_t padSize = utils::getNumPadBytes(attributeValueTable.size()); 923 std::vector<uint8_t> pad(padSize, 0); 924 if (padSize) 925 { 926 std::move(pad.begin(), pad.end(), 927 std::back_inserter(attributeValueTable)); 928 } 929 if (!attributeValueTable.empty()) 930 { 931 // compute checksum 932 boost::crc_32_type result; 933 result.process_bytes(attributeValueTable.data(), 934 attributeValueTable.size()); 935 uint32_t checkSum = result.checksum(); 936 size_t size = attributeValueTable.size(); 937 attributeValueTable.resize(size + sizeof(checkSum)); 938 std::copy_n(reinterpret_cast<uint8_t*>(&checkSum), sizeof(checkSum), 939 attributeValueTable.data() + size); 940 BIOSAttributeValueTable.store(attributeValueTable); 941 } 942 943 response.resize(sizeof(pldm_msg_hdr) + 944 PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + 945 attributeValueTable.size()); 946 responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 947 respPayloadLength = response.size(); 948 encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle, 949 transferFlag, attributeValueTable.data(), 950 respPayloadLength, responsePtr); 951 } 952 else 953 { // persisted table present, constructing response 954 respPayloadLength = response.size(); 955 encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle, 956 transferFlag, nullptr, respPayloadLength, 957 responsePtr); // filling up the header here 958 BIOSAttributeValueTable.load(response); 959 } 960 961 return response; 962 } 963 964 Response getBIOSTable(const pldm_msg* request, size_t payloadLength) 965 { 966 fs::create_directory(BIOS_TABLES_DIR); 967 auto response = internal::buildBIOSTables(request, payloadLength, 968 BIOS_JSONS_DIR, BIOS_TABLES_DIR); 969 970 return response; 971 } 972 973 namespace bios 974 { 975 976 void registerHandlers() 977 { 978 registerHandler(PLDM_BIOS, PLDM_GET_DATE_TIME, std::move(getDateTime)); 979 registerHandler(PLDM_BIOS, PLDM_GET_BIOS_TABLE, std::move(getBIOSTable)); 980 } 981 982 namespace internal 983 { 984 985 Response buildBIOSTables(const pldm_msg* request, size_t payloadLength, 986 const char* biosJsonDir, const char* biosTablePath) 987 { 988 Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES, 989 0); 990 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 991 992 uint32_t transferHandle{}; 993 uint8_t transferOpFlag{}; 994 uint8_t tableType{}; 995 996 auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle, 997 &transferOpFlag, &tableType); 998 if (rc == PLDM_SUCCESS) 999 { 1000 BIOSTable BIOSStringTable( 1001 ((std::string(biosTablePath) + "/stringTable")).c_str()); 1002 BIOSTable BIOSAttributeTable( 1003 ((std::string(biosTablePath) + "/attributeTable")).c_str()); 1004 BIOSTable BIOSAttributeValueTable( 1005 ((std::string(biosTablePath) + "/attributeValueTable")).c_str()); 1006 switch (tableType) 1007 { 1008 case PLDM_BIOS_STRING_TABLE: 1009 1010 response = getBIOSStringTable( 1011 BIOSStringTable, transferHandle, transferOpFlag, 1012 request->hdr.instance_id, biosJsonDir); 1013 break; 1014 case PLDM_BIOS_ATTR_TABLE: 1015 1016 if (BIOSStringTable.isEmpty()) 1017 { 1018 rc = PLDM_BIOS_TABLE_UNAVAILABLE; 1019 } 1020 else 1021 { 1022 response = getBIOSAttributeTable( 1023 BIOSAttributeTable, BIOSStringTable, transferHandle, 1024 transferOpFlag, request->hdr.instance_id, biosJsonDir); 1025 } 1026 break; 1027 case PLDM_BIOS_ATTR_VAL_TABLE: 1028 if (BIOSAttributeTable.isEmpty()) 1029 { 1030 rc = PLDM_BIOS_TABLE_UNAVAILABLE; 1031 } 1032 else 1033 { 1034 response = getBIOSAttributeValueTable( 1035 BIOSAttributeValueTable, BIOSAttributeTable, 1036 BIOSStringTable, transferHandle, transferOpFlag, 1037 request->hdr.instance_id, biosJsonDir); 1038 } 1039 break; 1040 default: 1041 rc = PLDM_INVALID_BIOS_TABLE_TYPE; 1042 break; 1043 } 1044 } 1045 1046 if (rc != PLDM_SUCCESS) 1047 { 1048 uint32_t nxtTransferHandle{}; 1049 uint8_t transferFlag{}; 1050 size_t respPayloadLength{}; 1051 1052 encode_get_bios_table_resp(request->hdr.instance_id, rc, 1053 nxtTransferHandle, transferFlag, nullptr, 1054 respPayloadLength, responsePtr); 1055 } 1056 1057 return response; 1058 } 1059 1060 } // end namespace internal 1061 } // namespace bios 1062 1063 } // namespace responder 1064 } // namespace pldm 1065