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 DataException(std::string(__FUNCTION__) + 922 "Failed to get value for keyword PG"); 923 } 924 } 925 } 926 } 927 928 void Worker::processCopyRecordFlag(const nlohmann::json& singleFru, 929 const types::VPDMapVariant& parsedVpdMap, 930 types::InterfaceMap& interfaces) 931 { 932 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 933 { 934 for (const auto& record : singleFru["copyRecords"]) 935 { 936 const std::string& recordName = record; 937 if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end()) 938 { 939 populateIPZVPDpropertyMap(interfaces, 940 (*ipzVpdMap).at(recordName), 941 constants::ipzVpdInf + recordName); 942 } 943 } 944 } 945 } 946 947 void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap, 948 types::InterfaceMap& interfaces) 949 { 950 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 951 { 952 for (const auto& [recordName, kwdValueMap] : *ipzVpdMap) 953 { 954 populateIPZVPDpropertyMap(interfaces, kwdValueMap, 955 constants::ipzVpdInf + recordName); 956 } 957 } 958 else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap)) 959 { 960 populateKwdVPDpropertyMap(*kwdVpdMap, interfaces); 961 } 962 963 if (m_parsedJson.contains("commonInterfaces")) 964 { 965 populateInterfaces(m_parsedJson["commonInterfaces"], interfaces, 966 parsedVpdMap); 967 } 968 } 969 970 bool Worker::processFruWithCCIN(const nlohmann::json& singleFru, 971 const types::VPDMapVariant& parsedVpdMap) 972 { 973 if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 974 { 975 auto itrToRec = (*ipzVPDMap).find("VINI"); 976 if (itrToRec == (*ipzVPDMap).end()) 977 { 978 return false; 979 } 980 981 std::string ccinFromVpd{ 982 vpdSpecificUtility::getKwVal(itrToRec->second, "CC")}; 983 984 if (ccinFromVpd.empty()) 985 { 986 return false; 987 } 988 989 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(), 990 ::toupper); 991 992 std::vector<std::string> ccinList; 993 for (std::string ccin : singleFru["ccin"]) 994 { 995 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper); 996 ccinList.push_back(ccin); 997 } 998 999 if (ccinList.empty()) 1000 { 1001 return false; 1002 } 1003 1004 if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) == 1005 ccinList.end()) 1006 { 1007 return false; 1008 } 1009 } 1010 return true; 1011 } 1012 1013 void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath, 1014 types::InterfaceMap& io_interfaces) 1015 { 1016 if (!dbusUtility::isChassisPowerOn()) 1017 { 1018 std::array<const char*, 1> l_operationalStatusInf = { 1019 constants::operationalStatusInf}; 1020 1021 auto mapperObjectMap = dbusUtility::getObjectMap( 1022 i_inventoryObjPath, l_operationalStatusInf); 1023 1024 // If the object has been found. Check if it is under PIM. 1025 if (mapperObjectMap.size() != 0) 1026 { 1027 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap) 1028 { 1029 if (l_serviceName == constants::pimServiceName) 1030 { 1031 // The object is already under PIM. No need to process 1032 // again. Retain the old value. 1033 return; 1034 } 1035 } 1036 } 1037 1038 // Implies value is not there in D-Bus. Populate it with default 1039 // value "true". 1040 types::PropertyMap l_functionalProp; 1041 l_functionalProp.emplace("Functional", true); 1042 vpdSpecificUtility::insertOrMerge(io_interfaces, 1043 constants::operationalStatusInf, 1044 move(l_functionalProp)); 1045 } 1046 1047 // if chassis is power on. Functional property should be there on D-Bus. 1048 // Don't process. 1049 return; 1050 } 1051 1052 void Worker::processEnabledProperty(const std::string& i_inventoryObjPath, 1053 types::InterfaceMap& io_interfaces) 1054 { 1055 if (!dbusUtility::isChassisPowerOn()) 1056 { 1057 std::array<const char*, 1> l_enableInf = {constants::enableInf}; 1058 1059 auto mapperObjectMap = 1060 dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf); 1061 1062 // If the object has been found. Check if it is under PIM. 1063 if (mapperObjectMap.size() != 0) 1064 { 1065 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap) 1066 { 1067 if (l_serviceName == constants::pimServiceName) 1068 { 1069 // The object is already under PIM. No need to process 1070 // again. Retain the old value. 1071 return; 1072 } 1073 } 1074 } 1075 1076 // Implies value is not there in D-Bus. Populate it with default 1077 // value "true". 1078 types::PropertyMap l_enabledProp; 1079 l_enabledProp.emplace("Enabled", true); 1080 vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf, 1081 move(l_enabledProp)); 1082 } 1083 1084 // if chassis is power on. Enabled property should be there on D-Bus. 1085 // Don't process. 1086 return; 1087 } 1088 1089 void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap, 1090 types::ObjectMap& objectInterfaceMap, 1091 const std::string& vpdFilePath) 1092 { 1093 if (vpdFilePath.empty()) 1094 { 1095 throw std::runtime_error( 1096 std::string(__FUNCTION__) + 1097 "Invalid parameter passed to populateDbus API."); 1098 } 1099 1100 // JSON config is mandatory for processing of "if". Add "else" for any 1101 // processing without config JSON. 1102 if (!m_parsedJson.empty()) 1103 { 1104 types::InterfaceMap interfaces; 1105 1106 for (const auto& aFru : m_parsedJson["frus"][vpdFilePath]) 1107 { 1108 const auto& inventoryPath = aFru["inventoryPath"]; 1109 sdbusplus::message::object_path fruObjectPath(inventoryPath); 1110 if (aFru.contains("ccin")) 1111 { 1112 if (!processFruWithCCIN(aFru, parsedVpdMap)) 1113 { 1114 continue; 1115 } 1116 } 1117 1118 if (aFru.value("inherit", true)) 1119 { 1120 processInheritFlag(parsedVpdMap, interfaces); 1121 } 1122 1123 // If specific record needs to be copied. 1124 if (aFru.contains("copyRecords")) 1125 { 1126 processCopyRecordFlag(aFru, parsedVpdMap, interfaces); 1127 } 1128 1129 if (aFru.contains("extraInterfaces")) 1130 { 1131 // Process extra interfaces w.r.t a FRU. 1132 processExtraInterfaces(aFru, interfaces, parsedVpdMap); 1133 } 1134 1135 // Process FRUS which are embedded in the parent FRU and whose VPD 1136 // will be synthesized. 1137 if ((aFru.value("embedded", true)) && 1138 (!aFru.value("synthesized", false))) 1139 { 1140 processEmbeddedAndSynthesizedFrus(aFru, interfaces); 1141 } 1142 1143 processFunctionalProperty(inventoryPath, interfaces); 1144 processEnabledProperty(inventoryPath, interfaces); 1145 1146 // Update collection status as successful 1147 types::PropertyMap l_collectionProperty = { 1148 {"CollectionStatus", constants::vpdCollectionSuccess}}; 1149 1150 vpdSpecificUtility::insertOrMerge(interfaces, 1151 constants::vpdCollectionInterface, 1152 std::move(l_collectionProperty)); 1153 1154 objectInterfaceMap.emplace(std::move(fruObjectPath), 1155 std::move(interfaces)); 1156 } 1157 } 1158 } 1159 1160 std::string Worker::createAssetTagString( 1161 const types::VPDMapVariant& i_parsedVpdMap) 1162 { 1163 std::string l_assetTag; 1164 1165 // system VPD will be in IPZ format. 1166 if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap)) 1167 { 1168 auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS); 1169 if (l_itrToVsys != (*l_parsedVpdMap).end()) 1170 { 1171 const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal( 1172 l_itrToVsys->second, constants::kwdTM)}; 1173 1174 if (l_tmKwdValue.empty()) 1175 { 1176 throw std::runtime_error( 1177 std::string("Failed to get value for keyword [") + 1178 constants::kwdTM + 1179 std::string("] while creating Asset tag.")); 1180 } 1181 1182 const std::string l_seKwdValue{vpdSpecificUtility::getKwVal( 1183 l_itrToVsys->second, constants::kwdSE)}; 1184 1185 if (l_seKwdValue.empty()) 1186 { 1187 throw std::runtime_error( 1188 std::string("Failed to get value for keyword [") + 1189 constants::kwdSE + 1190 std::string("] while creating Asset tag.")); 1191 } 1192 1193 l_assetTag = std::string{"Server-"} + l_tmKwdValue + 1194 std::string{"-"} + l_seKwdValue; 1195 } 1196 else 1197 { 1198 throw std::runtime_error( 1199 "VSYS record not found in parsed VPD map to create Asset tag."); 1200 } 1201 } 1202 else 1203 { 1204 throw std::runtime_error( 1205 "Invalid VPD type recieved to create Asset tag."); 1206 } 1207 1208 return l_assetTag; 1209 } 1210 1211 void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap) 1212 { 1213 types::ObjectMap objectInterfaceMap; 1214 1215 if (std::get_if<types::IPZVpdMap>(&parsedVpdMap)) 1216 { 1217 populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH); 1218 1219 try 1220 { 1221 if (m_isFactoryResetDone) 1222 { 1223 const auto& l_assetTag = createAssetTagString(parsedVpdMap); 1224 1225 auto l_itrToSystemPath = objectInterfaceMap.find( 1226 sdbusplus::message::object_path(constants::systemInvPath)); 1227 if (l_itrToSystemPath == objectInterfaceMap.end()) 1228 { 1229 throw std::runtime_error( 1230 "Asset tag update failed. System Path not found in object map."); 1231 } 1232 1233 types::PropertyMap l_assetTagProperty; 1234 l_assetTagProperty.emplace("AssetTag", l_assetTag); 1235 1236 (l_itrToSystemPath->second) 1237 .emplace(constants::assetTagInf, 1238 std::move(l_assetTagProperty)); 1239 } 1240 } 1241 catch (const std::exception& l_ex) 1242 { 1243 EventLogger::createSyncPel( 1244 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 1245 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 1246 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1247 } 1248 1249 // Notify PIM 1250 if (!dbusUtility::callPIM(move(objectInterfaceMap))) 1251 { 1252 throw std::runtime_error("Call to PIM failed for system VPD"); 1253 } 1254 } 1255 else 1256 { 1257 throw DataException("Invalid format of parsed VPD map."); 1258 } 1259 } 1260 1261 bool Worker::processPreAction(const std::string& i_vpdFilePath, 1262 const std::string& i_flagToProcess) 1263 { 1264 if (i_vpdFilePath.empty() || i_flagToProcess.empty()) 1265 { 1266 logging::logMessage( 1267 "Invalid input parameter. Abort processing pre action"); 1268 return false; 1269 } 1270 1271 if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction", 1272 i_vpdFilePath, i_flagToProcess)) && 1273 (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS)) 1274 { 1275 // TODO: Need a way to delete inventory object from Dbus and persisted 1276 // data section in case any FRU is not present or there is any 1277 // problem in collecting it. Once it has been deleted, it can be 1278 // re-created in the flow of priming the inventory. This needs to be 1279 // done either here or in the exception section of "parseAndPublishVPD" 1280 // API. Any failure in the process of collecting FRU will land up in the 1281 // excpetion of "parseAndPublishVPD". 1282 1283 // If the FRU is not there, clear the VINI/CCIN data. 1284 // Enity manager probes for this keyword to look for this 1285 // FRU, now if the data is persistent on BMC and FRU is 1286 // removed this can lead to ambiguity. Hence clearing this 1287 // Keyword if FRU is absent. 1288 const auto& inventoryPath = 1289 m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath", 1290 ""); 1291 1292 if (!inventoryPath.empty()) 1293 { 1294 types::ObjectMap l_pimObjMap{ 1295 {inventoryPath, 1296 {{constants::kwdVpdInf, 1297 {{constants::kwdCCIN, types::BinaryVector{}}}}}}}; 1298 1299 if (!dbusUtility::callPIM(std::move(l_pimObjMap))) 1300 { 1301 logging::logMessage( 1302 "Call to PIM failed for file " + i_vpdFilePath); 1303 } 1304 } 1305 else 1306 { 1307 logging::logMessage( 1308 "Inventory path is empty in Json for file " + i_vpdFilePath); 1309 } 1310 1311 return false; 1312 } 1313 return true; 1314 } 1315 1316 bool Worker::processPostAction( 1317 const std::string& i_vpdFruPath, const std::string& i_flagToProcess, 1318 const std::optional<types::VPDMapVariant> i_parsedVpd) 1319 { 1320 if (i_vpdFruPath.empty() || i_flagToProcess.empty()) 1321 { 1322 logging::logMessage( 1323 "Invalid input parameter. Abort processing post action"); 1324 return false; 1325 } 1326 1327 // Check if post action tag is to be triggered in the flow of collection 1328 // based on some CCIN value? 1329 if (m_parsedJson["frus"][i_vpdFruPath] 1330 .at(0)["postAction"][i_flagToProcess] 1331 .contains("ccin")) 1332 { 1333 if (!i_parsedVpd.has_value()) 1334 { 1335 logging::logMessage("Empty VPD Map"); 1336 return false; 1337 } 1338 1339 // CCIN match is required to process post action for this FRU as it 1340 // contains the flag. 1341 if (!vpdSpecificUtility::findCcinInVpd( 1342 m_parsedJson["frus"][i_vpdFruPath].at( 1343 0)["postAction"]["collection"], 1344 i_parsedVpd.value())) 1345 { 1346 // If CCIN is not found, implies post action processing is not 1347 // required for this FRU. Let the flow continue. 1348 return true; 1349 } 1350 } 1351 1352 if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction", 1353 i_vpdFruPath, i_flagToProcess)) 1354 { 1355 logging::logMessage( 1356 "Execution of post action failed for path: " + i_vpdFruPath); 1357 1358 // If post action was required and failed only in that case return 1359 // false. In all other case post action is considered passed. 1360 return false; 1361 } 1362 1363 return true; 1364 } 1365 1366 types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath) 1367 { 1368 try 1369 { 1370 if (i_vpdFilePath.empty()) 1371 { 1372 throw std::runtime_error( 1373 std::string(__FUNCTION__) + 1374 " Empty VPD file path passed. Abort processing"); 1375 } 1376 1377 bool isPreActionRequired = false; 1378 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1379 "preAction", "collection")) 1380 { 1381 isPreActionRequired = true; 1382 if (!processPreAction(i_vpdFilePath, "collection")) 1383 { 1384 throw std::runtime_error( 1385 std::string(__FUNCTION__) + " Pre-Action failed"); 1386 } 1387 } 1388 1389 if (!std::filesystem::exists(i_vpdFilePath)) 1390 { 1391 if (isPreActionRequired) 1392 { 1393 throw std::runtime_error( 1394 std::string(__FUNCTION__) + " Could not find file path " + 1395 i_vpdFilePath + "Skipping parser trigger for the EEPROM"); 1396 } 1397 return types::VPDMapVariant{}; 1398 } 1399 1400 std::shared_ptr<Parser> vpdParser = 1401 std::make_shared<Parser>(i_vpdFilePath, m_parsedJson); 1402 1403 types::VPDMapVariant l_parsedVpd = vpdParser->parse(); 1404 1405 // Before returning, as collection is over, check if FRU qualifies for 1406 // any post action in the flow of collection. 1407 // Note: Don't change the order, post action needs to be processed only 1408 // after collection for FRU is successfully done. 1409 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1410 "postAction", "collection")) 1411 { 1412 if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd)) 1413 { 1414 // Post action was required but failed while executing. 1415 // Behaviour can be undefined. 1416 EventLogger::createSyncPel( 1417 types::ErrorType::InternalFailure, 1418 types::SeverityType::Warning, __FILE__, __FUNCTION__, 0, 1419 std::string("Required post action failed for path [" + 1420 i_vpdFilePath + "]"), 1421 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1422 } 1423 } 1424 1425 return l_parsedVpd; 1426 } 1427 catch (std::exception& l_ex) 1428 { 1429 // If post fail action is required, execute it. 1430 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath, 1431 "postFailAction", "collection")) 1432 { 1433 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath, 1434 "collection")) 1435 { 1436 throw std::runtime_error( 1437 std::string(__FUNCTION__) + "VPD parsing failed for " + 1438 i_vpdFilePath + " due to error: " + l_ex.what() + 1439 ". Post Fail Action also failed, aborting collection for this FRU"); 1440 } 1441 } 1442 1443 throw std::runtime_error( 1444 std::string(__FUNCTION__) + "VPD parsing failed for " + 1445 i_vpdFilePath + " due to error: " + l_ex.what()); 1446 } 1447 } 1448 1449 std::tuple<bool, std::string> Worker::parseAndPublishVPD( 1450 const std::string& i_vpdFilePath) 1451 { 1452 std::string l_inventoryPath{}; 1453 1454 try 1455 { 1456 m_semaphore.acquire(); 1457 1458 // Thread launched. 1459 m_mutex.lock(); 1460 m_activeCollectionThreadCount++; 1461 m_mutex.unlock(); 1462 1463 // Set CollectionStatus as InProgress. Since it's an intermediate state 1464 // D-bus set-property call is good enough to update the status. 1465 l_inventoryPath = jsonUtility::getInventoryObjPathFromJson( 1466 m_parsedJson, i_vpdFilePath); 1467 1468 if (!l_inventoryPath.empty()) 1469 { 1470 if (!dbusUtility::writeDbusProperty( 1471 jsonUtility::getServiceName(m_parsedJson, l_inventoryPath), 1472 l_inventoryPath, constants::vpdCollectionInterface, 1473 "CollectionStatus", 1474 types::DbusVariantType{constants::vpdCollectionInProgress})) 1475 { 1476 logging::logMessage( 1477 "Unable to set CollectionStatus as InProgress for " + 1478 i_vpdFilePath + ". Error : " + "DBus write failed"); 1479 } 1480 } 1481 1482 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath); 1483 if (!std::holds_alternative<std::monostate>(parsedVpdMap)) 1484 { 1485 types::ObjectMap objectInterfaceMap; 1486 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath); 1487 1488 // Notify PIM 1489 if (!dbusUtility::callPIM(move(objectInterfaceMap))) 1490 { 1491 throw std::runtime_error( 1492 std::string(__FUNCTION__) + 1493 "Call to PIM failed while publishing VPD."); 1494 } 1495 } 1496 else 1497 { 1498 logging::logMessage("Empty parsedVpdMap recieved for path [" + 1499 i_vpdFilePath + "]. Check PEL for reason."); 1500 } 1501 } 1502 catch (const std::exception& ex) 1503 { 1504 // Notify FRU's VPD CollectionStatus as Failure 1505 if (!dbusUtility::notifyFRUCollectionStatus( 1506 l_inventoryPath, constants::vpdCollectionFailure)) 1507 { 1508 logging::logMessage( 1509 "Call to PIM Notify method failed to update Collection status as Failure for " + 1510 i_vpdFilePath); 1511 } 1512 1513 // handle all the exceptions internally. Return only true/false 1514 // based on status of execution. 1515 if (typeid(ex) == std::type_index(typeid(DataException))) 1516 { 1517 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip 1518 // logging error for these cases. 1519 if (vpdSpecificUtility::isPass1Planar()) 1520 { 1521 const std::string& l_invPathLeafValue = 1522 sdbusplus::message::object_path( 1523 jsonUtility::getInventoryObjPathFromJson(m_parsedJson, 1524 i_vpdFilePath)) 1525 .filename(); 1526 1527 if ((l_invPathLeafValue.find("pcie_card", 0) != 1528 std::string::npos)) 1529 { 1530 // skip logging any PEL for PCIe cards on pass 1 planar. 1531 return std::make_tuple(false, i_vpdFilePath); 1532 } 1533 } 1534 } 1535 1536 EventLogger::createSyncPel( 1537 EventLogger::getErrorType(ex), types::SeverityType::Informational, 1538 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex), 1539 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1540 1541 // TODO: Figure out a way to clear data in case of any failure at 1542 // runtime. 1543 1544 // set present property to false for any error case. In future this will 1545 // be replaced by presence logic. 1546 // Update Present property for this FRU only if we handle Present 1547 // property for the FRU. 1548 if (isPresentPropertyHandlingRequired( 1549 m_parsedJson["frus"][i_vpdFilePath].at(0))) 1550 { 1551 setPresentProperty(i_vpdFilePath, false); 1552 } 1553 1554 m_semaphore.release(); 1555 return std::make_tuple(false, i_vpdFilePath); 1556 } 1557 m_semaphore.release(); 1558 return std::make_tuple(true, i_vpdFilePath); 1559 } 1560 1561 bool Worker::skipPathForCollection(const std::string& i_vpdFilePath) 1562 { 1563 if (i_vpdFilePath.empty()) 1564 { 1565 return true; 1566 } 1567 1568 // skip processing of system VPD again as it has been already collected. 1569 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH) 1570 { 1571 return true; 1572 } 1573 1574 if (dbusUtility::isChassisPowerOn()) 1575 { 1576 // If chassis is powered on, skip collecting FRUs which are 1577 // powerOffOnly. 1578 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath)) 1579 { 1580 return true; 1581 } 1582 1583 const std::string& l_invPathLeafValue = 1584 sdbusplus::message::object_path( 1585 jsonUtility::getInventoryObjPathFromJson(m_parsedJson, 1586 i_vpdFilePath)) 1587 .filename(); 1588 1589 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos)) 1590 { 1591 return true; 1592 } 1593 } 1594 1595 return false; 1596 } 1597 1598 void Worker::collectFrusFromJson() 1599 { 1600 // A parsed JSON file should be present to pick FRUs EEPROM paths 1601 if (m_parsedJson.empty()) 1602 { 1603 throw JsonException( 1604 std::string(__FUNCTION__) + 1605 ": Config JSON is mandatory for processing of FRUs through this API.", 1606 m_configJsonPath); 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 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 1689 __FILE__, __FUNCTION__, 0, 1690 std::string( 1691 "Exception caught while backup and restore VPD keyword's.") + 1692 EventLogger::getErrorMsg(l_ex), 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 DbusException( 1858 std::string(__FUNCTION__) + 1859 "Call to PIM failed while setting present property for path " + 1860 i_vpdPath); 1861 } 1862 } 1863 catch (const std::exception& l_ex) 1864 { 1865 EventLogger::createSyncPel( 1866 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 1867 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 1868 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 1869 } 1870 } 1871 1872 } // namespace vpd 1873