1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2019 IBM Corporation
3
4 #include "data_interface.hpp"
5
6 #include "util.hpp"
7
8 #include <phosphor-logging/lg2.hpp>
9 #include <xyz/openbmc_project/State/BMC/server.hpp>
10 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
11
12 #include <filesystem>
13
14 #ifdef PEL_ENABLE_PHAL
15 #include <libekb.H>
16 #include <libpdbg.h>
17 #include <libphal.H>
18 #endif
19
20 // Use a timeout of 10s for D-Bus calls so if there are
21 // timeouts the callers of the PEL creation method won't
22 // also timeout.
23 constexpr auto dbusTimeout = 10000000;
24
25 namespace openpower
26 {
27 namespace pels
28 {
29
30 namespace service_name
31 {
32 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
33 constexpr auto vpdManager = "com.ibm.VPD.Manager";
34 constexpr auto ledGroupManager = "xyz.openbmc_project.LED.GroupManager";
35 constexpr auto biosConfigMgr = "xyz.openbmc_project.BIOSConfigManager";
36 constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw";
37 constexpr auto pldm = "xyz.openbmc_project.PLDM";
38 constexpr auto inventoryManager = "xyz.openbmc_project.Inventory.Manager";
39 constexpr auto entityManager = "xyz.openbmc_project.EntityManager";
40 constexpr auto systemd = "org.freedesktop.systemd1";
41 } // namespace service_name
42
43 namespace object_path
44 {
45 constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper";
46 constexpr auto systemInv = "/xyz/openbmc_project/inventory/system";
47 constexpr auto motherBoardInv =
48 "/xyz/openbmc_project/inventory/system/chassis/motherboard";
49 constexpr auto baseInv = "/xyz/openbmc_project/inventory";
50 constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0";
51 constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0";
52 constexpr auto hostState = "/xyz/openbmc_project/state/host0";
53 constexpr auto enableHostPELs =
54 "/xyz/openbmc_project/logging/send_event_logs_to_host";
55 constexpr auto vpdManager = "/com/ibm/VPD/Manager";
56 constexpr auto logSetting = "/xyz/openbmc_project/logging/settings";
57 constexpr auto biosConfigMgr = "/xyz/openbmc_project/bios_config/manager";
58 constexpr auto bootRawProgress = "/xyz/openbmc_project/state/boot/raw0";
59 constexpr auto systemd = "/org/freedesktop/systemd1";
60 } // namespace object_path
61
62 namespace interface
63 {
64 constexpr auto dbusProperty = "org.freedesktop.DBus.Properties";
65 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper";
66 constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset";
67 constexpr auto bootProgress = "xyz.openbmc_project.State.Boot.Progress";
68 constexpr auto enable = "xyz.openbmc_project.Object.Enable";
69 constexpr auto bmcState = "xyz.openbmc_project.State.BMC";
70 constexpr auto chassisState = "xyz.openbmc_project.State.Chassis";
71 constexpr auto hostState = "xyz.openbmc_project.State.Host";
72 constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI";
73 constexpr auto vsbpRecordVPD = "com.ibm.ipzvpd.VSBP";
74 constexpr auto locCode = "xyz.openbmc_project.Inventory.Decorator.LocationCode";
75 constexpr auto compatible =
76 "xyz.openbmc_project.Inventory.Decorator.Compatible";
77 constexpr auto vpdManager = "com.ibm.VPD.Manager";
78 constexpr auto ledGroup = "xyz.openbmc_project.Led.Group";
79 constexpr auto operationalStatus =
80 "xyz.openbmc_project.State.Decorator.OperationalStatus";
81 constexpr auto logSetting = "xyz.openbmc_project.Logging.Settings";
82 constexpr auto associationDef = "xyz.openbmc_project.Association.Definitions";
83 constexpr auto hwIsolationEntry = "xyz.openbmc_project.HardwareIsolation.Entry";
84 constexpr auto association = "xyz.openbmc_project.Association";
85 constexpr auto biosConfigMgr = "xyz.openbmc_project.BIOSConfig.Manager";
86 constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw";
87 constexpr auto invItem = "xyz.openbmc_project.Inventory.Item";
88 constexpr auto invFan = "xyz.openbmc_project.Inventory.Item.Fan";
89 constexpr auto invPowerSupply =
90 "xyz.openbmc_project.Inventory.Item.PowerSupply";
91 constexpr auto inventoryManager = "xyz.openbmc_project.Inventory.Manager";
92 constexpr auto systemdMgr = "org.freedesktop.systemd1.Manager";
93 } // namespace interface
94
95 using namespace sdbusplus::server::xyz::openbmc_project::state::boot;
96 using namespace sdbusplus::server::xyz::openbmc_project::state;
97 namespace match_rules = sdbusplus::bus::match::rules;
98
99 const DBusInterfaceList hotplugInterfaces{interface::invFan,
100 interface::invPowerSupply};
101 static constexpr auto PDBG_DTB_PATH =
102 "/var/lib/phosphor-software-manager/hostfw/running/DEVTREE";
103
104 std::pair<std::string, std::string>
extractConnectorFromLocCode(const std::string & locationCode)105 DataInterfaceBase::extractConnectorFromLocCode(
106 const std::string& locationCode)
107 {
108 auto base = locationCode;
109 std::string connector{};
110
111 auto pos = base.find("-T");
112 if (pos != std::string::npos)
113 {
114 connector = base.substr(pos);
115 base = base.substr(0, pos);
116 }
117
118 return {base, connector};
119 }
120
DataInterface(sdbusplus::bus_t & bus)121 DataInterface::DataInterface(sdbusplus::bus_t& bus) :
122 _bus(bus), _systemdSlot(nullptr)
123 {
124 readBMCFWVersion();
125 readServerFWVersion();
126 readBMCFWVersionID();
127
128 // Watch the BootProgress property
129 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
130 bus, object_path::hostState, interface::bootProgress, "BootProgress",
131 *this, [this](const auto& value) {
132 this->_bootState = std::get<std::string>(value);
133 auto status = Progress::convertProgressStagesFromString(
134 std::get<std::string>(value));
135
136 if ((status == Progress::ProgressStages::SystemInitComplete) ||
137 (status == Progress::ProgressStages::OSRunning))
138 {
139 setHostUp(true);
140 }
141 else
142 {
143 setHostUp(false);
144 }
145 }));
146
147 // Watch the host PEL enable property
148 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
149 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this,
150 [this](const auto& value) {
151 if (std::get<bool>(value) != this->_sendPELsToHost)
152 {
153 lg2::info("The send PELs to host setting changed to {VAL}",
154 "VAL", std::get<bool>(value));
155 }
156 this->_sendPELsToHost = std::get<bool>(value);
157 }));
158
159 // Watch the BMCState property
160 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
161 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState",
162 *this, [this](const auto& value) {
163 const auto& state = std::get<std::string>(value);
164 this->_bmcState = state;
165
166 // Wait for BMC ready to start watching for
167 // plugs so things calm down first.
168 if (BMC::convertBMCStateFromString(state) == BMC::BMCState::Ready)
169 {
170 startFruPlugWatch();
171 }
172 }));
173
174 // Watch the chassis current and requested power state properties
175 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>(
176 bus, object_path::chassisState, interface::chassisState, *this,
177 [this](const auto& properties) {
178 auto state = properties.find("CurrentPowerState");
179 if (state != properties.end())
180 {
181 this->_chassisState = std::get<std::string>(state->second);
182 }
183
184 auto trans = properties.find("RequestedPowerTransition");
185 if (trans != properties.end())
186 {
187 this->_chassisTransition = std::get<std::string>(trans->second);
188 }
189 }));
190
191 // Watch the CurrentHostState property
192 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
193 bus, object_path::hostState, interface::hostState, "CurrentHostState",
194 *this, [this](const auto& value) {
195 this->_hostState = std::get<std::string>(value);
196 }));
197
198 // Watch the BaseBIOSTable property for the hmc managed attribute
199 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>(
200 bus, object_path::biosConfigMgr, interface::biosConfigMgr,
201 "BaseBIOSTable", service_name::biosConfigMgr, *this,
202 [this](const auto& value) {
203 const auto& attributes = std::get<BiosAttributes>(value);
204
205 auto it = attributes.find("pvm_hmc_managed");
206 if (it != attributes.end())
207 {
208 const auto& currentValVariant = std::get<5>(it->second);
209 auto currentVal = std::get_if<std::string>(¤tValVariant);
210 if (currentVal)
211 {
212 this->_hmcManaged =
213 (*currentVal == "Enabled") ? true : false;
214 }
215 }
216 }));
217
218 if (isPHALDevTreeExist())
219 {
220 #ifdef PEL_ENABLE_PHAL
221 initPHAL();
222 #endif
223 }
224 else
225 {
226 // Watch the "openpower-update-bios-attr-table" service to init
227 // PHAL libraries
228 subscribeToSystemdSignals();
229 }
230 }
231
getAllProperties(const std::string & service,const std::string & objectPath,const std::string & interface) const232 DBusPropertyMap DataInterface::getAllProperties(
233 const std::string& service, 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
getProperty(const std::string & service,const std::string & objectPath,const std::string & interface,const std::string & property,DBusValue & value) const248 void DataInterface::getProperty(
249 const std::string& service, const std::string& objectPath,
250 const std::string& interface, const std::string& property,
251 DBusValue& value) const
252 {
253 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
254 interface::dbusProperty, "Get");
255 method.append(interface, property);
256 auto reply = _bus.call(method, dbusTimeout);
257
258 reply.read(value);
259 }
260
getPaths(const DBusInterfaceList & interfaces) const261 DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
262 {
263 auto method = _bus.new_method_call(
264 service_name::objectMapper, object_path::objectMapper,
265 interface::objectMapper, "GetSubTreePaths");
266
267 method.append(std::string{"/"}, 0, interfaces);
268
269 auto reply = _bus.call(method, dbusTimeout);
270
271 auto paths = reply.unpack<DBusPathList>();
272
273 return paths;
274 }
275
getSubTree(const DBusInterfaceList & interfaces) const276 DBusSubTree DataInterface::getSubTree(const DBusInterfaceList& interfaces) const
277 {
278 auto method = _bus.new_method_call(service_name::objectMapper,
279 object_path::objectMapper,
280 interface::objectMapper, "GetSubTree");
281 method.append(std::string{"/"}, 0, interfaces);
282 auto reply = _bus.call(method, dbusTimeout);
283
284 return reply.unpack<DBusSubTree>();
285 }
286
getService(const std::string & objectPath,const std::string & interface) const287 DBusService DataInterface::getService(const std::string& objectPath,
288 const std::string& interface) const
289 {
290 auto method = _bus.new_method_call(service_name::objectMapper,
291 object_path::objectMapper,
292 interface::objectMapper, "GetObject");
293
294 method.append(objectPath, std::vector<std::string>({interface}));
295
296 auto reply = _bus.call(method, dbusTimeout);
297
298 auto response = reply.unpack<std::map<DBusService, DBusInterfaceList>>();
299
300 if (!response.empty())
301 {
302 return response.begin()->first;
303 }
304
305 return std::string{};
306 }
307
readBMCFWVersion()308 void DataInterface::readBMCFWVersion()
309 {
310 _bmcFWVersion =
311 phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
312 }
313
readServerFWVersion()314 void DataInterface::readServerFWVersion()
315 {
316 auto value =
317 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
318 if ((value != "") && (value.find_last_of(')') != std::string::npos))
319 {
320 std::size_t pos = value.find_first_of('(') + 1;
321 _serverFWVersion = value.substr(pos, value.find_last_of(')') - pos);
322 }
323 }
324
readBMCFWVersionID()325 void DataInterface::readBMCFWVersionID()
326 {
327 _bmcFWVersionID =
328 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
329 }
330
getMachineTypeModel() const331 std::string DataInterface::getMachineTypeModel() const
332 {
333 std::string model;
334 try
335 {
336 auto service = getService(object_path::systemInv, interface::invAsset);
337 if (!service.empty())
338 {
339 DBusValue value;
340 getProperty(service, object_path::systemInv, interface::invAsset,
341 "Model", value);
342
343 model = std::get<std::string>(value);
344 }
345 }
346 catch (const std::exception& e)
347 {
348 lg2::warning("Failed reading Model property from "
349 "interface: {IFACE} exception: {ERROR}",
350 "IFACE", interface::invAsset, "ERROR", e);
351 }
352
353 return model;
354 }
355
getMachineSerialNumber() const356 std::string DataInterface::getMachineSerialNumber() const
357 {
358 std::string sn;
359 try
360 {
361 auto service = getService(object_path::systemInv, interface::invAsset);
362 if (!service.empty())
363 {
364 DBusValue value;
365 getProperty(service, object_path::systemInv, interface::invAsset,
366 "SerialNumber", value);
367
368 sn = std::get<std::string>(value);
369 }
370 }
371 catch (const std::exception& e)
372 {
373 lg2::warning("Failed reading SerialNumber property from "
374 "interface: {IFACE} exception: {ERROR}",
375 "IFACE", interface::invAsset, "ERROR", e);
376 }
377
378 return sn;
379 }
380
getMotherboardCCIN() const381 std::string DataInterface::getMotherboardCCIN() const
382 {
383 std::string ccin;
384
385 try
386 {
387 auto service =
388 getService(object_path::motherBoardInv, interface::viniRecordVPD);
389 if (!service.empty())
390 {
391 DBusValue value;
392 getProperty(service, object_path::motherBoardInv,
393 interface::viniRecordVPD, "CC", value);
394
395 auto cc = std::get<std::vector<uint8_t>>(value);
396 ccin = std::string{cc.begin(), cc.end()};
397 }
398 }
399 catch (const std::exception& e)
400 {
401 lg2::warning("Failed reading Motherboard CCIN property from "
402 "interface: {IFACE} exception: {ERROR}",
403 "IFACE", interface::viniRecordVPD, "ERROR", e);
404 }
405
406 return ccin;
407 }
408
getSystemIMKeyword() const409 std::vector<uint8_t> DataInterface::getSystemIMKeyword() const
410 {
411 static std::vector<uint8_t> systemIM;
412
413 if (!systemIM.empty())
414 {
415 return systemIM;
416 }
417
418 try
419 {
420 auto subtree = getSubTree({interface::vsbpRecordVPD});
421
422 if (subtree.empty())
423 {
424 lg2::warning("No VSBP VPD interface found");
425 return systemIM;
426 }
427
428 DBusValue imValue;
429 const auto& path = subtree.begin()->first;
430 const auto& interfaceMap = subtree.begin()->second;
431 const auto& service = interfaceMap.begin()->first;
432 getProperty(service, path, interface::vsbpRecordVPD, "IM", imValue);
433
434 systemIM = std::get<std::vector<uint8_t>>(imValue);
435 }
436 catch (const std::exception& e)
437 {
438 lg2::warning("Failed reading System IM property from "
439 "interface: {IFACE} exception: {ERROR}",
440 "IFACE", interface::vsbpRecordVPD, "ERROR", e);
441 }
442
443 return systemIM;
444 }
445
getHWCalloutFields(const std::string & inventoryPath,std::string & fruPartNumber,std::string & ccin,std::string & serialNumber) const446 void DataInterface::getHWCalloutFields(
447 const std::string& inventoryPath, std::string& fruPartNumber,
448 std::string& ccin, std::string& serialNumber) const
449 {
450 // For now, attempt to get all of the properties directly on the path
451 // passed in. In the future, may need to make use of an algorithm
452 // to figure out which inventory objects actually hold these
453 // interfaces in the case of non FRUs, or possibly another service
454 // will provide this info. Any missing interfaces will result
455 // in exceptions being thrown.
456
457 auto service = getService(inventoryPath, interface::viniRecordVPD);
458
459 auto properties =
460 getAllProperties(service, inventoryPath, interface::viniRecordVPD);
461
462 auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
463 fruPartNumber = std::string{value.begin(), value.end()};
464
465 value = std::get<std::vector<uint8_t>>(properties["CC"]);
466 ccin = std::string{value.begin(), value.end()};
467
468 value = std::get<std::vector<uint8_t>>(properties["SN"]);
469 serialNumber = std::string{value.begin(), value.end()};
470 }
471
getLocationCode(const std::string & inventoryPath) const472 std::string DataInterface::getLocationCode(
473 const std::string& inventoryPath) const
474 {
475 auto service = getService(inventoryPath, interface::locCode);
476
477 DBusValue locCode;
478 getProperty(service, inventoryPath, interface::locCode, "LocationCode",
479 locCode);
480
481 return std::get<std::string>(locCode);
482 }
483
addLocationCodePrefix(const std::string & locationCode)484 std::string DataInterface::addLocationCodePrefix(
485 const std::string& locationCode)
486 {
487 static const std::string locationCodePrefix{"Ufcs-"};
488
489 // Technically there are 2 location code prefixes, Ufcs and Umts, so
490 // if it already starts with a U then don't need to do anything.
491 if (locationCode.front() != 'U')
492 {
493 return locationCodePrefix + locationCode;
494 }
495
496 return locationCode;
497 }
498
expandLocationCode(const std::string & locationCode,uint16_t) const499 std::string DataInterface::expandLocationCode(const std::string& locationCode,
500 uint16_t /*node*/) const
501 {
502 // Location codes for connectors are the location code of the FRU they are
503 // on, plus a '-Tx' segment. Remove this last segment before expanding it
504 // and then add it back in afterwards. This way, the connector doesn't have
505 // to be in the model just so that it can be expanded.
506 auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode);
507
508 auto method =
509 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
510 interface::vpdManager, "GetExpandedLocationCode");
511
512 method.append(addLocationCodePrefix(baseLoc), static_cast<uint16_t>(0));
513
514 auto reply = _bus.call(method, dbusTimeout);
515
516 auto expandedLocationCode = reply.unpack<std::string>();
517
518 if (!connectorLoc.empty())
519 {
520 expandedLocationCode += connectorLoc;
521 }
522
523 return expandedLocationCode;
524 }
525
getInventoryFromLocCode(const std::string & locationCode,uint16_t node,bool expanded) const526 std::vector<std::string> DataInterface::getInventoryFromLocCode(
527 const std::string& locationCode, uint16_t node, bool expanded) const
528 {
529 std::string methodName = expanded ? "GetFRUsByExpandedLocationCode"
530 : "GetFRUsByUnexpandedLocationCode";
531
532 // Remove the connector segment, if present, so that this method call
533 // returns an inventory path that getHWCalloutFields() can be used with.
534 // (The serial number, etc, aren't stored on the connector in the
535 // inventory, and may not even be modeled.)
536 auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode);
537
538 auto method =
539 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
540 interface::vpdManager, methodName.c_str());
541
542 if (expanded)
543 {
544 method.append(baseLoc);
545 }
546 else
547 {
548 method.append(addLocationCodePrefix(baseLoc), node);
549 }
550
551 auto reply = _bus.call(method, dbusTimeout);
552
553 auto entries = reply.unpack<std::vector<sdbusplus::message::object_path>>();
554
555 std::vector<std::string> paths;
556
557 // Note: The D-Bus method will fail if nothing found.
558 std::for_each(entries.begin(), entries.end(),
559 [&paths](const auto& path) { paths.push_back(path); });
560
561 return paths;
562 }
563
assertLEDGroup(const std::string & ledGroup,bool value) const564 void DataInterface::assertLEDGroup(const std::string& ledGroup,
565 bool value) const
566 {
567 DBusValue variant = value;
568 auto method =
569 _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(),
570 interface::dbusProperty, "Set");
571 method.append(interface::ledGroup, "Asserted", variant);
572 _bus.call(method, dbusTimeout);
573 }
574
setFunctional(const std::string & objectPath,bool value) const575 void DataInterface::setFunctional(const std::string& objectPath,
576 bool value) const
577 {
578 DBusPropertyMap prop{{"Functional", value}};
579 DBusInterfaceMap iface{{interface::operationalStatus, prop}};
580
581 // PIM takes a relative path like /system/chassis so remove
582 // /xyz/openbmc_project/inventory if present.
583 std::string path{objectPath};
584 if (path.starts_with(object_path::baseInv))
585 {
586 path = objectPath.substr(strlen(object_path::baseInv));
587 }
588 DBusObjectMap object{{path, iface}};
589
590 auto method = _bus.new_method_call(service_name::inventoryManager,
591 object_path::baseInv,
592 interface::inventoryManager, "Notify");
593 method.append(std::move(object));
594 _bus.call(method, dbusTimeout);
595 }
596
597 using AssociationTuple = std::tuple<std::string, std::string, std::string>;
598 using AssociationsProperty = std::vector<AssociationTuple>;
599
setCriticalAssociation(const std::string & objectPath) const600 void DataInterface::setCriticalAssociation(const std::string& objectPath) const
601 {
602 DBusValue getAssociationValue;
603
604 auto service = getService(objectPath, interface::associationDef);
605
606 getProperty(service, objectPath, interface::associationDef, "Associations",
607 getAssociationValue);
608
609 auto association = std::get<AssociationsProperty>(getAssociationValue);
610
611 AssociationTuple critAssociation{
612 "health_rollup", "critical",
613 "/xyz/openbmc_project/inventory/system/chassis"};
614
615 if (std::find(association.begin(), association.end(), critAssociation) ==
616 association.end())
617 {
618 association.push_back(critAssociation);
619 DBusValue setAssociationValue = association;
620
621 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
622 interface::dbusProperty, "Set");
623
624 method.append(interface::associationDef, "Associations",
625 setAssociationValue);
626 _bus.call(method, dbusTimeout);
627 }
628 }
629
getSystemNames() const630 std::vector<std::string> DataInterface::getSystemNames() const
631 {
632 auto subtree = getSubTree({interface::compatible});
633
634 if (subtree.empty())
635 {
636 throw std::runtime_error("Compatible interface not on D-Bus");
637 }
638
639 for (const auto& [path, interfaceMap] : subtree)
640 {
641 auto iface = interfaceMap.find(service_name::entityManager);
642 if (iface == interfaceMap.end())
643 {
644 continue;
645 }
646
647 DBusValue names;
648
649 getProperty(iface->first, path, interface::compatible, "Names", names);
650
651 return std::get<std::vector<std::string>>(names);
652 }
653
654 throw std::runtime_error("EM Compatible interface not on D-Bus");
655 }
656
getQuiesceOnError() const657 bool DataInterface::getQuiesceOnError() const
658 {
659 bool ret = false;
660
661 try
662 {
663 auto service =
664 getService(object_path::logSetting, interface::logSetting);
665 if (!service.empty())
666 {
667 DBusValue value;
668 getProperty(service, object_path::logSetting, interface::logSetting,
669 "QuiesceOnHwError", value);
670
671 ret = std::get<bool>(value);
672 }
673 }
674 catch (const std::exception& e)
675 {
676 lg2::warning("Failed reading QuiesceOnHwError property from "
677 "interface: {IFACE} exception: {ERROR}",
678 "IFACE", interface::logSetting, "ERROR", e);
679 }
680
681 return ret;
682 }
683
684 #ifdef PEL_ENABLE_PHAL
createGuardRecord(const std::vector<uint8_t> & binPath,GardType eGardType,uint32_t plid) const685 void DataInterface::createGuardRecord(const std::vector<uint8_t>& binPath,
686 GardType eGardType, uint32_t plid) const
687 {
688 try
689 {
690 libguard::libguard_init(false);
691 libguard::create(binPath, plid, eGardType);
692 }
693 catch (libguard::exception::GuardException& e)
694 {
695 lg2::error("Exception in libguard {ERROR}", "ERROR", e);
696 }
697 }
698 #endif
699
createProgressSRC(const std::vector<uint8_t> & priSRC,const std::vector<uint8_t> & srcStruct) const700 void DataInterface::createProgressSRC(
701 const std::vector<uint8_t>& priSRC,
702 const std::vector<uint8_t>& srcStruct) const
703 {
704 DBusValue variant = std::make_tuple(priSRC, srcStruct);
705
706 auto method = _bus.new_method_call(service_name::bootRawProgress,
707 object_path::bootRawProgress,
708 interface::dbusProperty, "Set");
709
710 method.append(interface::bootRawProgress, "Value", variant);
711
712 _bus.call(method, dbusTimeout);
713 }
714
getLogIDWithHwIsolation() const715 std::vector<uint32_t> DataInterface::getLogIDWithHwIsolation() const
716 {
717 std::vector<std::string> association = {"xyz.openbmc_project.Association"};
718 std::string hwErrorLog = "/isolated_hw_errorlog";
719 std::string errorLog = "/error_log";
720 DBusPathList paths;
721 std::vector<uint32_t> ids;
722
723 // Get all latest mapper associations
724 paths = getPaths(association);
725 for (auto& path : paths)
726 {
727 // Look for object path with hardware isolation entry if any
728 size_t pos = path.find(hwErrorLog);
729 if (pos != std::string::npos)
730 {
731 // Get the object path
732 std::string ph = path;
733 ph.erase(pos, hwErrorLog.length());
734 auto service = getService(ph, interface::hwIsolationEntry);
735 if (!service.empty())
736 {
737 bool status;
738 DBusValue value;
739
740 // Read the Resolved property from object path
741 getProperty(service, ph, interface::hwIsolationEntry,
742 "Resolved", value);
743
744 status = std::get<bool>(value);
745
746 // If the entry isn't resolved
747 if (!status)
748 {
749 auto assocService =
750 getService(path, interface::association);
751 if (!assocService.empty())
752 {
753 DBusValue endpoints;
754
755 // Read Endpoints property
756 getProperty(assocService, path, interface::association,
757 "endpoints", endpoints);
758
759 auto logPath =
760 std::get<std::vector<std::string>>(endpoints);
761 if (!logPath.empty())
762 {
763 // Get OpenBMC event log Id
764 uint32_t id = stoi(logPath[0].substr(
765 logPath[0].find_last_of('/') + 1));
766 ids.push_back(id);
767 }
768 }
769 }
770 }
771 }
772
773 // Look for object path with error_log entry if any
774 pos = path.find(errorLog);
775 if (pos != std::string::npos)
776 {
777 auto service = getService(path, interface::association);
778 if (!service.empty())
779 {
780 DBusValue value;
781
782 // Read Endpoints property
783 getProperty(service, path, interface::association, "endpoints",
784 value);
785
786 auto logPath = std::get<std::vector<std::string>>(value);
787 if (!logPath.empty())
788 {
789 // Get OpenBMC event log Id
790 uint32_t id = stoi(
791 logPath[0].substr(logPath[0].find_last_of('/') + 1));
792 ids.push_back(id);
793 }
794 }
795 }
796 }
797
798 if (ids.size() > 1)
799 {
800 // remove duplicates to have only unique ids
801 std::sort(ids.begin(), ids.end());
802 ids.erase(std::unique(ids.begin(), ids.end()), ids.end());
803 }
804 return ids;
805 }
806
getRawProgressSRC(void) const807 std::vector<uint8_t> DataInterface::getRawProgressSRC(void) const
808 {
809 using RawProgressProperty =
810 std::tuple<std::vector<uint8_t>, std::vector<uint8_t>>;
811
812 DBusValue value;
813 getProperty(service_name::bootRawProgress, object_path::bootRawProgress,
814 interface::bootRawProgress, "Value", value);
815
816 const auto& rawProgress = std::get<RawProgressProperty>(value);
817 return std::get<1>(rawProgress);
818 }
819
getDIProperty(const std::string & locationCode) const820 std::optional<std::vector<uint8_t>> DataInterface::getDIProperty(
821 const std::string& locationCode) const
822 {
823 std::vector<uint8_t> viniDI;
824
825 try
826 {
827 // Note : The hardcoded value 0 should be changed when comes to
828 // multinode system.
829 auto objectPath = getInventoryFromLocCode(locationCode, 0, true);
830
831 DBusValue value;
832 getProperty(service_name::inventoryManager, objectPath[0],
833 interface::viniRecordVPD, "DI", value);
834
835 viniDI = std::get<std::vector<uint8_t>>(value);
836 }
837 catch (const std::exception& e)
838 {
839 lg2::warning(
840 "Failed reading DI property for the location code : {LOC_CODE} from "
841 "interface: {IFACE} exception: {ERROR}",
842 "LOC_CODE", locationCode, "IFACE", interface::viniRecordVPD,
843 "ERROR", e);
844 return std::nullopt;
845 }
846
847 return viniDI;
848 }
849
isDIMMLocCode(const std::string & locCode) const850 std::optional<bool> DataInterfaceBase::isDIMMLocCode(
851 const std::string& locCode) const
852 {
853 if (_locationCache.contains(locCode))
854 {
855 return _locationCache.at(locCode);
856 }
857 else
858 {
859 return std::nullopt;
860 }
861 }
862
addDIMMLocCode(const std::string & locCode,bool isFRUDIMM)863 void DataInterfaceBase::addDIMMLocCode(const std::string& locCode,
864 bool isFRUDIMM)
865 {
866 _locationCache.insert({locCode, isFRUDIMM});
867 }
868
isDIMM(const std::string & locCode)869 bool DataInterfaceBase::isDIMM(const std::string& locCode)
870 {
871 auto isDIMMType = isDIMMLocCode(locCode);
872 if (isDIMMType.has_value())
873 {
874 return isDIMMType.value();
875 }
876 #ifndef PEL_ENABLE_PHAL
877 return false;
878 #else
879 else
880 {
881 // Invoke pHAL API inorder to fetch the FRU Type
882 auto fruType = openpower::phal::pdbg::getFRUType(locCode);
883 bool isDIMMFRU{false};
884 if (fruType.has_value())
885 {
886 if (fruType.value() == ENUM_ATTR_TYPE_DIMM)
887 {
888 isDIMMFRU = true;
889 }
890 addDIMMLocCode(locCode, isDIMMFRU);
891 }
892 return isDIMMFRU;
893 }
894 #endif
895 }
896
getAssociatedPaths(const DBusPath & associatedPath,const DBusPath & subtree,int32_t depth,const DBusInterfaceList & interfaces) const897 DBusPathList DataInterface::getAssociatedPaths(
898 const DBusPath& associatedPath, const DBusPath& subtree, int32_t depth,
899 const DBusInterfaceList& interfaces) const
900 {
901 DBusPathList paths;
902 try
903 {
904 auto method = _bus.new_method_call(
905 service_name::objectMapper, object_path::objectMapper,
906 interface::objectMapper, "GetAssociatedSubTreePaths");
907 method.append(sdbusplus::message::object_path(associatedPath),
908 sdbusplus::message::object_path(subtree), depth,
909 interfaces);
910
911 auto reply = _bus.call(method, dbusTimeout);
912 reply.read(paths);
913 }
914 catch (const std::exception& e)
915 {
916 std::string ifaces(
917 std::ranges::fold_left_first(
918 interfaces,
919 [](std::string ifaces, const std::string& iface) {
920 return ifaces + ", " + iface;
921 })
922 .value_or(""));
923
924 lg2::error("Failed getting associated paths: {ERROR}. "
925 "AssociatedPath: {ASSOIC_PATH} Subtree: {SUBTREE} "
926 "Interfaces: {IFACES}",
927 "ERROR", e, "ASSOIC_PATH", associatedPath, "SUBTREE",
928 subtree, "IFACES", ifaces);
929 }
930 return paths;
931 }
932
startFruPlugWatch()933 void DataInterface::startFruPlugWatch()
934 {
935 // Add a watch on inventory InterfacesAdded and then find all
936 // existing hotpluggable interfaces and add propertiesChanged
937 // watches on them.
938
939 _invIaMatch = std::make_unique<sdbusplus::bus::match_t>(
940 _bus, match_rules::interfacesAdded(object_path::baseInv),
941 std::bind(&DataInterface::inventoryIfaceAdded, this,
942 std::placeholders::_1));
943 try
944 {
945 auto paths = getPaths(hotplugInterfaces);
946
947 _invPresentMatches.clear();
948
949 std::for_each(paths.begin(), paths.end(),
950 [this](const auto& path) { addHotplugWatch(path); });
951 }
952 catch (const sdbusplus::exception_t& e)
953 {
954 lg2::warning("Failed getting FRU paths to watch: {ERROR}", "ERROR", e);
955 }
956 }
957
addHotplugWatch(const std::string & path)958 void DataInterface::addHotplugWatch(const std::string& path)
959 {
960 if (!_invPresentMatches.contains(path))
961 {
962 _invPresentMatches.emplace(
963 path,
964 std::make_unique<sdbusplus::bus::match_t>(
965 _bus, match_rules::propertiesChanged(path, interface::invItem),
966 std::bind(&DataInterface::presenceChanged, this,
967 std::placeholders::_1)));
968 }
969 }
970
inventoryIfaceAdded(sdbusplus::message_t & msg)971 void DataInterface::inventoryIfaceAdded(sdbusplus::message_t& msg)
972 {
973 sdbusplus::message::object_path path;
974 DBusInterfaceMap interfaces;
975
976 msg.read(path, interfaces);
977
978 // Check if any of the new interfaces are for hot pluggable FRUs.
979 if (std::find_if(interfaces.begin(), interfaces.end(),
980 [](const auto& interfacePair) {
981 return std::find(hotplugInterfaces.begin(),
982 hotplugInterfaces.end(),
983 interfacePair.first) !=
984 hotplugInterfaces.end();
985 }) == interfaces.end())
986 {
987 return;
988 }
989
990 addHotplugWatch(path.str);
991
992 // If an Inventory.Item interface was also added, check presence now.
993
994 // Notes:
995 // * This assumes the Inv.Item and Inv.Fan/PS are added together which
996 // is currently the case.
997 // * If the code ever switches to something without a Present
998 // property, then the IA signal itself would probably indicate presence.
999
1000 auto itemIt = interfaces.find(interface::invItem);
1001 if (itemIt != interfaces.end())
1002 {
1003 notifyPresenceSubscribers(path.str, itemIt->second);
1004 }
1005 }
1006
presenceChanged(sdbusplus::message_t & msg)1007 void DataInterface::presenceChanged(sdbusplus::message_t& msg)
1008 {
1009 DBusInterface interface;
1010 DBusPropertyMap properties;
1011
1012 msg.read(interface, properties);
1013 if (interface != interface::invItem)
1014 {
1015 return;
1016 }
1017
1018 std::string path = msg.get_path();
1019 notifyPresenceSubscribers(path, properties);
1020 }
1021
notifyPresenceSubscribers(const std::string & path,const DBusPropertyMap & properties)1022 void DataInterface::notifyPresenceSubscribers(const std::string& path,
1023 const DBusPropertyMap& properties)
1024 {
1025 auto prop = properties.find("Present");
1026 if ((prop == properties.end()) || (!std::get<bool>(prop->second)))
1027 {
1028 return;
1029 }
1030
1031 std::string locCode;
1032
1033 try
1034 {
1035 auto service = getService(path, interface::locCode);
1036
1037 // If the hotplugged FRU is hosted by PLDM, then it is
1038 // in an IO expansion drawer and we don't care about it.
1039 if (service == service_name::pldm)
1040 {
1041 return;
1042 }
1043
1044 locCode = getLocationCode(path);
1045 }
1046 catch (const sdbusplus::exception_t& e)
1047 {
1048 lg2::debug("Could not get location code for {PATH}: {ERROR}", "PATH",
1049 path, "ERROR", e);
1050 return;
1051 }
1052
1053 lg2::debug("Detected FRU {PATH} ({LOC}) present ", "PATH", path, "LOC",
1054 locCode);
1055
1056 // Tell the subscribers.
1057 setFruPresent(locCode);
1058 }
1059
isPHALDevTreeExist() const1060 bool DataInterface::isPHALDevTreeExist() const
1061 {
1062 try
1063 {
1064 if (std::filesystem::exists(PDBG_DTB_PATH))
1065 {
1066 return true;
1067 }
1068 }
1069 catch (const std::exception& e)
1070 {
1071 lg2::error("Failed to check device tree {PHAL_DEVTREE_PATH} existence, "
1072 "{ERROR}",
1073 "PHAL_DEVTREE_PATH", PDBG_DTB_PATH, "ERROR", e);
1074 }
1075 return false;
1076 }
1077
1078 #ifdef PEL_ENABLE_PHAL
initPHAL()1079 void DataInterface::initPHAL()
1080 {
1081 if (setenv("PDBG_DTB", PDBG_DTB_PATH, 1))
1082 {
1083 // Log message and continue,
1084 // This is to help continue creating PEL in raw format.
1085 lg2::error("Failed to set PDBG_DTB: ({ERRNO})", "ERRNO",
1086 strerror(errno));
1087 }
1088
1089 if (!pdbg_targets_init(NULL))
1090 {
1091 lg2::error("pdbg_targets_init failed");
1092 return;
1093 }
1094
1095 if (libekb_init())
1096 {
1097 lg2::error("libekb_init failed, skipping ffdc processing");
1098 return;
1099 }
1100 }
1101 #endif
1102
subscribeToSystemdSignals()1103 void DataInterface::subscribeToSystemdSignals()
1104 {
1105 try
1106 {
1107 auto method =
1108 _bus.new_method_call(service_name::systemd, object_path::systemd,
1109 interface::systemdMgr, "Subscribe");
1110 _systemdSlot = method.call_async([this](sdbusplus::message_t&& msg) {
1111 // Initializing with nullptr to indicate that it is not subscribed
1112 // to any signal.
1113 this->_systemdSlot = sdbusplus::slot_t(nullptr);
1114 if (msg.is_method_error())
1115 {
1116 auto* error = msg.get_error();
1117 lg2::error("Failed to subscribe JobRemoved systemd signal, "
1118 "errorName: {ERR_NAME}, errorMsg: {ERR_MSG} ",
1119 "ERR_NAME", error->name, "ERR_MSG", error->message);
1120 return;
1121 }
1122
1123 namespace sdbusRule = sdbusplus::bus::match::rules;
1124 this->_systemdMatch =
1125 std::make_unique<decltype(this->_systemdMatch)::element_type>(
1126 this->_bus,
1127 sdbusRule::type::signal() +
1128 sdbusRule::member("JobRemoved") +
1129 sdbusRule::path(object_path::systemd) +
1130 sdbusRule::interface(interface::systemdMgr),
1131 [this](sdbusplus::message_t& msg) {
1132 uint32_t jobID;
1133 sdbusplus::message::object_path jobObjPath;
1134 std::string jobUnitName, jobUnitResult;
1135
1136 msg.read(jobID, jobObjPath, jobUnitName, jobUnitResult);
1137 if ((jobUnitName == "obmc-recover-pnor.service") &&
1138 (jobUnitResult == "done"))
1139 {
1140 #ifdef PEL_ENABLE_PHAL
1141 this->initPHAL();
1142 #endif
1143 // Invoke unsubscribe method to stop monitoring for
1144 // JobRemoved signals.
1145 this->unsubscribeFromSystemdSignals();
1146 }
1147 });
1148 });
1149 }
1150 catch (const sdbusplus::exception_t& e)
1151 {
1152 lg2::error(
1153 "Exception occured while handling JobRemoved systemd signal, "
1154 "exception: {ERROR}",
1155 "ERROR", e);
1156 }
1157 }
1158
unsubscribeFromSystemdSignals()1159 void DataInterface::unsubscribeFromSystemdSignals()
1160 {
1161 try
1162 {
1163 auto method =
1164 _bus.new_method_call(service_name::systemd, object_path::systemd,
1165 interface::systemdMgr, "Unsubscribe");
1166 _systemdSlot = method.call_async([this](sdbusplus::message_t&& msg) {
1167 // Unsubscribing the _systemdSlot from the subscribed signal
1168 this->_systemdSlot = sdbusplus::slot_t(nullptr);
1169 if (msg.is_method_error())
1170 {
1171 auto* error = msg.get_error();
1172 lg2::error(
1173 "Failed to unsubscribe from JobRemoved systemd signal, "
1174 "errorName: {ERR_NAME}, errorMsg: {ERR_MSG} ",
1175 "ERR_NAME", error->name, "ERR_MSG", error->message);
1176 return;
1177 }
1178 // Reset _systemdMatch to avoid reception of further JobRemoved
1179 // signals
1180 this->_systemdMatch.reset();
1181 });
1182 }
1183 catch (const sdbusplus::exception_t& e)
1184 {
1185 lg2::error(
1186 "Exception occured while unsubscribing from JobRemoved systemd signal, "
1187 "exception: {ERROR}",
1188 "ERROR", e);
1189 }
1190 }
1191
1192 } // namespace pels
1193 } // namespace openpower
1194