xref: /openbmc/phosphor-logging/extensions/openpower-pels/data_interface.cpp (revision 40fb54935ce7367636a7156039396ee91cc4d5e2)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2019 IBM Corporation
3 
4 #include "data_interface.hpp"
5 
6 #include "util.hpp"
7 
8 #include <phosphor-logging/lg2.hpp>
9 #include <xyz/openbmc_project/State/BMC/server.hpp>
10 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
11 
12 #include <filesystem>
13 
14 #ifdef PEL_ENABLE_PHAL
15 #include <libekb.H>
16 #include <libpdbg.h>
17 #include <libphal.H>
18 #endif
19 
20 // Use a timeout of 10s for D-Bus calls so if there are
21 // timeouts the callers of the PEL creation method won't
22 // also timeout.
23 constexpr auto dbusTimeout = 10000000;
24 
25 namespace openpower
26 {
27 namespace pels
28 {
29 
30 namespace service_name
31 {
32 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
33 constexpr auto vpdManager = "com.ibm.VPD.Manager";
34 constexpr auto ledGroupManager = "xyz.openbmc_project.LED.GroupManager";
35 constexpr auto biosConfigMgr = "xyz.openbmc_project.BIOSConfigManager";
36 constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw";
37 constexpr auto pldm = "xyz.openbmc_project.PLDM";
38 constexpr auto inventoryManager = "xyz.openbmc_project.Inventory.Manager";
39 constexpr auto entityManager = "xyz.openbmc_project.EntityManager";
40 constexpr auto systemd = "org.freedesktop.systemd1";
41 } // namespace service_name
42 
43 namespace object_path
44 {
45 constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper";
46 constexpr auto systemInv = "/xyz/openbmc_project/inventory/system";
47 constexpr auto motherBoardInv =
48     "/xyz/openbmc_project/inventory/system/chassis/motherboard";
49 constexpr auto baseInv = "/xyz/openbmc_project/inventory";
50 constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0";
51 constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0";
52 constexpr auto hostState = "/xyz/openbmc_project/state/host0";
53 constexpr auto enableHostPELs =
54     "/xyz/openbmc_project/logging/send_event_logs_to_host";
55 constexpr auto vpdManager = "/com/ibm/VPD/Manager";
56 constexpr auto logSetting = "/xyz/openbmc_project/logging/settings";
57 constexpr auto biosConfigMgr = "/xyz/openbmc_project/bios_config/manager";
58 constexpr auto bootRawProgress = "/xyz/openbmc_project/state/boot/raw0";
59 constexpr auto systemd = "/org/freedesktop/systemd1";
60 } // namespace object_path
61 
62 namespace interface
63 {
64 constexpr auto dbusProperty = "org.freedesktop.DBus.Properties";
65 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
66 constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset";
67 constexpr auto bootProgress = "xyz.openbmc_project.State.Boot.Progress";
68 constexpr auto enable = "xyz.openbmc_project.Object.Enable";
69 constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
70 constexpr auto chassisState = "xyz.openbmc_project.State.Chassis";
71 constexpr auto hostState = "xyz.openbmc_project.State.Host";
72 constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI";
73 constexpr auto vsbpRecordVPD = "com.ibm.ipzvpd.VSBP";
74 constexpr auto locCode = "xyz.openbmc_project.Inventory.Decorator.LocationCode";
75 constexpr auto compatible =
76     "xyz.openbmc_project.Inventory.Decorator.Compatible";
77 constexpr auto vpdManager = "com.ibm.VPD.Manager";
78 constexpr auto ledGroup = "xyz.openbmc_project.Led.Group";
79 constexpr auto operationalStatus =
80     "xyz.openbmc_project.State.Decorator.OperationalStatus";
81 constexpr auto logSetting = "xyz.openbmc_project.Logging.Settings";
82 constexpr auto associationDef = "xyz.openbmc_project.Association.Definitions";
83 constexpr auto hwIsolationEntry = "xyz.openbmc_project.HardwareIsolation.Entry";
84 constexpr auto association = "xyz.openbmc_project.Association";
85 constexpr auto biosConfigMgr = "xyz.openbmc_project.BIOSConfig.Manager";
86 constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw";
87 constexpr auto invItem = "xyz.openbmc_project.Inventory.Item";
88 constexpr auto invFan = "xyz.openbmc_project.Inventory.Item.Fan";
89 constexpr auto invPowerSupply =
90     "xyz.openbmc_project.Inventory.Item.PowerSupply";
91 constexpr auto inventoryManager = "xyz.openbmc_project.Inventory.Manager";
92 constexpr auto systemdMgr = "org.freedesktop.systemd1.Manager";
93 } // namespace interface
94 
95 using namespace sdbusplus::server::xyz::openbmc_project::state::boot;
96 using namespace sdbusplus::server::xyz::openbmc_project::state;
97 namespace match_rules = sdbusplus::bus::match::rules;
98 
99 const DBusInterfaceList hotplugInterfaces{interface::invFan,
100                                           interface::invPowerSupply};
101 static constexpr auto PDBG_DTB_PATH =
102     "/var/lib/phosphor-software-manager/hostfw/running/DEVTREE";
103 
104 std::pair<std::string, std::string>
105     DataInterfaceBase::extractConnectorFromLocCode(
106         const std::string& locationCode)
107 {
108     auto base = locationCode;
109     std::string connector{};
110 
111     auto pos = base.find("-T");
112     if (pos != std::string::npos)
113     {
114         connector = base.substr(pos);
115         base = base.substr(0, pos);
116     }
117 
118     return {base, connector};
119 }
120 
121 DataInterface::DataInterface(sdbusplus::bus_t& bus) :
122     _bus(bus), _systemdSlot(nullptr)
123 {
124     readBMCFWVersion();
125     readServerFWVersion();
126     readBMCFWVersionID();
127 
128     // Watch the BootProgress property
129     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
130         bus, object_path::hostState, interface::bootProgress, "BootProgress",
131         *this, [this](const auto& value) {
132             this->_bootState = std::get<std::string>(value);
133             auto status = Progress::convertProgressStagesFromString(
134                 std::get<std::string>(value));
135 
136             if ((status == Progress::ProgressStages::SystemInitComplete) ||
137                 (status == Progress::ProgressStages::OSRunning))
138             {
139                 setHostUp(true);
140             }
141             else
142             {
143                 setHostUp(false);
144             }
145         }));
146 
147     // Watch the host PEL enable property
148     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
149         bus, object_path::enableHostPELs, interface::enable, "Enabled", *this,
150         [this](const auto& value) {
151             if (std::get<bool>(value) != this->_sendPELsToHost)
152             {
153                 lg2::info("The send PELs to host setting changed to {VAL}",
154                           "VAL", std::get<bool>(value));
155             }
156             this->_sendPELsToHost = std::get<bool>(value);
157         }));
158 
159     // Watch the BMCState property
160     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
161         bus, object_path::bmcState, interface::bmcState, "CurrentBMCState",
162         *this, [this](const auto& value) {
163             const auto& state = std::get<std::string>(value);
164             this->_bmcState = state;
165 
166             // Wait for BMC ready to start watching for
167             // plugs so things calm down first.
168             if (BMC::convertBMCStateFromString(state) == BMC::BMCState::Ready)
169             {
170                 startFruPlugWatch();
171             }
172         }));
173 
174     // Watch the chassis current and requested power state properties
175     _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
176         bus, object_path::chassisState, interface::chassisState, *this,
177         [this](const auto& properties) {
178             auto state = properties.find("CurrentPowerState");
179             if (state != properties.end())
180             {
181                 this->_chassisState = std::get<std::string>(state->second);
182             }
183 
184             auto trans = properties.find("RequestedPowerTransition");
185             if (trans != properties.end())
186             {
187                 this->_chassisTransition = std::get<std::string>(trans->second);
188             }
189         }));
190 
191     // Watch the CurrentHostState property
192     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
193         bus, object_path::hostState, interface::hostState, "CurrentHostState",
194         *this, [this](const auto& value) {
195             this->_hostState = std::get<std::string>(value);
196         }));
197 
198     // Watch the BaseBIOSTable property for the hmc managed attribute
199     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
200         bus, object_path::biosConfigMgr, interface::biosConfigMgr,
201         "BaseBIOSTable", service_name::biosConfigMgr, *this,
202         [this](const auto& value) {
203             const auto& attributes = std::get<BiosAttributes>(value);
204 
205             auto it = attributes.find("pvm_hmc_managed");
206             if (it != attributes.end())
207             {
208                 const auto& currentValVariant = std::get<5>(it->second);
209                 auto currentVal = std::get_if<std::string>(&currentValVariant);
210                 if (currentVal)
211                 {
212                     this->_hmcManaged =
213                         (*currentVal == "Enabled") ? true : false;
214                 }
215             }
216         }));
217 
218     if (isPHALDevTreeExist())
219     {
220 #ifdef PEL_ENABLE_PHAL
221         initPHAL();
222 #endif
223     }
224     else
225     {
226         // Watch the "openpower-update-bios-attr-table" service to init
227         // PHAL libraries
228         subscribeToSystemdSignals();
229     }
230 }
231 
232 DBusPropertyMap DataInterface::getAllProperties(
233     const std::string& service, const std::string& objectPath,
234     const std::string& interface) const
235 {
236     DBusPropertyMap properties;
237 
238     auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
239                                        interface::dbusProperty, "GetAll");
240     method.append(interface);
241     auto reply = _bus.call(method, dbusTimeout);
242 
243     reply.read(properties);
244 
245     return properties;
246 }
247 
248 void DataInterface::getProperty(
249     const std::string& service, const std::string& objectPath,
250     const std::string& interface, const std::string& property,
251     DBusValue& value) const
252 {
253     auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
254                                        interface::dbusProperty, "Get");
255     method.append(interface, property);
256     auto reply = _bus.call(method, dbusTimeout);
257 
258     reply.read(value);
259 }
260 
261 DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
262 {
263     auto method = _bus.new_method_call(
264         service_name::objectMapper, object_path::objectMapper,
265         interface::objectMapper, "GetSubTreePaths");
266 
267     method.append(std::string{"/"}, 0, interfaces);
268 
269     auto reply = _bus.call(method, dbusTimeout);
270 
271     DBusPathList paths;
272     reply.read(paths);
273 
274     return paths;
275 }
276 
277 DBusService DataInterface::getService(const std::string& objectPath,
278                                       const std::string& interface) const
279 {
280     auto method = _bus.new_method_call(service_name::objectMapper,
281                                        object_path::objectMapper,
282                                        interface::objectMapper, "GetObject");
283 
284     method.append(objectPath, std::vector<std::string>({interface}));
285 
286     auto reply = _bus.call(method, dbusTimeout);
287 
288     std::map<DBusService, DBusInterfaceList> response;
289     reply.read(response);
290 
291     if (!response.empty())
292     {
293         return response.begin()->first;
294     }
295 
296     return std::string{};
297 }
298 
299 void DataInterface::readBMCFWVersion()
300 {
301     _bmcFWVersion =
302         phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
303 }
304 
305 void DataInterface::readServerFWVersion()
306 {
307     auto value =
308         phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
309     if ((value != "") && (value.find_last_of(')') != std::string::npos))
310     {
311         std::size_t pos = value.find_first_of('(') + 1;
312         _serverFWVersion = value.substr(pos, value.find_last_of(')') - pos);
313     }
314 }
315 
316 void DataInterface::readBMCFWVersionID()
317 {
318     _bmcFWVersionID =
319         phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
320 }
321 
322 std::string DataInterface::getMachineTypeModel() const
323 {
324     std::string model;
325     try
326     {
327         auto service = getService(object_path::systemInv, interface::invAsset);
328         if (!service.empty())
329         {
330             DBusValue value;
331             getProperty(service, object_path::systemInv, interface::invAsset,
332                         "Model", value);
333 
334             model = std::get<std::string>(value);
335         }
336     }
337     catch (const std::exception& e)
338     {
339         lg2::warning("Failed reading Model property from "
340                      "interface: {IFACE} exception: {ERROR}",
341                      "IFACE", interface::invAsset, "ERROR", e);
342     }
343 
344     return model;
345 }
346 
347 std::string DataInterface::getMachineSerialNumber() const
348 {
349     std::string sn;
350     try
351     {
352         auto service = getService(object_path::systemInv, interface::invAsset);
353         if (!service.empty())
354         {
355             DBusValue value;
356             getProperty(service, object_path::systemInv, interface::invAsset,
357                         "SerialNumber", value);
358 
359             sn = std::get<std::string>(value);
360         }
361     }
362     catch (const std::exception& e)
363     {
364         lg2::warning("Failed reading SerialNumber property from "
365                      "interface: {IFACE} exception: {ERROR}",
366                      "IFACE", interface::invAsset, "ERROR", e);
367     }
368 
369     return sn;
370 }
371 
372 std::string DataInterface::getMotherboardCCIN() const
373 {
374     std::string ccin;
375 
376     try
377     {
378         auto service =
379             getService(object_path::motherBoardInv, interface::viniRecordVPD);
380         if (!service.empty())
381         {
382             DBusValue value;
383             getProperty(service, object_path::motherBoardInv,
384                         interface::viniRecordVPD, "CC", value);
385 
386             auto cc = std::get<std::vector<uint8_t>>(value);
387             ccin = std::string{cc.begin(), cc.end()};
388         }
389     }
390     catch (const std::exception& e)
391     {
392         lg2::warning("Failed reading Motherboard CCIN property from "
393                      "interface: {IFACE} exception: {ERROR}",
394                      "IFACE", interface::viniRecordVPD, "ERROR", e);
395     }
396 
397     return ccin;
398 }
399 
400 std::vector<uint8_t> DataInterface::getSystemIMKeyword() const
401 {
402     std::vector<uint8_t> systemIM;
403 
404     try
405     {
406         auto service =
407             getService(object_path::motherBoardInv, interface::vsbpRecordVPD);
408         if (!service.empty())
409         {
410             DBusValue value;
411             getProperty(service, object_path::motherBoardInv,
412                         interface::vsbpRecordVPD, "IM", value);
413 
414             systemIM = std::get<std::vector<uint8_t>>(value);
415         }
416     }
417     catch (const std::exception& e)
418     {
419         lg2::warning("Failed reading System IM property from "
420                      "interface: {IFACE} exception: {ERROR}",
421                      "IFACE", interface::vsbpRecordVPD, "ERROR", e);
422     }
423 
424     return systemIM;
425 }
426 
427 void DataInterface::getHWCalloutFields(
428     const std::string& inventoryPath, std::string& fruPartNumber,
429     std::string& ccin, std::string& serialNumber) const
430 {
431     // For now, attempt to get all of the properties directly on the path
432     // passed in.  In the future, may need to make use of an algorithm
433     // to figure out which inventory objects actually hold these
434     // interfaces in the case of non FRUs, or possibly another service
435     // will provide this info.  Any missing interfaces will result
436     // in exceptions being thrown.
437 
438     auto service = getService(inventoryPath, interface::viniRecordVPD);
439 
440     auto properties =
441         getAllProperties(service, inventoryPath, interface::viniRecordVPD);
442 
443     auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
444     fruPartNumber = std::string{value.begin(), value.end()};
445 
446     value = std::get<std::vector<uint8_t>>(properties["CC"]);
447     ccin = std::string{value.begin(), value.end()};
448 
449     value = std::get<std::vector<uint8_t>>(properties["SN"]);
450     serialNumber = std::string{value.begin(), value.end()};
451 }
452 
453 std::string DataInterface::getLocationCode(
454     const std::string& inventoryPath) const
455 {
456     auto service = getService(inventoryPath, interface::locCode);
457 
458     DBusValue locCode;
459     getProperty(service, inventoryPath, interface::locCode, "LocationCode",
460                 locCode);
461 
462     return std::get<std::string>(locCode);
463 }
464 
465 std::string DataInterface::addLocationCodePrefix(
466     const std::string& locationCode)
467 {
468     static const std::string locationCodePrefix{"Ufcs-"};
469 
470     // Technically there are 2 location code prefixes, Ufcs and Umts, so
471     // if it already starts with a U then don't need to do anything.
472     if (locationCode.front() != 'U')
473     {
474         return locationCodePrefix + locationCode;
475     }
476 
477     return locationCode;
478 }
479 
480 std::string DataInterface::expandLocationCode(const std::string& locationCode,
481                                               uint16_t /*node*/) const
482 {
483     // Location codes for connectors are the location code of the FRU they are
484     // on, plus a '-Tx' segment.  Remove this last segment before expanding it
485     // and then add it back in afterwards.  This way, the connector doesn't have
486     // to be in the model just so that it can be expanded.
487     auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode);
488 
489     auto method =
490         _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
491                              interface::vpdManager, "GetExpandedLocationCode");
492 
493     method.append(addLocationCodePrefix(baseLoc), static_cast<uint16_t>(0));
494 
495     auto reply = _bus.call(method, dbusTimeout);
496 
497     std::string expandedLocationCode;
498     reply.read(expandedLocationCode);
499 
500     if (!connectorLoc.empty())
501     {
502         expandedLocationCode += connectorLoc;
503     }
504 
505     return expandedLocationCode;
506 }
507 
508 std::vector<std::string> DataInterface::getInventoryFromLocCode(
509     const std::string& locationCode, uint16_t node, bool expanded) const
510 {
511     std::string methodName = expanded ? "GetFRUsByExpandedLocationCode"
512                                       : "GetFRUsByUnexpandedLocationCode";
513 
514     // Remove the connector segment, if present, so that this method call
515     // returns an inventory path that getHWCalloutFields() can be used with.
516     // (The serial number, etc, aren't stored on the connector in the
517     // inventory, and may not even be modeled.)
518     auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode);
519 
520     auto method =
521         _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
522                              interface::vpdManager, methodName.c_str());
523 
524     if (expanded)
525     {
526         method.append(baseLoc);
527     }
528     else
529     {
530         method.append(addLocationCodePrefix(baseLoc), node);
531     }
532 
533     auto reply = _bus.call(method, dbusTimeout);
534 
535     std::vector<sdbusplus::message::object_path> entries;
536     reply.read(entries);
537 
538     std::vector<std::string> paths;
539 
540     // Note: The D-Bus method will fail if nothing found.
541     std::for_each(entries.begin(), entries.end(),
542                   [&paths](const auto& path) { paths.push_back(path); });
543 
544     return paths;
545 }
546 
547 void DataInterface::assertLEDGroup(const std::string& ledGroup,
548                                    bool value) const
549 {
550     DBusValue variant = value;
551     auto method =
552         _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(),
553                              interface::dbusProperty, "Set");
554     method.append(interface::ledGroup, "Asserted", variant);
555     _bus.call(method, dbusTimeout);
556 }
557 
558 void DataInterface::setFunctional(const std::string& objectPath,
559                                   bool value) const
560 {
561     DBusPropertyMap prop{{"Functional", value}};
562     DBusInterfaceMap iface{{interface::operationalStatus, prop}};
563 
564     // PIM takes a relative path like /system/chassis so remove
565     // /xyz/openbmc_project/inventory if present.
566     std::string path{objectPath};
567     if (path.starts_with(object_path::baseInv))
568     {
569         path = objectPath.substr(strlen(object_path::baseInv));
570     }
571     DBusObjectMap object{{path, iface}};
572 
573     auto method = _bus.new_method_call(service_name::inventoryManager,
574                                        object_path::baseInv,
575                                        interface::inventoryManager, "Notify");
576     method.append(std::move(object));
577     _bus.call(method, dbusTimeout);
578 }
579 
580 using AssociationTuple = std::tuple<std::string, std::string, std::string>;
581 using AssociationsProperty = std::vector<AssociationTuple>;
582 
583 void DataInterface::setCriticalAssociation(const std::string& objectPath) const
584 {
585     DBusValue getAssociationValue;
586 
587     auto service = getService(objectPath, interface::associationDef);
588 
589     getProperty(service, objectPath, interface::associationDef, "Associations",
590                 getAssociationValue);
591 
592     auto association = std::get<AssociationsProperty>(getAssociationValue);
593 
594     AssociationTuple critAssociation{
595         "health_rollup", "critical",
596         "/xyz/openbmc_project/inventory/system/chassis"};
597 
598     if (std::find(association.begin(), association.end(), critAssociation) ==
599         association.end())
600     {
601         association.push_back(critAssociation);
602         DBusValue setAssociationValue = association;
603 
604         auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
605                                            interface::dbusProperty, "Set");
606 
607         method.append(interface::associationDef, "Associations",
608                       setAssociationValue);
609         _bus.call(method, dbusTimeout);
610     }
611 }
612 
613 std::vector<std::string> DataInterface::getSystemNames() const
614 {
615     DBusSubTree subtree;
616     DBusValue names;
617 
618     auto method = _bus.new_method_call(service_name::objectMapper,
619                                        object_path::objectMapper,
620                                        interface::objectMapper, "GetSubTree");
621     method.append(std::string{"/"}, 0,
622                   std::vector<std::string>{interface::compatible});
623     auto reply = _bus.call(method, dbusTimeout);
624 
625     reply.read(subtree);
626     if (subtree.empty())
627     {
628         throw std::runtime_error("Compatible interface not on D-Bus");
629     }
630 
631     for (const auto& [path, interfaceMap] : subtree)
632     {
633         auto iface = interfaceMap.find(service_name::entityManager);
634         if (iface == interfaceMap.end())
635         {
636             continue;
637         }
638 
639         getProperty(iface->first, path, interface::compatible, "Names", names);
640 
641         return std::get<std::vector<std::string>>(names);
642     }
643 
644     throw std::runtime_error("EM Compatible interface not on D-Bus");
645 }
646 
647 bool DataInterface::getQuiesceOnError() const
648 {
649     bool ret = false;
650 
651     try
652     {
653         auto service =
654             getService(object_path::logSetting, interface::logSetting);
655         if (!service.empty())
656         {
657             DBusValue value;
658             getProperty(service, object_path::logSetting, interface::logSetting,
659                         "QuiesceOnHwError", value);
660 
661             ret = std::get<bool>(value);
662         }
663     }
664     catch (const std::exception& e)
665     {
666         lg2::warning("Failed reading QuiesceOnHwError property from "
667                      "interface: {IFACE} exception: {ERROR}",
668                      "IFACE", interface::logSetting, "ERROR", e);
669     }
670 
671     return ret;
672 }
673 
674 #ifdef PEL_ENABLE_PHAL
675 void DataInterface::createGuardRecord(const std::vector<uint8_t>& binPath,
676                                       GardType eGardType, uint32_t plid) const
677 {
678     try
679     {
680         libguard::libguard_init(false);
681         libguard::create(binPath, plid, eGardType);
682     }
683     catch (libguard::exception::GuardException& e)
684     {
685         lg2::error("Exception in libguard {ERROR}", "ERROR", e);
686     }
687 }
688 #endif
689 
690 void DataInterface::createProgressSRC(
691     const std::vector<uint8_t>& priSRC,
692     const std::vector<uint8_t>& srcStruct) const
693 {
694     DBusValue variant = std::make_tuple(priSRC, srcStruct);
695 
696     auto method = _bus.new_method_call(service_name::bootRawProgress,
697                                        object_path::bootRawProgress,
698                                        interface::dbusProperty, "Set");
699 
700     method.append(interface::bootRawProgress, "Value", variant);
701 
702     _bus.call(method, dbusTimeout);
703 }
704 
705 std::vector<uint32_t> DataInterface::getLogIDWithHwIsolation() const
706 {
707     std::vector<std::string> association = {"xyz.openbmc_project.Association"};
708     std::string hwErrorLog = "/isolated_hw_errorlog";
709     std::string errorLog = "/error_log";
710     DBusPathList paths;
711     std::vector<uint32_t> ids;
712 
713     // Get all latest mapper associations
714     paths = getPaths(association);
715     for (auto& path : paths)
716     {
717         // Look for object path with hardware isolation entry if any
718         size_t pos = path.find(hwErrorLog);
719         if (pos != std::string::npos)
720         {
721             // Get the object path
722             std::string ph = path;
723             ph.erase(pos, hwErrorLog.length());
724             auto service = getService(ph, interface::hwIsolationEntry);
725             if (!service.empty())
726             {
727                 bool status;
728                 DBusValue value;
729 
730                 // Read the Resolved property from object path
731                 getProperty(service, ph, interface::hwIsolationEntry,
732                             "Resolved", value);
733 
734                 status = std::get<bool>(value);
735 
736                 // If the entry isn't resolved
737                 if (!status)
738                 {
739                     auto assocService =
740                         getService(path, interface::association);
741                     if (!assocService.empty())
742                     {
743                         DBusValue endpoints;
744 
745                         // Read Endpoints property
746                         getProperty(assocService, path, interface::association,
747                                     "endpoints", endpoints);
748 
749                         auto logPath =
750                             std::get<std::vector<std::string>>(endpoints);
751                         if (!logPath.empty())
752                         {
753                             // Get OpenBMC event log Id
754                             uint32_t id = stoi(logPath[0].substr(
755                                 logPath[0].find_last_of('/') + 1));
756                             ids.push_back(id);
757                         }
758                     }
759                 }
760             }
761         }
762 
763         // Look for object path with error_log entry if any
764         pos = path.find(errorLog);
765         if (pos != std::string::npos)
766         {
767             auto service = getService(path, interface::association);
768             if (!service.empty())
769             {
770                 DBusValue value;
771 
772                 // Read Endpoints property
773                 getProperty(service, path, interface::association, "endpoints",
774                             value);
775 
776                 auto logPath = std::get<std::vector<std::string>>(value);
777                 if (!logPath.empty())
778                 {
779                     // Get OpenBMC event log Id
780                     uint32_t id = stoi(
781                         logPath[0].substr(logPath[0].find_last_of('/') + 1));
782                     ids.push_back(id);
783                 }
784             }
785         }
786     }
787 
788     if (ids.size() > 1)
789     {
790         // remove duplicates to have only unique ids
791         std::sort(ids.begin(), ids.end());
792         ids.erase(std::unique(ids.begin(), ids.end()), ids.end());
793     }
794     return ids;
795 }
796 
797 std::vector<uint8_t> DataInterface::getRawProgressSRC(void) const
798 {
799     using RawProgressProperty =
800         std::tuple<std::vector<uint8_t>, std::vector<uint8_t>>;
801 
802     DBusValue value;
803     getProperty(service_name::bootRawProgress, object_path::bootRawProgress,
804                 interface::bootRawProgress, "Value", value);
805 
806     const auto& rawProgress = std::get<RawProgressProperty>(value);
807     return std::get<1>(rawProgress);
808 }
809 
810 std::optional<std::vector<uint8_t>> DataInterface::getDIProperty(
811     const std::string& locationCode) const
812 {
813     std::vector<uint8_t> viniDI;
814 
815     try
816     {
817         // Note : The hardcoded value 0 should be changed when comes to
818         // multinode system.
819         auto objectPath = getInventoryFromLocCode(locationCode, 0, true);
820 
821         DBusValue value;
822         getProperty(service_name::inventoryManager, objectPath[0],
823                     interface::viniRecordVPD, "DI", value);
824 
825         viniDI = std::get<std::vector<uint8_t>>(value);
826     }
827     catch (const std::exception& e)
828     {
829         lg2::warning(
830             "Failed reading DI property for the location code : {LOC_CODE} from "
831             "interface: {IFACE} exception: {ERROR}",
832             "LOC_CODE", locationCode, "IFACE", interface::viniRecordVPD,
833             "ERROR", e);
834         return std::nullopt;
835     }
836 
837     return viniDI;
838 }
839 
840 std::optional<bool> DataInterfaceBase::isDIMMLocCode(
841     const std::string& locCode) const
842 {
843     if (_locationCache.contains(locCode))
844     {
845         return _locationCache.at(locCode);
846     }
847     else
848     {
849         return std::nullopt;
850     }
851 }
852 
853 void DataInterfaceBase::addDIMMLocCode(const std::string& locCode,
854                                        bool isFRUDIMM)
855 {
856     _locationCache.insert({locCode, isFRUDIMM});
857 }
858 
859 bool DataInterfaceBase::isDIMM(const std::string& locCode)
860 {
861     auto isDIMMType = isDIMMLocCode(locCode);
862     if (isDIMMType.has_value())
863     {
864         return isDIMMType.value();
865     }
866 #ifndef PEL_ENABLE_PHAL
867     return false;
868 #else
869     else
870     {
871         // Invoke pHAL API inorder to fetch the FRU Type
872         auto fruType = openpower::phal::pdbg::getFRUType(locCode);
873         bool isDIMMFRU{false};
874         if (fruType.has_value())
875         {
876             if (fruType.value() == ENUM_ATTR_TYPE_DIMM)
877             {
878                 isDIMMFRU = true;
879             }
880             addDIMMLocCode(locCode, isDIMMFRU);
881         }
882         return isDIMMFRU;
883     }
884 #endif
885 }
886 
887 DBusPathList DataInterface::getAssociatedPaths(
888     const DBusPath& associatedPath, const DBusPath& subtree, int32_t depth,
889     const DBusInterfaceList& interfaces) const
890 {
891     DBusPathList paths;
892     try
893     {
894         auto method = _bus.new_method_call(
895             service_name::objectMapper, object_path::objectMapper,
896             interface::objectMapper, "GetAssociatedSubTreePaths");
897         method.append(sdbusplus::message::object_path(associatedPath),
898                       sdbusplus::message::object_path(subtree), depth,
899                       interfaces);
900 
901         auto reply = _bus.call(method, dbusTimeout);
902         reply.read(paths);
903     }
904     catch (const std::exception& e)
905     {
906         std::string ifaces(
907             std::ranges::fold_left_first(
908                 interfaces,
909                 [](std::string ifaces, const std::string& iface) {
910                     return ifaces + ", " + iface;
911                 })
912                 .value_or(""));
913 
914         lg2::error("Failed getting associated paths: {ERROR}. "
915                    "AssociatedPath: {ASSOIC_PATH} Subtree: {SUBTREE} "
916                    "Interfaces: {IFACES}",
917                    "ERROR", e, "ASSOIC_PATH", associatedPath, "SUBTREE",
918                    subtree, "IFACES", ifaces);
919     }
920     return paths;
921 }
922 
923 void DataInterface::startFruPlugWatch()
924 {
925     // Add a watch on inventory InterfacesAdded and then find all
926     // existing hotpluggable interfaces and add propertiesChanged
927     // watches on them.
928 
929     _invIaMatch = std::make_unique<sdbusplus::bus::match_t>(
930         _bus, match_rules::interfacesAdded(object_path::baseInv),
931         std::bind(&DataInterface::inventoryIfaceAdded, this,
932                   std::placeholders::_1));
933     try
934     {
935         auto paths = getPaths(hotplugInterfaces);
936 
937         _invPresentMatches.clear();
938 
939         std::for_each(paths.begin(), paths.end(),
940                       [this](const auto& path) { addHotplugWatch(path); });
941     }
942     catch (const sdbusplus::exception_t& e)
943     {
944         lg2::warning("Failed getting FRU paths to watch: {ERROR}", "ERROR", e);
945     }
946 }
947 
948 void DataInterface::addHotplugWatch(const std::string& path)
949 {
950     if (!_invPresentMatches.contains(path))
951     {
952         _invPresentMatches.emplace(
953             path,
954             std::make_unique<sdbusplus::bus::match_t>(
955                 _bus, match_rules::propertiesChanged(path, interface::invItem),
956                 std::bind(&DataInterface::presenceChanged, this,
957                           std::placeholders::_1)));
958     }
959 }
960 
961 void DataInterface::inventoryIfaceAdded(sdbusplus::message_t& msg)
962 {
963     sdbusplus::message::object_path path;
964     DBusInterfaceMap interfaces;
965 
966     msg.read(path, interfaces);
967 
968     // Check if any of the new interfaces are for hot pluggable FRUs.
969     if (std::find_if(interfaces.begin(), interfaces.end(),
970                      [](const auto& interfacePair) {
971                          return std::find(hotplugInterfaces.begin(),
972                                           hotplugInterfaces.end(),
973                                           interfacePair.first) !=
974                                 hotplugInterfaces.end();
975                      }) == interfaces.end())
976     {
977         return;
978     }
979 
980     addHotplugWatch(path.str);
981 
982     // If an Inventory.Item interface was also added, check presence now.
983 
984     // Notes:
985     // * This assumes the Inv.Item and Inv.Fan/PS are added together which
986     //   is currently the case.
987     // * If the code ever switches to something without a Present
988     //   property, then the IA signal itself would probably indicate presence.
989 
990     auto itemIt = interfaces.find(interface::invItem);
991     if (itemIt != interfaces.end())
992     {
993         notifyPresenceSubsribers(path.str, itemIt->second);
994     }
995 }
996 
997 void DataInterface::presenceChanged(sdbusplus::message_t& msg)
998 {
999     DBusInterface interface;
1000     DBusPropertyMap properties;
1001 
1002     msg.read(interface, properties);
1003     if (interface != interface::invItem)
1004     {
1005         return;
1006     }
1007 
1008     std::string path = msg.get_path();
1009     notifyPresenceSubsribers(path, properties);
1010 }
1011 
1012 void DataInterface::notifyPresenceSubsribers(const std::string& path,
1013                                              const DBusPropertyMap& properties)
1014 {
1015     auto prop = properties.find("Present");
1016     if ((prop == properties.end()) || (!std::get<bool>(prop->second)))
1017     {
1018         return;
1019     }
1020 
1021     std::string locCode;
1022 
1023     try
1024     {
1025         auto service = getService(path, interface::locCode);
1026 
1027         // If the hotplugged FRU is hosted by PLDM, then it is
1028         // in an IO expansion drawer and we don't care about it.
1029         if (service == service_name::pldm)
1030         {
1031             return;
1032         }
1033 
1034         locCode = getLocationCode(path);
1035     }
1036     catch (const sdbusplus::exception_t& e)
1037     {
1038         lg2::debug("Could not get location code for {PATH}: {ERROR}", "PATH",
1039                    path, "ERROR", e);
1040         return;
1041     }
1042 
1043     lg2::debug("Detected FRU {PATH} ({LOC}) present ", "PATH", path, "LOC",
1044                locCode);
1045 
1046     // Tell the subscribers.
1047     setFruPresent(locCode);
1048 }
1049 
1050 bool DataInterface::isPHALDevTreeExist() const
1051 {
1052     try
1053     {
1054         if (std::filesystem::exists(PDBG_DTB_PATH))
1055         {
1056             return true;
1057         }
1058     }
1059     catch (const std::exception& e)
1060     {
1061         lg2::error("Failed to check device tree {PHAL_DEVTREE_PATH} existence, "
1062                    "{ERROR}",
1063                    "PHAL_DEVTREE_PATH", PDBG_DTB_PATH, "ERROR", e);
1064     }
1065     return false;
1066 }
1067 
1068 #ifdef PEL_ENABLE_PHAL
1069 void DataInterface::initPHAL()
1070 {
1071     if (setenv("PDBG_DTB", PDBG_DTB_PATH, 1))
1072     {
1073         // Log message and continue,
1074         // This is to help continue creating PEL in raw format.
1075         lg2::error("Failed to set PDBG_DTB: ({ERRNO})", "ERRNO",
1076                    strerror(errno));
1077     }
1078 
1079     if (!pdbg_targets_init(NULL))
1080     {
1081         lg2::error("pdbg_targets_init failed");
1082         return;
1083     }
1084 
1085     if (libekb_init())
1086     {
1087         lg2::error("libekb_init failed, skipping ffdc processing");
1088         return;
1089     }
1090 }
1091 #endif
1092 
1093 void DataInterface::subscribeToSystemdSignals()
1094 {
1095     try
1096     {
1097         auto method =
1098             _bus.new_method_call(service_name::systemd, object_path::systemd,
1099                                  interface::systemdMgr, "Subscribe");
1100         _systemdSlot = method.call_async([this](sdbusplus::message_t&& msg) {
1101             // Initializing with nullptr to indicate that it is not subscribed
1102             // to any signal.
1103             this->_systemdSlot = sdbusplus::slot_t(nullptr);
1104             if (msg.is_method_error())
1105             {
1106                 auto* error = msg.get_error();
1107                 lg2::error("Failed to subscribe JobRemoved systemd signal, "
1108                            "errorName: {ERR_NAME}, errorMsg: {ERR_MSG} ",
1109                            "ERR_NAME", error->name, "ERR_MSG", error->message);
1110                 return;
1111             }
1112 
1113             namespace sdbusRule = sdbusplus::bus::match::rules;
1114             this->_systemdMatch =
1115                 std::make_unique<decltype(this->_systemdMatch)::element_type>(
1116                     this->_bus,
1117                     sdbusRule::type::signal() +
1118                         sdbusRule::member("JobRemoved") +
1119                         sdbusRule::path(object_path::systemd) +
1120                         sdbusRule::interface(interface::systemdMgr),
1121                     [this](sdbusplus::message_t& msg) {
1122                         uint32_t jobID;
1123                         sdbusplus::message::object_path jobObjPath;
1124                         std::string jobUnitName, jobUnitResult;
1125 
1126                         msg.read(jobID, jobObjPath, jobUnitName, jobUnitResult);
1127                         if ((jobUnitName == "obmc-recover-pnor.service") &&
1128                             (jobUnitResult == "done"))
1129                         {
1130 #ifdef PEL_ENABLE_PHAL
1131                             this->initPHAL();
1132 #endif
1133                             // Invoke unsubscribe method to stop monitoring for
1134                             // JobRemoved signals.
1135                             this->unsubscribeFromSystemdSignals();
1136                         }
1137                     });
1138         });
1139     }
1140     catch (const sdbusplus::exception_t& e)
1141     {
1142         lg2::error(
1143             "Exception occured while handling JobRemoved systemd signal, "
1144             "exception: {ERROR}",
1145             "ERROR", e);
1146     }
1147 }
1148 
1149 void DataInterface::unsubscribeFromSystemdSignals()
1150 {
1151     try
1152     {
1153         auto method =
1154             _bus.new_method_call(service_name::systemd, object_path::systemd,
1155                                  interface::systemdMgr, "Unsubscribe");
1156         _systemdSlot = method.call_async([this](sdbusplus::message_t&& msg) {
1157             // Unsubscribing the _systemdSlot from the subscribed signal
1158             this->_systemdSlot = sdbusplus::slot_t(nullptr);
1159             if (msg.is_method_error())
1160             {
1161                 auto* error = msg.get_error();
1162                 lg2::error(
1163                     "Failed to unsubscribe from JobRemoved systemd signal, "
1164                     "errorName: {ERR_NAME}, errorMsg: {ERR_MSG} ",
1165                     "ERR_NAME", error->name, "ERR_MSG", error->message);
1166                 return;
1167             }
1168             // Reset _systemdMatch to avoid reception of further JobRemoved
1169             // signals
1170             this->_systemdMatch.reset();
1171         });
1172     }
1173     catch (const sdbusplus::exception_t& e)
1174     {
1175         lg2::error(
1176             "Exception occured while unsubscribing from JobRemoved systemd signal, "
1177             "exception: {ERROR}",
1178             "ERROR", e);
1179     }
1180 }
1181 
1182 } // namespace pels
1183 } // namespace openpower
1184