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 
17 #include "data_interface.hpp"
18 
19 #include "util.hpp"
20 
21 #include <phosphor-logging/lg2.hpp>
22 #include <xyz/openbmc_project/State/BMC/server.hpp>
23 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
24 
25 // Use a timeout of 10s for D-Bus calls so if there are
26 // timeouts the callers of the PEL creation method won't
27 // also timeout.
28 constexpr auto dbusTimeout = 10000000;
29 
30 namespace openpower
31 {
32 namespace pels
33 {
34 
35 namespace service_name
36 {
37 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
38 constexpr auto vpdManager = "com.ibm.VPD.Manager";
39 constexpr auto ledGroupManager = "xyz.openbmc_project.LED.GroupManager";
40 constexpr auto hwIsolation = "org.open_power.HardwareIsolation";
41 constexpr auto biosConfigMgr = "xyz.openbmc_project.BIOSConfigManager";
42 constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw";
43 constexpr auto pldm = "xyz.openbmc_project.PLDM";
44 constexpr auto inventoryManager = "xyz.openbmc_project.Inventory.Manager";
45 constexpr auto entityManager = "xyz.openbmc_project.EntityManager";
46 } // namespace service_name
47 
48 namespace object_path
49 {
50 constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper";
51 constexpr auto systemInv = "/xyz/openbmc_project/inventory/system";
52 constexpr auto motherBoardInv =
53     "/xyz/openbmc_project/inventory/system/chassis/motherboard";
54 constexpr auto baseInv = "/xyz/openbmc_project/inventory";
55 constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0";
56 constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0";
57 constexpr auto hostState = "/xyz/openbmc_project/state/host0";
58 constexpr auto enableHostPELs =
59     "/xyz/openbmc_project/logging/send_event_logs_to_host";
60 constexpr auto vpdManager = "/com/ibm/VPD/Manager";
61 constexpr auto logSetting = "/xyz/openbmc_project/logging/settings";
62 constexpr auto hwIsolation = "/xyz/openbmc_project/hardware_isolation";
63 constexpr auto biosConfigMgr = "/xyz/openbmc_project/bios_config/manager";
64 constexpr auto bootRawProgress = "/xyz/openbmc_project/state/boot/raw0";
65 } // namespace object_path
66 
67 namespace interface
68 {
69 constexpr auto dbusProperty = "org.freedesktop.DBus.Properties";
70 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
71 constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset";
72 constexpr auto bootProgress = "xyz.openbmc_project.State.Boot.Progress";
73 constexpr auto enable = "xyz.openbmc_project.Object.Enable";
74 constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
75 constexpr auto chassisState = "xyz.openbmc_project.State.Chassis";
76 constexpr auto hostState = "xyz.openbmc_project.State.Host";
77 constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI";
78 constexpr auto vsbpRecordVPD = "com.ibm.ipzvpd.VSBP";
79 constexpr auto locCode = "xyz.openbmc_project.Inventory.Decorator.LocationCode";
80 constexpr auto compatible =
81     "xyz.openbmc_project.Inventory.Decorator.Compatible";
82 constexpr auto vpdManager = "com.ibm.VPD.Manager";
83 constexpr auto ledGroup = "xyz.openbmc_project.Led.Group";
84 constexpr auto operationalStatus =
85     "xyz.openbmc_project.State.Decorator.OperationalStatus";
86 constexpr auto logSetting = "xyz.openbmc_project.Logging.Settings";
87 constexpr auto associationDef = "xyz.openbmc_project.Association.Definitions";
88 constexpr auto dumpEntry = "xyz.openbmc_project.Dump.Entry";
89 constexpr auto dumpProgress = "xyz.openbmc_project.Common.Progress";
90 constexpr auto hwIsolationCreate = "org.open_power.HardwareIsolation.Create";
91 constexpr auto hwIsolationEntry = "xyz.openbmc_project.HardwareIsolation.Entry";
92 constexpr auto association = "xyz.openbmc_project.Association";
93 constexpr auto biosConfigMgr = "xyz.openbmc_project.BIOSConfig.Manager";
94 constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw";
95 constexpr auto invItem = "xyz.openbmc_project.Inventory.Item";
96 constexpr auto invFan = "xyz.openbmc_project.Inventory.Item.Fan";
97 constexpr auto invPowerSupply =
98     "xyz.openbmc_project.Inventory.Item.PowerSupply";
99 constexpr auto inventoryManager = "xyz.openbmc_project.Inventory.Manager";
100 } // namespace interface
101 
102 using namespace sdbusplus::server::xyz::openbmc_project::state::boot;
103 using namespace sdbusplus::server::xyz::openbmc_project::state;
104 namespace match_rules = sdbusplus::bus::match::rules;
105 
106 const DBusInterfaceList hotplugInterfaces{interface::invFan,
107                                           interface::invPowerSupply};
108 
109 std::pair<std::string, std::string>
extractConnectorFromLocCode(const std::string & locationCode)110     DataInterfaceBase::extractConnectorFromLocCode(
111         const std::string& locationCode)
112 {
113     auto base = locationCode;
114     std::string connector{};
115 
116     auto pos = base.find("-T");
117     if (pos != std::string::npos)
118     {
119         connector = base.substr(pos);
120         base = base.substr(0, pos);
121     }
122 
123     return {base, connector};
124 }
125 
DataInterface(sdbusplus::bus_t & bus)126 DataInterface::DataInterface(sdbusplus::bus_t& bus) : _bus(bus)
127 {
128     readBMCFWVersion();
129     readServerFWVersion();
130     readBMCFWVersionID();
131 
132     // Watch the BootProgress property
133     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
134         bus, object_path::hostState, interface::bootProgress, "BootProgress",
135         *this, [this](const auto& value) {
136         this->_bootState = std::get<std::string>(value);
137         auto status = Progress::convertProgressStagesFromString(
138             std::get<std::string>(value));
139 
140         if ((status == Progress::ProgressStages::SystemInitComplete) ||
141             (status == Progress::ProgressStages::OSRunning))
142         {
143             setHostUp(true);
144         }
145         else
146         {
147             setHostUp(false);
148         }
149     }));
150 
151     // Watch the host PEL enable property
152     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
153         bus, object_path::enableHostPELs, interface::enable, "Enabled", *this,
154         [this](const auto& value) {
155         if (std::get<bool>(value) != this->_sendPELsToHost)
156         {
157             lg2::info("The send PELs to host setting changed to {VAL}", "VAL",
158                       std::get<bool>(value));
159         }
160         this->_sendPELsToHost = std::get<bool>(value);
161     }));
162 
163     // Watch the BMCState property
164     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
165         bus, object_path::bmcState, interface::bmcState, "CurrentBMCState",
166         *this, [this](const auto& value) {
167         const auto& state = std::get<std::string>(value);
168         this->_bmcState = state;
169 
170         // Wait for BMC ready to start watching for
171         // plugs so things calm down first.
172         if (BMC::convertBMCStateFromString(state) == BMC::BMCState::Ready)
173         {
174             startFruPlugWatch();
175         }
176     }));
177 
178     // Watch the chassis current and requested power state properties
179     _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
180         bus, object_path::chassisState, interface::chassisState, *this,
181         [this](const auto& properties) {
182         auto state = properties.find("CurrentPowerState");
183         if (state != properties.end())
184         {
185             this->_chassisState = std::get<std::string>(state->second);
186         }
187 
188         auto trans = properties.find("RequestedPowerTransition");
189         if (trans != properties.end())
190         {
191             this->_chassisTransition = std::get<std::string>(trans->second);
192         }
193     }));
194 
195     // Watch the CurrentHostState property
196     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
197         bus, object_path::hostState, interface::hostState, "CurrentHostState",
198         *this, [this](const auto& value) {
199         this->_hostState = std::get<std::string>(value);
200     }));
201 
202     // Watch the BaseBIOSTable property for the hmc managed attribute
203     _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
204         bus, object_path::biosConfigMgr, interface::biosConfigMgr,
205         "BaseBIOSTable", service_name::biosConfigMgr, *this,
206         [this](const auto& value) {
207         const auto& attributes = std::get<BiosAttributes>(value);
208 
209         auto it = attributes.find("pvm_hmc_managed");
210         if (it != attributes.end())
211         {
212             const auto& currentValVariant = std::get<5>(it->second);
213             auto currentVal = std::get_if<std::string>(&currentValVariant);
214             if (currentVal)
215             {
216                 this->_hmcManaged = (*currentVal == "Enabled") ? true : false;
217             }
218         }
219     }));
220 }
221 
222 DBusPropertyMap
getAllProperties(const std::string & service,const std::string & objectPath,const std::string & interface) const223     DataInterface::getAllProperties(const std::string& service,
224                                     const std::string& objectPath,
225                                     const std::string& interface) const
226 {
227     DBusPropertyMap properties;
228 
229     auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
230                                        interface::dbusProperty, "GetAll");
231     method.append(interface);
232     auto reply = _bus.call(method, dbusTimeout);
233 
234     reply.read(properties);
235 
236     return properties;
237 }
238 
getProperty(const std::string & service,const std::string & objectPath,const std::string & interface,const std::string & property,DBusValue & value) const239 void DataInterface::getProperty(const std::string& service,
240                                 const std::string& objectPath,
241                                 const std::string& interface,
242                                 const std::string& property,
243                                 DBusValue& value) const
244 {
245     auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
246                                        interface::dbusProperty, "Get");
247     method.append(interface, property);
248     auto reply = _bus.call(method, dbusTimeout);
249 
250     reply.read(value);
251 }
252 
getPaths(const DBusInterfaceList & interfaces) const253 DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
254 {
255     auto method = _bus.new_method_call(
256         service_name::objectMapper, object_path::objectMapper,
257         interface::objectMapper, "GetSubTreePaths");
258 
259     method.append(std::string{"/"}, 0, interfaces);
260 
261     auto reply = _bus.call(method, dbusTimeout);
262 
263     DBusPathList paths;
264     reply.read(paths);
265 
266     return paths;
267 }
268 
getService(const std::string & objectPath,const std::string & interface) const269 DBusService DataInterface::getService(const std::string& objectPath,
270                                       const std::string& interface) const
271 {
272     auto method = _bus.new_method_call(service_name::objectMapper,
273                                        object_path::objectMapper,
274                                        interface::objectMapper, "GetObject");
275 
276     method.append(objectPath, std::vector<std::string>({interface}));
277 
278     auto reply = _bus.call(method, dbusTimeout);
279 
280     std::map<DBusService, DBusInterfaceList> response;
281     reply.read(response);
282 
283     if (!response.empty())
284     {
285         return response.begin()->first;
286     }
287 
288     return std::string{};
289 }
290 
readBMCFWVersion()291 void DataInterface::readBMCFWVersion()
292 {
293     _bmcFWVersion =
294         phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
295 }
296 
readServerFWVersion()297 void DataInterface::readServerFWVersion()
298 {
299     auto value =
300         phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
301     if ((value != "") && (value.find_last_of(')') != std::string::npos))
302     {
303         std::size_t pos = value.find_first_of('(') + 1;
304         _serverFWVersion = value.substr(pos, value.find_last_of(')') - pos);
305     }
306 }
307 
readBMCFWVersionID()308 void DataInterface::readBMCFWVersionID()
309 {
310     _bmcFWVersionID =
311         phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
312 }
313 
getMachineTypeModel() const314 std::string DataInterface::getMachineTypeModel() const
315 {
316     std::string model;
317     try
318     {
319         auto service = getService(object_path::systemInv, interface::invAsset);
320         if (!service.empty())
321         {
322             DBusValue value;
323             getProperty(service, object_path::systemInv, interface::invAsset,
324                         "Model", value);
325 
326             model = std::get<std::string>(value);
327         }
328     }
329     catch (const std::exception& e)
330     {
331         lg2::warning("Failed reading Model property from "
332                      "interface: {IFACE} exception: {ERROR}",
333                      "IFACE", interface::invAsset, "ERROR", e);
334     }
335 
336     return model;
337 }
338 
getMachineSerialNumber() const339 std::string DataInterface::getMachineSerialNumber() const
340 {
341     std::string sn;
342     try
343     {
344         auto service = getService(object_path::systemInv, interface::invAsset);
345         if (!service.empty())
346         {
347             DBusValue value;
348             getProperty(service, object_path::systemInv, interface::invAsset,
349                         "SerialNumber", value);
350 
351             sn = std::get<std::string>(value);
352         }
353     }
354     catch (const std::exception& e)
355     {
356         lg2::warning("Failed reading SerialNumber property from "
357                      "interface: {IFACE} exception: {ERROR}",
358                      "IFACE", interface::invAsset, "ERROR", e);
359     }
360 
361     return sn;
362 }
363 
getMotherboardCCIN() const364 std::string DataInterface::getMotherboardCCIN() const
365 {
366     std::string ccin;
367 
368     try
369     {
370         auto service = getService(object_path::motherBoardInv,
371                                   interface::viniRecordVPD);
372         if (!service.empty())
373         {
374             DBusValue value;
375             getProperty(service, object_path::motherBoardInv,
376                         interface::viniRecordVPD, "CC", value);
377 
378             auto cc = std::get<std::vector<uint8_t>>(value);
379             ccin = std::string{cc.begin(), cc.end()};
380         }
381     }
382     catch (const std::exception& e)
383     {
384         lg2::warning("Failed reading Motherboard CCIN property from "
385                      "interface: {IFACE} exception: {ERROR}",
386                      "IFACE", interface::viniRecordVPD, "ERROR", e);
387     }
388 
389     return ccin;
390 }
391 
getSystemIMKeyword() const392 std::vector<uint8_t> DataInterface::getSystemIMKeyword() const
393 {
394     std::vector<uint8_t> systemIM;
395 
396     try
397     {
398         auto service = getService(object_path::motherBoardInv,
399                                   interface::vsbpRecordVPD);
400         if (!service.empty())
401         {
402             DBusValue value;
403             getProperty(service, object_path::motherBoardInv,
404                         interface::vsbpRecordVPD, "IM", value);
405 
406             systemIM = std::get<std::vector<uint8_t>>(value);
407         }
408     }
409     catch (const std::exception& e)
410     {
411         lg2::warning("Failed reading System IM property from "
412                      "interface: {IFACE} exception: {ERROR}",
413                      "IFACE", interface::vsbpRecordVPD, "ERROR", e);
414     }
415 
416     return systemIM;
417 }
418 
getHWCalloutFields(const std::string & inventoryPath,std::string & fruPartNumber,std::string & ccin,std::string & serialNumber) const419 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
getLocationCode(const std::string & inventoryPath) const447     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
addLocationCodePrefix(const std::string & locationCode)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 
expandLocationCode(const std::string & locationCode,uint16_t) const473 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>
getInventoryFromLocCode(const std::string & locationCode,uint16_t node,bool expanded) const502     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 
assertLEDGroup(const std::string & ledGroup,bool value) const541 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 
setFunctional(const std::string & objectPath,bool value) const552 void DataInterface::setFunctional(const std::string& objectPath,
553                                   bool value) const
554 {
555     DBusPropertyMap prop{{"Functional", value}};
556     DBusInterfaceMap iface{{interface::operationalStatus, prop}};
557 
558     // PIM takes a relative path like /system/chassis so remove
559     // /xyz/openbmc_project/inventory if present.
560     std::string path{objectPath};
561     if (path.starts_with(object_path::baseInv))
562     {
563         path = objectPath.substr(strlen(object_path::baseInv));
564     }
565     DBusObjectMap object{{path, iface}};
566 
567     auto method = _bus.new_method_call(service_name::inventoryManager,
568                                        object_path::baseInv,
569                                        interface::inventoryManager, "Notify");
570     method.append(std::move(object));
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 
setCriticalAssociation(const std::string & objectPath) const577 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 
getSystemNames() const607 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     for (const auto& [path, interfaceMap] : subtree)
626     {
627         auto iface = interfaceMap.find(service_name::entityManager);
628         if (iface == interfaceMap.end())
629         {
630             continue;
631         }
632 
633         getProperty(iface->first, path, interface::compatible, "Names", names);
634 
635         return std::get<std::vector<std::string>>(names);
636     }
637 
638     throw std::runtime_error("EM Compatible interface not on D-Bus");
639 }
640 
getQuiesceOnError() const641 bool DataInterface::getQuiesceOnError() const
642 {
643     bool ret = false;
644 
645     try
646     {
647         auto service = getService(object_path::logSetting,
648                                   interface::logSetting);
649         if (!service.empty())
650         {
651             DBusValue value;
652             getProperty(service, object_path::logSetting, interface::logSetting,
653                         "QuiesceOnHwError", value);
654 
655             ret = std::get<bool>(value);
656         }
657     }
658     catch (const std::exception& e)
659     {
660         lg2::warning("Failed reading QuiesceOnHwError property from "
661                      "interface: {IFACE} exception: {ERROR}",
662                      "IFACE", interface::logSetting, "ERROR", e);
663     }
664 
665     return ret;
666 }
667 
668 std::vector<bool>
checkDumpStatus(const std::vector<std::string> & type) const669     DataInterface::checkDumpStatus(const std::vector<std::string>& type) const
670 {
671     DBusSubTree subtree;
672     std::vector<bool> result(type.size(), false);
673 
674     // Query GetSubTree for the availability of dump interface
675     auto method = _bus.new_method_call(service_name::objectMapper,
676                                        object_path::objectMapper,
677                                        interface::objectMapper, "GetSubTree");
678     method.append(std::string{"/"}, 0,
679                   std::vector<std::string>{interface::dumpEntry});
680     auto reply = _bus.call(method, dbusTimeout);
681 
682     reply.read(subtree);
683 
684     if (subtree.empty())
685     {
686         return result;
687     }
688 
689     std::vector<bool>::iterator itDumpStatus = result.begin();
690     uint8_t count = 0;
691     for (const auto& [path, serviceInfo] : subtree)
692     {
693         const auto& service = serviceInfo.begin()->first;
694         // Check for dump type on the object path
695         for (const auto& it : type)
696         {
697             if (path.find(it) != std::string::npos)
698             {
699                 DBusValue value, progress;
700 
701                 // If dump type status is already available go for next path
702                 if (*itDumpStatus)
703                 {
704                     break;
705                 }
706 
707                 // Check for valid dump to be available if following
708                 // conditions are met for the dump entry path -
709                 // Offloaded == false and Status == Completed
710                 getProperty(service, path, interface::dumpEntry, "Offloaded",
711                             value);
712                 getProperty(service, path, interface::dumpProgress, "Status",
713                             progress);
714                 auto offload = std::get<bool>(value);
715                 auto status = std::get<std::string>(progress);
716                 if (!offload && (status.find("Completed") != std::string::npos))
717                 {
718                     *itDumpStatus = true;
719                     count++;
720                     if (count >= type.size())
721                     {
722                         return result;
723                     }
724                     break;
725                 }
726             }
727             ++itDumpStatus;
728         }
729         itDumpStatus = result.begin();
730     }
731 
732     return result;
733 }
734 
createGuardRecord(const std::vector<uint8_t> & binPath,const std::string & type,const std::string & logPath) const735 void DataInterface::createGuardRecord(const std::vector<uint8_t>& binPath,
736                                       const std::string& type,
737                                       const std::string& logPath) const
738 {
739     try
740     {
741         auto method = _bus.new_method_call(
742             service_name::hwIsolation, object_path::hwIsolation,
743             interface::hwIsolationCreate, "CreateWithEntityPath");
744         method.append(binPath, type, sdbusplus::message::object_path(logPath));
745         // Note: hw isolation "CreateWithEntityPath" got dependency on logging
746         // api's. Making d-bus call no reply type to avoid cyclic dependency.
747         // Added minimal timeout to catch initial failures.
748         // Need to revisit this design later to avoid cyclic dependency.
749         constexpr auto hwIsolationTimeout = 100000; // in micro seconds
750         _bus.call_noreply(method, hwIsolationTimeout);
751     }
752 
753     catch (const sdbusplus::exception_t& e)
754     {
755         std::string errName = e.name();
756         // SD_BUS_ERROR_TIMEOUT error is expected, due to PEL api dependency
757         // mentioned above. Ignoring the error.
758         if (errName != SD_BUS_ERROR_TIMEOUT)
759         {
760             lg2::error("GUARD D-Bus call exception. Path={PATH}, "
761                        "interface = {IFACE}, exception = {ERROR}",
762                        "PATH", object_path::hwIsolation, "IFACE",
763                        interface::hwIsolationCreate, "ERROR", e);
764         }
765     }
766 }
767 
createProgressSRC(const uint64_t & priSRC,const std::vector<uint8_t> & srcStruct) const768 void DataInterface::createProgressSRC(
769     const uint64_t& priSRC, const std::vector<uint8_t>& srcStruct) const
770 {
771     DBusValue variant = std::make_tuple(priSRC, srcStruct);
772 
773     auto method = _bus.new_method_call(service_name::bootRawProgress,
774                                        object_path::bootRawProgress,
775                                        interface::dbusProperty, "Set");
776 
777     method.append(interface::bootRawProgress, "Value", variant);
778 
779     _bus.call(method, dbusTimeout);
780 }
781 
getLogIDWithHwIsolation() const782 std::vector<uint32_t> DataInterface::getLogIDWithHwIsolation() const
783 {
784     std::vector<std::string> association = {"xyz.openbmc_project.Association"};
785     std::string hwErrorLog = "/isolated_hw_errorlog";
786     std::string errorLog = "/error_log";
787     DBusPathList paths;
788     std::vector<uint32_t> ids;
789 
790     // Get all latest mapper associations
791     paths = getPaths(association);
792     for (auto& path : paths)
793     {
794         // Look for object path with hardware isolation entry if any
795         size_t pos = path.find(hwErrorLog);
796         if (pos != std::string::npos)
797         {
798             // Get the object path
799             std::string ph = path;
800             ph.erase(pos, hwErrorLog.length());
801             auto service = getService(ph, interface::hwIsolationEntry);
802             if (!service.empty())
803             {
804                 bool status;
805                 DBusValue value;
806 
807                 // Read the Resolved property from object path
808                 getProperty(service, ph, interface::hwIsolationEntry,
809                             "Resolved", value);
810 
811                 status = std::get<bool>(value);
812 
813                 // If the entry isn't resolved
814                 if (!status)
815                 {
816                     auto assocService = getService(path,
817                                                    interface::association);
818                     if (!assocService.empty())
819                     {
820                         DBusValue endpoints;
821 
822                         // Read Endpoints property
823                         getProperty(assocService, path, interface::association,
824                                     "endpoints", endpoints);
825 
826                         auto logPath =
827                             std::get<std::vector<std::string>>(endpoints);
828                         if (!logPath.empty())
829                         {
830                             // Get OpenBMC event log Id
831                             uint32_t id = stoi(logPath[0].substr(
832                                 logPath[0].find_last_of('/') + 1));
833                             ids.push_back(id);
834                         }
835                     }
836                 }
837             }
838         }
839 
840         // Look for object path with error_log entry if any
841         pos = path.find(errorLog);
842         if (pos != std::string::npos)
843         {
844             auto service = getService(path, interface::association);
845             if (!service.empty())
846             {
847                 DBusValue value;
848 
849                 // Read Endpoints property
850                 getProperty(service, path, interface::association, "endpoints",
851                             value);
852 
853                 auto logPath = std::get<std::vector<std::string>>(value);
854                 if (!logPath.empty())
855                 {
856                     // Get OpenBMC event log Id
857                     uint32_t id = stoi(
858                         logPath[0].substr(logPath[0].find_last_of('/') + 1));
859                     ids.push_back(id);
860                 }
861             }
862         }
863     }
864 
865     if (ids.size() > 1)
866     {
867         // remove duplicates to have only unique ids
868         std::sort(ids.begin(), ids.end());
869         ids.erase(std::unique(ids.begin(), ids.end()), ids.end());
870     }
871     return ids;
872 }
873 
getRawProgressSRC(void) const874 std::vector<uint8_t> DataInterface::getRawProgressSRC(void) const
875 {
876     using RawProgressProperty = std::tuple<uint64_t, std::vector<uint8_t>>;
877 
878     DBusValue value;
879     getProperty(service_name::bootRawProgress, object_path::bootRawProgress,
880                 interface::bootRawProgress, "Value", value);
881 
882     const auto& rawProgress = std::get<RawProgressProperty>(value);
883     return std::get<1>(rawProgress);
884 }
885 
startFruPlugWatch()886 void DataInterface::startFruPlugWatch()
887 {
888     // Add a watch on inventory InterfacesAdded and then find all
889     // existing hotpluggable interfaces and add propertiesChanged
890     // watches on them.
891 
892     _invIaMatch = std::make_unique<sdbusplus::bus::match_t>(
893         _bus, match_rules::interfacesAdded(object_path::baseInv),
894         std::bind(&DataInterface::inventoryIfaceAdded, this,
895                   std::placeholders::_1));
896     try
897     {
898         auto paths = getPaths(hotplugInterfaces);
899 
900         _invPresentMatches.clear();
901 
902         std::for_each(paths.begin(), paths.end(),
903                       [this](const auto& path) { addHotplugWatch(path); });
904     }
905     catch (const sdbusplus::exception_t& e)
906     {
907         lg2::warning("Failed getting FRU paths to watch: {ERROR}", "ERROR", e);
908     }
909 }
910 
addHotplugWatch(const std::string & path)911 void DataInterface::addHotplugWatch(const std::string& path)
912 {
913     if (!_invPresentMatches.contains(path))
914     {
915         _invPresentMatches.emplace(
916             path,
917             std::make_unique<sdbusplus::bus::match_t>(
918                 _bus, match_rules::propertiesChanged(path, interface::invItem),
919                 std::bind(&DataInterface::presenceChanged, this,
920                           std::placeholders::_1)));
921     }
922 }
923 
inventoryIfaceAdded(sdbusplus::message_t & msg)924 void DataInterface::inventoryIfaceAdded(sdbusplus::message_t& msg)
925 {
926     sdbusplus::message::object_path path;
927     DBusInterfaceMap interfaces;
928 
929     msg.read(path, interfaces);
930 
931     // Check if any of the new interfaces are for hot pluggable FRUs.
932     if (std::find_if(interfaces.begin(), interfaces.end(),
933                      [](const auto& interfacePair) {
934         return std::find(hotplugInterfaces.begin(), hotplugInterfaces.end(),
935                          interfacePair.first) != hotplugInterfaces.end();
936     }) == interfaces.end())
937     {
938         return;
939     }
940 
941     addHotplugWatch(path.str);
942 
943     // If an Inventory.Item interface was also added, check presence now.
944 
945     // Notes:
946     // * This assumes the Inv.Item and Inv.Fan/PS are added together which
947     //   is currently the case.
948     // * If the code ever switches to something without a Present
949     //   property, then the IA signal itself would probably indicate presence.
950 
951     auto itemIt = interfaces.find(interface::invItem);
952     if (itemIt != interfaces.end())
953     {
954         notifyPresenceSubsribers(path.str, itemIt->second);
955     }
956 }
957 
presenceChanged(sdbusplus::message_t & msg)958 void DataInterface::presenceChanged(sdbusplus::message_t& msg)
959 {
960     DBusInterface interface;
961     DBusPropertyMap properties;
962 
963     msg.read(interface, properties);
964     if (interface != interface::invItem)
965     {
966         return;
967     }
968 
969     std::string path = msg.get_path();
970     notifyPresenceSubsribers(path, properties);
971 }
972 
notifyPresenceSubsribers(const std::string & path,const DBusPropertyMap & properties)973 void DataInterface::notifyPresenceSubsribers(const std::string& path,
974                                              const DBusPropertyMap& properties)
975 {
976     auto prop = properties.find("Present");
977     if ((prop == properties.end()) || (!std::get<bool>(prop->second)))
978     {
979         return;
980     }
981 
982     std::string locCode;
983 
984     try
985     {
986         auto service = getService(path, interface::locCode);
987 
988         // If the hotplugged FRU is hosted by PLDM, then it is
989         // in an IO expansion drawer and we don't care about it.
990         if (service == service_name::pldm)
991         {
992             return;
993         }
994 
995         locCode = getLocationCode(path);
996     }
997     catch (const sdbusplus::exception_t& e)
998     {
999         lg2::debug("Could not get location code for {PATH}: {ERROR}", "PATH",
1000                    path, "ERROR", e);
1001         return;
1002     }
1003 
1004     lg2::debug("Detected FRU {PATH} ({LOC}) present ", "PATH", path, "LOC",
1005                locCode);
1006 
1007     // Tell the subscribers.
1008     setFruPresent(locCode);
1009 }
1010 } // namespace pels
1011 } // namespace openpower
1012