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