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