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