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