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