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