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
getService(const std::string & objectPath,const std::string & interface) const276 DBusService DataInterface::getService(const std::string& objectPath,
277 const std::string& interface) const
278 {
279 auto method = _bus.new_method_call(service_name::objectMapper,
280 object_path::objectMapper,
281 interface::objectMapper, "GetObject");
282
283 method.append(objectPath, std::vector<std::string>({interface}));
284
285 auto reply = _bus.call(method, dbusTimeout);
286
287 auto response = reply.unpack<std::map<DBusService, DBusInterfaceList>>();
288
289 if (!response.empty())
290 {
291 return response.begin()->first;
292 }
293
294 return std::string{};
295 }
296
readBMCFWVersion()297 void DataInterface::readBMCFWVersion()
298 {
299 _bmcFWVersion =
300 phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
301 }
302
readServerFWVersion()303 void DataInterface::readServerFWVersion()
304 {
305 auto value =
306 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
307 if ((value != "") && (value.find_last_of(')') != std::string::npos))
308 {
309 std::size_t pos = value.find_first_of('(') + 1;
310 _serverFWVersion = value.substr(pos, value.find_last_of(')') - pos);
311 }
312 }
313
readBMCFWVersionID()314 void DataInterface::readBMCFWVersionID()
315 {
316 _bmcFWVersionID =
317 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
318 }
319
getMachineTypeModel() const320 std::string DataInterface::getMachineTypeModel() const
321 {
322 std::string model;
323 try
324 {
325 auto service = getService(object_path::systemInv, interface::invAsset);
326 if (!service.empty())
327 {
328 DBusValue value;
329 getProperty(service, object_path::systemInv, interface::invAsset,
330 "Model", value);
331
332 model = std::get<std::string>(value);
333 }
334 }
335 catch (const std::exception& e)
336 {
337 lg2::warning("Failed reading Model property from "
338 "interface: {IFACE} exception: {ERROR}",
339 "IFACE", interface::invAsset, "ERROR", e);
340 }
341
342 return model;
343 }
344
getMachineSerialNumber() const345 std::string DataInterface::getMachineSerialNumber() const
346 {
347 std::string sn;
348 try
349 {
350 auto service = getService(object_path::systemInv, interface::invAsset);
351 if (!service.empty())
352 {
353 DBusValue value;
354 getProperty(service, object_path::systemInv, interface::invAsset,
355 "SerialNumber", value);
356
357 sn = std::get<std::string>(value);
358 }
359 }
360 catch (const std::exception& e)
361 {
362 lg2::warning("Failed reading SerialNumber property from "
363 "interface: {IFACE} exception: {ERROR}",
364 "IFACE", interface::invAsset, "ERROR", e);
365 }
366
367 return sn;
368 }
369
getMotherboardCCIN() const370 std::string DataInterface::getMotherboardCCIN() const
371 {
372 std::string ccin;
373
374 try
375 {
376 auto service =
377 getService(object_path::motherBoardInv, interface::viniRecordVPD);
378 if (!service.empty())
379 {
380 DBusValue value;
381 getProperty(service, object_path::motherBoardInv,
382 interface::viniRecordVPD, "CC", value);
383
384 auto cc = std::get<std::vector<uint8_t>>(value);
385 ccin = std::string{cc.begin(), cc.end()};
386 }
387 }
388 catch (const std::exception& e)
389 {
390 lg2::warning("Failed reading Motherboard CCIN property from "
391 "interface: {IFACE} exception: {ERROR}",
392 "IFACE", interface::viniRecordVPD, "ERROR", e);
393 }
394
395 return ccin;
396 }
397
getSystemIMKeyword() const398 std::vector<uint8_t> DataInterface::getSystemIMKeyword() const
399 {
400 std::vector<uint8_t> systemIM;
401
402 try
403 {
404 auto service =
405 getService(object_path::motherBoardInv, interface::vsbpRecordVPD);
406 if (!service.empty())
407 {
408 DBusValue value;
409 getProperty(service, object_path::motherBoardInv,
410 interface::vsbpRecordVPD, "IM", value);
411
412 systemIM = std::get<std::vector<uint8_t>>(value);
413 }
414 }
415 catch (const std::exception& e)
416 {
417 lg2::warning("Failed reading System IM property from "
418 "interface: {IFACE} exception: {ERROR}",
419 "IFACE", interface::vsbpRecordVPD, "ERROR", e);
420 }
421
422 return systemIM;
423 }
424
getHWCalloutFields(const std::string & inventoryPath,std::string & fruPartNumber,std::string & ccin,std::string & serialNumber) const425 void DataInterface::getHWCalloutFields(
426 const std::string& inventoryPath, std::string& fruPartNumber,
427 std::string& ccin, std::string& serialNumber) const
428 {
429 // For now, attempt to get all of the properties directly on the path
430 // passed in. In the future, may need to make use of an algorithm
431 // to figure out which inventory objects actually hold these
432 // interfaces in the case of non FRUs, or possibly another service
433 // will provide this info. Any missing interfaces will result
434 // in exceptions being thrown.
435
436 auto service = getService(inventoryPath, interface::viniRecordVPD);
437
438 auto properties =
439 getAllProperties(service, inventoryPath, interface::viniRecordVPD);
440
441 auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
442 fruPartNumber = std::string{value.begin(), value.end()};
443
444 value = std::get<std::vector<uint8_t>>(properties["CC"]);
445 ccin = std::string{value.begin(), value.end()};
446
447 value = std::get<std::vector<uint8_t>>(properties["SN"]);
448 serialNumber = std::string{value.begin(), value.end()};
449 }
450
getLocationCode(const std::string & inventoryPath) const451 std::string DataInterface::getLocationCode(
452 const std::string& inventoryPath) const
453 {
454 auto service = getService(inventoryPath, interface::locCode);
455
456 DBusValue locCode;
457 getProperty(service, inventoryPath, interface::locCode, "LocationCode",
458 locCode);
459
460 return std::get<std::string>(locCode);
461 }
462
addLocationCodePrefix(const std::string & locationCode)463 std::string DataInterface::addLocationCodePrefix(
464 const std::string& locationCode)
465 {
466 static const std::string locationCodePrefix{"Ufcs-"};
467
468 // Technically there are 2 location code prefixes, Ufcs and Umts, so
469 // if it already starts with a U then don't need to do anything.
470 if (locationCode.front() != 'U')
471 {
472 return locationCodePrefix + locationCode;
473 }
474
475 return locationCode;
476 }
477
expandLocationCode(const std::string & locationCode,uint16_t) const478 std::string DataInterface::expandLocationCode(const std::string& locationCode,
479 uint16_t /*node*/) const
480 {
481 // Location codes for connectors are the location code of the FRU they are
482 // on, plus a '-Tx' segment. Remove this last segment before expanding it
483 // and then add it back in afterwards. This way, the connector doesn't have
484 // to be in the model just so that it can be expanded.
485 auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode);
486
487 auto method =
488 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
489 interface::vpdManager, "GetExpandedLocationCode");
490
491 method.append(addLocationCodePrefix(baseLoc), static_cast<uint16_t>(0));
492
493 auto reply = _bus.call(method, dbusTimeout);
494
495 auto expandedLocationCode = reply.unpack<std::string>();
496
497 if (!connectorLoc.empty())
498 {
499 expandedLocationCode += connectorLoc;
500 }
501
502 return expandedLocationCode;
503 }
504
getInventoryFromLocCode(const std::string & locationCode,uint16_t node,bool expanded) const505 std::vector<std::string> DataInterface::getInventoryFromLocCode(
506 const std::string& locationCode, uint16_t node, bool expanded) const
507 {
508 std::string methodName = expanded ? "GetFRUsByExpandedLocationCode"
509 : "GetFRUsByUnexpandedLocationCode";
510
511 // Remove the connector segment, if present, so that this method call
512 // returns an inventory path that getHWCalloutFields() can be used with.
513 // (The serial number, etc, aren't stored on the connector in the
514 // inventory, and may not even be modeled.)
515 auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode);
516
517 auto method =
518 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
519 interface::vpdManager, methodName.c_str());
520
521 if (expanded)
522 {
523 method.append(baseLoc);
524 }
525 else
526 {
527 method.append(addLocationCodePrefix(baseLoc), node);
528 }
529
530 auto reply = _bus.call(method, dbusTimeout);
531
532 auto entries = reply.unpack<std::vector<sdbusplus::message::object_path>>();
533
534 std::vector<std::string> paths;
535
536 // Note: The D-Bus method will fail if nothing found.
537 std::for_each(entries.begin(), entries.end(),
538 [&paths](const auto& path) { paths.push_back(path); });
539
540 return paths;
541 }
542
assertLEDGroup(const std::string & ledGroup,bool value) const543 void DataInterface::assertLEDGroup(const std::string& ledGroup,
544 bool value) const
545 {
546 DBusValue variant = value;
547 auto method =
548 _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(),
549 interface::dbusProperty, "Set");
550 method.append(interface::ledGroup, "Asserted", variant);
551 _bus.call(method, dbusTimeout);
552 }
553
setFunctional(const std::string & objectPath,bool value) const554 void DataInterface::setFunctional(const std::string& objectPath,
555 bool value) const
556 {
557 DBusPropertyMap prop{{"Functional", value}};
558 DBusInterfaceMap iface{{interface::operationalStatus, prop}};
559
560 // PIM takes a relative path like /system/chassis so remove
561 // /xyz/openbmc_project/inventory if present.
562 std::string path{objectPath};
563 if (path.starts_with(object_path::baseInv))
564 {
565 path = objectPath.substr(strlen(object_path::baseInv));
566 }
567 DBusObjectMap object{{path, iface}};
568
569 auto method = _bus.new_method_call(service_name::inventoryManager,
570 object_path::baseInv,
571 interface::inventoryManager, "Notify");
572 method.append(std::move(object));
573 _bus.call(method, dbusTimeout);
574 }
575
576 using AssociationTuple = std::tuple<std::string, std::string, std::string>;
577 using AssociationsProperty = std::vector<AssociationTuple>;
578
setCriticalAssociation(const std::string & objectPath) const579 void DataInterface::setCriticalAssociation(const std::string& objectPath) const
580 {
581 DBusValue getAssociationValue;
582
583 auto service = getService(objectPath, interface::associationDef);
584
585 getProperty(service, objectPath, interface::associationDef, "Associations",
586 getAssociationValue);
587
588 auto association = std::get<AssociationsProperty>(getAssociationValue);
589
590 AssociationTuple critAssociation{
591 "health_rollup", "critical",
592 "/xyz/openbmc_project/inventory/system/chassis"};
593
594 if (std::find(association.begin(), association.end(), critAssociation) ==
595 association.end())
596 {
597 association.push_back(critAssociation);
598 DBusValue setAssociationValue = association;
599
600 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
601 interface::dbusProperty, "Set");
602
603 method.append(interface::associationDef, "Associations",
604 setAssociationValue);
605 _bus.call(method, dbusTimeout);
606 }
607 }
608
getSystemNames() const609 std::vector<std::string> DataInterface::getSystemNames() const
610 {
611 DBusSubTree subtree;
612 DBusValue names;
613
614 auto method = _bus.new_method_call(service_name::objectMapper,
615 object_path::objectMapper,
616 interface::objectMapper, "GetSubTree");
617 method.append(std::string{"/"}, 0,
618 std::vector<std::string>{interface::compatible});
619 auto reply = _bus.call(method, dbusTimeout);
620
621 reply.read(subtree);
622 if (subtree.empty())
623 {
624 throw std::runtime_error("Compatible interface not on D-Bus");
625 }
626
627 for (const auto& [path, interfaceMap] : subtree)
628 {
629 auto iface = interfaceMap.find(service_name::entityManager);
630 if (iface == interfaceMap.end())
631 {
632 continue;
633 }
634
635 getProperty(iface->first, path, interface::compatible, "Names", names);
636
637 return std::get<std::vector<std::string>>(names);
638 }
639
640 throw std::runtime_error("EM Compatible interface not on D-Bus");
641 }
642
getQuiesceOnError() const643 bool DataInterface::getQuiesceOnError() const
644 {
645 bool ret = false;
646
647 try
648 {
649 auto service =
650 getService(object_path::logSetting, interface::logSetting);
651 if (!service.empty())
652 {
653 DBusValue value;
654 getProperty(service, object_path::logSetting, interface::logSetting,
655 "QuiesceOnHwError", value);
656
657 ret = std::get<bool>(value);
658 }
659 }
660 catch (const std::exception& e)
661 {
662 lg2::warning("Failed reading QuiesceOnHwError property from "
663 "interface: {IFACE} exception: {ERROR}",
664 "IFACE", interface::logSetting, "ERROR", e);
665 }
666
667 return ret;
668 }
669
670 #ifdef PEL_ENABLE_PHAL
createGuardRecord(const std::vector<uint8_t> & binPath,GardType eGardType,uint32_t plid) const671 void DataInterface::createGuardRecord(const std::vector<uint8_t>& binPath,
672 GardType eGardType, uint32_t plid) const
673 {
674 try
675 {
676 libguard::libguard_init(false);
677 libguard::create(binPath, plid, eGardType);
678 }
679 catch (libguard::exception::GuardException& e)
680 {
681 lg2::error("Exception in libguard {ERROR}", "ERROR", e);
682 }
683 }
684 #endif
685
createProgressSRC(const std::vector<uint8_t> & priSRC,const std::vector<uint8_t> & srcStruct) const686 void DataInterface::createProgressSRC(
687 const std::vector<uint8_t>& priSRC,
688 const std::vector<uint8_t>& srcStruct) const
689 {
690 DBusValue variant = std::make_tuple(priSRC, srcStruct);
691
692 auto method = _bus.new_method_call(service_name::bootRawProgress,
693 object_path::bootRawProgress,
694 interface::dbusProperty, "Set");
695
696 method.append(interface::bootRawProgress, "Value", variant);
697
698 _bus.call(method, dbusTimeout);
699 }
700
getLogIDWithHwIsolation() const701 std::vector<uint32_t> DataInterface::getLogIDWithHwIsolation() const
702 {
703 std::vector<std::string> association = {"xyz.openbmc_project.Association"};
704 std::string hwErrorLog = "/isolated_hw_errorlog";
705 std::string errorLog = "/error_log";
706 DBusPathList paths;
707 std::vector<uint32_t> ids;
708
709 // Get all latest mapper associations
710 paths = getPaths(association);
711 for (auto& path : paths)
712 {
713 // Look for object path with hardware isolation entry if any
714 size_t pos = path.find(hwErrorLog);
715 if (pos != std::string::npos)
716 {
717 // Get the object path
718 std::string ph = path;
719 ph.erase(pos, hwErrorLog.length());
720 auto service = getService(ph, interface::hwIsolationEntry);
721 if (!service.empty())
722 {
723 bool status;
724 DBusValue value;
725
726 // Read the Resolved property from object path
727 getProperty(service, ph, interface::hwIsolationEntry,
728 "Resolved", value);
729
730 status = std::get<bool>(value);
731
732 // If the entry isn't resolved
733 if (!status)
734 {
735 auto assocService =
736 getService(path, interface::association);
737 if (!assocService.empty())
738 {
739 DBusValue endpoints;
740
741 // Read Endpoints property
742 getProperty(assocService, path, interface::association,
743 "endpoints", endpoints);
744
745 auto logPath =
746 std::get<std::vector<std::string>>(endpoints);
747 if (!logPath.empty())
748 {
749 // Get OpenBMC event log Id
750 uint32_t id = stoi(logPath[0].substr(
751 logPath[0].find_last_of('/') + 1));
752 ids.push_back(id);
753 }
754 }
755 }
756 }
757 }
758
759 // Look for object path with error_log entry if any
760 pos = path.find(errorLog);
761 if (pos != std::string::npos)
762 {
763 auto service = getService(path, interface::association);
764 if (!service.empty())
765 {
766 DBusValue value;
767
768 // Read Endpoints property
769 getProperty(service, path, interface::association, "endpoints",
770 value);
771
772 auto logPath = std::get<std::vector<std::string>>(value);
773 if (!logPath.empty())
774 {
775 // Get OpenBMC event log Id
776 uint32_t id = stoi(
777 logPath[0].substr(logPath[0].find_last_of('/') + 1));
778 ids.push_back(id);
779 }
780 }
781 }
782 }
783
784 if (ids.size() > 1)
785 {
786 // remove duplicates to have only unique ids
787 std::sort(ids.begin(), ids.end());
788 ids.erase(std::unique(ids.begin(), ids.end()), ids.end());
789 }
790 return ids;
791 }
792
getRawProgressSRC(void) const793 std::vector<uint8_t> DataInterface::getRawProgressSRC(void) const
794 {
795 using RawProgressProperty =
796 std::tuple<std::vector<uint8_t>, std::vector<uint8_t>>;
797
798 DBusValue value;
799 getProperty(service_name::bootRawProgress, object_path::bootRawProgress,
800 interface::bootRawProgress, "Value", value);
801
802 const auto& rawProgress = std::get<RawProgressProperty>(value);
803 return std::get<1>(rawProgress);
804 }
805
getDIProperty(const std::string & locationCode) const806 std::optional<std::vector<uint8_t>> DataInterface::getDIProperty(
807 const std::string& locationCode) const
808 {
809 std::vector<uint8_t> viniDI;
810
811 try
812 {
813 // Note : The hardcoded value 0 should be changed when comes to
814 // multinode system.
815 auto objectPath = getInventoryFromLocCode(locationCode, 0, true);
816
817 DBusValue value;
818 getProperty(service_name::inventoryManager, objectPath[0],
819 interface::viniRecordVPD, "DI", value);
820
821 viniDI = std::get<std::vector<uint8_t>>(value);
822 }
823 catch (const std::exception& e)
824 {
825 lg2::warning(
826 "Failed reading DI property for the location code : {LOC_CODE} from "
827 "interface: {IFACE} exception: {ERROR}",
828 "LOC_CODE", locationCode, "IFACE", interface::viniRecordVPD,
829 "ERROR", e);
830 return std::nullopt;
831 }
832
833 return viniDI;
834 }
835
isDIMMLocCode(const std::string & locCode) const836 std::optional<bool> DataInterfaceBase::isDIMMLocCode(
837 const std::string& locCode) const
838 {
839 if (_locationCache.contains(locCode))
840 {
841 return _locationCache.at(locCode);
842 }
843 else
844 {
845 return std::nullopt;
846 }
847 }
848
addDIMMLocCode(const std::string & locCode,bool isFRUDIMM)849 void DataInterfaceBase::addDIMMLocCode(const std::string& locCode,
850 bool isFRUDIMM)
851 {
852 _locationCache.insert({locCode, isFRUDIMM});
853 }
854
isDIMM(const std::string & locCode)855 bool DataInterfaceBase::isDIMM(const std::string& locCode)
856 {
857 auto isDIMMType = isDIMMLocCode(locCode);
858 if (isDIMMType.has_value())
859 {
860 return isDIMMType.value();
861 }
862 #ifndef PEL_ENABLE_PHAL
863 return false;
864 #else
865 else
866 {
867 // Invoke pHAL API inorder to fetch the FRU Type
868 auto fruType = openpower::phal::pdbg::getFRUType(locCode);
869 bool isDIMMFRU{false};
870 if (fruType.has_value())
871 {
872 if (fruType.value() == ENUM_ATTR_TYPE_DIMM)
873 {
874 isDIMMFRU = true;
875 }
876 addDIMMLocCode(locCode, isDIMMFRU);
877 }
878 return isDIMMFRU;
879 }
880 #endif
881 }
882
getAssociatedPaths(const DBusPath & associatedPath,const DBusPath & subtree,int32_t depth,const DBusInterfaceList & interfaces) const883 DBusPathList DataInterface::getAssociatedPaths(
884 const DBusPath& associatedPath, const DBusPath& subtree, int32_t depth,
885 const DBusInterfaceList& interfaces) const
886 {
887 DBusPathList paths;
888 try
889 {
890 auto method = _bus.new_method_call(
891 service_name::objectMapper, object_path::objectMapper,
892 interface::objectMapper, "GetAssociatedSubTreePaths");
893 method.append(sdbusplus::message::object_path(associatedPath),
894 sdbusplus::message::object_path(subtree), depth,
895 interfaces);
896
897 auto reply = _bus.call(method, dbusTimeout);
898 reply.read(paths);
899 }
900 catch (const std::exception& e)
901 {
902 std::string ifaces(
903 std::ranges::fold_left_first(
904 interfaces,
905 [](std::string ifaces, const std::string& iface) {
906 return ifaces + ", " + iface;
907 })
908 .value_or(""));
909
910 lg2::error("Failed getting associated paths: {ERROR}. "
911 "AssociatedPath: {ASSOIC_PATH} Subtree: {SUBTREE} "
912 "Interfaces: {IFACES}",
913 "ERROR", e, "ASSOIC_PATH", associatedPath, "SUBTREE",
914 subtree, "IFACES", ifaces);
915 }
916 return paths;
917 }
918
startFruPlugWatch()919 void DataInterface::startFruPlugWatch()
920 {
921 // Add a watch on inventory InterfacesAdded and then find all
922 // existing hotpluggable interfaces and add propertiesChanged
923 // watches on them.
924
925 _invIaMatch = std::make_unique<sdbusplus::bus::match_t>(
926 _bus, match_rules::interfacesAdded(object_path::baseInv),
927 std::bind(&DataInterface::inventoryIfaceAdded, this,
928 std::placeholders::_1));
929 try
930 {
931 auto paths = getPaths(hotplugInterfaces);
932
933 _invPresentMatches.clear();
934
935 std::for_each(paths.begin(), paths.end(),
936 [this](const auto& path) { addHotplugWatch(path); });
937 }
938 catch (const sdbusplus::exception_t& e)
939 {
940 lg2::warning("Failed getting FRU paths to watch: {ERROR}", "ERROR", e);
941 }
942 }
943
addHotplugWatch(const std::string & path)944 void DataInterface::addHotplugWatch(const std::string& path)
945 {
946 if (!_invPresentMatches.contains(path))
947 {
948 _invPresentMatches.emplace(
949 path,
950 std::make_unique<sdbusplus::bus::match_t>(
951 _bus, match_rules::propertiesChanged(path, interface::invItem),
952 std::bind(&DataInterface::presenceChanged, this,
953 std::placeholders::_1)));
954 }
955 }
956
inventoryIfaceAdded(sdbusplus::message_t & msg)957 void DataInterface::inventoryIfaceAdded(sdbusplus::message_t& msg)
958 {
959 sdbusplus::message::object_path path;
960 DBusInterfaceMap interfaces;
961
962 msg.read(path, interfaces);
963
964 // Check if any of the new interfaces are for hot pluggable FRUs.
965 if (std::find_if(interfaces.begin(), interfaces.end(),
966 [](const auto& interfacePair) {
967 return std::find(hotplugInterfaces.begin(),
968 hotplugInterfaces.end(),
969 interfacePair.first) !=
970 hotplugInterfaces.end();
971 }) == interfaces.end())
972 {
973 return;
974 }
975
976 addHotplugWatch(path.str);
977
978 // If an Inventory.Item interface was also added, check presence now.
979
980 // Notes:
981 // * This assumes the Inv.Item and Inv.Fan/PS are added together which
982 // is currently the case.
983 // * If the code ever switches to something without a Present
984 // property, then the IA signal itself would probably indicate presence.
985
986 auto itemIt = interfaces.find(interface::invItem);
987 if (itemIt != interfaces.end())
988 {
989 notifyPresenceSubsribers(path.str, itemIt->second);
990 }
991 }
992
presenceChanged(sdbusplus::message_t & msg)993 void DataInterface::presenceChanged(sdbusplus::message_t& msg)
994 {
995 DBusInterface interface;
996 DBusPropertyMap properties;
997
998 msg.read(interface, properties);
999 if (interface != interface::invItem)
1000 {
1001 return;
1002 }
1003
1004 std::string path = msg.get_path();
1005 notifyPresenceSubsribers(path, properties);
1006 }
1007
notifyPresenceSubsribers(const std::string & path,const DBusPropertyMap & properties)1008 void DataInterface::notifyPresenceSubsribers(const std::string& path,
1009 const DBusPropertyMap& properties)
1010 {
1011 auto prop = properties.find("Present");
1012 if ((prop == properties.end()) || (!std::get<bool>(prop->second)))
1013 {
1014 return;
1015 }
1016
1017 std::string locCode;
1018
1019 try
1020 {
1021 auto service = getService(path, interface::locCode);
1022
1023 // If the hotplugged FRU is hosted by PLDM, then it is
1024 // in an IO expansion drawer and we don't care about it.
1025 if (service == service_name::pldm)
1026 {
1027 return;
1028 }
1029
1030 locCode = getLocationCode(path);
1031 }
1032 catch (const sdbusplus::exception_t& e)
1033 {
1034 lg2::debug("Could not get location code for {PATH}: {ERROR}", "PATH",
1035 path, "ERROR", e);
1036 return;
1037 }
1038
1039 lg2::debug("Detected FRU {PATH} ({LOC}) present ", "PATH", path, "LOC",
1040 locCode);
1041
1042 // Tell the subscribers.
1043 setFruPresent(locCode);
1044 }
1045
isPHALDevTreeExist() const1046 bool DataInterface::isPHALDevTreeExist() const
1047 {
1048 try
1049 {
1050 if (std::filesystem::exists(PDBG_DTB_PATH))
1051 {
1052 return true;
1053 }
1054 }
1055 catch (const std::exception& e)
1056 {
1057 lg2::error("Failed to check device tree {PHAL_DEVTREE_PATH} existence, "
1058 "{ERROR}",
1059 "PHAL_DEVTREE_PATH", PDBG_DTB_PATH, "ERROR", e);
1060 }
1061 return false;
1062 }
1063
1064 #ifdef PEL_ENABLE_PHAL
initPHAL()1065 void DataInterface::initPHAL()
1066 {
1067 if (setenv("PDBG_DTB", PDBG_DTB_PATH, 1))
1068 {
1069 // Log message and continue,
1070 // This is to help continue creating PEL in raw format.
1071 lg2::error("Failed to set PDBG_DTB: ({ERRNO})", "ERRNO",
1072 strerror(errno));
1073 }
1074
1075 if (!pdbg_targets_init(NULL))
1076 {
1077 lg2::error("pdbg_targets_init failed");
1078 return;
1079 }
1080
1081 if (libekb_init())
1082 {
1083 lg2::error("libekb_init failed, skipping ffdc processing");
1084 return;
1085 }
1086 }
1087 #endif
1088
subscribeToSystemdSignals()1089 void DataInterface::subscribeToSystemdSignals()
1090 {
1091 try
1092 {
1093 auto method =
1094 _bus.new_method_call(service_name::systemd, object_path::systemd,
1095 interface::systemdMgr, "Subscribe");
1096 _systemdSlot = method.call_async([this](sdbusplus::message_t&& msg) {
1097 // Initializing with nullptr to indicate that it is not subscribed
1098 // to any signal.
1099 this->_systemdSlot = sdbusplus::slot_t(nullptr);
1100 if (msg.is_method_error())
1101 {
1102 auto* error = msg.get_error();
1103 lg2::error("Failed to subscribe JobRemoved systemd signal, "
1104 "errorName: {ERR_NAME}, errorMsg: {ERR_MSG} ",
1105 "ERR_NAME", error->name, "ERR_MSG", error->message);
1106 return;
1107 }
1108
1109 namespace sdbusRule = sdbusplus::bus::match::rules;
1110 this->_systemdMatch =
1111 std::make_unique<decltype(this->_systemdMatch)::element_type>(
1112 this->_bus,
1113 sdbusRule::type::signal() +
1114 sdbusRule::member("JobRemoved") +
1115 sdbusRule::path(object_path::systemd) +
1116 sdbusRule::interface(interface::systemdMgr),
1117 [this](sdbusplus::message_t& msg) {
1118 uint32_t jobID;
1119 sdbusplus::message::object_path jobObjPath;
1120 std::string jobUnitName, jobUnitResult;
1121
1122 msg.read(jobID, jobObjPath, jobUnitName, jobUnitResult);
1123 if ((jobUnitName == "obmc-recover-pnor.service") &&
1124 (jobUnitResult == "done"))
1125 {
1126 #ifdef PEL_ENABLE_PHAL
1127 this->initPHAL();
1128 #endif
1129 // Invoke unsubscribe method to stop monitoring for
1130 // JobRemoved signals.
1131 this->unsubscribeFromSystemdSignals();
1132 }
1133 });
1134 });
1135 }
1136 catch (const sdbusplus::exception_t& e)
1137 {
1138 lg2::error(
1139 "Exception occured while handling JobRemoved systemd signal, "
1140 "exception: {ERROR}",
1141 "ERROR", e);
1142 }
1143 }
1144
unsubscribeFromSystemdSignals()1145 void DataInterface::unsubscribeFromSystemdSignals()
1146 {
1147 try
1148 {
1149 auto method =
1150 _bus.new_method_call(service_name::systemd, object_path::systemd,
1151 interface::systemdMgr, "Unsubscribe");
1152 _systemdSlot = method.call_async([this](sdbusplus::message_t&& msg) {
1153 // Unsubscribing the _systemdSlot from the subscribed signal
1154 this->_systemdSlot = sdbusplus::slot_t(nullptr);
1155 if (msg.is_method_error())
1156 {
1157 auto* error = msg.get_error();
1158 lg2::error(
1159 "Failed to unsubscribe from JobRemoved systemd signal, "
1160 "errorName: {ERR_NAME}, errorMsg: {ERR_MSG} ",
1161 "ERR_NAME", error->name, "ERR_MSG", error->message);
1162 return;
1163 }
1164 // Reset _systemdMatch to avoid reception of further JobRemoved
1165 // signals
1166 this->_systemdMatch.reset();
1167 });
1168 }
1169 catch (const sdbusplus::exception_t& e)
1170 {
1171 lg2::error(
1172 "Exception occured while unsubscribing from JobRemoved systemd signal, "
1173 "exception: {ERROR}",
1174 "ERROR", e);
1175 }
1176 }
1177
1178 } // namespace pels
1179 } // namespace openpower
1180