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