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