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