1 #include "config.h" 2 3 #include "manager.hpp" 4 5 #include "common_utility.hpp" 6 #include "editor_impl.hpp" 7 #include "ibm_vpd_utils.hpp" 8 #include "ipz_parser.hpp" 9 #include "parser_factory.hpp" 10 #include "reader_impl.hpp" 11 #include "vpd_exceptions.hpp" 12 13 #include <filesystem> 14 #include <phosphor-logging/elog-errors.hpp> 15 #include <xyz/openbmc_project/Common/error.hpp> 16 17 using namespace openpower::vpd::constants; 18 using namespace openpower::vpd::inventory; 19 using namespace openpower::vpd::manager::editor; 20 using namespace openpower::vpd::manager::reader; 21 using namespace std; 22 using namespace openpower::vpd::parser; 23 using namespace openpower::vpd::parser::factory; 24 using namespace openpower::vpd::ipz::parser; 25 using namespace openpower::vpd::exceptions; 26 using namespace phosphor::logging; 27 28 namespace openpower 29 { 30 namespace vpd 31 { 32 namespace manager 33 { 34 Manager::Manager(std::shared_ptr<boost::asio::io_context>& ioCon, 35 std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace, 36 std::shared_ptr<sdbusplus::asio::connection>& conn) : 37 ioContext(ioCon), 38 interface(iFace), conn(conn) 39 { 40 interface->register_method( 41 "WriteKeyword", 42 [this](const sdbusplus::message::object_path& path, 43 const std::string& recordName, const std::string& keyword, 44 const Binary& value) { 45 this->writeKeyword(path, recordName, keyword, value); 46 }); 47 48 interface->register_method( 49 "GetFRUsByUnexpandedLocationCode", 50 [this](const std::string& locationCode, 51 const uint16_t nodeNumber) -> inventory::ListOfPaths { 52 return this->getFRUsByUnexpandedLocationCode(locationCode, 53 nodeNumber); 54 }); 55 56 interface->register_method( 57 "GetFRUsByExpandedLocationCode", 58 [this](const std::string& locationCode) -> inventory::ListOfPaths { 59 return this->getFRUsByExpandedLocationCode(locationCode); 60 }); 61 62 interface->register_method( 63 "GetExpandedLocationCode", 64 [this](const std::string& locationCode, 65 const uint16_t nodeNumber) -> std::string { 66 return this->getExpandedLocationCode(locationCode, nodeNumber); 67 }); 68 69 interface->register_method("PerformVPDRecollection", 70 [this]() { this->performVPDRecollection(); }); 71 72 interface->register_method( 73 "deleteFRUVPD", [this](const sdbusplus::message::object_path& path) { 74 this->deleteFRUVPD(path); 75 }); 76 77 interface->register_method( 78 "CollectFRUVPD", [this](const sdbusplus::message::object_path& path) { 79 this->collectFRUVPD(path); 80 }); 81 82 sd_bus_default(&sdBus); 83 initManager(); 84 } 85 86 void Manager::initManager() 87 { 88 try 89 { 90 processJSON(); 91 restoreSystemVpd(); 92 listenHostState(); 93 listenAssetTag(); 94 95 // Create an instance of the BIOS handler 96 biosHandler = std::make_shared<BiosHandler>(conn, *this); 97 98 // instantiate gpioMonitor class 99 gpioMon = std::make_shared<GpioMonitor>(jsonFile, ioContext); 100 } 101 catch (const std::exception& e) 102 { 103 std::cerr << e.what() << "\n"; 104 } 105 } 106 107 /** 108 * @brief An api to get list of blank system VPD properties. 109 * @param[in] vpdMap - IPZ vpd map. 110 * @param[in] objectPath - Object path for the FRU. 111 * @param[out] blankPropertyList - Properties which are blank in System VPD and 112 * needs to be updated as standby. 113 */ 114 static void 115 getListOfBlankSystemVpd(Parsed& vpdMap, const string& objectPath, 116 std::vector<RestoredEeproms>& blankPropertyList) 117 { 118 for (const auto& systemRecKwdPair : svpdKwdMap) 119 { 120 auto it = vpdMap.find(systemRecKwdPair.first); 121 122 // check if record is found in map we got by parser 123 if (it != vpdMap.end()) 124 { 125 const auto& kwdListForRecord = systemRecKwdPair.second; 126 for (const auto& keywordInfo : kwdListForRecord) 127 { 128 const auto& keyword = get<0>(keywordInfo); 129 130 DbusPropertyMap& kwdValMap = it->second; 131 auto iterator = kwdValMap.find(keyword); 132 133 if (iterator != kwdValMap.end()) 134 { 135 string& kwdValue = iterator->second; 136 137 // check bus data 138 const string& recordName = systemRecKwdPair.first; 139 const string& busValue = readBusProperty( 140 objectPath, ipzVpdInf + recordName, keyword); 141 142 const auto& defaultValue = get<1>(keywordInfo); 143 144 if (Binary(busValue.begin(), busValue.end()) != 145 defaultValue) 146 { 147 if (Binary(kwdValue.begin(), kwdValue.end()) == 148 defaultValue) 149 { 150 // implies data is blank on EEPROM but not on cache. 151 // So EEPROM vpd update is required. 152 Binary busData(busValue.begin(), busValue.end()); 153 154 blankPropertyList.push_back(std::make_tuple( 155 objectPath, recordName, keyword, busData)); 156 } 157 } 158 } 159 } 160 } 161 } 162 } 163 164 void Manager::restoreSystemVpd() 165 { 166 std::cout << "Attempting system VPD restore" << std::endl; 167 ParserInterface* parser = nullptr; 168 try 169 { 170 auto vpdVector = getVpdDataInVector(jsonFile, systemVpdFilePath); 171 uint32_t vpdStartOffset = 0; 172 const auto& inventoryPath = 173 jsonFile["frus"][systemVpdFilePath][0]["inventoryPath"] 174 .get_ref<const nlohmann::json::string_t&>(); 175 176 parser = ParserFactory::getParser(vpdVector, (pimPath + inventoryPath), 177 systemVpdFilePath, vpdStartOffset); 178 auto parseResult = parser->parse(); 179 180 if (auto pVal = std::get_if<Store>(&parseResult)) 181 { 182 // map to hold all the keywords whose value is blank and 183 // needs to be updated at standby. 184 std::vector<RestoredEeproms> blankSystemVpdProperties{}; 185 getListOfBlankSystemVpd(pVal->getVpdMap(), SYSTEM_OBJECT, 186 blankSystemVpdProperties); 187 188 // if system VPD restore is required, update the 189 // EEPROM 190 for (const auto& item : blankSystemVpdProperties) 191 { 192 std::cout << "Restoring keyword: " << std::get<2>(item) 193 << std::endl; 194 writeKeyword(std::get<0>(item), std::get<1>(item), 195 std::get<2>(item), std::get<3>(item)); 196 } 197 } 198 else 199 { 200 std::cerr << "Not a valid format to restore system VPD" 201 << std::endl; 202 } 203 } 204 catch (const std::exception& e) 205 { 206 std::cerr << "Failed to restore system VPD due to exception: " 207 << e.what() << std::endl; 208 } 209 // release the parser object 210 ParserFactory::freeParser(parser); 211 } 212 213 void Manager::listenHostState() 214 { 215 static std::shared_ptr<sdbusplus::bus::match_t> hostState = 216 std::make_shared<sdbusplus::bus::match_t>( 217 *conn, 218 sdbusplus::bus::match::rules::propertiesChanged( 219 "/xyz/openbmc_project/state/host0", 220 "xyz.openbmc_project.State.Host"), 221 [this](sdbusplus::message_t& msg) { hostStateCallBack(msg); }); 222 } 223 224 void Manager::checkEssentialFrus() 225 { 226 for (const auto& invPath : essentialFrus) 227 { 228 const auto res = readBusProperty(invPath, invItemIntf, "Present"); 229 230 // implies the essential FRU is missing. Log PEL. 231 if (res == "false") 232 { 233 auto rc = sd_bus_call_method_async( 234 sdBus, NULL, loggerService, loggerObjectPath, 235 loggerCreateInterface, "Create", NULL, NULL, "ssa{ss}", 236 errIntfForEssentialFru, 237 "xyz.openbmc_project.Logging.Entry.Level.Warning", 2, 238 "DESCRIPTION", "Essential fru missing from the system.", 239 "CALLOUT_INVENTORY_PATH", (pimPath + invPath).c_str()); 240 241 if (rc < 0) 242 { 243 log<level::ERR>("Error calling sd_bus_call_method_async", 244 entry("RC=%d", rc), 245 entry("MSG=%s", strerror(-rc))); 246 } 247 } 248 } 249 } 250 251 void Manager::hostStateCallBack(sdbusplus::message_t& msg) 252 { 253 if (msg.is_method_error()) 254 { 255 std::cerr << "Error in reading signal " << std::endl; 256 } 257 258 Path object; 259 PropertyMap propMap; 260 msg.read(object, propMap); 261 const auto itr = propMap.find("CurrentHostState"); 262 if (itr != propMap.end()) 263 { 264 if (auto hostState = std::get_if<std::string>(&(itr->second))) 265 { 266 // implies system is moving from standby to power on state 267 if (*hostState == "xyz.openbmc_project.State.Host.HostState." 268 "TransitioningToRunning") 269 { 270 // detect if essential frus are present in the system. 271 checkEssentialFrus(); 272 273 // check and perfrom recollection for FRUs replaceable at 274 // standby. 275 performVPDRecollection(); 276 return; 277 } 278 } 279 } 280 } 281 282 void Manager::listenAssetTag() 283 { 284 static std::shared_ptr<sdbusplus::bus::match_t> assetMatcher = 285 std::make_shared<sdbusplus::bus::match_t>( 286 *conn, 287 sdbusplus::bus::match::rules::propertiesChanged( 288 "/xyz/openbmc_project/inventory/system", 289 "xyz.openbmc_project.Inventory.Decorator.AssetTag"), 290 [this](sdbusplus::message_t& msg) { assetTagCallback(msg); }); 291 } 292 293 void Manager::assetTagCallback(sdbusplus::message_t& msg) 294 { 295 if (msg.is_method_error()) 296 { 297 std::cerr << "Error in reading signal " << std::endl; 298 } 299 300 Path object; 301 PropertyMap propMap; 302 msg.read(object, propMap); 303 const auto itr = propMap.find("AssetTag"); 304 if (itr != propMap.end()) 305 { 306 if (auto assetTag = std::get_if<std::string>(&(itr->second))) 307 { 308 // Call Notify to persist the AssetTag 309 inventory::ObjectMap objectMap = { 310 {std::string{"/system"}, 311 {{"xyz.openbmc_project.Inventory.Decorator.AssetTag", 312 {{"AssetTag", *assetTag}}}}}}; 313 314 common::utility::callPIM(std::move(objectMap)); 315 } 316 else 317 { 318 std::cerr << "Failed to read asset tag" << std::endl; 319 } 320 } 321 } 322 323 void Manager::processJSON() 324 { 325 std::ifstream json(INVENTORY_JSON_SYM_LINK, std::ios::binary); 326 327 if (!json) 328 { 329 throw std::runtime_error("json file not found"); 330 } 331 332 jsonFile = nlohmann::json::parse(json); 333 if (jsonFile.find("frus") == jsonFile.end()) 334 { 335 throw std::runtime_error("frus group not found in json"); 336 } 337 338 const nlohmann::json& groupFRUS = 339 jsonFile["frus"].get_ref<const nlohmann::json::object_t&>(); 340 for (const auto& itemFRUS : groupFRUS.items()) 341 { 342 const std::vector<nlohmann::json>& groupEEPROM = 343 itemFRUS.value().get_ref<const nlohmann::json::array_t&>(); 344 for (const auto& itemEEPROM : groupEEPROM) 345 { 346 bool isMotherboard = false; 347 std::string redundantPath; 348 349 if (itemEEPROM["extraInterfaces"].find( 350 "xyz.openbmc_project.Inventory.Item.Board.Motherboard") != 351 itemEEPROM["extraInterfaces"].end()) 352 { 353 isMotherboard = true; 354 } 355 if (itemEEPROM.find("redundantEeprom") != itemEEPROM.end()) 356 { 357 redundantPath = itemEEPROM["redundantEeprom"] 358 .get_ref<const nlohmann::json::string_t&>(); 359 } 360 frus.emplace( 361 itemEEPROM["inventoryPath"] 362 .get_ref<const nlohmann::json::string_t&>(), 363 std::make_tuple(itemFRUS.key(), redundantPath, isMotherboard)); 364 365 if (itemEEPROM["extraInterfaces"].find(IBM_LOCATION_CODE_INF) != 366 itemEEPROM["extraInterfaces"].end()) 367 { 368 fruLocationCode.emplace( 369 itemEEPROM["extraInterfaces"][IBM_LOCATION_CODE_INF] 370 ["LocationCode"] 371 .get_ref<const nlohmann::json::string_t&>(), 372 itemEEPROM["inventoryPath"] 373 .get_ref<const nlohmann::json::string_t&>()); 374 } 375 376 if (itemEEPROM.value("replaceableAtStandby", false)) 377 { 378 replaceableFrus.emplace_back(itemFRUS.key()); 379 } 380 381 if (itemEEPROM.value("essentialFru", false)) 382 { 383 essentialFrus.emplace_back(itemEEPROM["inventoryPath"]); 384 } 385 } 386 } 387 } 388 389 void Manager::writeKeyword(const sdbusplus::message::object_path& path, 390 const std::string& recordName, 391 const std::string& keyword, const Binary& value) 392 { 393 try 394 { 395 std::string objPath{path}; 396 // Strip any inventory prefix in path 397 if (objPath.find(INVENTORY_PATH) == 0) 398 { 399 objPath = objPath.substr(sizeof(INVENTORY_PATH) - 1); 400 } 401 402 if (frus.find(objPath) == frus.end()) 403 { 404 throw std::runtime_error("Inventory path not found"); 405 } 406 407 inventory::Path vpdFilePath = std::get<0>(frus.find(objPath)->second); 408 409 // instantiate editor class to update the data 410 EditorImpl edit(vpdFilePath, jsonFile, recordName, keyword, objPath); 411 412 uint32_t offset = 0; 413 // Setup offset, if any 414 for (const auto& item : jsonFile["frus"][vpdFilePath]) 415 { 416 if (item.find("offset") != item.end()) 417 { 418 offset = item["offset"]; 419 break; 420 } 421 } 422 423 edit.updateKeyword(value, offset, true); 424 425 // If we have a redundant EEPROM to update, then update just the EEPROM, 426 // not the cache since that is already done when we updated the primary 427 if (!std::get<1>(frus.find(objPath)->second).empty()) 428 { 429 EditorImpl edit(std::get<1>(frus.find(objPath)->second), jsonFile, 430 recordName, keyword, objPath); 431 edit.updateKeyword(value, offset, false); 432 } 433 434 // if it is a motehrboard FRU need to check for location expansion 435 if (std::get<2>(frus.find(objPath)->second)) 436 { 437 if (recordName == "VCEN" && (keyword == "FC" || keyword == "SE")) 438 { 439 edit.expandLocationCode("fcs"); 440 } 441 else if (recordName == "VSYS" && 442 (keyword == "TM" || keyword == "SE")) 443 { 444 edit.expandLocationCode("mts"); 445 } 446 } 447 448 return; 449 } 450 catch (const std::exception& e) 451 { 452 std::cerr << e.what() << std::endl; 453 } 454 } 455 456 ListOfPaths 457 Manager::getFRUsByUnexpandedLocationCode(const LocationCode& locationCode, 458 const NodeNumber nodeNumber) 459 { 460 ReaderImpl read; 461 return read.getFrusAtLocation(locationCode, nodeNumber, fruLocationCode); 462 } 463 464 ListOfPaths 465 Manager::getFRUsByExpandedLocationCode(const LocationCode& locationCode) 466 { 467 ReaderImpl read; 468 return read.getFRUsByExpandedLocationCode(locationCode, fruLocationCode); 469 } 470 471 LocationCode Manager::getExpandedLocationCode(const LocationCode& locationCode, 472 const NodeNumber nodeNumber) 473 { 474 ReaderImpl read; 475 return read.getExpandedLocationCode(locationCode, nodeNumber, 476 fruLocationCode); 477 } 478 479 void Manager::performVPDRecollection() 480 { 481 // get list of FRUs replaceable at standby 482 for (const auto& item : replaceableFrus) 483 { 484 const vector<nlohmann::json>& groupEEPROM = jsonFile["frus"][item]; 485 const nlohmann::json& singleFru = groupEEPROM[0]; 486 487 const string& inventoryPath = 488 singleFru["inventoryPath"] 489 .get_ref<const nlohmann::json::string_t&>(); 490 491 bool prePostActionRequired = false; 492 493 if ((jsonFile["frus"][item].at(0)).find("preAction") != 494 jsonFile["frus"][item].at(0).end()) 495 { 496 try 497 { 498 if (!executePreAction(jsonFile, item)) 499 { 500 // if the FRU has preAction defined then its execution 501 // should pass to ensure bind/unbind of data. 502 // preAction execution failed. should not call 503 // bind/unbind. 504 log<level::ERR>( 505 "Pre-Action execution failed for the FRU", 506 entry("ERROR=%s", 507 ("Inventory path: " + inventoryPath).c_str())); 508 continue; 509 } 510 } 511 catch (const GpioException& e) 512 { 513 log<level::ERR>(e.what()); 514 PelAdditionalData additionalData{}; 515 additionalData.emplace("DESCRIPTION", e.what()); 516 createPEL(additionalData, PelSeverity::WARNING, 517 errIntfForGpioError, sdBus); 518 continue; 519 } 520 prePostActionRequired = true; 521 } 522 523 // unbind, bind the driver to trigger parser. 524 triggerVpdCollection(singleFru, inventoryPath); 525 526 // this check is added to avoid file system expensive call in case not 527 // required. 528 if (prePostActionRequired) 529 { 530 // Check if device showed up (test for file) 531 if (!filesystem::exists(item)) 532 { 533 try 534 { 535 // If not, then take failure postAction 536 executePostFailAction(jsonFile, item); 537 } 538 catch (const GpioException& e) 539 { 540 PelAdditionalData additionalData{}; 541 additionalData.emplace("DESCRIPTION", e.what()); 542 createPEL(additionalData, PelSeverity::WARNING, 543 errIntfForGpioError, sdBus); 544 } 545 } 546 else 547 { 548 // bind the LED driver 549 string chipAddr = singleFru.value("pcaChipAddress", ""); 550 cout << "performVPDRecollection: Executing driver binding for " 551 "chip " 552 "address - " 553 << chipAddr << endl; 554 555 executeCmd(createBindUnbindDriverCmnd(chipAddr, "i2c", 556 "leds-pca955x", "/bind")); 557 } 558 } 559 } 560 } 561 562 void Manager::collectFRUVPD(const sdbusplus::message::object_path& path) 563 { 564 std::cout << "Manager called to collect vpd for fru: " << std::string{path} 565 << std::endl; 566 567 using InvalidArgument = 568 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; 569 using Argument = xyz::openbmc_project::Common::InvalidArgument; 570 571 std::string objPath{path}; 572 573 // Strip any inventory prefix in path 574 if (objPath.find(INVENTORY_PATH) == 0) 575 { 576 objPath = objPath.substr(sizeof(INVENTORY_PATH) - 1); 577 } 578 579 // if path not found in Json. 580 if (frus.find(objPath) == frus.end()) 581 { 582 elog<InvalidArgument>(Argument::ARGUMENT_NAME("Object Path"), 583 Argument::ARGUMENT_VALUE(objPath.c_str())); 584 } 585 586 inventory::Path vpdFilePath = std::get<0>(frus.find(objPath)->second); 587 588 const std::vector<nlohmann::json>& groupEEPROM = 589 jsonFile["frus"][vpdFilePath].get_ref<const nlohmann::json::array_t&>(); 590 591 nlohmann::json singleFru{}; 592 for (const auto& item : groupEEPROM) 593 { 594 if (item["inventoryPath"] == objPath) 595 { 596 // this is the inventory we are looking for 597 singleFru = item; 598 break; 599 } 600 } 601 602 // check if the device qualifies for CM. 603 if (singleFru.value("concurrentlyMaintainable", false)) 604 { 605 bool prePostActionRequired = false; 606 607 if ((jsonFile["frus"][vpdFilePath].at(0)).find("preAction") != 608 jsonFile["frus"][vpdFilePath].at(0).end()) 609 { 610 if (!executePreAction(jsonFile, vpdFilePath)) 611 { 612 // if the FRU has preAction defined then its execution should 613 // pass to ensure bind/unbind of data. 614 // preAction execution failed. should not call bind/unbind. 615 log<level::ERR>("Pre-Action execution failed for the FRU"); 616 return; 617 } 618 619 prePostActionRequired = true; 620 } 621 622 // unbind, bind the driver to trigger parser. 623 triggerVpdCollection(singleFru, objPath); 624 625 // this check is added to avoid file system expensive call in case not 626 // required. 627 if (prePostActionRequired) 628 { 629 // Check if device showed up (test for file) 630 if (!filesystem::exists(vpdFilePath)) 631 { 632 try 633 { 634 // If not, then take failure postAction 635 executePostFailAction(jsonFile, vpdFilePath); 636 } 637 catch (const GpioException& e) 638 { 639 PelAdditionalData additionalData{}; 640 additionalData.emplace("DESCRIPTION", e.what()); 641 createPEL(additionalData, PelSeverity::WARNING, 642 errIntfForGpioError, sdBus); 643 } 644 } 645 else 646 { 647 // bind the LED driver 648 string chipAddr = jsonFile["frus"][vpdFilePath].at(0).value( 649 "pcaChipAddress", ""); 650 cout << "Executing driver binding for chip address - " 651 << chipAddr << endl; 652 653 executeCmd(createBindUnbindDriverCmnd(chipAddr, "i2c", 654 "leds-pca955x", "/bind")); 655 } 656 } 657 return; 658 } 659 else 660 { 661 elog<InvalidArgument>(Argument::ARGUMENT_NAME("Object Path"), 662 Argument::ARGUMENT_VALUE(objPath.c_str())); 663 } 664 } 665 666 void Manager::triggerVpdCollection(const nlohmann::json& singleFru, 667 const std::string& path) 668 { 669 if ((singleFru.find("devAddress") == singleFru.end()) || 670 (singleFru.find("driverType") == singleFru.end()) || 671 (singleFru.find("busType") == singleFru.end())) 672 { 673 // The FRUs is marked for collection but missing mandatory 674 // fields for collection. Log error and return. 675 log<level::ERR>( 676 "Collection Failed as mandatory field missing in Json", 677 entry("ERROR=%s", ("Recollection failed for " + (path)).c_str())); 678 679 return; 680 } 681 682 string deviceAddress = singleFru["devAddress"]; 683 const string& driverType = singleFru["driverType"]; 684 const string& busType = singleFru["busType"]; 685 686 // devTreeStatus flag is present in json as false to mention 687 // that the EEPROM is not mentioned in device tree. If this flag 688 // is absent consider the value to be true, i.e EEPROM is 689 // mentioned in device tree 690 if (!singleFru.value("devTreeStatus", true)) 691 { 692 auto pos = deviceAddress.find('-'); 693 if (pos != string::npos) 694 { 695 string busNum = deviceAddress.substr(0, pos); 696 deviceAddress = "0x" + deviceAddress.substr(pos + 1, string::npos); 697 698 string deleteDevice = "echo" + deviceAddress + " > /sys/bus/" + 699 busType + "/devices/" + busType + "-" + 700 busNum + "/delete_device"; 701 executeCmd(deleteDevice); 702 703 string addDevice = "echo" + driverType + " " + deviceAddress + 704 " > /sys/bus/" + busType + "/devices/" + 705 busType + "-" + busNum + "/new_device"; 706 executeCmd(addDevice); 707 } 708 else 709 { 710 const string& inventoryPath = 711 singleFru["inventoryPath"] 712 .get_ref<const nlohmann::json::string_t&>(); 713 714 log<level::ERR>( 715 "Wrong format of device address in Json", 716 entry("ERROR=%s", 717 ("Recollection failed for " + inventoryPath).c_str())); 718 } 719 } 720 else 721 { 722 executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType, 723 driverType, "/unbind")); 724 executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType, 725 driverType, "/bind")); 726 } 727 } 728 729 void Manager::deleteFRUVPD(const sdbusplus::message::object_path& path) 730 { 731 std::cout << "Manager called to delete vpd for fru: " << std::string{path} 732 << std::endl; 733 734 using InvalidArgument = 735 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; 736 using Argument = xyz::openbmc_project::Common::InvalidArgument; 737 738 std::string objPath{path}; 739 740 // Strip any inventory prefix in path 741 if (objPath.find(INVENTORY_PATH) == 0) 742 { 743 objPath = objPath.substr(sizeof(INVENTORY_PATH) - 1); 744 } 745 746 // if path not found in Json. 747 if (frus.find(objPath) == frus.end()) 748 { 749 elog<InvalidArgument>(Argument::ARGUMENT_NAME("Object Path"), 750 Argument::ARGUMENT_VALUE(objPath.c_str())); 751 } 752 753 inventory::Path& vpdFilePath = std::get<0>(frus.find(objPath)->second); 754 755 string chipAddress = 756 jsonFile["frus"][vpdFilePath].at(0).value("pcaChipAddress", ""); 757 758 // Unbind the LED driver for this FRU 759 cout << "Unbinding device- " << chipAddress << endl; 760 executeCmd(createBindUnbindDriverCmnd(chipAddress, "i2c", "leds-pca955x", 761 "/unbind")); 762 763 // if the FRU is not present then log error 764 if (readBusProperty(objPath, "xyz.openbmc_project.Inventory.Item", 765 "Present") == "false") 766 { 767 elog<InvalidArgument>(Argument::ARGUMENT_NAME("FRU not preset"), 768 Argument::ARGUMENT_VALUE(objPath.c_str())); 769 } 770 else 771 { 772 // Set present property of FRU as false as it has been removed. 773 // CC data for FRU is also removed as 774 // a) FRU is not there so CC does not make sense. 775 // b) Sensors dependent on Panel uses CC data. 776 inventory::InterfaceMap interfaces{ 777 {"xyz.openbmc_project.Inventory.Item", {{"Present", false}}}, 778 {"com.ibm.ipzvpd.VINI", {{"CC", Binary{}}}}}; 779 780 inventory::ObjectMap objectMap; 781 objectMap.emplace(objPath, move(interfaces)); 782 783 common::utility::callPIM(move(objectMap)); 784 } 785 } 786 787 } // namespace manager 788 } // namespace vpd 789 } // namespace openpower