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