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