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