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