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