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