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