1 #include "config.h" 2 3 #include "manager.hpp" 4 5 #include "backup_restore.hpp" 6 #include "constants.hpp" 7 #include "exceptions.hpp" 8 #include "logger.hpp" 9 #include "parser.hpp" 10 #include "parser_factory.hpp" 11 #include "parser_interface.hpp" 12 #include "types.hpp" 13 #include "utility/dbus_utility.hpp" 14 #include "utility/json_utility.hpp" 15 #include "utility/vpd_specific_utility.hpp" 16 17 #include <boost/asio/steady_timer.hpp> 18 #include <sdbusplus/bus/match.hpp> 19 #include <sdbusplus/message.hpp> 20 21 namespace vpd 22 { 23 Manager::Manager( 24 const std::shared_ptr<boost::asio::io_context>& ioCon, 25 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace, 26 const std::shared_ptr<sdbusplus::asio::connection>& asioConnection) : 27 m_ioContext(ioCon), m_interface(iFace), m_asioConnection(asioConnection) 28 { 29 try 30 { 31 #ifdef IBM_SYSTEM 32 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT); 33 34 // Set up minimal things that is needed before bus name is claimed. 35 m_worker->performInitialSetup(); 36 37 // set callback to detect any asset tag change 38 registerAssetTagChangeCallback(); 39 40 // set async timer to detect if system VPD is published on D-Bus. 41 SetTimerToDetectSVPDOnDbus(); 42 43 // set async timer to detect if VPD collection is done. 44 SetTimerToDetectVpdCollectionStatus(); 45 46 // Instantiate GpioMonitor class 47 m_gpioMonitor = std::make_shared<GpioMonitor>( 48 m_worker->getSysCfgJsonObj(), m_worker, m_ioContext); 49 50 #endif 51 // set callback to detect host state change. 52 registerHostStateChangeCallback(); 53 54 // For backward compatibility. Should be depricated. 55 iFace->register_method( 56 "WriteKeyword", 57 [this](const sdbusplus::message::object_path i_path, 58 const std::string i_recordName, const std::string i_keyword, 59 const types::BinaryVector i_value) -> int { 60 return this->updateKeyword( 61 i_path, std::make_tuple(i_recordName, i_keyword, i_value)); 62 }); 63 64 // Register methods under com.ibm.VPD.Manager interface 65 iFace->register_method( 66 "UpdateKeyword", 67 [this](const types::Path i_vpdPath, 68 const types::WriteVpdParams i_paramsToWriteData) -> int { 69 return this->updateKeyword(i_vpdPath, i_paramsToWriteData); 70 }); 71 72 iFace->register_method( 73 "WriteKeywordOnHardware", 74 [this](const types::Path i_fruPath, 75 const types::WriteVpdParams i_paramsToWriteData) -> int { 76 return this->updateKeywordOnHardware(i_fruPath, 77 i_paramsToWriteData); 78 }); 79 80 iFace->register_method( 81 "ReadKeyword", 82 [this](const types::Path i_fruPath, 83 const types::ReadVpdParams i_paramsToReadData) 84 -> types::DbusVariantType { 85 return this->readKeyword(i_fruPath, i_paramsToReadData); 86 }); 87 88 iFace->register_method( 89 "CollectFRUVPD", 90 [this](const sdbusplus::message::object_path& i_dbusObjPath) { 91 this->collectSingleFruVpd(i_dbusObjPath); 92 }); 93 94 iFace->register_method( 95 "deleteFRUVPD", 96 [this](const sdbusplus::message::object_path& i_dbusObjPath) { 97 this->deleteSingleFruVpd(i_dbusObjPath); 98 }); 99 100 iFace->register_method( 101 "GetExpandedLocationCode", 102 [this](const std::string& i_unexpandedLocationCode, 103 uint16_t& i_nodeNumber) -> std::string { 104 return this->getExpandedLocationCode(i_unexpandedLocationCode, 105 i_nodeNumber); 106 }); 107 108 iFace->register_method("GetFRUsByExpandedLocationCode", 109 [this](const std::string& i_expandedLocationCode) 110 -> types::ListOfPaths { 111 return this->getFrusByExpandedLocationCode( 112 i_expandedLocationCode); 113 }); 114 115 iFace->register_method( 116 "GetFRUsByUnexpandedLocationCode", 117 [this](const std::string& i_unexpandedLocationCode, 118 uint16_t& i_nodeNumber) -> types::ListOfPaths { 119 return this->getFrusByUnexpandedLocationCode( 120 i_unexpandedLocationCode, i_nodeNumber); 121 }); 122 123 iFace->register_method( 124 "GetHardwarePath", 125 [this](const sdbusplus::message::object_path& i_dbusObjPath) 126 -> std::string { return this->getHwPath(i_dbusObjPath); }); 127 128 iFace->register_method("PerformVPDRecollection", [this]() { 129 this->performVpdRecollection(); 130 }); 131 132 // Indicates FRU VPD collection for the system has not started. 133 iFace->register_property_rw<std::string>( 134 "CollectionStatus", sdbusplus::vtable::property_::emits_change, 135 [this](const std::string l_currStatus, const auto&) { 136 m_vpdCollectionStatus = l_currStatus; 137 return 0; 138 }, 139 [this](const auto&) { return m_vpdCollectionStatus; }); 140 } 141 catch (const std::exception& e) 142 { 143 logging::logMessage( 144 "VPD-Manager service failed. " + std::string(e.what())); 145 throw; 146 } 147 } 148 149 #ifdef IBM_SYSTEM 150 void Manager::registerAssetTagChangeCallback() 151 { 152 static std::shared_ptr<sdbusplus::bus::match_t> l_assetMatch = 153 std::make_shared<sdbusplus::bus::match_t>( 154 *m_asioConnection, 155 sdbusplus::bus::match::rules::propertiesChanged( 156 constants::systemInvPath, constants::assetTagInf), 157 [this](sdbusplus::message_t& l_msg) { 158 processAssetTagChangeCallback(l_msg); 159 }); 160 } 161 162 void Manager::processAssetTagChangeCallback(sdbusplus::message_t& i_msg) 163 { 164 try 165 { 166 if (i_msg.is_method_error()) 167 { 168 throw std::runtime_error( 169 "Error reading callback msg for asset tag."); 170 } 171 172 std::string l_objectPath; 173 types::PropertyMap l_propMap; 174 i_msg.read(l_objectPath, l_propMap); 175 176 const auto& l_itrToAssetTag = l_propMap.find("AssetTag"); 177 if (l_itrToAssetTag != l_propMap.end()) 178 { 179 if (auto l_assetTag = 180 std::get_if<std::string>(&(l_itrToAssetTag->second))) 181 { 182 // Call Notify to persist the AssetTag 183 types::ObjectMap l_objectMap = { 184 {sdbusplus::message::object_path(constants::systemInvPath), 185 {{constants::assetTagInf, {{"AssetTag", *l_assetTag}}}}}}; 186 187 // Notify PIM 188 if (!dbusUtility::callPIM(move(l_objectMap))) 189 { 190 throw std::runtime_error( 191 "Call to PIM failed for asset tag update."); 192 } 193 } 194 } 195 else 196 { 197 throw std::runtime_error( 198 "Could not find asset tag in callback message."); 199 } 200 } 201 catch (const std::exception& l_ex) 202 { 203 // TODO: Log PEL with below description. 204 logging::logMessage("Asset tag callback update failed with error: " + 205 std::string(l_ex.what())); 206 } 207 } 208 209 void Manager::SetTimerToDetectSVPDOnDbus() 210 { 211 static boost::asio::steady_timer timer(*m_ioContext); 212 213 // timer for 2 seconds 214 auto asyncCancelled = timer.expires_after(std::chrono::seconds(2)); 215 216 (asyncCancelled == 0) ? logging::logMessage("Timer started") 217 : logging::logMessage("Timer re-started"); 218 219 timer.async_wait([this](const boost::system::error_code& ec) { 220 if (ec == boost::asio::error::operation_aborted) 221 { 222 throw std::runtime_error( 223 "Timer to detect system VPD collection status was aborted"); 224 } 225 226 if (ec) 227 { 228 throw std::runtime_error( 229 "Timer to detect System VPD collection failed"); 230 } 231 232 if (m_worker->isSystemVPDOnDBus()) 233 { 234 // cancel the timer 235 timer.cancel(); 236 237 // Triggering FRU VPD collection. Setting status to "In 238 // Progress". 239 m_interface->set_property("CollectionStatus", 240 std::string("InProgress")); 241 m_worker->collectFrusFromJson(); 242 } 243 }); 244 } 245 246 void Manager::SetTimerToDetectVpdCollectionStatus() 247 { 248 // Keeping max retry for 2 minutes. TODO: Make it cinfigurable based on 249 // system type. 250 static constexpr auto MAX_RETRY = 40; 251 252 static boost::asio::steady_timer l_timer(*m_ioContext); 253 static uint8_t l_timerRetry = 0; 254 255 auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(3)); 256 257 (l_asyncCancelled == 0) 258 ? logging::logMessage("Collection Timer started") 259 : logging::logMessage("Collection Timer re-started"); 260 261 l_timer.async_wait([this](const boost::system::error_code& ec) { 262 if (ec == boost::asio::error::operation_aborted) 263 { 264 throw std::runtime_error( 265 "Timer to detect thread collection status was aborted"); 266 } 267 268 if (ec) 269 { 270 throw std::runtime_error( 271 "Timer to detect thread collection failed"); 272 } 273 274 if (m_worker->isAllFruCollectionDone()) 275 { 276 // cancel the timer 277 l_timer.cancel(); 278 m_interface->set_property("CollectionStatus", 279 std::string("Completed")); 280 281 const nlohmann::json& l_sysCfgJsonObj = 282 m_worker->getSysCfgJsonObj(); 283 if (jsonUtility::isBackupAndRestoreRequired(l_sysCfgJsonObj)) 284 { 285 BackupAndRestore l_backupAndRestoreObj(l_sysCfgJsonObj); 286 l_backupAndRestoreObj.backupAndRestore(); 287 } 288 } 289 else 290 { 291 auto l_threadCount = m_worker->getActiveThreadCount(); 292 if (l_timerRetry == MAX_RETRY) 293 { 294 l_timer.cancel(); 295 logging::logMessage("Taking too long. Active thread = " + 296 std::to_string(l_threadCount)); 297 } 298 else 299 { 300 l_timerRetry++; 301 logging::logMessage("Waiting... active thread = " + 302 std::to_string(l_threadCount) + "After " + 303 std::to_string(l_timerRetry) + " re-tries"); 304 305 SetTimerToDetectVpdCollectionStatus(); 306 } 307 } 308 }); 309 } 310 #endif 311 312 int Manager::updateKeyword(const types::Path i_vpdPath, 313 const types::WriteVpdParams i_paramsToWriteData) 314 { 315 if (i_vpdPath.empty()) 316 { 317 logging::logMessage("Given VPD path is empty."); 318 return -1; 319 } 320 321 types::Path l_fruPath; 322 nlohmann::json l_sysCfgJsonObj{}; 323 324 if (m_worker.get() != nullptr) 325 { 326 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj(); 327 328 // Get the EEPROM path 329 if (!l_sysCfgJsonObj.empty()) 330 { 331 try 332 { 333 l_fruPath = 334 jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_vpdPath); 335 } 336 catch (const std::exception& l_exception) 337 { 338 logging::logMessage( 339 "Error while getting FRU path, Path: " + i_vpdPath + 340 ", error: " + std::string(l_exception.what())); 341 return -1; 342 } 343 } 344 } 345 346 if (l_fruPath.empty()) 347 { 348 l_fruPath = i_vpdPath; 349 } 350 351 try 352 { 353 std::shared_ptr<Parser> l_parserObj = 354 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj); 355 return l_parserObj->updateVpdKeyword(i_paramsToWriteData); 356 } 357 catch (const std::exception& l_exception) 358 { 359 // TODO:: error log needed 360 logging::logMessage("Update keyword failed for file[" + i_vpdPath + 361 "], reason: " + std::string(l_exception.what())); 362 return -1; 363 } 364 } 365 366 int Manager::updateKeywordOnHardware( 367 const types::Path i_fruPath, 368 const types::WriteVpdParams i_paramsToWriteData) noexcept 369 { 370 try 371 { 372 if (i_fruPath.empty()) 373 { 374 throw std::runtime_error("Given FRU path is empty"); 375 } 376 377 nlohmann::json l_sysCfgJsonObj{}; 378 379 if (m_worker.get() != nullptr) 380 { 381 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj(); 382 } 383 384 std::shared_ptr<Parser> l_parserObj = 385 std::make_shared<Parser>(i_fruPath, l_sysCfgJsonObj); 386 return l_parserObj->updateVpdKeywordOnHardware(i_paramsToWriteData); 387 } 388 catch (const std::exception& l_exception) 389 { 390 EventLogger::createAsyncPel( 391 types::ErrorType::InvalidEeprom, types::SeverityType::Informational, 392 __FILE__, __FUNCTION__, 0, 393 "Update keyword on hardware failed for file[" + i_fruPath + 394 "], reason: " + std::string(l_exception.what()), 395 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 396 397 return constants::FAILURE; 398 } 399 } 400 401 types::DbusVariantType Manager::readKeyword( 402 const types::Path i_fruPath, const types::ReadVpdParams i_paramsToReadData) 403 { 404 try 405 { 406 nlohmann::json l_jsonObj{}; 407 408 if (m_worker.get() != nullptr) 409 { 410 l_jsonObj = m_worker->getSysCfgJsonObj(); 411 } 412 413 std::error_code ec; 414 415 // Check if given path is filesystem path 416 if (!std::filesystem::exists(i_fruPath, ec) && (ec)) 417 { 418 throw std::runtime_error( 419 "Given file path " + i_fruPath + " not found."); 420 } 421 422 logging::logMessage("Performing VPD read on " + i_fruPath); 423 424 std::shared_ptr<vpd::Parser> l_parserObj = 425 std::make_shared<vpd::Parser>(i_fruPath, l_jsonObj); 426 427 std::shared_ptr<vpd::ParserInterface> l_vpdParserInstance = 428 l_parserObj->getVpdParserInstance(); 429 430 return ( 431 l_vpdParserInstance->readKeywordFromHardware(i_paramsToReadData)); 432 } 433 catch (const std::exception& e) 434 { 435 logging::logMessage( 436 e.what() + std::string(". VPD manager read operation failed for ") + 437 i_fruPath); 438 throw types::DeviceError::ReadFailure(); 439 } 440 } 441 442 void Manager::collectSingleFruVpd( 443 const sdbusplus::message::object_path& i_dbusObjPath) 444 { 445 try 446 { 447 if (m_vpdCollectionStatus != "Completed") 448 { 449 throw std::runtime_error( 450 "Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " + 451 std::string(i_dbusObjPath)); 452 } 453 454 // Get system config JSON object from worker class 455 nlohmann::json l_sysCfgJsonObj{}; 456 457 if (m_worker.get() != nullptr) 458 { 459 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj(); 460 } 461 462 // Check if system config JSON is present 463 if (l_sysCfgJsonObj.empty()) 464 { 465 throw std::runtime_error( 466 "System config JSON object not present. Single FRU VPD collection failed for " + 467 std::string(i_dbusObjPath)); 468 } 469 470 // Get FRU path for the given D-bus object path from JSON 471 const std::string& l_fruPath = 472 jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_dbusObjPath); 473 474 if (l_fruPath.empty()) 475 { 476 throw std::runtime_error( 477 "D-bus object path not present in JSON. Single FRU VPD collection failed for " + 478 std::string(i_dbusObjPath)); 479 } 480 481 // Check if host is up and running 482 if (dbusUtility::isHostRunning()) 483 { 484 if (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj, 485 l_fruPath)) 486 { 487 throw std::runtime_error( 488 "Given FRU is not replaceable at host runtime. Single FRU VPD collection failed for " + 489 std::string(i_dbusObjPath)); 490 } 491 } 492 else if (dbusUtility::isBMCReady()) 493 { 494 if (!jsonUtility::isFruReplaceableAtStandby(l_sysCfgJsonObj, 495 l_fruPath) && 496 (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj, 497 l_fruPath))) 498 { 499 throw std::runtime_error( 500 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection failed for " + 501 std::string(i_dbusObjPath)); 502 } 503 } 504 505 // Parse VPD 506 types::VPDMapVariant l_parsedVpd = m_worker->parseVpdFile(l_fruPath); 507 508 // If l_parsedVpd is pointing to std::monostate 509 if (l_parsedVpd.index() == 0) 510 { 511 throw std::runtime_error( 512 "VPD parsing failed for " + std::string(i_dbusObjPath)); 513 } 514 515 // Get D-bus object map from worker class 516 types::ObjectMap l_dbusObjectMap; 517 m_worker->populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath); 518 519 if (l_dbusObjectMap.empty()) 520 { 521 throw std::runtime_error( 522 "Failed to create D-bus object map. Single FRU VPD collection failed for " + 523 std::string(i_dbusObjPath)); 524 } 525 526 // Call PIM's Notify method 527 if (!dbusUtility::callPIM(move(l_dbusObjectMap))) 528 { 529 throw std::runtime_error( 530 "Notify PIM failed. Single FRU VPD collection failed for " + 531 std::string(i_dbusObjPath)); 532 } 533 } 534 catch (const std::exception& l_error) 535 { 536 // TODO: Log PEL 537 logging::logMessage(std::string(l_error.what())); 538 } 539 } 540 541 void Manager::deleteSingleFruVpd( 542 const sdbusplus::message::object_path& i_dbusObjPath) 543 { 544 try 545 { 546 if (std::string(i_dbusObjPath).empty()) 547 { 548 throw std::runtime_error( 549 "Given DBus object path is empty. Aborting FRU VPD deletion."); 550 } 551 552 if (m_worker.get() == nullptr) 553 { 554 throw std::runtime_error( 555 "Worker object not found, can't perform FRU VPD deletion for: " + 556 std::string(i_dbusObjPath)); 557 } 558 559 m_worker->deleteFruVpd(std::string(i_dbusObjPath)); 560 } 561 catch (const std::exception& l_ex) 562 { 563 // TODO: Log PEL 564 logging::logMessage(l_ex.what()); 565 } 566 } 567 568 bool Manager::isValidUnexpandedLocationCode( 569 const std::string& i_unexpandedLocationCode) 570 { 571 if ((i_unexpandedLocationCode.length() < 572 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) || 573 ((i_unexpandedLocationCode.compare(0, 4, "Ufcs") != 574 constants::STR_CMP_SUCCESS) && 575 (i_unexpandedLocationCode.compare(0, 4, "Umts") != 576 constants::STR_CMP_SUCCESS)) || 577 ((i_unexpandedLocationCode.length() > 578 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) && 579 (i_unexpandedLocationCode.find("-") != 4))) 580 { 581 return false; 582 } 583 584 return true; 585 } 586 587 std::string Manager::getExpandedLocationCode( 588 const std::string& i_unexpandedLocationCode, 589 [[maybe_unused]] const uint16_t i_nodeNumber) 590 { 591 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode)) 592 { 593 phosphor::logging::elog<types::DbusInvalidArgument>( 594 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 595 types::InvalidArgument::ARGUMENT_VALUE( 596 i_unexpandedLocationCode.c_str())); 597 } 598 599 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj(); 600 if (!l_sysCfgJsonObj.contains("frus")) 601 { 602 logging::logMessage("Missing frus tag in system config JSON"); 603 } 604 605 const nlohmann::json& l_listOfFrus = 606 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>(); 607 608 for (const auto& l_frus : l_listOfFrus.items()) 609 { 610 for (const auto& l_aFru : l_frus.value()) 611 { 612 if (l_aFru["extraInterfaces"].contains( 613 constants::locationCodeInf) && 614 l_aFru["extraInterfaces"][constants::locationCodeInf].value( 615 "LocationCode", "") == i_unexpandedLocationCode) 616 { 617 return std::get<std::string>(dbusUtility::readDbusProperty( 618 l_aFru["serviceName"], l_aFru["inventoryPath"], 619 constants::locationCodeInf, "LocationCode")); 620 } 621 } 622 } 623 phosphor::logging::elog<types::DbusInvalidArgument>( 624 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 625 types::InvalidArgument::ARGUMENT_VALUE( 626 i_unexpandedLocationCode.c_str())); 627 } 628 629 types::ListOfPaths Manager::getFrusByUnexpandedLocationCode( 630 const std::string& i_unexpandedLocationCode, 631 [[maybe_unused]] const uint16_t i_nodeNumber) 632 { 633 types::ListOfPaths l_inventoryPaths; 634 635 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode)) 636 { 637 phosphor::logging::elog<types::DbusInvalidArgument>( 638 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 639 types::InvalidArgument::ARGUMENT_VALUE( 640 i_unexpandedLocationCode.c_str())); 641 } 642 643 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj(); 644 if (!l_sysCfgJsonObj.contains("frus")) 645 { 646 logging::logMessage("Missing frus tag in system config JSON"); 647 } 648 649 const nlohmann::json& l_listOfFrus = 650 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>(); 651 652 for (const auto& l_frus : l_listOfFrus.items()) 653 { 654 for (const auto& l_aFru : l_frus.value()) 655 { 656 if (l_aFru["extraInterfaces"].contains( 657 constants::locationCodeInf) && 658 l_aFru["extraInterfaces"][constants::locationCodeInf].value( 659 "LocationCode", "") == i_unexpandedLocationCode) 660 { 661 l_inventoryPaths.push_back( 662 l_aFru.at("inventoryPath") 663 .get_ref<const nlohmann::json::string_t&>()); 664 } 665 } 666 } 667 668 if (l_inventoryPaths.empty()) 669 { 670 phosphor::logging::elog<types::DbusInvalidArgument>( 671 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 672 types::InvalidArgument::ARGUMENT_VALUE( 673 i_unexpandedLocationCode.c_str())); 674 } 675 676 return l_inventoryPaths; 677 } 678 679 std::string 680 Manager::getHwPath(const sdbusplus::message::object_path& i_dbusObjPath) 681 { 682 // Dummy code to supress unused variable warning. To be removed. 683 logging::logMessage(std::string(i_dbusObjPath)); 684 685 return std::string{}; 686 } 687 688 std::tuple<std::string, uint16_t> Manager::getUnexpandedLocationCode( 689 const std::string& i_expandedLocationCode) 690 { 691 /** 692 * Location code should always start with U and fulfil minimum length 693 * criteria. 694 */ 695 if (i_expandedLocationCode[0] != 'U' || 696 i_expandedLocationCode.length() < 697 constants::EXP_LOCATION_CODE_MIN_LENGTH) 698 { 699 phosphor::logging::elog<types::DbusInvalidArgument>( 700 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 701 types::InvalidArgument::ARGUMENT_VALUE( 702 i_expandedLocationCode.c_str())); 703 } 704 705 std::string l_fcKwd; 706 707 auto l_fcKwdValue = dbusUtility::readDbusProperty( 708 "xyz.openbmc_project.Inventory.Manager", 709 "/xyz/openbmc_project/inventory/system/chassis/motherboard", 710 "com.ibm.ipzvpd.VCEN", "FC"); 711 712 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_fcKwdValue)) 713 { 714 l_fcKwd.assign(l_kwdValue->begin(), l_kwdValue->end()); 715 } 716 717 // Get the first part of expanded location code to check for FC or TM. 718 std::string l_firstKwd = i_expandedLocationCode.substr(1, 4); 719 720 std::string l_unexpandedLocationCode{}; 721 uint16_t l_nodeNummber = constants::INVALID_NODE_NUMBER; 722 723 // Check if this value matches the value of FC keyword. 724 if (l_fcKwd.substr(0, 4) == l_firstKwd) 725 { 726 /** 727 * Period(.) should be there in expanded location code to seggregate 728 * FC, node number and SE values. 729 */ 730 size_t l_nodeStartPos = i_expandedLocationCode.find('.'); 731 if (l_nodeStartPos == std::string::npos) 732 { 733 phosphor::logging::elog<types::DbusInvalidArgument>( 734 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 735 types::InvalidArgument::ARGUMENT_VALUE( 736 i_expandedLocationCode.c_str())); 737 } 738 739 size_t l_nodeEndPos = 740 i_expandedLocationCode.find('.', l_nodeStartPos + 1); 741 if (l_nodeEndPos == std::string::npos) 742 { 743 phosphor::logging::elog<types::DbusInvalidArgument>( 744 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 745 types::InvalidArgument::ARGUMENT_VALUE( 746 i_expandedLocationCode.c_str())); 747 } 748 749 // Skip 3 bytes for '.ND' 750 l_nodeNummber = std::stoi(i_expandedLocationCode.substr( 751 l_nodeStartPos + 3, (l_nodeEndPos - l_nodeStartPos - 3))); 752 753 /** 754 * Confirm if there are other details apart FC, node number and SE 755 * in location code 756 */ 757 if (i_expandedLocationCode.length() > 758 constants::EXP_LOCATION_CODE_MIN_LENGTH) 759 { 760 l_unexpandedLocationCode = 761 i_expandedLocationCode[0] + std::string("fcs") + 762 i_expandedLocationCode.substr( 763 l_nodeEndPos + 1 + constants::SE_KWD_LENGTH, 764 std::string::npos); 765 } 766 else 767 { 768 l_unexpandedLocationCode = "Ufcs"; 769 } 770 } 771 else 772 { 773 std::string l_tmKwd; 774 // Read TM keyword value. 775 auto l_tmKwdValue = dbusUtility::readDbusProperty( 776 "xyz.openbmc_project.Inventory.Manager", 777 "/xyz/openbmc_project/inventory/system/chassis/motherboard", 778 "com.ibm.ipzvpd.VSYS", "TM"); 779 780 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_tmKwdValue)) 781 { 782 l_tmKwd.assign(l_kwdValue->begin(), l_kwdValue->end()); 783 } 784 785 // Check if the substr matches to TM keyword value. 786 if (l_tmKwd.substr(0, 4) == l_firstKwd) 787 { 788 /** 789 * System location code will not have node number and any other 790 * details. 791 */ 792 l_unexpandedLocationCode = "Umts"; 793 } 794 // The given location code is neither "fcs" or "mts". 795 else 796 { 797 phosphor::logging::elog<types::DbusInvalidArgument>( 798 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 799 types::InvalidArgument::ARGUMENT_VALUE( 800 i_expandedLocationCode.c_str())); 801 } 802 } 803 804 return std::make_tuple(l_unexpandedLocationCode, l_nodeNummber); 805 } 806 807 types::ListOfPaths Manager::getFrusByExpandedLocationCode( 808 const std::string& i_expandedLocationCode) 809 { 810 std::tuple<std::string, uint16_t> l_locationAndNodePair = 811 getUnexpandedLocationCode(i_expandedLocationCode); 812 813 return getFrusByUnexpandedLocationCode(std::get<0>(l_locationAndNodePair), 814 std::get<1>(l_locationAndNodePair)); 815 } 816 817 void Manager::registerHostStateChangeCallback() 818 { 819 static std::shared_ptr<sdbusplus::bus::match_t> l_hostState = 820 std::make_shared<sdbusplus::bus::match_t>( 821 *m_asioConnection, 822 sdbusplus::bus::match::rules::propertiesChanged( 823 constants::hostObjectPath, constants::hostInterface), 824 [this](sdbusplus::message_t& i_msg) { 825 hostStateChangeCallBack(i_msg); 826 }); 827 } 828 829 void Manager::hostStateChangeCallBack(sdbusplus::message_t& i_msg) 830 { 831 try 832 { 833 if (i_msg.is_method_error()) 834 { 835 throw std::runtime_error( 836 "Error reading callback message for host state"); 837 } 838 839 std::string l_objectPath; 840 types::PropertyMap l_propMap; 841 i_msg.read(l_objectPath, l_propMap); 842 843 const auto l_itr = l_propMap.find("CurrentHostState"); 844 845 if (l_itr == l_propMap.end()) 846 { 847 throw std::runtime_error( 848 "CurrentHostState field is missing in callback message"); 849 } 850 851 if (auto l_hostState = std::get_if<std::string>(&(l_itr->second))) 852 { 853 // implies system is moving from standby to power on state 854 if (*l_hostState == "xyz.openbmc_project.State.Host.HostState." 855 "TransitioningToRunning") 856 { 857 // TODO: check for all the essential FRUs in the system. 858 859 // Perform recollection. 860 performVpdRecollection(); 861 return; 862 } 863 } 864 else 865 { 866 throw std::runtime_error( 867 "Invalid type recieved in variant for host state."); 868 } 869 } 870 catch (const std::exception& l_ex) 871 { 872 // TODO: Log PEL. 873 logging::logMessage(l_ex.what()); 874 } 875 } 876 877 void Manager::performVpdRecollection() 878 { 879 try 880 { 881 if (m_worker.get() != nullptr) 882 { 883 nlohmann::json l_sysCfgJsonObj = m_worker->getSysCfgJsonObj(); 884 885 // Check if system config JSON is present 886 if (l_sysCfgJsonObj.empty()) 887 { 888 throw std::runtime_error( 889 "System config json object is empty, can't process recollection."); 890 } 891 892 const auto& l_frusReplaceableAtStandby = 893 jsonUtility::getListOfFrusReplaceableAtStandby(l_sysCfgJsonObj); 894 895 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby) 896 { 897 // ToDo: Add some logic/trace to know the flow to 898 // collectSingleFruVpd has been directed via 899 // performVpdRecollection. 900 collectSingleFruVpd(l_fruInventoryPath); 901 } 902 return; 903 } 904 905 throw std::runtime_error( 906 "Worker object not found can't process recollection"); 907 } 908 catch (const std::exception& l_ex) 909 { 910 // TODO Log PEL 911 logging::logMessage( 912 "VPD recollection failed with error: " + std::string(l_ex.what())); 913 } 914 } 915 } // namespace vpd 916