1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 17 #include "mdrv2.hpp" 18 19 #include "pcieslot.hpp" 20 21 #include <sys/mman.h> 22 23 #include <phosphor-logging/elog-errors.hpp> 24 #include <sdbusplus/exception.hpp> 25 #include <xyz/openbmc_project/Smbios/MDR_V2/error.hpp> 26 27 #include <fstream> 28 29 namespace phosphor 30 { 31 namespace smbios 32 { 33 34 std::vector<uint8_t> MDRV2::getDirectoryInformation(uint8_t dirIndex) 35 { 36 std::vector<uint8_t> responseDir; 37 38 std::ifstream smbiosFile(smbiosFilePath, std::ios_base::binary); 39 if (!smbiosFile.good()) 40 { 41 lg2::error( 42 "Read data from flash error - Open MDRV2 table file failure"); 43 throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: 44 InvalidParameter(); 45 } 46 if (dirIndex > smbiosDir.dirEntries) 47 { 48 responseDir.push_back(0); 49 throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: 50 InvalidParameter(); 51 } 52 responseDir.push_back(mdr2Version); 53 responseDir.push_back(smbiosDir.dirVersion); 54 uint8_t returnedEntries = smbiosDir.dirEntries - dirIndex; 55 responseDir.push_back(returnedEntries); 56 if ((dirIndex + returnedEntries) >= smbiosDir.dirEntries) 57 { 58 responseDir.push_back(0); 59 } 60 else 61 { 62 responseDir.push_back( 63 smbiosDir.dirEntries - dirIndex - returnedEntries); 64 } 65 for (uint8_t index = dirIndex; index < smbiosDir.dirEntries; index++) 66 { 67 for (uint8_t indexId = 0; indexId < sizeof(DataIdStruct); indexId++) 68 { 69 responseDir.push_back( 70 smbiosDir.dir[index].common.id.dataInfo[indexId]); 71 } 72 } 73 74 return responseDir; 75 } 76 77 bool MDRV2::smbiosIsAvailForUpdate(uint8_t index) 78 { 79 bool ret = false; 80 if (index >= maxDirEntries) 81 { 82 return ret; 83 } 84 85 switch (smbiosDir.dir[index].stage) 86 { 87 case MDR2SMBIOSStatusEnum::mdr2Updating: 88 ret = false; 89 break; 90 91 case MDR2SMBIOSStatusEnum::mdr2Init: 92 // This *looks* like there should be a break statement here, 93 // as the effects of the previous statement are a noop given 94 // the following code that this falls through to. 95 // We've elected not to change it, though, since it's been 96 // this way since the old generation, and it would affect 97 // the way the code functions. 98 // If it ain't broke, don't fix it. 99 100 case MDR2SMBIOSStatusEnum::mdr2Loaded: 101 case MDR2SMBIOSStatusEnum::mdr2Updated: 102 if (smbiosDir.dir[index].lock == MDR2DirLockEnum::mdr2DirLock) 103 { 104 ret = false; 105 } 106 else 107 { 108 ret = true; 109 } 110 break; 111 112 default: 113 break; 114 } 115 116 return ret; 117 } 118 119 std::vector<uint8_t> MDRV2::getDataOffer() 120 { 121 std::vector<uint8_t> offer(sizeof(DataIdStruct)); 122 if (smbiosIsAvailForUpdate(0)) 123 { 124 std::copy(smbiosDir.dir[0].common.id.dataInfo, 125 &smbiosDir.dir[0].common.id.dataInfo[16], offer.data()); 126 } 127 else 128 { 129 lg2::error("smbios is not ready for update"); 130 throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: 131 UpdateInProgress(); 132 } 133 return offer; 134 } 135 136 inline uint8_t MDRV2::smbiosValidFlag(uint8_t index) 137 { 138 FlagStatus ret = FlagStatus::flagIsInvalid; 139 MDR2SMBIOSStatusEnum stage = smbiosDir.dir[index].stage; 140 MDR2DirLockEnum lock = smbiosDir.dir[index].lock; 141 142 switch (stage) 143 { 144 case MDR2SMBIOSStatusEnum::mdr2Loaded: 145 case MDR2SMBIOSStatusEnum::mdr2Updated: 146 if (lock == MDR2DirLockEnum::mdr2DirLock) 147 { 148 ret = FlagStatus::flagIsLocked; // locked 149 } 150 else 151 { 152 ret = FlagStatus::flagIsValid; // valid 153 } 154 break; 155 156 case MDR2SMBIOSStatusEnum::mdr2Updating: 157 case MDR2SMBIOSStatusEnum::mdr2Init: 158 ret = FlagStatus::flagIsInvalid; // invalid 159 break; 160 161 default: 162 break; 163 } 164 165 return static_cast<uint8_t>(ret); 166 } 167 168 // If source variable size is 4 bytes (uint32_t) and destination is Vector type 169 // is 1 byte (uint8_t), then by using this API can copy data byte by byte. For 170 // Example copying data from smbiosDir.dir[idIndex].common.size and 171 // smbiosDir.dir[idIndex].common.timestamp to vector variable responseInfo 172 template <typename T> 173 void appendReversed(std::vector<uint8_t>& vector, const T& value) 174 { 175 auto data = reinterpret_cast<const uint8_t*>(&value); 176 std::reverse_copy(data, data + sizeof(value), std::back_inserter(vector)); 177 } 178 179 std::vector<uint8_t> MDRV2::getDataInformation(uint8_t idIndex) 180 { 181 std::vector<uint8_t> responseInfo; 182 responseInfo.push_back(mdr2Version); 183 184 if (idIndex >= maxDirEntries) 185 { 186 throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: 187 InvalidParameter(); 188 } 189 190 for (uint8_t index = 0; index < sizeof(DataIdStruct); index++) 191 { 192 responseInfo.push_back( 193 smbiosDir.dir[idIndex].common.id.dataInfo[index]); 194 } 195 196 responseInfo.push_back(smbiosValidFlag(idIndex)); 197 appendReversed(responseInfo, smbiosDir.dir[idIndex].common.size); 198 responseInfo.push_back(smbiosDir.dir[idIndex].common.dataVersion); 199 appendReversed(responseInfo, smbiosDir.dir[idIndex].common.timestamp); 200 201 return responseInfo; 202 } 203 204 bool MDRV2::readDataFromFlash(MDRSMBIOSHeader* mdrHdr, uint8_t* data) 205 { 206 if (mdrHdr == nullptr) 207 { 208 lg2::error("Read data from flash error - Invalid mdr header"); 209 return false; 210 } 211 if (data == nullptr) 212 { 213 lg2::error("Read data from flash error - Invalid data point"); 214 return false; 215 } 216 std::ifstream smbiosFile(smbiosFilePath, std::ios_base::binary); 217 if (!smbiosFile.good()) 218 { 219 lg2::error( 220 "Read data from flash error - Open MDRV2 table file failure"); 221 return false; 222 } 223 smbiosFile.clear(); 224 smbiosFile.seekg(0, std::ios_base::end); 225 size_t fileLength = smbiosFile.tellg(); 226 smbiosFile.seekg(0, std::ios_base::beg); 227 if (fileLength < sizeof(MDRSMBIOSHeader)) 228 { 229 lg2::error("MDR V2 file size is smaller than mdr header"); 230 return false; 231 } 232 smbiosFile.read(reinterpret_cast<char*>(mdrHdr), sizeof(MDRSMBIOSHeader)); 233 if (mdrHdr->dataSize > smbiosTableStorageSize) 234 { 235 lg2::error("Data size out of limitation"); 236 smbiosFile.close(); 237 return false; 238 } 239 fileLength -= sizeof(MDRSMBIOSHeader); 240 if (fileLength < mdrHdr->dataSize) 241 { 242 smbiosFile.read(reinterpret_cast<char*>(data), fileLength); 243 } 244 else 245 { 246 smbiosFile.read(reinterpret_cast<char*>(data), mdrHdr->dataSize); 247 } 248 smbiosFile.close(); 249 return true; 250 } 251 252 bool MDRV2::sendDirectoryInformation( 253 uint8_t dirVersion, uint8_t dirIndex, uint8_t returnedEntries, 254 uint8_t remainingEntries, std::vector<uint8_t> dirEntry) 255 { 256 bool terminate = false; 257 if ((dirIndex >= maxDirEntries) || (returnedEntries < 1)) 258 { 259 lg2::error("Send Dir info failed - input parameter invalid"); 260 throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: 261 InvalidParameter(); 262 } 263 if ((static_cast<size_t>(returnedEntries) * sizeof(DataIdStruct)) != 264 dirEntry.size()) 265 { 266 lg2::error("Directory size invalid"); 267 throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: 268 InvalidParameter(); 269 } 270 if (dirVersion == smbiosDir.dirVersion) 271 { 272 terminate = true; 273 } 274 else 275 { 276 if (remainingEntries > 0) 277 { 278 terminate = false; 279 } 280 else 281 { 282 terminate = true; 283 smbiosDir.dirVersion = dirVersion; 284 } 285 uint8_t idIndex = dirIndex; 286 smbiosDir.dirEntries = returnedEntries; 287 288 uint8_t* pData = dirEntry.data(); 289 if (pData == nullptr) 290 { 291 return false; 292 } 293 for (uint8_t index = 0; index < returnedEntries; index++) 294 { 295 auto data = reinterpret_cast<const DataIdStruct*>(pData); 296 std::copy(data->dataInfo, data->dataInfo + sizeof(DataIdStruct), 297 smbiosDir.dir[idIndex + index].common.id.dataInfo); 298 pData += sizeof(DataIdStruct); 299 } 300 } 301 return terminate; 302 } 303 304 bool MDRV2::sendDataInformation(uint8_t idIndex, uint8_t /* flag */, 305 uint32_t dataLen, uint32_t dataVer, 306 uint32_t timeStamp) 307 { 308 if (idIndex >= maxDirEntries) 309 { 310 throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: 311 InvalidParameter(); 312 } 313 int entryChanged = 0; 314 if (smbiosDir.dir[idIndex].common.dataSetSize != dataLen) 315 { 316 entryChanged++; 317 smbiosDir.dir[idIndex].common.dataSetSize = dataLen; 318 } 319 320 if (smbiosDir.dir[idIndex].common.dataVersion != dataVer) 321 { 322 entryChanged++; 323 smbiosDir.dir[idIndex].common.dataVersion = dataVer; 324 } 325 326 if (smbiosDir.dir[idIndex].common.timestamp != timeStamp) 327 { 328 entryChanged++; 329 smbiosDir.dir[idIndex].common.timestamp = timeStamp; 330 } 331 if (entryChanged == 0) 332 { 333 return false; 334 } 335 return true; 336 } 337 338 int MDRV2::findIdIndex(std::vector<uint8_t> dataInfo) 339 { 340 if (dataInfo.size() != sizeof(DataIdStruct)) 341 { 342 lg2::error("Length of dataInfo invalid"); 343 throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error:: 344 InvalidId(); 345 } 346 std::array<uint8_t, 16> arrayDataInfo; 347 348 std::copy(dataInfo.begin(), dataInfo.end(), arrayDataInfo.begin()); 349 350 for (int index = 0; index < smbiosDir.dirEntries; index++) 351 { 352 size_t info = 0; 353 for (; info < arrayDataInfo.size(); info++) 354 { 355 if (arrayDataInfo[info] != 356 smbiosDir.dir[index].common.id.dataInfo[info]) 357 { 358 break; 359 } 360 } 361 if (info == arrayDataInfo.size()) 362 { 363 return index; 364 } 365 } 366 throw sdbusplus::xyz::openbmc_project::Smbios::MDR_V2::Error::InvalidId(); 367 } 368 369 uint8_t MDRV2::directoryEntries(uint8_t value) 370 { 371 std::ifstream smbiosFile(smbiosFilePath, std::ios_base::binary); 372 if (!smbiosFile.good()) 373 { 374 lg2::error( 375 "Read data from flash error - Open MDRV2 table file failure"); 376 value = 0; 377 } 378 else 379 { 380 value = smbiosDir.dirEntries; 381 } 382 return sdbusplus::server::xyz::openbmc_project::smbios::MDRV2:: 383 directoryEntries(value); 384 } 385 386 void MDRV2::systemInfoUpdate() 387 { 388 // By default, look for System interface on any system/board/* object 389 std::string mapperAncestorPath = smbiosInventoryPath; 390 std::string matchParentPath = smbiosInventoryPath + "/board/"; 391 bool requireExactMatch = false; 392 393 // If customized, look for System on only that custom object 394 if (smbiosInventoryPath != defaultInventoryPath) 395 { 396 std::filesystem::path path(smbiosInventoryPath); 397 398 // Search under parent to find exact match for self 399 mapperAncestorPath = path.parent_path().string(); 400 matchParentPath = mapperAncestorPath; 401 requireExactMatch = true; 402 } 403 404 std::string motherboardPath; 405 auto method = bus->new_method_call(mapperBusName, mapperPath, 406 mapperInterface, "GetSubTreePaths"); 407 method.append(mapperAncestorPath); 408 method.append(0); 409 410 // If customized, also accept Board as anchor, not just System 411 std::vector<std::string> desiredInterfaces{systemInterface}; 412 if (requireExactMatch) 413 { 414 desiredInterfaces.emplace_back(boardInterface); 415 } 416 method.append(desiredInterfaces); 417 418 try 419 { 420 std::vector<std::string> paths; 421 sdbusplus::message_t reply = bus->call(method); 422 reply.read(paths); 423 424 size_t pathsCount = paths.size(); 425 for (size_t i = 0; i < pathsCount; ++i) 426 { 427 if (requireExactMatch && (paths[i] != smbiosInventoryPath)) 428 { 429 continue; 430 } 431 432 motherboardPath = std::move(paths[i]); 433 break; 434 } 435 } 436 catch (const sdbusplus::exception_t& e) 437 { 438 lg2::error( 439 "Exception while trying to find Inventory anchor object for SMBIOS content {I}: {E}", 440 "I", smbiosInventoryPath, "E", e.what()); 441 lg2::error("Failed to query system motherboard: {ERROR}", "ERROR", 442 e.what()); 443 } 444 445 if (motherboardPath.empty()) 446 { 447 lg2::error( 448 "Failed to get system motherboard dbus path. Setting up a match rule"); 449 450 if (motherboardConfigMatch) 451 { 452 lg2::info("Motherboard match rule already exists"); 453 } 454 else 455 { 456 motherboardConfigMatch = std::make_unique<sdbusplus::bus::match_t>( 457 *bus, 458 sdbusplus::bus::match::rules::interfacesAdded() + 459 sdbusplus::bus::match::rules::argNpath(0, matchParentPath), 460 [this, requireExactMatch](sdbusplus::message_t& msg) { 461 sdbusplus::message::object_path objectName; 462 boost::container::flat_map< 463 std::string, 464 boost::container::flat_map< 465 std::string, std::variant<std::string, uint64_t>>> 466 msgData; 467 msg.read(objectName, msgData); 468 bool gotMatch = false; 469 470 if (msgData.contains(systemInterface)) 471 { 472 lg2::info("Successful match on system interface"); 473 gotMatch = true; 474 } 475 476 // If customized, also accept Board as anchor, not just 477 // System 478 if (requireExactMatch && msgData.contains(boardInterface)) 479 { 480 lg2::info("Successful match on board interface"); 481 gotMatch = true; 482 } 483 484 if (gotMatch) 485 { 486 // There is a race condition here: our desired interface 487 // has just been created, triggering the D-Bus callback, 488 // but Object Mapper has not been told of it yet. The 489 // mapper must also add it. Stall for time, so it can. 490 sleep(2); 491 systemInfoUpdate(); 492 } 493 }); 494 } 495 } 496 else 497 { 498 #ifdef ASSOC_TRIM_PATH 499 // When enabled, chop off last component of motherboardPath, to trim one 500 // layer, so that associations are built to the underlying chassis 501 // itself, not the system boards in the chassis. This is for 502 // compatibility with traditional systems which only have one 503 // motherboard per chassis. 504 std::filesystem::path foundPath(motherboardPath); 505 motherboardPath = foundPath.parent_path().string(); 506 #endif 507 508 lg2::info("Found Inventory anchor object for SMBIOS content {I}: {M}", 509 "I", smbiosInventoryPath, "M", motherboardPath); 510 } 511 512 lg2::info("Using Inventory anchor object for SMBIOS content {I}: {M}", "I", 513 smbiosInventoryPath, "M", motherboardPath); 514 515 std::optional<size_t> num = getTotalCpuSlot(); 516 if (!num) 517 { 518 lg2::error("get cpu total slot failed"); 519 return; 520 } 521 522 // In case the new size is smaller than old, trim the vector 523 if (*num < cpus.size()) 524 { 525 cpus.resize(*num); 526 } 527 528 for (unsigned int index = 0; index < *num; index++) 529 { 530 std::string path = 531 smbiosInventoryPath + cpuSuffix + std::to_string(index); 532 if (index + 1 > cpus.size()) 533 { 534 cpus.emplace_back(std::make_unique<phosphor::smbios::Cpu>( 535 *bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage, 536 motherboardPath)); 537 } 538 else 539 { 540 cpus[index]->infoUpdate(smbiosDir.dir[smbiosDirIndex].dataStorage, 541 motherboardPath); 542 } 543 } 544 545 #ifdef DIMM_DBUS 546 547 num = getTotalDimmSlot(); 548 if (!num) 549 { 550 lg2::error("get dimm total slot failed"); 551 return; 552 } 553 554 // In case the new size is smaller than old, trim the vector 555 if (*num < dimms.size()) 556 { 557 dimms.resize(*num); 558 } 559 560 for (unsigned int index = 0; index < *num; index++) 561 { 562 std::string path = 563 smbiosInventoryPath + dimmSuffix + std::to_string(index); 564 if (index + 1 > dimms.size()) 565 { 566 dimms.emplace_back(std::make_unique<phosphor::smbios::Dimm>( 567 *bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage, 568 motherboardPath)); 569 } 570 else 571 { 572 dimms[index]->memoryInfoUpdate( 573 smbiosDir.dir[smbiosDirIndex].dataStorage, motherboardPath); 574 } 575 } 576 577 #endif 578 579 num = getTotalPcieSlot(); 580 if (!num) 581 { 582 lg2::error("get pcie total slot failed"); 583 return; 584 } 585 586 // In case the new size is smaller than old, trim the vector 587 if (*num < pcies.size()) 588 { 589 pcies.resize(*num); 590 } 591 592 for (unsigned int index = 0; index < *num; index++) 593 { 594 std::string path = 595 smbiosInventoryPath + pcieSuffix + std::to_string(index); 596 if (index + 1 > pcies.size()) 597 { 598 pcies.emplace_back(std::make_unique<phosphor::smbios::Pcie>( 599 *bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage, 600 motherboardPath)); 601 } 602 else 603 { 604 pcies[index]->pcieInfoUpdate( 605 smbiosDir.dir[smbiosDirIndex].dataStorage, motherboardPath); 606 } 607 } 608 609 #ifdef TPM_DBUS 610 611 num = getTotalTpm(); 612 if (!num) 613 { 614 lg2::error("get tpm failed"); 615 return; 616 } 617 // In case the new size is smaller than old, trim the vector 618 if (*num < tpms.size()) 619 { 620 tpms.resize(*num); 621 } 622 623 for (unsigned int index = 0; index < *num; index++) 624 { 625 std::string path = 626 smbiosInventoryPath + tpmSuffix + std::to_string(index); 627 if (index + 1 > tpms.size()) 628 { 629 tpms.emplace_back(std::make_unique<phosphor::smbios::Tpm>( 630 *bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage, 631 motherboardPath)); 632 } 633 else 634 { 635 tpms[index]->tpmInfoUpdate( 636 smbiosDir.dir[smbiosDirIndex].dataStorage, motherboardPath); 637 } 638 } 639 #endif 640 641 #ifdef FIRMWARE_INVENTORY_DBUS 642 643 auto existingVersionPaths = utils::getExistingVersionPaths(*bus); 644 num = getTotalFirmwareInventory(); 645 if (!num) 646 { 647 lg2::error("get firmware inventory failed"); 648 existingVersionPaths.clear(); 649 return; 650 } 651 652 // In case the new size is smaller than old, trim the vector 653 if (*num < firmwareCollection.size()) 654 { 655 firmwareCollection.resize(*num); 656 } 657 for (unsigned int index = 0; index < *num; index++) 658 { 659 auto path = FirmwareInventory::checkAndCreateFirmwarePath( 660 smbiosDir.dir[smbiosDirIndex].dataStorage, index, 661 existingVersionPaths); 662 if (path.empty()) 663 { 664 continue; 665 } 666 firmwareCollection.emplace_back( 667 std::make_unique<phosphor::smbios::FirmwareInventory>( 668 *bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage)); 669 } 670 671 #endif 672 673 system.reset(); 674 system = std::make_unique<System>(bus, smbiosInventoryPath + systemSuffix, 675 smbiosDir.dir[smbiosDirIndex].dataStorage, 676 smbiosFilePath); 677 } 678 679 std::optional<size_t> MDRV2::getTotalCpuSlot() 680 { 681 uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage; 682 size_t num = 0; 683 684 if (dataIn == nullptr) 685 { 686 lg2::error("get cpu total slot failed - no storage data"); 687 return std::nullopt; 688 } 689 690 while (1) 691 { 692 dataIn = getSMBIOSTypePtr(dataIn, processorsType); 693 if (dataIn == nullptr) 694 { 695 break; 696 } 697 num++; 698 dataIn = smbiosNextPtr(dataIn); 699 if (dataIn == nullptr) 700 { 701 break; 702 } 703 if (num >= limitEntryLen) 704 { 705 break; 706 } 707 } 708 709 return num; 710 } 711 712 std::optional<size_t> MDRV2::getTotalDimmSlot() 713 { 714 uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage; 715 size_t num = 0; 716 717 if (dataIn == nullptr) 718 { 719 lg2::error("Fail to get dimm total slot - no storage data"); 720 return std::nullopt; 721 } 722 723 while (1) 724 { 725 dataIn = getSMBIOSTypePtr(dataIn, memoryDeviceType); 726 if (dataIn == nullptr) 727 { 728 break; 729 } 730 num++; 731 dataIn = smbiosNextPtr(dataIn); 732 if (dataIn == nullptr) 733 { 734 break; 735 } 736 if (num >= limitEntryLen) 737 { 738 break; 739 } 740 } 741 742 return num; 743 } 744 745 std::optional<size_t> MDRV2::getTotalPcieSlot() 746 { 747 uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage; 748 size_t num = 0; 749 750 if (dataIn == nullptr) 751 { 752 lg2::error("Fail to get total system slot - no storage data"); 753 return std::nullopt; 754 } 755 756 while (1) 757 { 758 dataIn = getSMBIOSTypePtr(dataIn, systemSlots); 759 if (dataIn == nullptr) 760 { 761 break; 762 } 763 764 /* System slot type offset. Check if the slot is a PCIE slots. All 765 * PCIE slot type are hardcoded in a table. 766 */ 767 if (pcieSmbiosType.find(*(dataIn + 5)) != pcieSmbiosType.end()) 768 { 769 num++; 770 } 771 dataIn = smbiosNextPtr(dataIn); 772 if (dataIn == nullptr) 773 { 774 break; 775 } 776 if (num >= limitEntryLen) 777 { 778 break; 779 } 780 } 781 782 return num; 783 } 784 785 std::optional<size_t> MDRV2::getTotalTpm() 786 { 787 uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage; 788 size_t num = 0; 789 790 if (dataIn == nullptr) 791 { 792 lg2::error("Fail to get tpm total slot - no storage data"); 793 return std::nullopt; 794 } 795 796 while (1) 797 { 798 dataIn = getSMBIOSTypePtr(dataIn, tpmDeviceType); 799 if (dataIn == nullptr) 800 { 801 break; 802 } 803 num++; 804 dataIn = smbiosNextPtr(dataIn); 805 if (dataIn == nullptr) 806 { 807 break; 808 } 809 if (num >= limitEntryLen) 810 { 811 break; 812 } 813 } 814 815 return num; 816 } 817 818 std::optional<size_t> MDRV2::getTotalFirmwareInventory() 819 { 820 uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage; 821 size_t num = 0; 822 823 if (dataIn == nullptr) 824 { 825 lg2::error("Fail to get firmware inventory - no storage data"); 826 return std::nullopt; 827 } 828 829 while (1) 830 { 831 dataIn = getSMBIOSTypePtr(dataIn, firmwareInventoryInformationType); 832 if (dataIn == nullptr) 833 { 834 break; 835 } 836 num++; 837 dataIn = smbiosNextPtr(dataIn); 838 if (dataIn == nullptr) 839 { 840 break; 841 } 842 if (num >= limitEntryLen) 843 { 844 break; 845 } 846 } 847 848 return num; 849 } 850 851 bool MDRV2::checkSMBIOSVersion(uint8_t* dataIn) 852 { 853 const std::string anchorString21 = "_SM_"; 854 const std::string anchorString30 = "_SM3_"; 855 std::string buffer(reinterpret_cast<const char*>(dataIn), 856 smbiosTableStorageSize); 857 858 auto it = std::search(std::begin(buffer), std::end(buffer), 859 std::begin(anchorString21), std::end(anchorString21)); 860 bool smbios21Found = it != std::end(buffer); 861 if (!smbios21Found) 862 { 863 lg2::info("SMBIOS 2.1 Anchor String not found. Looking for SMBIOS 3.0"); 864 it = std::search(std::begin(buffer), std::end(buffer), 865 std::begin(anchorString30), std::end(anchorString30)); 866 if (it == std::end(buffer)) 867 { 868 lg2::error("SMBIOS 2.1 and 3.0 Anchor Strings not found"); 869 return false; 870 } 871 } 872 873 auto pos = std::distance(std::begin(buffer), it); 874 size_t length = smbiosTableStorageSize - pos; 875 uint8_t foundMajorVersion; 876 uint8_t foundMinorVersion; 877 878 if (smbios21Found) 879 { 880 if (length < sizeof(EntryPointStructure21)) 881 { 882 lg2::error("Invalid entry point structure for SMBIOS 2.1"); 883 return false; 884 } 885 886 auto epStructure = 887 reinterpret_cast<const EntryPointStructure21*>(&dataIn[pos]); 888 foundMajorVersion = epStructure->smbiosVersion.majorVersion; 889 foundMinorVersion = epStructure->smbiosVersion.minorVersion; 890 } 891 else 892 { 893 if (length < sizeof(EntryPointStructure30)) 894 { 895 lg2::error("Invalid entry point structure for SMBIOS 3.0"); 896 return false; 897 } 898 899 auto epStructure = 900 reinterpret_cast<const EntryPointStructure30*>(&dataIn[pos]); 901 foundMajorVersion = epStructure->smbiosVersion.majorVersion; 902 foundMinorVersion = epStructure->smbiosVersion.minorVersion; 903 } 904 lg2::info("SMBIOS VERSION - {MAJOR}.{MINOR}", "MAJOR", foundMajorVersion, 905 "MINOR", foundMinorVersion); 906 907 auto itr = std::find_if( 908 std::begin(supportedSMBIOSVersions), std::end(supportedSMBIOSVersions), 909 [&](SMBIOSVersion versionItr) { 910 return versionItr.majorVersion == foundMajorVersion && 911 versionItr.minorVersion == foundMinorVersion; 912 }); 913 if (itr == std::end(supportedSMBIOSVersions)) 914 { 915 lg2::error("Unsupported SMBIOS table version"); 916 return false; 917 } 918 return true; 919 } 920 921 bool MDRV2::agentSynchronizeData() 922 { 923 struct MDRSMBIOSHeader mdr2SMBIOS; 924 bool status = readDataFromFlash(&mdr2SMBIOS, 925 smbiosDir.dir[smbiosDirIndex].dataStorage); 926 if (!status) 927 { 928 lg2::error("agent data sync failed - read data from flash failed"); 929 return false; 930 } 931 932 if (!checkSMBIOSVersion(smbiosDir.dir[smbiosDirIndex].dataStorage)) 933 { 934 lg2::error("Unsupported SMBIOS table version"); 935 return false; 936 } 937 938 if (0 == static_cast<uint8_t>(sdbusplus::server::xyz::openbmc_project:: 939 smbios::MDRV2::directoryEntries())) 940 { 941 directoryEntries(smbiosDir.dirEntries); 942 } 943 944 systemInfoUpdate(); 945 smbiosDir.dir[smbiosDirIndex].common.dataVersion = mdr2SMBIOS.dirVer; 946 smbiosDir.dir[smbiosDirIndex].common.timestamp = mdr2SMBIOS.timestamp; 947 smbiosDir.dir[smbiosDirIndex].common.size = mdr2SMBIOS.dataSize; 948 smbiosDir.dir[smbiosDirIndex].stage = MDR2SMBIOSStatusEnum::mdr2Loaded; 949 smbiosDir.dir[smbiosDirIndex].lock = MDR2DirLockEnum::mdr2DirUnlock; 950 951 return true; 952 } 953 954 std::vector<uint32_t> MDRV2::synchronizeDirectoryCommonData(uint8_t idIndex, 955 uint32_t size) 956 { 957 std::chrono::microseconds usec( 958 defaultTimeout); // default lock time out is 2s 959 std::vector<uint32_t> result; 960 smbiosDir.dir[idIndex].common.size = size; 961 result.push_back(smbiosDir.dir[idIndex].common.dataSetSize); 962 result.push_back(smbiosDir.dir[idIndex].common.dataVersion); 963 result.push_back(smbiosDir.dir[idIndex].common.timestamp); 964 965 timer.expires_after(usec); 966 timer.async_wait([this](boost::system::error_code ec) { 967 if (ec) 968 { 969 lg2::error("Timer Error!"); 970 return; 971 } 972 agentSynchronizeData(); 973 }); 974 return result; 975 } 976 977 std::vector<boost::container::flat_map<std::string, RecordVariant>> 978 MDRV2::getRecordType(size_t type) 979 { 980 std::vector<boost::container::flat_map<std::string, RecordVariant>> ret; 981 if (type == memoryDeviceType) 982 { 983 uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage; 984 985 if (dataIn == nullptr) 986 { 987 throw std::runtime_error("Data not populated"); 988 } 989 990 do 991 { 992 dataIn = 993 getSMBIOSTypePtr(dataIn, memoryDeviceType, sizeof(MemoryInfo)); 994 if (dataIn == nullptr) 995 { 996 break; 997 } 998 boost::container::flat_map<std::string, RecordVariant>& record = 999 ret.emplace_back(); 1000 1001 auto memoryInfo = reinterpret_cast<MemoryInfo*>(dataIn); 1002 1003 record["Type"] = memoryInfo->type; 1004 record["Length"] = memoryInfo->length; 1005 record["Handle"] = uint16_t(memoryInfo->handle); 1006 record["Physical Memory Array Handle"] = 1007 uint16_t(memoryInfo->phyArrayHandle); 1008 record["Memory Error Information Handle"] = 1009 uint16_t(memoryInfo->errInfoHandle); 1010 record["Total Width"] = uint16_t(memoryInfo->totalWidth); 1011 record["Data Width"] = uint16_t(memoryInfo->dataWidth); 1012 record["Size"] = uint16_t(memoryInfo->size); 1013 record["Form Factor"] = memoryInfo->formFactor; 1014 record["Device Set"] = memoryInfo->deviceSet; 1015 record["Device Locator"] = positionToString( 1016 memoryInfo->deviceLocator, memoryInfo->length, dataIn); 1017 record["Bank Locator"] = positionToString( 1018 memoryInfo->bankLocator, memoryInfo->length, dataIn); 1019 record["Memory Type"] = memoryInfo->memoryType; 1020 record["Type Detail"] = uint16_t(memoryInfo->typeDetail); 1021 record["Speed"] = uint16_t(memoryInfo->speed); 1022 record["Manufacturer"] = positionToString( 1023 memoryInfo->manufacturer, memoryInfo->length, dataIn); 1024 record["Serial Number"] = positionToString( 1025 memoryInfo->serialNum, memoryInfo->length, dataIn); 1026 record["Asset Tag"] = positionToString(memoryInfo->assetTag, 1027 memoryInfo->length, dataIn); 1028 record["Part Number"] = positionToString( 1029 memoryInfo->partNum, memoryInfo->length, dataIn); 1030 record["Attributes"] = uint32_t(memoryInfo->attributes); 1031 record["Extended Size"] = uint32_t(memoryInfo->extendedSize); 1032 record["Configured Memory Speed"] = 1033 uint32_t(memoryInfo->confClockSpeed); 1034 record["Minimum voltage"] = uint16_t(memoryInfo->minimumVoltage); 1035 record["Maximum voltage"] = uint16_t(memoryInfo->maximumVoltage); 1036 record["Configured voltage"] = 1037 uint16_t(memoryInfo->configuredVoltage); 1038 record["Memory Technology"] = memoryInfo->memoryTechnology; 1039 record["Memory Operating Mode Capability"] = 1040 uint16_t(memoryInfo->memoryOperatingModeCap); 1041 record["Firmware Version"] = memoryInfo->firwareVersion; 1042 record["Module Manufacturer ID"] = 1043 uint16_t(memoryInfo->modelManufId); 1044 record["Module Product ID"] = uint16_t(memoryInfo->modelProdId); 1045 record["Memory Subsystem Controller Manufacturer ID"] = 1046 uint16_t(memoryInfo->memSubConManufId); 1047 record["Memory Subsystem Controller Product Id"] = 1048 uint16_t(memoryInfo->memSubConProdId); 1049 record["Non-volatile Size"] = uint64_t(memoryInfo->nvSize); 1050 record["Volatile Size"] = uint64_t(memoryInfo->volatileSize); 1051 record["Cache Size"] = uint64_t(memoryInfo->cacheSize); 1052 record["Logical Size"] = uint64_t(memoryInfo->logicalSize); 1053 } while ((dataIn = smbiosNextPtr(dataIn)) != nullptr); 1054 1055 return ret; 1056 } 1057 1058 throw std::invalid_argument("Invalid record type"); 1059 return ret; 1060 } 1061 1062 } // namespace smbios 1063 } // namespace phosphor 1064