xref: /openbmc/phosphor-logging/extensions/openpower-pels/data_interface.cpp (revision e92ae97abc542c8ede3b423b9c74847eb8048647)
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>
extractConnectorFromLocCode(const std::string & locationCode)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 
DataInterface(sdbusplus::bus_t & bus)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 
getAllProperties(const std::string & service,const std::string & objectPath,const std::string & interface) const232 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 
getProperty(const std::string & service,const std::string & objectPath,const std::string & interface,const std::string & property,DBusValue & value) const248 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 
getPaths(const DBusInterfaceList & interfaces) const261 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     auto paths = reply.unpack<DBusPathList>();
272 
273     return paths;
274 }
275 
getSubTree(const DBusInterfaceList & interfaces) const276 DBusSubTree DataInterface::getSubTree(const DBusInterfaceList& interfaces) const
277 {
278     auto method = _bus.new_method_call(service_name::objectMapper,
279                                        object_path::objectMapper,
280                                        interface::objectMapper, "GetSubTree");
281     method.append(std::string{"/"}, 0, interfaces);
282     auto reply = _bus.call(method, dbusTimeout);
283 
284     return reply.unpack<DBusSubTree>();
285 }
286 
getService(const std::string & objectPath,const std::string & interface) const287 DBusService DataInterface::getService(const std::string& objectPath,
288                                       const std::string& interface) const
289 {
290     auto method = _bus.new_method_call(service_name::objectMapper,
291                                        object_path::objectMapper,
292                                        interface::objectMapper, "GetObject");
293 
294     method.append(objectPath, std::vector<std::string>({interface}));
295 
296     auto reply = _bus.call(method, dbusTimeout);
297 
298     auto response = reply.unpack<std::map<DBusService, DBusInterfaceList>>();
299 
300     if (!response.empty())
301     {
302         return response.begin()->first;
303     }
304 
305     return std::string{};
306 }
307 
readBMCFWVersion()308 void DataInterface::readBMCFWVersion()
309 {
310     _bmcFWVersion =
311         phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
312 }
313 
readServerFWVersion()314 void DataInterface::readServerFWVersion()
315 {
316     auto value =
317         phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
318     if ((value != "") && (value.find_last_of(')') != std::string::npos))
319     {
320         std::size_t pos = value.find_first_of('(') + 1;
321         _serverFWVersion = value.substr(pos, value.find_last_of(')') - pos);
322     }
323 }
324 
readBMCFWVersionID()325 void DataInterface::readBMCFWVersionID()
326 {
327     _bmcFWVersionID =
328         phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
329 }
330 
getMachineTypeModel() const331 std::string DataInterface::getMachineTypeModel() const
332 {
333     std::string model;
334     try
335     {
336         auto service = getService(object_path::systemInv, interface::invAsset);
337         if (!service.empty())
338         {
339             DBusValue value;
340             getProperty(service, object_path::systemInv, interface::invAsset,
341                         "Model", value);
342 
343             model = std::get<std::string>(value);
344         }
345     }
346     catch (const std::exception& e)
347     {
348         lg2::warning("Failed reading Model property from "
349                      "interface: {IFACE} exception: {ERROR}",
350                      "IFACE", interface::invAsset, "ERROR", e);
351     }
352 
353     return model;
354 }
355 
getMachineSerialNumber() const356 std::string DataInterface::getMachineSerialNumber() const
357 {
358     std::string sn;
359     try
360     {
361         auto service = getService(object_path::systemInv, interface::invAsset);
362         if (!service.empty())
363         {
364             DBusValue value;
365             getProperty(service, object_path::systemInv, interface::invAsset,
366                         "SerialNumber", value);
367 
368             sn = std::get<std::string>(value);
369         }
370     }
371     catch (const std::exception& e)
372     {
373         lg2::warning("Failed reading SerialNumber property from "
374                      "interface: {IFACE} exception: {ERROR}",
375                      "IFACE", interface::invAsset, "ERROR", e);
376     }
377 
378     return sn;
379 }
380 
getMotherboardCCIN() const381 std::string DataInterface::getMotherboardCCIN() const
382 {
383     std::string ccin;
384 
385     try
386     {
387         auto service =
388             getService(object_path::motherBoardInv, interface::viniRecordVPD);
389         if (!service.empty())
390         {
391             DBusValue value;
392             getProperty(service, object_path::motherBoardInv,
393                         interface::viniRecordVPD, "CC", value);
394 
395             auto cc = std::get<std::vector<uint8_t>>(value);
396             ccin = std::string{cc.begin(), cc.end()};
397         }
398     }
399     catch (const std::exception& e)
400     {
401         lg2::warning("Failed reading Motherboard CCIN property from "
402                      "interface: {IFACE} exception: {ERROR}",
403                      "IFACE", interface::viniRecordVPD, "ERROR", e);
404     }
405 
406     return ccin;
407 }
408 
getSystemIMKeyword() const409 std::vector<uint8_t> DataInterface::getSystemIMKeyword() const
410 {
411     static std::vector<uint8_t> systemIM;
412 
413     if (!systemIM.empty())
414     {
415         return systemIM;
416     }
417 
418     try
419     {
420         auto subtree = getSubTree({interface::vsbpRecordVPD});
421 
422         if (subtree.empty())
423         {
424             lg2::warning("No VSBP VPD interface found");
425             return systemIM;
426         }
427 
428         DBusValue imValue;
429         const auto& path = subtree.begin()->first;
430         const auto& interfaceMap = subtree.begin()->second;
431         const auto& service = interfaceMap.begin()->first;
432         getProperty(service, path, interface::vsbpRecordVPD, "IM", imValue);
433 
434         systemIM = std::get<std::vector<uint8_t>>(imValue);
435     }
436     catch (const std::exception& e)
437     {
438         lg2::warning("Failed reading System IM property from "
439                      "interface: {IFACE} exception: {ERROR}",
440                      "IFACE", interface::vsbpRecordVPD, "ERROR", e);
441     }
442 
443     return systemIM;
444 }
445 
getHWCalloutFields(const std::string & inventoryPath,std::string & fruPartNumber,std::string & ccin,std::string & serialNumber) const446 void DataInterface::getHWCalloutFields(
447     const std::string& inventoryPath, std::string& fruPartNumber,
448     std::string& ccin, std::string& serialNumber) const
449 {
450     // For now, attempt to get all of the properties directly on the path
451     // passed in.  In the future, may need to make use of an algorithm
452     // to figure out which inventory objects actually hold these
453     // interfaces in the case of non FRUs, or possibly another service
454     // will provide this info.  Any missing interfaces will result
455     // in exceptions being thrown.
456 
457     auto service = getService(inventoryPath, interface::viniRecordVPD);
458 
459     auto properties =
460         getAllProperties(service, inventoryPath, interface::viniRecordVPD);
461 
462     auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
463     fruPartNumber = std::string{value.begin(), value.end()};
464 
465     value = std::get<std::vector<uint8_t>>(properties["CC"]);
466     ccin = std::string{value.begin(), value.end()};
467 
468     value = std::get<std::vector<uint8_t>>(properties["SN"]);
469     serialNumber = std::string{value.begin(), value.end()};
470 }
471 
getLocationCode(const std::string & inventoryPath) const472 std::string DataInterface::getLocationCode(
473     const std::string& inventoryPath) const
474 {
475     auto service = getService(inventoryPath, interface::locCode);
476 
477     DBusValue locCode;
478     getProperty(service, inventoryPath, interface::locCode, "LocationCode",
479                 locCode);
480 
481     return std::get<std::string>(locCode);
482 }
483 
addLocationCodePrefix(const std::string & locationCode)484 std::string DataInterface::addLocationCodePrefix(
485     const std::string& locationCode)
486 {
487     static const std::string locationCodePrefix{"Ufcs-"};
488 
489     // Technically there are 2 location code prefixes, Ufcs and Umts, so
490     // if it already starts with a U then don't need to do anything.
491     if (locationCode.front() != 'U')
492     {
493         return locationCodePrefix + locationCode;
494     }
495 
496     return locationCode;
497 }
498 
expandLocationCode(const std::string & locationCode,uint16_t) const499 std::string DataInterface::expandLocationCode(const std::string& locationCode,
500                                               uint16_t /*node*/) const
501 {
502     // Location codes for connectors are the location code of the FRU they are
503     // on, plus a '-Tx' segment.  Remove this last segment before expanding it
504     // and then add it back in afterwards.  This way, the connector doesn't have
505     // to be in the model just so that it can be expanded.
506     auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode);
507 
508     auto method =
509         _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
510                              interface::vpdManager, "GetExpandedLocationCode");
511 
512     method.append(addLocationCodePrefix(baseLoc), static_cast<uint16_t>(0));
513 
514     auto reply = _bus.call(method, dbusTimeout);
515 
516     auto expandedLocationCode = reply.unpack<std::string>();
517 
518     if (!connectorLoc.empty())
519     {
520         expandedLocationCode += connectorLoc;
521     }
522 
523     return expandedLocationCode;
524 }
525 
getInventoryFromLocCode(const std::string & locationCode,uint16_t node,bool expanded) const526 std::vector<std::string> DataInterface::getInventoryFromLocCode(
527     const std::string& locationCode, uint16_t node, bool expanded) const
528 {
529     std::string methodName = expanded ? "GetFRUsByExpandedLocationCode"
530                                       : "GetFRUsByUnexpandedLocationCode";
531 
532     // Remove the connector segment, if present, so that this method call
533     // returns an inventory path that getHWCalloutFields() can be used with.
534     // (The serial number, etc, aren't stored on the connector in the
535     // inventory, and may not even be modeled.)
536     auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode);
537 
538     auto method =
539         _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
540                              interface::vpdManager, methodName.c_str());
541 
542     if (expanded)
543     {
544         method.append(baseLoc);
545     }
546     else
547     {
548         method.append(addLocationCodePrefix(baseLoc), node);
549     }
550 
551     auto reply = _bus.call(method, dbusTimeout);
552 
553     auto entries = reply.unpack<std::vector<sdbusplus::message::object_path>>();
554 
555     std::vector<std::string> paths;
556 
557     // Note: The D-Bus method will fail if nothing found.
558     std::for_each(entries.begin(), entries.end(),
559                   [&paths](const auto& path) { paths.push_back(path); });
560 
561     return paths;
562 }
563 
assertLEDGroup(const std::string & ledGroup,bool value) const564 void DataInterface::assertLEDGroup(const std::string& ledGroup,
565                                    bool value) const
566 {
567     DBusValue variant = value;
568     auto method =
569         _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(),
570                              interface::dbusProperty, "Set");
571     method.append(interface::ledGroup, "Asserted", variant);
572     _bus.call(method, dbusTimeout);
573 }
574 
setFunctional(const std::string & objectPath,bool value) const575 void DataInterface::setFunctional(const std::string& objectPath,
576                                   bool value) const
577 {
578     DBusPropertyMap prop{{"Functional", value}};
579     DBusInterfaceMap iface{{interface::operationalStatus, prop}};
580 
581     // PIM takes a relative path like /system/chassis so remove
582     // /xyz/openbmc_project/inventory if present.
583     std::string path{objectPath};
584     if (path.starts_with(object_path::baseInv))
585     {
586         path = objectPath.substr(strlen(object_path::baseInv));
587     }
588     DBusObjectMap object{{path, iface}};
589 
590     auto method = _bus.new_method_call(service_name::inventoryManager,
591                                        object_path::baseInv,
592                                        interface::inventoryManager, "Notify");
593     method.append(std::move(object));
594     _bus.call(method, dbusTimeout);
595 }
596 
597 using AssociationTuple = std::tuple<std::string, std::string, std::string>;
598 using AssociationsProperty = std::vector<AssociationTuple>;
599 
setCriticalAssociation(const std::string & objectPath) const600 void DataInterface::setCriticalAssociation(const std::string& objectPath) const
601 {
602     DBusValue getAssociationValue;
603 
604     auto service = getService(objectPath, interface::associationDef);
605 
606     getProperty(service, objectPath, interface::associationDef, "Associations",
607                 getAssociationValue);
608 
609     auto association = std::get<AssociationsProperty>(getAssociationValue);
610 
611     AssociationTuple critAssociation{
612         "health_rollup", "critical",
613         "/xyz/openbmc_project/inventory/system/chassis"};
614 
615     if (std::find(association.begin(), association.end(), critAssociation) ==
616         association.end())
617     {
618         association.push_back(critAssociation);
619         DBusValue setAssociationValue = association;
620 
621         auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
622                                            interface::dbusProperty, "Set");
623 
624         method.append(interface::associationDef, "Associations",
625                       setAssociationValue);
626         _bus.call(method, dbusTimeout);
627     }
628 }
629 
getSystemNames() const630 std::vector<std::string> DataInterface::getSystemNames() const
631 {
632     auto subtree = getSubTree({interface::compatible});
633 
634     if (subtree.empty())
635     {
636         throw std::runtime_error("Compatible interface not on D-Bus");
637     }
638 
639     for (const auto& [path, interfaceMap] : subtree)
640     {
641         auto iface = interfaceMap.find(service_name::entityManager);
642         if (iface == interfaceMap.end())
643         {
644             continue;
645         }
646 
647         DBusValue names;
648 
649         getProperty(iface->first, path, interface::compatible, "Names", names);
650 
651         return std::get<std::vector<std::string>>(names);
652     }
653 
654     throw std::runtime_error("EM Compatible interface not on D-Bus");
655 }
656 
getQuiesceOnError() const657 bool DataInterface::getQuiesceOnError() const
658 {
659     bool ret = false;
660 
661     try
662     {
663         auto service =
664             getService(object_path::logSetting, interface::logSetting);
665         if (!service.empty())
666         {
667             DBusValue value;
668             getProperty(service, object_path::logSetting, interface::logSetting,
669                         "QuiesceOnHwError", value);
670 
671             ret = std::get<bool>(value);
672         }
673     }
674     catch (const std::exception& e)
675     {
676         lg2::warning("Failed reading QuiesceOnHwError property from "
677                      "interface: {IFACE} exception: {ERROR}",
678                      "IFACE", interface::logSetting, "ERROR", e);
679     }
680 
681     return ret;
682 }
683 
684 #ifdef PEL_ENABLE_PHAL
createGuardRecord(const std::vector<uint8_t> & binPath,GardType eGardType,uint32_t plid) const685 void DataInterface::createGuardRecord(const std::vector<uint8_t>& binPath,
686                                       GardType eGardType, uint32_t plid) const
687 {
688     try
689     {
690         libguard::libguard_init(false);
691         libguard::create(binPath, plid, eGardType);
692     }
693     catch (libguard::exception::GuardException& e)
694     {
695         lg2::error("Exception in libguard {ERROR}", "ERROR", e);
696     }
697 }
698 #endif
699 
createProgressSRC(const std::vector<uint8_t> & priSRC,const std::vector<uint8_t> & srcStruct) const700 void DataInterface::createProgressSRC(
701     const std::vector<uint8_t>& priSRC,
702     const std::vector<uint8_t>& srcStruct) const
703 {
704     DBusValue variant = std::make_tuple(priSRC, srcStruct);
705 
706     auto method = _bus.new_method_call(service_name::bootRawProgress,
707                                        object_path::bootRawProgress,
708                                        interface::dbusProperty, "Set");
709 
710     method.append(interface::bootRawProgress, "Value", variant);
711 
712     _bus.call(method, dbusTimeout);
713 }
714 
getLogIDWithHwIsolation() const715 std::vector<uint32_t> DataInterface::getLogIDWithHwIsolation() const
716 {
717     std::vector<std::string> association = {"xyz.openbmc_project.Association"};
718     std::string hwErrorLog = "/isolated_hw_errorlog";
719     std::string errorLog = "/error_log";
720     DBusPathList paths;
721     std::vector<uint32_t> ids;
722 
723     // Get all latest mapper associations
724     paths = getPaths(association);
725     for (auto& path : paths)
726     {
727         // Look for object path with hardware isolation entry if any
728         size_t pos = path.find(hwErrorLog);
729         if (pos != std::string::npos)
730         {
731             // Get the object path
732             std::string ph = path;
733             ph.erase(pos, hwErrorLog.length());
734             auto service = getService(ph, interface::hwIsolationEntry);
735             if (!service.empty())
736             {
737                 bool status;
738                 DBusValue value;
739 
740                 // Read the Resolved property from object path
741                 getProperty(service, ph, interface::hwIsolationEntry,
742                             "Resolved", value);
743 
744                 status = std::get<bool>(value);
745 
746                 // If the entry isn't resolved
747                 if (!status)
748                 {
749                     auto assocService =
750                         getService(path, interface::association);
751                     if (!assocService.empty())
752                     {
753                         DBusValue endpoints;
754 
755                         // Read Endpoints property
756                         getProperty(assocService, path, interface::association,
757                                     "endpoints", endpoints);
758 
759                         auto logPath =
760                             std::get<std::vector<std::string>>(endpoints);
761                         if (!logPath.empty())
762                         {
763                             // Get OpenBMC event log Id
764                             uint32_t id = stoi(logPath[0].substr(
765                                 logPath[0].find_last_of('/') + 1));
766                             ids.push_back(id);
767                         }
768                     }
769                 }
770             }
771         }
772 
773         // Look for object path with error_log entry if any
774         pos = path.find(errorLog);
775         if (pos != std::string::npos)
776         {
777             auto service = getService(path, interface::association);
778             if (!service.empty())
779             {
780                 DBusValue value;
781 
782                 // Read Endpoints property
783                 getProperty(service, path, interface::association, "endpoints",
784                             value);
785 
786                 auto logPath = std::get<std::vector<std::string>>(value);
787                 if (!logPath.empty())
788                 {
789                     // Get OpenBMC event log Id
790                     uint32_t id = stoi(
791                         logPath[0].substr(logPath[0].find_last_of('/') + 1));
792                     ids.push_back(id);
793                 }
794             }
795         }
796     }
797 
798     if (ids.size() > 1)
799     {
800         // remove duplicates to have only unique ids
801         std::sort(ids.begin(), ids.end());
802         ids.erase(std::unique(ids.begin(), ids.end()), ids.end());
803     }
804     return ids;
805 }
806 
getRawProgressSRC(void) const807 std::vector<uint8_t> DataInterface::getRawProgressSRC(void) const
808 {
809     using RawProgressProperty =
810         std::tuple<std::vector<uint8_t>, std::vector<uint8_t>>;
811 
812     DBusValue value;
813     getProperty(service_name::bootRawProgress, object_path::bootRawProgress,
814                 interface::bootRawProgress, "Value", value);
815 
816     const auto& rawProgress = std::get<RawProgressProperty>(value);
817     return std::get<1>(rawProgress);
818 }
819 
getDIProperty(const std::string & locationCode) const820 std::optional<std::vector<uint8_t>> DataInterface::getDIProperty(
821     const std::string& locationCode) const
822 {
823     std::vector<uint8_t> viniDI;
824 
825     try
826     {
827         // Note : The hardcoded value 0 should be changed when comes to
828         // multinode system.
829         auto objectPath = getInventoryFromLocCode(locationCode, 0, true);
830 
831         DBusValue value;
832         getProperty(service_name::inventoryManager, objectPath[0],
833                     interface::viniRecordVPD, "DI", value);
834 
835         viniDI = std::get<std::vector<uint8_t>>(value);
836     }
837     catch (const std::exception& e)
838     {
839         lg2::warning(
840             "Failed reading DI property for the location code : {LOC_CODE} from "
841             "interface: {IFACE} exception: {ERROR}",
842             "LOC_CODE", locationCode, "IFACE", interface::viniRecordVPD,
843             "ERROR", e);
844         return std::nullopt;
845     }
846 
847     return viniDI;
848 }
849 
isDIMMLocCode(const std::string & locCode) const850 std::optional<bool> DataInterfaceBase::isDIMMLocCode(
851     const std::string& locCode) const
852 {
853     if (_locationCache.contains(locCode))
854     {
855         return _locationCache.at(locCode);
856     }
857     else
858     {
859         return std::nullopt;
860     }
861 }
862 
addDIMMLocCode(const std::string & locCode,bool isFRUDIMM)863 void DataInterfaceBase::addDIMMLocCode(const std::string& locCode,
864                                        bool isFRUDIMM)
865 {
866     _locationCache.insert({locCode, isFRUDIMM});
867 }
868 
isDIMM(const std::string & locCode)869 bool DataInterfaceBase::isDIMM(const std::string& locCode)
870 {
871     auto isDIMMType = isDIMMLocCode(locCode);
872     if (isDIMMType.has_value())
873     {
874         return isDIMMType.value();
875     }
876 #ifndef PEL_ENABLE_PHAL
877     return false;
878 #else
879     else
880     {
881         // Invoke pHAL API inorder to fetch the FRU Type
882         auto fruType = openpower::phal::pdbg::getFRUType(locCode);
883         bool isDIMMFRU{false};
884         if (fruType.has_value())
885         {
886             if (fruType.value() == ENUM_ATTR_TYPE_DIMM)
887             {
888                 isDIMMFRU = true;
889             }
890             addDIMMLocCode(locCode, isDIMMFRU);
891         }
892         return isDIMMFRU;
893     }
894 #endif
895 }
896 
getAssociatedPaths(const DBusPath & associatedPath,const DBusPath & subtree,int32_t depth,const DBusInterfaceList & interfaces) const897 DBusPathList DataInterface::getAssociatedPaths(
898     const DBusPath& associatedPath, const DBusPath& subtree, int32_t depth,
899     const DBusInterfaceList& interfaces) const
900 {
901     DBusPathList paths;
902     try
903     {
904         auto method = _bus.new_method_call(
905             service_name::objectMapper, object_path::objectMapper,
906             interface::objectMapper, "GetAssociatedSubTreePaths");
907         method.append(sdbusplus::message::object_path(associatedPath),
908                       sdbusplus::message::object_path(subtree), depth,
909                       interfaces);
910 
911         auto reply = _bus.call(method, dbusTimeout);
912         reply.read(paths);
913     }
914     catch (const std::exception& e)
915     {
916         std::string ifaces(
917             std::ranges::fold_left_first(
918                 interfaces,
919                 [](std::string ifaces, const std::string& iface) {
920                     return ifaces + ", " + iface;
921                 })
922                 .value_or(""));
923 
924         lg2::error("Failed getting associated paths: {ERROR}. "
925                    "AssociatedPath: {ASSOIC_PATH} Subtree: {SUBTREE} "
926                    "Interfaces: {IFACES}",
927                    "ERROR", e, "ASSOIC_PATH", associatedPath, "SUBTREE",
928                    subtree, "IFACES", ifaces);
929     }
930     return paths;
931 }
932 
startFruPlugWatch()933 void DataInterface::startFruPlugWatch()
934 {
935     // Add a watch on inventory InterfacesAdded and then find all
936     // existing hotpluggable interfaces and add propertiesChanged
937     // watches on them.
938 
939     _invIaMatch = std::make_unique<sdbusplus::bus::match_t>(
940         _bus, match_rules::interfacesAdded(object_path::baseInv),
941         std::bind(&DataInterface::inventoryIfaceAdded, this,
942                   std::placeholders::_1));
943     try
944     {
945         auto paths = getPaths(hotplugInterfaces);
946 
947         _invPresentMatches.clear();
948 
949         std::for_each(paths.begin(), paths.end(),
950                       [this](const auto& path) { addHotplugWatch(path); });
951     }
952     catch (const sdbusplus::exception_t& e)
953     {
954         lg2::warning("Failed getting FRU paths to watch: {ERROR}", "ERROR", e);
955     }
956 }
957 
addHotplugWatch(const std::string & path)958 void DataInterface::addHotplugWatch(const std::string& path)
959 {
960     if (!_invPresentMatches.contains(path))
961     {
962         _invPresentMatches.emplace(
963             path,
964             std::make_unique<sdbusplus::bus::match_t>(
965                 _bus, match_rules::propertiesChanged(path, interface::invItem),
966                 std::bind(&DataInterface::presenceChanged, this,
967                           std::placeholders::_1)));
968     }
969 }
970 
inventoryIfaceAdded(sdbusplus::message_t & msg)971 void DataInterface::inventoryIfaceAdded(sdbusplus::message_t& msg)
972 {
973     sdbusplus::message::object_path path;
974     DBusInterfaceMap interfaces;
975 
976     msg.read(path, interfaces);
977 
978     // Check if any of the new interfaces are for hot pluggable FRUs.
979     if (std::find_if(interfaces.begin(), interfaces.end(),
980                      [](const auto& interfacePair) {
981                          return std::find(hotplugInterfaces.begin(),
982                                           hotplugInterfaces.end(),
983                                           interfacePair.first) !=
984                                 hotplugInterfaces.end();
985                      }) == interfaces.end())
986     {
987         return;
988     }
989 
990     addHotplugWatch(path.str);
991 
992     // If an Inventory.Item interface was also added, check presence now.
993 
994     // Notes:
995     // * This assumes the Inv.Item and Inv.Fan/PS are added together which
996     //   is currently the case.
997     // * If the code ever switches to something without a Present
998     //   property, then the IA signal itself would probably indicate presence.
999 
1000     auto itemIt = interfaces.find(interface::invItem);
1001     if (itemIt != interfaces.end())
1002     {
1003         notifyPresenceSubscribers(path.str, itemIt->second);
1004     }
1005 }
1006 
presenceChanged(sdbusplus::message_t & msg)1007 void DataInterface::presenceChanged(sdbusplus::message_t& msg)
1008 {
1009     DBusInterface interface;
1010     DBusPropertyMap properties;
1011 
1012     msg.read(interface, properties);
1013     if (interface != interface::invItem)
1014     {
1015         return;
1016     }
1017 
1018     std::string path = msg.get_path();
1019     notifyPresenceSubscribers(path, properties);
1020 }
1021 
notifyPresenceSubscribers(const std::string & path,const DBusPropertyMap & properties)1022 void DataInterface::notifyPresenceSubscribers(const std::string& path,
1023                                               const DBusPropertyMap& properties)
1024 {
1025     auto prop = properties.find("Present");
1026     if ((prop == properties.end()) || (!std::get<bool>(prop->second)))
1027     {
1028         return;
1029     }
1030 
1031     std::string locCode;
1032 
1033     try
1034     {
1035         auto service = getService(path, interface::locCode);
1036 
1037         // If the hotplugged FRU is hosted by PLDM, then it is
1038         // in an IO expansion drawer and we don't care about it.
1039         if (service == service_name::pldm)
1040         {
1041             return;
1042         }
1043 
1044         locCode = getLocationCode(path);
1045     }
1046     catch (const sdbusplus::exception_t& e)
1047     {
1048         lg2::debug("Could not get location code for {PATH}: {ERROR}", "PATH",
1049                    path, "ERROR", e);
1050         return;
1051     }
1052 
1053     lg2::debug("Detected FRU {PATH} ({LOC}) present ", "PATH", path, "LOC",
1054                locCode);
1055 
1056     // Tell the subscribers.
1057     setFruPresent(locCode);
1058 }
1059 
isPHALDevTreeExist() const1060 bool DataInterface::isPHALDevTreeExist() const
1061 {
1062     try
1063     {
1064         if (std::filesystem::exists(PDBG_DTB_PATH))
1065         {
1066             return true;
1067         }
1068     }
1069     catch (const std::exception& e)
1070     {
1071         lg2::error("Failed to check device tree {PHAL_DEVTREE_PATH} existence, "
1072                    "{ERROR}",
1073                    "PHAL_DEVTREE_PATH", PDBG_DTB_PATH, "ERROR", e);
1074     }
1075     return false;
1076 }
1077 
1078 #ifdef PEL_ENABLE_PHAL
initPHAL()1079 void DataInterface::initPHAL()
1080 {
1081     if (setenv("PDBG_DTB", PDBG_DTB_PATH, 1))
1082     {
1083         // Log message and continue,
1084         // This is to help continue creating PEL in raw format.
1085         lg2::error("Failed to set PDBG_DTB: ({ERRNO})", "ERRNO",
1086                    strerror(errno));
1087     }
1088 
1089     if (!pdbg_targets_init(NULL))
1090     {
1091         lg2::error("pdbg_targets_init failed");
1092         return;
1093     }
1094 
1095     if (libekb_init())
1096     {
1097         lg2::error("libekb_init failed, skipping ffdc processing");
1098         return;
1099     }
1100 }
1101 #endif
1102 
subscribeToSystemdSignals()1103 void DataInterface::subscribeToSystemdSignals()
1104 {
1105     try
1106     {
1107         auto method =
1108             _bus.new_method_call(service_name::systemd, object_path::systemd,
1109                                  interface::systemdMgr, "Subscribe");
1110         _systemdSlot = method.call_async([this](sdbusplus::message_t&& msg) {
1111             // Initializing with nullptr to indicate that it is not subscribed
1112             // to any signal.
1113             this->_systemdSlot = sdbusplus::slot_t(nullptr);
1114             if (msg.is_method_error())
1115             {
1116                 auto* error = msg.get_error();
1117                 lg2::error("Failed to subscribe JobRemoved systemd signal, "
1118                            "errorName: {ERR_NAME}, errorMsg: {ERR_MSG} ",
1119                            "ERR_NAME", error->name, "ERR_MSG", error->message);
1120                 return;
1121             }
1122 
1123             namespace sdbusRule = sdbusplus::bus::match::rules;
1124             this->_systemdMatch =
1125                 std::make_unique<decltype(this->_systemdMatch)::element_type>(
1126                     this->_bus,
1127                     sdbusRule::type::signal() +
1128                         sdbusRule::member("JobRemoved") +
1129                         sdbusRule::path(object_path::systemd) +
1130                         sdbusRule::interface(interface::systemdMgr),
1131                     [this](sdbusplus::message_t& msg) {
1132                         uint32_t jobID;
1133                         sdbusplus::message::object_path jobObjPath;
1134                         std::string jobUnitName, jobUnitResult;
1135 
1136                         msg.read(jobID, jobObjPath, jobUnitName, jobUnitResult);
1137                         if ((jobUnitName == "obmc-recover-pnor.service") &&
1138                             (jobUnitResult == "done"))
1139                         {
1140 #ifdef PEL_ENABLE_PHAL
1141                             this->initPHAL();
1142 #endif
1143                             // Invoke unsubscribe method to stop monitoring for
1144                             // JobRemoved signals.
1145                             this->unsubscribeFromSystemdSignals();
1146                         }
1147                     });
1148         });
1149     }
1150     catch (const sdbusplus::exception_t& e)
1151     {
1152         lg2::error(
1153             "Exception occured while handling JobRemoved systemd signal, "
1154             "exception: {ERROR}",
1155             "ERROR", e);
1156     }
1157 }
1158 
unsubscribeFromSystemdSignals()1159 void DataInterface::unsubscribeFromSystemdSignals()
1160 {
1161     try
1162     {
1163         auto method =
1164             _bus.new_method_call(service_name::systemd, object_path::systemd,
1165                                  interface::systemdMgr, "Unsubscribe");
1166         _systemdSlot = method.call_async([this](sdbusplus::message_t&& msg) {
1167             // Unsubscribing the _systemdSlot from the subscribed signal
1168             this->_systemdSlot = sdbusplus::slot_t(nullptr);
1169             if (msg.is_method_error())
1170             {
1171                 auto* error = msg.get_error();
1172                 lg2::error(
1173                     "Failed to unsubscribe from JobRemoved systemd signal, "
1174                     "errorName: {ERR_NAME}, errorMsg: {ERR_MSG} ",
1175                     "ERR_NAME", error->name, "ERR_MSG", error->message);
1176                 return;
1177             }
1178             // Reset _systemdMatch to avoid reception of further JobRemoved
1179             // signals
1180             this->_systemdMatch.reset();
1181         });
1182     }
1183     catch (const sdbusplus::exception_t& e)
1184     {
1185         lg2::error(
1186             "Exception occured while unsubscribing from JobRemoved systemd signal, "
1187             "exception: {ERROR}",
1188             "ERROR", e);
1189     }
1190 }
1191 
1192 } // namespace pels
1193 } // namespace openpower
1194