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