1 #include "config.h" 2 3 #include "worker.hpp" 4 5 #include "backup_restore.hpp" 6 #include "configuration.hpp" 7 #include "constants.hpp" 8 #include "event_logger.hpp" 9 #include "exceptions.hpp" 10 #include "logger.hpp" 11 #include "parser.hpp" 12 #include "parser_factory.hpp" 13 #include "parser_interface.hpp" 14 15 #include <utility/dbus_utility.hpp> 16 #include <utility/json_utility.hpp> 17 #include <utility/vpd_specific_utility.hpp> 18 19 #include <filesystem> 20 #include <fstream> 21 #include <future> 22 #include <typeindex> 23 #include <unordered_set> 24 25 namespace vpd 26 { 27 28 Worker::Worker(std::string pathToConfigJson, uint8_t i_maxThreadCount) : 29 m_configJsonPath(pathToConfigJson), m_semaphore(i_maxThreadCount) 30 { 31 // Implies the processing is based on some config JSON 32 if (!m_configJsonPath.empty()) 33 { 34 // Check if symlink is already there to confirm fresh boot/factory 35 // reset. 36 if (std::filesystem::exists(INVENTORY_JSON_SYM_LINK)) 37 { 38 logging::logMessage("Sym Link already present"); 39 m_configJsonPath = INVENTORY_JSON_SYM_LINK; 40 m_isSymlinkPresent = true; 41 } 42 43 try 44 { 45 m_parsedJson = jsonUtility::getParsedJson(m_configJsonPath); 46 47 // check for mandatory fields at this point itself. 48 if (!m_parsedJson.contains("frus")) 49 { 50 throw std::runtime_error("Mandatory tag(s) missing from JSON"); 51 } 52 } 53 catch (const std::exception& ex) 54 { 55 throw(JsonException(ex.what(), m_configJsonPath)); 56 } 57 } 58 else 59 { 60 logging::logMessage("Processing in not based on any config JSON"); 61 } 62 } 63 64 void Worker::enableMuxChips() 65 { 66 if (m_parsedJson.empty()) 67 { 68 // config JSON should not be empty at this point of execution. 69 throw std::runtime_error("Config JSON is empty. Can't enable muxes"); 70 return; 71 } 72 73 if (!m_parsedJson.contains("muxes")) 74 { 75 logging::logMessage("No mux defined for the system in config JSON"); 76 return; 77 } 78 79 // iterate over each MUX detail and enable them. 80 for (const auto& item : m_parsedJson["muxes"]) 81 { 82 if (item.contains("holdidlepath")) 83 { 84 std::string cmd = "echo 0 > "; 85 cmd += item["holdidlepath"]; 86 87 logging::logMessage("Enabling mux with command = " + cmd); 88 89 commonUtility::executeCmd(cmd); 90 continue; 91 } 92 93 logging::logMessage( 94 "Mux Entry does not have hold idle path. Can't enable the mux"); 95 } 96 } 97 98 #ifdef IBM_SYSTEM 99 void Worker::primeSystemBlueprint() 100 { 101 if (m_parsedJson.empty()) 102 { 103 return; 104 } 105 106 const nlohmann::json& l_listOfFrus = 107 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>(); 108 109 for (const auto& l_itemFRUS : l_listOfFrus.items()) 110 { 111 const std::string& l_vpdFilePath = l_itemFRUS.key(); 112 113 if (l_vpdFilePath == SYSTEM_VPD_FILE_PATH) 114 { 115 continue; 116 } 117 118 // Prime the inventry for FRUs which 119 // are not present/processing had some error. 120 if (!primeInventory(l_vpdFilePath)) 121 { 122 logging::logMessage( 123 "Priming of inventory failed for FRU " + l_vpdFilePath); 124 } 125 } 126 } 127 128 void Worker::performInitialSetup() 129 { 130 try 131 { 132 if (!dbusUtility::isChassisPowerOn()) 133 { 134 logging::logMessage("Chassis is in Off state."); 135 setDeviceTreeAndJson(); 136 primeSystemBlueprint(); 137 } 138 139 // Enable all mux which are used for connecting to the i2c on the 140 // pcie slots for pcie cards. These are not enabled by kernel due to 141 // an issue seen with Castello cards, where the i2c line hangs on a 142 // probe. 143 enableMuxChips(); 144 145 // Nothing needs to be done. Service restarted or BMC re-booted for 146 // some reason at system power on. 147 return; 148 } 149 catch (const std::exception& l_ex) 150 { 151 // Any issue in system's inital set up is handled in this catch. Error 152 // will not propogate to manager. 153 EventLogger::createSyncPel( 154 EventLogger::getErrorType(l_ex), types::SeverityType::Critical, 155 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 156 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 157 } 158 } 159 #endif 160 161 static std::string readFitConfigValue() 162 { 163 std::vector<std::string> output = 164 commonUtility::executeCmd("/sbin/fw_printenv"); 165 std::string fitConfigValue; 166 167 for (const auto& entry : output) 168 { 169 auto pos = entry.find("="); 170 auto key = entry.substr(0, pos); 171 if (key != "fitconfig") 172 { 173 continue; 174 } 175 176 if (pos + 1 < entry.size()) 177 { 178 fitConfigValue = entry.substr(pos + 1); 179 } 180 } 181 182 return fitConfigValue; 183 } 184 185 bool Worker::isSystemVPDOnDBus() const 186 { 187 const std::string& mboardPath = 188 m_parsedJson["frus"][SYSTEM_VPD_FILE_PATH].at(0).value( 189 "inventoryPath", ""); 190 191 if (mboardPath.empty()) 192 { 193 throw JsonException("System vpd file path missing in JSON", 194 INVENTORY_JSON_SYM_LINK); 195 } 196 197 std::array<const char*, 1> interfaces = { 198 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; 199 200 const types::MapperGetObject& objectMap = 201 dbusUtility::getObjectMap(mboardPath, interfaces); 202 203 if (objectMap.empty()) 204 { 205 return false; 206 } 207 return true; 208 } 209 210 std::string Worker::getIMValue(const types::IPZVpdMap& parsedVpd) const 211 { 212 if (parsedVpd.empty()) 213 { 214 throw std::runtime_error("Empty VPD map. Can't Extract IM value"); 215 } 216 217 const auto& itrToVSBP = parsedVpd.find("VSBP"); 218 if (itrToVSBP == parsedVpd.end()) 219 { 220 throw DataException("VSBP record missing."); 221 } 222 223 const auto& itrToIM = (itrToVSBP->second).find("IM"); 224 if (itrToIM == (itrToVSBP->second).end()) 225 { 226 throw DataException("IM keyword missing."); 227 } 228 229 types::BinaryVector imVal; 230 std::copy(itrToIM->second.begin(), itrToIM->second.end(), 231 back_inserter(imVal)); 232 233 std::ostringstream imData; 234 for (auto& aByte : imVal) 235 { 236 imData << std::setw(2) << std::setfill('0') << std::hex 237 << static_cast<int>(aByte); 238 } 239 240 return imData.str(); 241 } 242 243 std::string Worker::getHWVersion(const types::IPZVpdMap& parsedVpd) const 244 { 245 if (parsedVpd.empty()) 246 { 247 throw std::runtime_error("Empty VPD map. Can't Extract HW value"); 248 } 249 250 const auto& itrToVINI = parsedVpd.find("VINI"); 251 if (itrToVINI == parsedVpd.end()) 252 { 253 throw DataException("VINI record missing."); 254 } 255 256 const auto& itrToHW = (itrToVINI->second).find("HW"); 257 if (itrToHW == (itrToVINI->second).end()) 258 { 259 throw DataException("HW keyword missing."); 260 } 261 262 types::BinaryVector hwVal; 263 std::copy(itrToHW->second.begin(), itrToHW->second.end(), 264 back_inserter(hwVal)); 265 266 // The planar pass only comes from the LSB of the HW keyword, 267 // where as the MSB is used for other purposes such as signifying clock 268 // termination. 269 hwVal[0] = 0x00; 270 271 std::ostringstream hwString; 272 for (auto& aByte : hwVal) 273 { 274 hwString << std::setw(2) << std::setfill('0') << std::hex 275 << static_cast<int>(aByte); 276 } 277 278 return hwString.str(); 279 } 280 281 void Worker::fillVPDMap(const std::string& vpdFilePath, 282 types::VPDMapVariant& vpdMap) 283 { 284 logging::logMessage(std::string("Parsing file = ") + vpdFilePath); 285 286 if (vpdFilePath.empty()) 287 { 288 throw std::runtime_error("Invalid file path passed to fillVPDMap API."); 289 } 290 291 if (!std::filesystem::exists(vpdFilePath)) 292 { 293 throw std::runtime_error("Can't Find physical file"); 294 } 295 296 std::shared_ptr<Parser> vpdParser = 297 std::make_shared<Parser>(vpdFilePath, m_parsedJson); 298 vpdMap = vpdParser->parse(); 299 } 300 301 void Worker::getSystemJson(std::string& systemJson, 302 const types::VPDMapVariant& parsedVpdMap) 303 { 304 if (auto pVal = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 305 { 306 std::string hwKWdValue = getHWVersion(*pVal); 307 if (hwKWdValue.empty()) 308 { 309 throw DataException("HW value fetched is empty."); 310 } 311 312 const std::string& imKwdValue = getIMValue(*pVal); 313 if (imKwdValue.empty()) 314 { 315 throw DataException("IM value fetched is empty."); 316 } 317 318 auto itrToIM = config::systemType.find(imKwdValue); 319 if (itrToIM == config::systemType.end()) 320 { 321 throw DataException("IM keyword does not map to any system type"); 322 } 323 324 const types::HWVerList hwVersionList = itrToIM->second.second; 325 if (!hwVersionList.empty()) 326 { 327 transform(hwKWdValue.begin(), hwKWdValue.end(), hwKWdValue.begin(), 328 ::toupper); 329 330 auto itrToHW = 331 std::find_if(hwVersionList.begin(), hwVersionList.end(), 332 [&hwKWdValue](const auto& aPair) { 333 return aPair.first == hwKWdValue; 334 }); 335 336 if (itrToHW != hwVersionList.end()) 337 { 338 if (!(*itrToHW).second.empty()) 339 { 340 systemJson += (*itrToIM).first + "_" + (*itrToHW).second + 341 ".json"; 342 } 343 else 344 { 345 systemJson += (*itrToIM).first + ".json"; 346 } 347 return; 348 } 349 } 350 systemJson += itrToIM->second.first + ".json"; 351 return; 352 } 353 354 throw DataException( 355 "Invalid VPD type returned from Parser. Can't get system JSON."); 356 } 357 358 static void setEnvAndReboot(const std::string& key, const std::string& value) 359 { 360 // set env and reboot and break. 361 commonUtility::executeCmd("/sbin/fw_setenv", key, value); 362 logging::logMessage("Rebooting BMC to pick up new device tree"); 363 364 // make dbus call to reboot 365 auto bus = sdbusplus::bus::new_default_system(); 366 auto method = bus.new_method_call( 367 "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 368 "org.freedesktop.systemd1.Manager", "Reboot"); 369 bus.call_noreply(method); 370 } 371 372 void Worker::setJsonSymbolicLink(const std::string& i_systemJson) 373 { 374 std::error_code l_ec; 375 l_ec.clear(); 376 377 // Check if symlink file path exists and if the JSON at this location is a 378 // symlink. 379 if (m_isSymlinkPresent && 380 std::filesystem::is_symlink(INVENTORY_JSON_SYM_LINK, l_ec)) 381 { // Don't care about exception in "is_symlink". Will continue with creation 382 // of symlink. 383 384 const auto& l_symlinkFilePth = 385 std::filesystem::read_symlink(INVENTORY_JSON_SYM_LINK, l_ec); 386 387 if (l_ec) 388 { 389 logging::logMessage( 390 "Can't read existing symlink. Error =" + l_ec.message() + 391 "Trying removal of symlink and creation of new symlink."); 392 } 393 394 // If currently set JSON is the required one. No further processing 395 // required. 396 if (i_systemJson == l_symlinkFilePth) 397 { 398 // Correct symlink already set. 399 return; 400 } 401 402 if (!std::filesystem::remove(INVENTORY_JSON_SYM_LINK, l_ec)) 403 { 404 // No point going further. If removal fails for existing symlink, 405 // create will anyways throw. 406 throw std::runtime_error( 407 "Removal of symlink failed with Error = " + l_ec.message() + 408 ". Can't proceed with create_symlink."); 409 } 410 } 411 412 if (!std::filesystem::exists(VPD_SYMLIMK_PATH, l_ec)) 413 { 414 if (l_ec) 415 { 416 throw std::runtime_error( 417 "File system call to exist failed with error = " + 418 l_ec.message()); 419 } 420 421 // implies it is a fresh boot/factory reset. 422 // Create the directory for hosting the symlink 423 if (!std::filesystem::create_directories(VPD_SYMLIMK_PATH, l_ec)) 424 { 425 if (l_ec) 426 { 427 throw std::runtime_error( 428 "File system call to create directory failed with error = " + 429 l_ec.message()); 430 } 431 } 432 } 433 434 // create a new symlink based on the system 435 std::filesystem::create_symlink(i_systemJson, INVENTORY_JSON_SYM_LINK, 436 l_ec); 437 438 if (l_ec) 439 { 440 throw std::runtime_error( 441 "create_symlink system call failed with error: " + l_ec.message()); 442 } 443 444 // If the flow is at this point implies the symlink was not present there. 445 // Considering this as factory reset. 446 m_isFactoryResetDone = true; 447 } 448 449 void Worker::setDeviceTreeAndJson() 450 { 451 // JSON is madatory for processing of this API. 452 if (m_parsedJson.empty()) 453 { 454 throw JsonException("System config JSON is empty", m_configJsonPath); 455 } 456 457 types::VPDMapVariant parsedVpdMap; 458 fillVPDMap(SYSTEM_VPD_FILE_PATH, parsedVpdMap); 459 460 // Implies it is default JSON. 461 std::string systemJson{JSON_ABSOLUTE_PATH_PREFIX}; 462 463 // ToDo: Need to check if INVENTORY_JSON_SYM_LINK pointing to correct system 464 // This is required to support movement from rainier to Blue Ridge on the 465 // fly. 466 467 getSystemJson(systemJson, parsedVpdMap); 468 469 if (!systemJson.compare(JSON_ABSOLUTE_PATH_PREFIX)) 470 { 471 throw DataException( 472 "No system JSON found corresponding to IM read from VPD."); 473 } 474 475 // re-parse the JSON once appropriate JSON has been selected. 476 m_parsedJson = jsonUtility::getParsedJson(systemJson); 477 478 if (m_parsedJson.empty()) 479 { 480 throw(JsonException("Json parsing failed", systemJson)); 481 } 482 483 std::string devTreeFromJson; 484 if (m_parsedJson.contains("devTree")) 485 { 486 devTreeFromJson = m_parsedJson["devTree"]; 487 488 if (devTreeFromJson.empty()) 489 { 490 EventLogger::createSyncPel( 491 types::ErrorType::JsonFailure, types::SeverityType::Error, 492 __FILE__, __FUNCTION__, 0, 493 "Mandatory value for device tree missing from JSON[" + 494 systemJson + "]", 495 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 496 } 497 } 498 499 auto fitConfigVal = readFitConfigValue(); 500 501 if (devTreeFromJson.empty() || 502 fitConfigVal.find(devTreeFromJson) != std::string::npos) 503 { // Skipping setting device tree as either devtree info is missing from 504 // Json or it is rightly set. 505 506 setJsonSymbolicLink(systemJson); 507 508 if (isSystemVPDOnDBus() && 509 jsonUtility::isBackupAndRestoreRequired(m_parsedJson)) 510 { 511 performBackupAndRestore(parsedVpdMap); 512 } 513 514 // proceed to publish system VPD. 515 publishSystemVPD(parsedVpdMap); 516 return; 517 } 518 519 setEnvAndReboot("fitconfig", devTreeFromJson); 520 exit(EXIT_SUCCESS); 521 } 522 523 void Worker::populateIPZVPDpropertyMap( 524 types::InterfaceMap& interfacePropMap, 525 const types::IPZKwdValueMap& keyordValueMap, 526 const std::string& interfaceName) 527 { 528 types::PropertyMap propertyValueMap; 529 for (const auto& kwdVal : keyordValueMap) 530 { 531 auto kwd = kwdVal.first; 532 533 if (kwd[0] == '#') 534 { 535 kwd = std::string("PD_") + kwd[1]; 536 } 537 else if (isdigit(kwd[0])) 538 { 539 kwd = std::string("N_") + kwd; 540 } 541 542 types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end()); 543 propertyValueMap.emplace(move(kwd), move(value)); 544 } 545 546 if (!propertyValueMap.empty()) 547 { 548 interfacePropMap.emplace(interfaceName, propertyValueMap); 549 } 550 } 551 552 void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap, 553 types::InterfaceMap& interfaceMap) 554 { 555 for (const auto& kwdValMap : keyordVPDMap) 556 { 557 types::PropertyMap propertyValueMap; 558 auto kwd = kwdValMap.first; 559 560 if (kwd[0] == '#') 561 { 562 kwd = std::string("PD_") + kwd[1]; 563 } 564 else if (isdigit(kwd[0])) 565 { 566 kwd = std::string("N_") + kwd; 567 } 568 569 if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second)) 570 { 571 types::BinaryVector value((*keywordValue).begin(), 572 (*keywordValue).end()); 573 propertyValueMap.emplace(move(kwd), move(value)); 574 } 575 else if (auto keywordValue = get_if<std::string>(&kwdValMap.second)) 576 { 577 types::BinaryVector value((*keywordValue).begin(), 578 (*keywordValue).end()); 579 propertyValueMap.emplace(move(kwd), move(value)); 580 } 581 else if (auto keywordValue = get_if<size_t>(&kwdValMap.second)) 582 { 583 if (kwd == "MemorySizeInKB") 584 { 585 types::PropertyMap memProp; 586 memProp.emplace(move(kwd), ((*keywordValue))); 587 interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm", 588 move(memProp)); 589 continue; 590 } 591 else 592 { 593 logging::logMessage( 594 "Unknown Keyword =" + kwd + " found in keyword VPD map"); 595 continue; 596 } 597 } 598 else 599 { 600 logging::logMessage( 601 "Unknown variant type found in keyword VPD map."); 602 continue; 603 } 604 605 if (!propertyValueMap.empty()) 606 { 607 vpdSpecificUtility::insertOrMerge( 608 interfaceMap, constants::kwdVpdInf, move(propertyValueMap)); 609 } 610 } 611 } 612 613 void Worker::populateInterfaces(const nlohmann::json& interfaceJson, 614 types::InterfaceMap& interfaceMap, 615 const types::VPDMapVariant& parsedVpdMap) 616 { 617 for (const auto& interfacesPropPair : interfaceJson.items()) 618 { 619 const std::string& interface = interfacesPropPair.key(); 620 types::PropertyMap propertyMap; 621 622 for (const auto& propValuePair : interfacesPropPair.value().items()) 623 { 624 const std::string property = propValuePair.key(); 625 626 if (propValuePair.value().is_boolean()) 627 { 628 propertyMap.emplace(property, 629 propValuePair.value().get<bool>()); 630 } 631 else if (propValuePair.value().is_string()) 632 { 633 if (property.compare("LocationCode") == 0 && 634 interface.compare("com.ibm.ipzvpd.Location") == 0) 635 { 636 std::string value = 637 vpdSpecificUtility::getExpandedLocationCode( 638 propValuePair.value().get<std::string>(), 639 parsedVpdMap); 640 propertyMap.emplace(property, value); 641 642 auto l_locCodeProperty = propertyMap; 643 vpdSpecificUtility::insertOrMerge( 644 interfaceMap, 645 std::string(constants::xyzLocationCodeInf), 646 move(l_locCodeProperty)); 647 } 648 else 649 { 650 propertyMap.emplace( 651 property, propValuePair.value().get<std::string>()); 652 } 653 } 654 else if (propValuePair.value().is_array()) 655 { 656 try 657 { 658 propertyMap.emplace( 659 property, 660 propValuePair.value().get<types::BinaryVector>()); 661 } 662 catch (const nlohmann::detail::type_error& e) 663 { 664 std::cerr << "Type exception: " << e.what() << "\n"; 665 } 666 } 667 else if (propValuePair.value().is_number()) 668 { 669 // For now assume the value is a size_t. In the future it would 670 // be nice to come up with a way to get the type from the JSON. 671 propertyMap.emplace(property, 672 propValuePair.value().get<size_t>()); 673 } 674 else if (propValuePair.value().is_object()) 675 { 676 const std::string& record = 677 propValuePair.value().value("recordName", ""); 678 const std::string& keyword = 679 propValuePair.value().value("keywordName", ""); 680 const std::string& encoding = 681 propValuePair.value().value("encoding", ""); 682 683 if (auto ipzVpdMap = 684 std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 685 { 686 if (!record.empty() && !keyword.empty() && 687 (*ipzVpdMap).count(record) && 688 (*ipzVpdMap).at(record).count(keyword)) 689 { 690 auto encoded = vpdSpecificUtility::encodeKeyword( 691 ((*ipzVpdMap).at(record).at(keyword)), encoding); 692 propertyMap.emplace(property, encoded); 693 } 694 } 695 else if (auto kwdVpdMap = 696 std::get_if<types::KeywordVpdMap>(&parsedVpdMap)) 697 { 698 if (!keyword.empty() && (*kwdVpdMap).count(keyword)) 699 { 700 if (auto kwValue = std::get_if<types::BinaryVector>( 701 &(*kwdVpdMap).at(keyword))) 702 { 703 auto encodedValue = 704 vpdSpecificUtility::encodeKeyword( 705 std::string((*kwValue).begin(), 706 (*kwValue).end()), 707 encoding); 708 709 propertyMap.emplace(property, encodedValue); 710 } 711 else if (auto kwValue = std::get_if<std::string>( 712 &(*kwdVpdMap).at(keyword))) 713 { 714 auto encodedValue = 715 vpdSpecificUtility::encodeKeyword( 716 std::string((*kwValue).begin(), 717 (*kwValue).end()), 718 encoding); 719 720 propertyMap.emplace(property, encodedValue); 721 } 722 else if (auto uintValue = std::get_if<size_t>( 723 &(*kwdVpdMap).at(keyword))) 724 { 725 propertyMap.emplace(property, *uintValue); 726 } 727 else 728 { 729 logging::logMessage( 730 "Unknown keyword found, Keywrod = " + keyword); 731 } 732 } 733 } 734 } 735 } 736 vpdSpecificUtility::insertOrMerge(interfaceMap, interface, 737 move(propertyMap)); 738 } 739 } 740 741 bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword) 742 { 743 const unsigned char l_io[] = { 744 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 745 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF}; 746 747 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0). 748 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs' 749 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as 750 // IO. 751 if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG, 752 constants::SIZE_OF_8EQ_IN_PG) == 0) 753 { 754 return true; 755 } 756 757 // The CPU is not an IO 758 return false; 759 } 760 761 bool Worker::primeInventory(const std::string& i_vpdFilePath) 762 { 763 if (i_vpdFilePath.empty()) 764 { 765 logging::logMessage("Empty VPD file path given"); 766 return false; 767 } 768 769 if (m_parsedJson.empty()) 770 { 771 logging::logMessage("Empty JSON detected for " + i_vpdFilePath); 772 return false; 773 } 774 else if (!m_parsedJson["frus"].contains(i_vpdFilePath)) 775 { 776 logging::logMessage("File " + i_vpdFilePath + 777 ", is not found in the system config JSON file."); 778 return false; 779 } 780 781 types::ObjectMap l_objectInterfaceMap; 782 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdFilePath]) 783 { 784 types::InterfaceMap l_interfaces; 785 sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]); 786 787 if (l_Fru.contains("ccin")) 788 { 789 continue; 790 } 791 792 if (l_Fru.contains("noprime") && l_Fru.value("noprime", false)) 793 { 794 continue; 795 } 796 797 // Reset data under PIM for this FRU only if the FRU is not synthesized 798 // and we handle it's Present property. 799 if (isPresentPropertyHandlingRequired(l_Fru)) 800 { 801 // Clear data under PIM if already exists. 802 vpdSpecificUtility::resetDataUnderPIM( 803 std::string(l_Fru["inventoryPath"]), l_interfaces); 804 } 805 806 // Add extra interfaces mentioned in the Json config file 807 if (l_Fru.contains("extraInterfaces")) 808 { 809 populateInterfaces(l_Fru["extraInterfaces"], l_interfaces, 810 std::monostate{}); 811 } 812 813 types::PropertyMap l_propertyValueMap; 814 815 // Update Present property for this FRU only if we handle Present 816 // property for the FRU. 817 if (isPresentPropertyHandlingRequired(l_Fru)) 818 { 819 l_propertyValueMap.emplace("Present", false); 820 821 // TODO: Present based on file will be taken care in future. 822 // By default present is set to false for FRU at the time of 823 // priming. Once collection goes through, it will be set to true in 824 // that flow. 825 /*if (std::filesystem::exists(i_vpdFilePath)) 826 { 827 l_propertyValueMap["Present"] = true; 828 }*/ 829 } 830 831 vpdSpecificUtility::insertOrMerge(l_interfaces, 832 "xyz.openbmc_project.Inventory.Item", 833 move(l_propertyValueMap)); 834 835 if (l_Fru.value("inherit", true) && 836 m_parsedJson.contains("commonInterfaces")) 837 { 838 populateInterfaces(m_parsedJson["commonInterfaces"], l_interfaces, 839 std::monostate{}); 840 } 841 842 processFunctionalProperty(l_Fru["inventoryPath"], l_interfaces); 843 processEnabledProperty(l_Fru["inventoryPath"], l_interfaces); 844 845 // Emplace the default state of FRU VPD collection 846 types::PropertyMap l_fruCollectionProperty = { 847 {"CollectionStatus", constants::vpdCollectionNotStarted}}; 848 849 vpdSpecificUtility::insertOrMerge(l_interfaces, 850 constants::vpdCollectionInterface, 851 std::move(l_fruCollectionProperty)); 852 853 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath), 854 std::move(l_interfaces)); 855 } 856 857 // Notify PIM 858 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 859 { 860 logging::logMessage("Call to PIM failed for VPD file " + i_vpdFilePath); 861 return false; 862 } 863 864 return true; 865 } 866 867 void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru, 868 types::InterfaceMap& interfaces) 869 { 870 // embedded property(true or false) says whether the subfru is embedded 871 // into the parent fru (or) not. VPD sets Present property only for 872 // embedded frus. If the subfru is not an embedded FRU, the subfru may 873 // or may not be physically present. Those non embedded frus will always 874 // have Present=false irrespective of its physical presence or absence. 875 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set 876 // Present to true for such sub frus. 877 // Eg: ethernet port is embedded into bmc card. So set Present to true 878 // for such sub frus. Also donot populate present property for embedded 879 // subfru which is synthesized. Currently there is no subfru which are 880 // both embedded and synthesized. But still the case is handled here. 881 882 // Check if its required to handle presence for this FRU. 883 if (singleFru.value("handlePresence", true)) 884 { 885 types::PropertyMap presProp; 886 presProp.emplace("Present", true); 887 vpdSpecificUtility::insertOrMerge( 888 interfaces, "xyz.openbmc_project.Inventory.Item", move(presProp)); 889 } 890 } 891 892 void Worker::processExtraInterfaces(const nlohmann::json& singleFru, 893 types::InterfaceMap& interfaces, 894 const types::VPDMapVariant& parsedVpdMap) 895 { 896 populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap); 897 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 898 { 899 if (singleFru["extraInterfaces"].contains( 900 "xyz.openbmc_project.Inventory.Item.Cpu")) 901 { 902 auto itrToRec = (*ipzVpdMap).find("CP00"); 903 if (itrToRec == (*ipzVpdMap).end()) 904 { 905 return; 906 } 907 908 const std::string pgKeywordValue{ 909 vpdSpecificUtility::getKwVal(itrToRec->second, "PG")}; 910 911 if (!pgKeywordValue.empty()) 912 { 913 if (isCPUIOGoodOnly(pgKeywordValue)) 914 { 915 interfaces["xyz.openbmc_project.Inventory.Item"] 916 ["PrettyName"] = "IO Module"; 917 } 918 } 919 else 920 { 921 throw std::runtime_error("Failed to get value for keyword PG"); 922 } 923 } 924 } 925 } 926 927 void Worker::processCopyRecordFlag(const nlohmann::json& singleFru, 928 const types::VPDMapVariant& parsedVpdMap, 929 types::InterfaceMap& interfaces) 930 { 931 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 932 { 933 for (const auto& record : singleFru["copyRecords"]) 934 { 935 const std::string& recordName = record; 936 if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end()) 937 { 938 populateIPZVPDpropertyMap(interfaces, 939 (*ipzVpdMap).at(recordName), 940 constants::ipzVpdInf + recordName); 941 } 942 } 943 } 944 } 945 946 void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap, 947 types::InterfaceMap& interfaces) 948 { 949 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 950 { 951 for (const auto& [recordName, kwdValueMap] : *ipzVpdMap) 952 { 953 populateIPZVPDpropertyMap(interfaces, kwdValueMap, 954 constants::ipzVpdInf + recordName); 955 } 956 } 957 else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap)) 958 { 959 populateKwdVPDpropertyMap(*kwdVpdMap, interfaces); 960 } 961 962 if (m_parsedJson.contains("commonInterfaces")) 963 { 964 populateInterfaces(m_parsedJson["commonInterfaces"], interfaces, 965 parsedVpdMap); 966 } 967 } 968 969 bool Worker::processFruWithCCIN(const nlohmann::json& singleFru, 970 const types::VPDMapVariant& parsedVpdMap) 971 { 972 if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 973 { 974 auto itrToRec = (*ipzVPDMap).find("VINI"); 975 if (itrToRec == (*ipzVPDMap).end()) 976 { 977 return false; 978 } 979 980 std::string ccinFromVpd{ 981 vpdSpecificUtility::getKwVal(itrToRec->second, "CC")}; 982 983 if (ccinFromVpd.empty()) 984 { 985 return false; 986 } 987 988 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(), 989 ::toupper); 990 991 std::vector<std::string> ccinList; 992 for (std::string ccin : singleFru["ccin"]) 993 { 994 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper); 995 ccinList.push_back(ccin); 996 } 997 998 if (ccinList.empty()) 999 { 1000 return false; 1001 } 1002 1003 if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) == 1004 ccinList.end()) 1005 { 1006 return false; 1007 } 1008 } 1009 return true; 1010 } 1011 1012 void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath, 1013 types::InterfaceMap& io_interfaces) 1014 { 1015 if (!dbusUtility::isChassisPowerOn()) 1016 { 1017 std::array<const char*, 1> l_operationalStatusInf = { 1018 constants::operationalStatusInf}; 1019 1020 auto mapperObjectMap = dbusUtility::getObjectMap( 1021 i_inventoryObjPath, l_operationalStatusInf); 1022 1023 // If the object has been found. Check if it is under PIM. 1024 if (mapperObjectMap.size() != 0) 1025 { 1026 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap) 1027 { 1028 if (l_serviceName == constants::pimServiceName) 1029 { 1030 // The object is already under PIM. No need to process 1031 // again. Retain the old value. 1032 return; 1033 } 1034 } 1035 } 1036 1037 // Implies value is not there in D-Bus. Populate it with default 1038 // value "true". 1039 types::PropertyMap l_functionalProp; 1040 l_functionalProp.emplace("Functional", true); 1041 vpdSpecificUtility::insertOrMerge(io_interfaces, 1042 constants::operationalStatusInf, 1043 move(l_functionalProp)); 1044 } 1045 1046 // if chassis is power on. Functional property should be there on D-Bus. 1047 // Don't process. 1048 return; 1049 } 1050 1051 void Worker::processEnabledProperty(const std::string& i_inventoryObjPath, 1052 types::InterfaceMap& io_interfaces) 1053 { 1054 if (!dbusUtility::isChassisPowerOn()) 1055 { 1056 std::array<const char*, 1> l_enableInf = {constants::enableInf}; 1057 1058 auto mapperObjectMap = 1059 dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf); 1060 1061 // If the object has been found. Check if it is under PIM. 1062 if (mapperObjectMap.size() != 0) 1063 { 1064 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap) 1065 { 1066 if (l_serviceName == constants::pimServiceName) 1067 { 1068 // The object is already under PIM. No need to process 1069 // again. Retain the old value. 1070 return; 1071 } 1072 } 1073 } 1074 1075 // Implies value is not there in D-Bus. Populate it with default 1076 // value "true". 1077 types::PropertyMap l_enabledProp; 1078 l_enabledProp.emplace("Enabled", true); 1079 vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf, 1080 move(l_enabledProp)); 1081 } 1082 1083 // if chassis is power on. Enabled property should be there on D-Bus. 1084 // Don't process. 1085 return; 1086 } 1087 1088 void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap, 1089 types::ObjectMap& objectInterfaceMap, 1090 const std::string& vpdFilePath) 1091 { 1092 if (vpdFilePath.empty()) 1093 { 1094 throw std::runtime_error( 1095 "Invalid parameter passed to populateDbus API."); 1096 } 1097 1098 // JSON config is mandatory for processing of "if". Add "else" for any 1099 // processing without config JSON. 1100 if (!m_parsedJson.empty()) 1101 { 1102 types::InterfaceMap interfaces; 1103 1104 for (const auto& aFru : m_parsedJson["frus"][vpdFilePath]) 1105 { 1106 const auto& inventoryPath = aFru["inventoryPath"]; 1107 sdbusplus::message::object_path fruObjectPath(inventoryPath); 1108 if (aFru.contains("ccin")) 1109 { 1110 if (!processFruWithCCIN(aFru, parsedVpdMap)) 1111 { 1112 continue; 1113 } 1114 } 1115 1116 if (aFru.value("inherit", true)) 1117 { 1118 processInheritFlag(parsedVpdMap, interfaces); 1119 } 1120 1121 // If specific record needs to be copied. 1122 if (aFru.contains("copyRecords")) 1123 { 1124 processCopyRecordFlag(aFru, parsedVpdMap, interfaces); 1125 } 1126 1127 if (aFru.contains("extraInterfaces")) 1128 { 1129 // Process extra interfaces w.r.t a FRU. 1130 processExtraInterfaces(aFru, interfaces, parsedVpdMap); 1131 } 1132 1133 // Process FRUS which are embedded in the parent FRU and whose VPD 1134 // will be synthesized. 1135 if ((aFru.value("embedded", true)) && 1136 (!aFru.value("synthesized", false))) 1137 { 1138 processEmbeddedAndSynthesizedFrus(aFru, interfaces); 1139 } 1140 1141 processFunctionalProperty(inventoryPath, interfaces); 1142 processEnabledProperty(inventoryPath, interfaces); 1143 1144 // Update collection status as successful 1145 types::PropertyMap l_collectionProperty = { 1146 {"CollectionStatus", constants::vpdCollectionSuccess}}; 1147 1148 vpdSpecificUtility::insertOrMerge(interfaces, 1149 constants::vpdCollectionInterface, 1150 std::move(l_collectionProperty)); 1151 1152 objectInterfaceMap.emplace(std::move(fruObjectPath), 1153 std::move(interfaces)); 1154 } 1155 } 1156 } 1157 1158 std::string Worker::createAssetTagString( 1159 const types::VPDMapVariant& i_parsedVpdMap) 1160 { 1161 std::string l_assetTag; 1162 1163 // system VPD will be in IPZ format. 1164 if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap)) 1165 { 1166 auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS); 1167 if (l_itrToVsys != (*l_parsedVpdMap).end()) 1168 { 1169 const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal( 1170 l_itrToVsys->second, constants::kwdTM)}; 1171 1172 if (l_tmKwdValue.empty()) 1173 { 1174 throw std::runtime_error( 1175 std::string("Failed to get value for keyword [") + 1176 constants::kwdTM + 1177 std::string("] while creating Asset tag.")); 1178 } 1179 1180 const std::string l_seKwdValue{vpdSpecificUtility::getKwVal( 1181 l_itrToVsys->second, constants::kwdSE)}; 1182 1183 if (l_seKwdValue.empty()) 1184 { 1185 throw std::runtime_error( 1186 std::string("Failed to get value for keyword [") + 1187 constants::kwdSE + 1188 std::string("] while creating Asset tag.")); 1189 } 1190 1191 l_assetTag = std::string{"Server-"} + l_tmKwdValue + 1192 std::string{"-"} + l_seKwdValue; 1193 } 1194 else 1195 { 1196 throw std::runtime_error( 1197 "VSYS record not found in parsed VPD map to create Asset tag."); 1198 } 1199 } 1200 else 1201 { 1202 throw std::runtime_error( 1203 "Invalid VPD type recieved to create Asset tag."); 1204 } 1205 1206 return l_assetTag; 1207 } 1208 1209 void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap) 1210 { 1211 types::ObjectMap objectInterfaceMap; 1212 1213 if (std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 1214 { 1215 populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH); 1216 1217 try 1218 { 1219 if (m_isFactoryResetDone) 1220 { 1221 const auto& l_assetTag = createAssetTagString(parsedVpdMap); 1222 1223 auto l_itrToSystemPath = objectInterfaceMap.find( 1224 sdbusplus::message::object_path(constants::systemInvPath)); 1225 if (l_itrToSystemPath == objectInterfaceMap.end()) 1226 { 1227 throw std::runtime_error( 1228 "Asset tag update failed. System Path not found in object map."); 1229 } 1230 1231 types::PropertyMap l_assetTagProperty; 1232 l_assetTagProperty.emplace("AssetTag", l_assetTag); 1233 1234 (l_itrToSystemPath->second) 1235 .emplace(constants::assetTagInf, 1236 std::move(l_assetTagProperty)); 1237 } 1238 } 1239 catch (const std::exception& l_ex) 1240 { 1241 EventLogger::createSyncPel( 1242 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 1243 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 1244 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1245 } 1246 1247 // Notify PIM 1248 if (!dbusUtility::callPIM(move(objectInterfaceMap))) 1249 { 1250 throw std::runtime_error("Call to PIM failed for system VPD"); 1251 } 1252 } 1253 else 1254 { 1255 throw DataException("Invalid format of parsed VPD map."); 1256 } 1257 } 1258 1259 bool Worker::processPreAction(const std::string& i_vpdFilePath, 1260 const std::string& i_flagToProcess) 1261 { 1262 if (i_vpdFilePath.empty() || i_flagToProcess.empty()) 1263 { 1264 logging::logMessage( 1265 "Invalid input parameter. Abort processing pre action"); 1266 return false; 1267 } 1268 1269 if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction", 1270 i_vpdFilePath, i_flagToProcess)) && 1271 (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS)) 1272 { 1273 // TODO: Need a way to delete inventory object from Dbus and persisted 1274 // data section in case any FRU is not present or there is any 1275 // problem in collecting it. Once it has been deleted, it can be 1276 // re-created in the flow of priming the inventory. This needs to be 1277 // done either here or in the exception section of "parseAndPublishVPD" 1278 // API. Any failure in the process of collecting FRU will land up in the 1279 // excpetion of "parseAndPublishVPD". 1280 1281 // If the FRU is not there, clear the VINI/CCIN data. 1282 // Enity manager probes for this keyword to look for this 1283 // FRU, now if the data is persistent on BMC and FRU is 1284 // removed this can lead to ambiguity. Hence clearing this 1285 // Keyword if FRU is absent. 1286 const auto& inventoryPath = 1287 m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath", 1288 ""); 1289 1290 if (!inventoryPath.empty()) 1291 { 1292 types::ObjectMap l_pimObjMap{ 1293 {inventoryPath, 1294 {{constants::kwdVpdInf, 1295 {{constants::kwdCCIN, types::BinaryVector{}}}}}}}; 1296 1297 if (!dbusUtility::callPIM(std::move(l_pimObjMap))) 1298 { 1299 logging::logMessage( 1300 "Call to PIM failed for file " + i_vpdFilePath); 1301 } 1302 } 1303 else 1304 { 1305 logging::logMessage( 1306 "Inventory path is empty in Json for file " + i_vpdFilePath); 1307 } 1308 1309 return false; 1310 } 1311 return true; 1312 } 1313 1314 bool Worker::processPostAction( 1315 const std::string& i_vpdFruPath, const std::string& i_flagToProcess, 1316 const std::optional<types::VPDMapVariant> i_parsedVpd) 1317 { 1318 if (i_vpdFruPath.empty() || i_flagToProcess.empty()) 1319 { 1320 logging::logMessage( 1321 "Invalid input parameter. Abort processing post action"); 1322 return false; 1323 } 1324 1325 // Check if post action tag is to be triggered in the flow of collection 1326 // based on some CCIN value? 1327 if (m_parsedJson["frus"][i_vpdFruPath] 1328 .at(0)["postAction"][i_flagToProcess] 1329 .contains("ccin")) 1330 { 1331 if (!i_parsedVpd.has_value()) 1332 { 1333 logging::logMessage("Empty VPD Map"); 1334 return false; 1335 } 1336 1337 // CCIN match is required to process post action for this FRU as it 1338 // contains the flag. 1339 if (!vpdSpecificUtility::findCcinInVpd( 1340 m_parsedJson["frus"][i_vpdFruPath].at( 1341 0)["postAction"]["collection"], 1342 i_parsedVpd.value())) 1343 { 1344 // If CCIN is not found, implies post action processing is not 1345 // required for this FRU. Let the flow continue. 1346 return true; 1347 } 1348 } 1349 1350 if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction", 1351 i_vpdFruPath, i_flagToProcess)) 1352 { 1353 logging::logMessage( 1354 "Execution of post action failed for path: " + i_vpdFruPath); 1355 1356 // If post action was required and failed only in that case return 1357 // false. In all other case post action is considered passed. 1358 return false; 1359 } 1360 1361 return true; 1362 } 1363 1364 types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath) 1365 { 1366 if (i_vpdFilePath.empty()) 1367 { 1368 throw std::runtime_error( 1369 "Empty VPD file path passed to Worker::parseVpdFile. Abort processing"); 1370 } 1371 1372 try 1373 { 1374 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1375 "preAction", "collection")) 1376 { 1377 if (!processPreAction(i_vpdFilePath, "collection")) 1378 { 1379 throw std::runtime_error("Pre-Action failed"); 1380 } 1381 } 1382 1383 if (!std::filesystem::exists(i_vpdFilePath)) 1384 { 1385 throw std::runtime_error( 1386 "Could not find file path " + i_vpdFilePath + 1387 "Skipping parser trigger for the EEPROM"); 1388 } 1389 1390 std::shared_ptr<Parser> vpdParser = 1391 std::make_shared<Parser>(i_vpdFilePath, m_parsedJson); 1392 1393 types::VPDMapVariant l_parsedVpd = vpdParser->parse(); 1394 1395 // Before returning, as collection is over, check if FRU qualifies for 1396 // any post action in the flow of collection. 1397 // Note: Don't change the order, post action needs to be processed only 1398 // after collection for FRU is successfully done. 1399 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1400 "postAction", "collection")) 1401 { 1402 if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd)) 1403 { 1404 // TODO: Log PEL 1405 logging::logMessage("Required post action failed for path [" + 1406 i_vpdFilePath + "]"); 1407 } 1408 } 1409 1410 return l_parsedVpd; 1411 } 1412 catch (std::exception& l_ex) 1413 { 1414 // If post fail action is required, execute it. 1415 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1416 "postFailAction", "collection")) 1417 { 1418 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath, 1419 "collection")) 1420 { 1421 // TODO: Log PEL 1422 throw std::runtime_error( 1423 "VPD parsing failed for " + i_vpdFilePath + 1424 " due to error: " + l_ex.what() + 1425 ". Post Fail Action also failed, aborting collection for this FRU"); 1426 } 1427 } 1428 1429 // TODO: Log PEL 1430 throw std::runtime_error("VPD parsing failed for " + i_vpdFilePath + 1431 " due to error: " + l_ex.what()); 1432 } 1433 } 1434 1435 std::tuple<bool, std::string> Worker::parseAndPublishVPD( 1436 const std::string& i_vpdFilePath) 1437 { 1438 std::string l_inventoryPath{}; 1439 1440 try 1441 { 1442 m_semaphore.acquire(); 1443 1444 // Thread launched. 1445 m_mutex.lock(); 1446 m_activeCollectionThreadCount++; 1447 m_mutex.unlock(); 1448 1449 // Set CollectionStatus as InProgress. Since it's an intermediate state 1450 // D-bus set-property call is good enough to update the status. 1451 l_inventoryPath = jsonUtility::getInventoryObjPathFromJson( 1452 m_parsedJson, i_vpdFilePath); 1453 1454 if (!l_inventoryPath.empty()) 1455 { 1456 if (!dbusUtility::writeDbusProperty( 1457 jsonUtility::getServiceName(m_parsedJson, l_inventoryPath), 1458 l_inventoryPath, constants::vpdCollectionInterface, 1459 "CollectionStatus", 1460 types::DbusVariantType{constants::vpdCollectionInProgress})) 1461 { 1462 logging::logMessage( 1463 "Unable to set CollectionStatus as InProgress for " + 1464 i_vpdFilePath + ". Error : " + "DBus write failed"); 1465 } 1466 } 1467 1468 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath); 1469 1470 types::ObjectMap objectInterfaceMap; 1471 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath); 1472 1473 // logging::logMessage("Dbus sucessfully populated for FRU " + 1474 // i_vpdFilePath); 1475 1476 // Notify PIM 1477 if (!dbusUtility::callPIM(move(objectInterfaceMap))) 1478 { 1479 throw std::runtime_error( 1480 "Call to PIM failed while publishing VPD."); 1481 } 1482 } 1483 catch (const std::exception& ex) 1484 { 1485 // Notify FRU's VPD CollectionStatus as Failure 1486 if (!dbusUtility::notifyFRUCollectionStatus( 1487 l_inventoryPath, constants::vpdCollectionFailure)) 1488 { 1489 logging::logMessage( 1490 "Call to PIM Notify method failed to update Collection status as Failure for " + 1491 i_vpdFilePath); 1492 } 1493 1494 // handle all the exceptions internally. Return only true/false 1495 // based on status of execution. 1496 if (typeid(ex) == std::type_index(typeid(DataException))) 1497 { 1498 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip 1499 // logging error for these cases. 1500 if (vpdSpecificUtility::isPass1Planar()) 1501 { 1502 const std::string& l_invPathLeafValue = 1503 sdbusplus::message::object_path( 1504 jsonUtility::getInventoryObjPathFromJson(m_parsedJson, 1505 i_vpdFilePath)) 1506 .filename(); 1507 1508 if ((l_invPathLeafValue.find("pcie_card", 0) != 1509 std::string::npos)) 1510 { 1511 // skip logging any PEL for PCIe cards on pass 1 planar. 1512 return std::make_tuple(false, i_vpdFilePath); 1513 } 1514 } 1515 1516 // TODO: Add custom handling 1517 logging::logMessage(ex.what()); 1518 } 1519 else if (typeid(ex) == std::type_index(typeid(EccException))) 1520 { 1521 // TODO: Add custom handling 1522 logging::logMessage(ex.what()); 1523 } 1524 else if (typeid(ex) == std::type_index(typeid(JsonException))) 1525 { 1526 // TODO: Add custom handling 1527 logging::logMessage(ex.what()); 1528 } 1529 else 1530 { 1531 logging::logMessage(ex.what()); 1532 } 1533 1534 // TODO: Figure out a way to clear data in case of any failure at 1535 // runtime. 1536 1537 // set present property to false for any error case. In future this will 1538 // be replaced by presence logic. 1539 // Update Present property for this FRU only if we handle Present 1540 // property for the FRU. 1541 if (isPresentPropertyHandlingRequired( 1542 m_parsedJson["frus"][i_vpdFilePath].at(0))) 1543 { 1544 setPresentProperty(i_vpdFilePath, false); 1545 } 1546 1547 m_semaphore.release(); 1548 return std::make_tuple(false, i_vpdFilePath); 1549 } 1550 m_semaphore.release(); 1551 return std::make_tuple(true, i_vpdFilePath); 1552 } 1553 1554 bool Worker::skipPathForCollection(const std::string& i_vpdFilePath) 1555 { 1556 if (i_vpdFilePath.empty()) 1557 { 1558 return true; 1559 } 1560 1561 // skip processing of system VPD again as it has been already collected. 1562 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH) 1563 { 1564 return true; 1565 } 1566 1567 if (dbusUtility::isChassisPowerOn()) 1568 { 1569 // If chassis is powered on, skip collecting FRUs which are 1570 // powerOffOnly. 1571 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath)) 1572 { 1573 return true; 1574 } 1575 1576 const std::string& l_invPathLeafValue = 1577 sdbusplus::message::object_path( 1578 jsonUtility::getInventoryObjPathFromJson(m_parsedJson, 1579 i_vpdFilePath)) 1580 .filename(); 1581 1582 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos)) 1583 { 1584 return true; 1585 } 1586 } 1587 1588 return false; 1589 } 1590 1591 void Worker::collectFrusFromJson() 1592 { 1593 // A parsed JSON file should be present to pick FRUs EEPROM paths 1594 if (m_parsedJson.empty()) 1595 { 1596 throw std::runtime_error( 1597 "A config JSON is required for processing of FRUs"); 1598 } 1599 1600 const nlohmann::json& listOfFrus = 1601 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>(); 1602 1603 for (const auto& itemFRUS : listOfFrus.items()) 1604 { 1605 const std::string& vpdFilePath = itemFRUS.key(); 1606 1607 if (skipPathForCollection(vpdFilePath)) 1608 { 1609 continue; 1610 } 1611 1612 try 1613 { 1614 std::thread{[vpdFilePath, this]() { 1615 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath); 1616 1617 m_mutex.lock(); 1618 m_activeCollectionThreadCount--; 1619 m_mutex.unlock(); 1620 1621 if (!m_activeCollectionThreadCount) 1622 { 1623 m_isAllFruCollected = true; 1624 } 1625 }}.detach(); 1626 } 1627 catch (const std::exception& l_ex) 1628 { 1629 // add vpdFilePath(EEPROM path) to failed list 1630 m_failedEepromPaths.push_front(vpdFilePath); 1631 } 1632 } 1633 } 1634 1635 // ToDo: Move the API under IBM_SYSTEM 1636 void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap) 1637 { 1638 try 1639 { 1640 std::string l_backupAndRestoreCfgFilePath = 1641 m_parsedJson.value("backupRestoreConfigPath", ""); 1642 1643 nlohmann::json l_backupAndRestoreCfgJsonObj = 1644 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath); 1645 1646 if (l_backupAndRestoreCfgJsonObj.empty()) 1647 { 1648 throw JsonException("JSON parsing failed", 1649 l_backupAndRestoreCfgFilePath); 1650 } 1651 1652 // check if either of "source" or "destination" has inventory path. 1653 // this indicates that this sytem has System VPD on hardware 1654 // and other copy on D-Bus (BMC cache). 1655 if (!l_backupAndRestoreCfgJsonObj.empty() && 1656 ((l_backupAndRestoreCfgJsonObj.contains("source") && 1657 l_backupAndRestoreCfgJsonObj["source"].contains( 1658 "inventoryPath")) || 1659 (l_backupAndRestoreCfgJsonObj.contains("destination") && 1660 l_backupAndRestoreCfgJsonObj["destination"].contains( 1661 "inventoryPath")))) 1662 { 1663 BackupAndRestore l_backupAndRestoreObj(m_parsedJson); 1664 auto [l_srcVpdVariant, 1665 l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore(); 1666 1667 // ToDo: Revisit is this check is required or not. 1668 if (auto l_srcVpdMap = 1669 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant); 1670 l_srcVpdMap && !(*l_srcVpdMap).empty()) 1671 { 1672 io_srcVpdMap = std::move(l_srcVpdVariant); 1673 } 1674 } 1675 } 1676 catch (const std::exception& l_ex) 1677 { 1678 EventLogger::createSyncPel( 1679 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 1680 __FILE__, __FUNCTION__, 0, 1681 std::string( 1682 "Exception caught while backup and restore VPD keyword's.") + 1683 EventLogger::getErrorMsg(l_ex), 1684 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1685 } 1686 } 1687 1688 void Worker::deleteFruVpd(const std::string& i_dbusObjPath) 1689 { 1690 if (i_dbusObjPath.empty()) 1691 { 1692 throw std::runtime_error("Given DBus object path is empty."); 1693 } 1694 1695 const std::string& l_fruPath = 1696 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath); 1697 1698 try 1699 { 1700 auto l_presentPropValue = dbusUtility::readDbusProperty( 1701 constants::pimServiceName, i_dbusObjPath, 1702 constants::inventoryItemInf, "Present"); 1703 1704 if (auto l_value = std::get_if<bool>(&l_presentPropValue)) 1705 { 1706 if (!(*l_value)) 1707 { 1708 throw std::runtime_error("Given FRU is not present"); 1709 } 1710 else 1711 { 1712 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, 1713 "preAction", "deletion")) 1714 { 1715 if (!processPreAction(l_fruPath, "deletion")) 1716 { 1717 throw std::runtime_error("Pre action failed"); 1718 } 1719 } 1720 1721 std::vector<std::string> l_interfaceList{ 1722 constants::operationalStatusInf}; 1723 1724 types::MapperGetSubTree l_subTreeMap = 1725 dbusUtility::getObjectSubTree(i_dbusObjPath, 0, 1726 l_interfaceList); 1727 1728 types::ObjectMap l_objectMap; 1729 1730 // Updates VPD specific interfaces property value under PIM for 1731 // sub FRUs. 1732 for (const auto& [l_objectPath, l_serviceInterfaceMap] : 1733 l_subTreeMap) 1734 { 1735 types::InterfaceMap l_interfaceMap; 1736 vpdSpecificUtility::resetDataUnderPIM(l_objectPath, 1737 l_interfaceMap); 1738 l_objectMap.emplace(l_objectPath, 1739 std::move(l_interfaceMap)); 1740 } 1741 1742 types::InterfaceMap l_interfaceMap; 1743 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath, 1744 l_interfaceMap); 1745 1746 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap)); 1747 1748 if (!dbusUtility::callPIM(std::move(l_objectMap))) 1749 { 1750 throw std::runtime_error("Call to PIM failed."); 1751 } 1752 1753 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, 1754 "postAction", "deletion")) 1755 { 1756 if (!processPostAction(l_fruPath, "deletion")) 1757 { 1758 throw std::runtime_error("Post action failed"); 1759 } 1760 } 1761 } 1762 } 1763 else 1764 { 1765 logging::logMessage( 1766 "Can't process delete VPD for FRU [" + i_dbusObjPath + 1767 "] as unable to read present property"); 1768 return; 1769 } 1770 1771 logging::logMessage( 1772 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath); 1773 } 1774 catch (const std::exception& l_ex) 1775 { 1776 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath, 1777 "postFailAction", "deletion")) 1778 { 1779 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath, 1780 "deletion")) 1781 { 1782 logging::logMessage( 1783 "Post fail action failed for: " + i_dbusObjPath); 1784 } 1785 } 1786 1787 logging::logMessage("Failed to delete VPD for FRU : " + i_dbusObjPath + 1788 " error: " + std::string(l_ex.what())); 1789 } 1790 } 1791 1792 void Worker::setPresentProperty(const std::string& i_vpdPath, 1793 const bool& i_value) 1794 { 1795 try 1796 { 1797 if (i_vpdPath.empty()) 1798 { 1799 throw std::runtime_error( 1800 "Path is empty. Can't set present property"); 1801 } 1802 1803 types::ObjectMap l_objectInterfaceMap; 1804 1805 // If the given path is EEPROM path. 1806 if (m_parsedJson["frus"].contains(i_vpdPath)) 1807 { 1808 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath]) 1809 { 1810 sdbusplus::message::object_path l_fruObjectPath( 1811 l_Fru["inventoryPath"]); 1812 1813 types::PropertyMap l_propertyValueMap; 1814 l_propertyValueMap.emplace("Present", i_value); 1815 1816 types::InterfaceMap l_interfaces; 1817 vpdSpecificUtility::insertOrMerge(l_interfaces, 1818 constants::inventoryItemInf, 1819 move(l_propertyValueMap)); 1820 1821 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath), 1822 std::move(l_interfaces)); 1823 } 1824 } 1825 else 1826 { 1827 // consider it as an inventory path. 1828 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0) 1829 { 1830 throw std::runtime_error( 1831 "Invalid inventory path: " + i_vpdPath); 1832 } 1833 1834 types::PropertyMap l_propertyValueMap; 1835 l_propertyValueMap.emplace("Present", i_value); 1836 1837 types::InterfaceMap l_interfaces; 1838 vpdSpecificUtility::insertOrMerge(l_interfaces, 1839 constants::inventoryItemInf, 1840 move(l_propertyValueMap)); 1841 1842 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces)); 1843 } 1844 1845 // Notify PIM 1846 if (!dbusUtility::callPIM(move(l_objectInterfaceMap))) 1847 { 1848 throw std::runtime_error( 1849 "Call to PIM failed while setting present property for path " + 1850 i_vpdPath); 1851 } 1852 } 1853 catch (const std::exception& l_ex) 1854 { 1855 logging::logMessage(l_ex.what()); 1856 } 1857 } 1858 1859 } // namespace vpd 1860