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