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