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 #endif
238 }
239 else
240 {
241 // Watch the "openpower-update-bios-attr-table" service to init
242 // PHAL libraries
243 subscribeToSystemdSignals();
244 }
245 }
246
getAllProperties(const std::string & service,const std::string & objectPath,const std::string & interface) const247 DBusPropertyMap DataInterface::getAllProperties(
248 const std::string& service, const std::string& objectPath,
249 const std::string& interface) const
250 {
251 DBusPropertyMap properties;
252
253 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
254 interface::dbusProperty, "GetAll");
255 method.append(interface);
256 auto reply = _bus.call(method, dbusTimeout);
257
258 reply.read(properties);
259
260 return properties;
261 }
262
getProperty(const std::string & service,const std::string & objectPath,const std::string & interface,const std::string & property,DBusValue & value) const263 void DataInterface::getProperty(
264 const std::string& service, const std::string& objectPath,
265 const std::string& interface, const std::string& property,
266 DBusValue& value) const
267 {
268 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
269 interface::dbusProperty, "Get");
270 method.append(interface, property);
271 auto reply = _bus.call(method, dbusTimeout);
272
273 reply.read(value);
274 }
275
getPaths(const DBusInterfaceList & interfaces) const276 DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const
277 {
278 auto method = _bus.new_method_call(
279 service_name::objectMapper, object_path::objectMapper,
280 interface::objectMapper, "GetSubTreePaths");
281
282 method.append(std::string{"/"}, 0, interfaces);
283
284 auto reply = _bus.call(method, dbusTimeout);
285
286 DBusPathList paths;
287 reply.read(paths);
288
289 return paths;
290 }
291
getService(const std::string & objectPath,const std::string & interface) const292 DBusService DataInterface::getService(const std::string& objectPath,
293 const std::string& interface) const
294 {
295 auto method = _bus.new_method_call(service_name::objectMapper,
296 object_path::objectMapper,
297 interface::objectMapper, "GetObject");
298
299 method.append(objectPath, std::vector<std::string>({interface}));
300
301 auto reply = _bus.call(method, dbusTimeout);
302
303 std::map<DBusService, DBusInterfaceList> response;
304 reply.read(response);
305
306 if (!response.empty())
307 {
308 return response.begin()->first;
309 }
310
311 return std::string{};
312 }
313
readBMCFWVersion()314 void DataInterface::readBMCFWVersion()
315 {
316 _bmcFWVersion =
317 phosphor::logging::util::getOSReleaseValue("VERSION").value_or("");
318 }
319
readServerFWVersion()320 void DataInterface::readServerFWVersion()
321 {
322 auto value =
323 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
324 if ((value != "") && (value.find_last_of(')') != std::string::npos))
325 {
326 std::size_t pos = value.find_first_of('(') + 1;
327 _serverFWVersion = value.substr(pos, value.find_last_of(')') - pos);
328 }
329 }
330
readBMCFWVersionID()331 void DataInterface::readBMCFWVersionID()
332 {
333 _bmcFWVersionID =
334 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or("");
335 }
336
getMachineTypeModel() const337 std::string DataInterface::getMachineTypeModel() const
338 {
339 std::string model;
340 try
341 {
342 auto service = getService(object_path::systemInv, interface::invAsset);
343 if (!service.empty())
344 {
345 DBusValue value;
346 getProperty(service, object_path::systemInv, interface::invAsset,
347 "Model", value);
348
349 model = std::get<std::string>(value);
350 }
351 }
352 catch (const std::exception& e)
353 {
354 lg2::warning("Failed reading Model property from "
355 "interface: {IFACE} exception: {ERROR}",
356 "IFACE", interface::invAsset, "ERROR", e);
357 }
358
359 return model;
360 }
361
getMachineSerialNumber() const362 std::string DataInterface::getMachineSerialNumber() const
363 {
364 std::string sn;
365 try
366 {
367 auto service = getService(object_path::systemInv, interface::invAsset);
368 if (!service.empty())
369 {
370 DBusValue value;
371 getProperty(service, object_path::systemInv, interface::invAsset,
372 "SerialNumber", value);
373
374 sn = std::get<std::string>(value);
375 }
376 }
377 catch (const std::exception& e)
378 {
379 lg2::warning("Failed reading SerialNumber property from "
380 "interface: {IFACE} exception: {ERROR}",
381 "IFACE", interface::invAsset, "ERROR", e);
382 }
383
384 return sn;
385 }
386
getMotherboardCCIN() const387 std::string DataInterface::getMotherboardCCIN() const
388 {
389 std::string ccin;
390
391 try
392 {
393 auto service =
394 getService(object_path::motherBoardInv, interface::viniRecordVPD);
395 if (!service.empty())
396 {
397 DBusValue value;
398 getProperty(service, object_path::motherBoardInv,
399 interface::viniRecordVPD, "CC", value);
400
401 auto cc = std::get<std::vector<uint8_t>>(value);
402 ccin = std::string{cc.begin(), cc.end()};
403 }
404 }
405 catch (const std::exception& e)
406 {
407 lg2::warning("Failed reading Motherboard CCIN property from "
408 "interface: {IFACE} exception: {ERROR}",
409 "IFACE", interface::viniRecordVPD, "ERROR", e);
410 }
411
412 return ccin;
413 }
414
getSystemIMKeyword() const415 std::vector<uint8_t> DataInterface::getSystemIMKeyword() const
416 {
417 std::vector<uint8_t> systemIM;
418
419 try
420 {
421 auto service =
422 getService(object_path::motherBoardInv, interface::vsbpRecordVPD);
423 if (!service.empty())
424 {
425 DBusValue value;
426 getProperty(service, object_path::motherBoardInv,
427 interface::vsbpRecordVPD, "IM", value);
428
429 systemIM = std::get<std::vector<uint8_t>>(value);
430 }
431 }
432 catch (const std::exception& e)
433 {
434 lg2::warning("Failed reading System IM property from "
435 "interface: {IFACE} exception: {ERROR}",
436 "IFACE", interface::vsbpRecordVPD, "ERROR", e);
437 }
438
439 return systemIM;
440 }
441
getHWCalloutFields(const std::string & inventoryPath,std::string & fruPartNumber,std::string & ccin,std::string & serialNumber) const442 void DataInterface::getHWCalloutFields(
443 const std::string& inventoryPath, std::string& fruPartNumber,
444 std::string& ccin, std::string& serialNumber) const
445 {
446 // For now, attempt to get all of the properties directly on the path
447 // passed in. In the future, may need to make use of an algorithm
448 // to figure out which inventory objects actually hold these
449 // interfaces in the case of non FRUs, or possibly another service
450 // will provide this info. Any missing interfaces will result
451 // in exceptions being thrown.
452
453 auto service = getService(inventoryPath, interface::viniRecordVPD);
454
455 auto properties =
456 getAllProperties(service, inventoryPath, interface::viniRecordVPD);
457
458 auto value = std::get<std::vector<uint8_t>>(properties["FN"]);
459 fruPartNumber = std::string{value.begin(), value.end()};
460
461 value = std::get<std::vector<uint8_t>>(properties["CC"]);
462 ccin = std::string{value.begin(), value.end()};
463
464 value = std::get<std::vector<uint8_t>>(properties["SN"]);
465 serialNumber = std::string{value.begin(), value.end()};
466 }
467
468 std::string
getLocationCode(const std::string & inventoryPath) const469 DataInterface::getLocationCode(const std::string& inventoryPath) const
470 {
471 auto service = getService(inventoryPath, interface::locCode);
472
473 DBusValue locCode;
474 getProperty(service, inventoryPath, interface::locCode, "LocationCode",
475 locCode);
476
477 return std::get<std::string>(locCode);
478 }
479
480 std::string
addLocationCodePrefix(const std::string & locationCode)481 DataInterface::addLocationCodePrefix(const std::string& locationCode)
482 {
483 static const std::string locationCodePrefix{"Ufcs-"};
484
485 // Technically there are 2 location code prefixes, Ufcs and Umts, so
486 // if it already starts with a U then don't need to do anything.
487 if (locationCode.front() != 'U')
488 {
489 return locationCodePrefix + locationCode;
490 }
491
492 return locationCode;
493 }
494
expandLocationCode(const std::string & locationCode,uint16_t) const495 std::string DataInterface::expandLocationCode(const std::string& locationCode,
496 uint16_t /*node*/) const
497 {
498 // Location codes for connectors are the location code of the FRU they are
499 // on, plus a '-Tx' segment. Remove this last segment before expanding it
500 // and then add it back in afterwards. This way, the connector doesn't have
501 // to be in the model just so that it can be expanded.
502 auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode);
503
504 auto method =
505 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
506 interface::vpdManager, "GetExpandedLocationCode");
507
508 method.append(addLocationCodePrefix(baseLoc), static_cast<uint16_t>(0));
509
510 auto reply = _bus.call(method, dbusTimeout);
511
512 std::string expandedLocationCode;
513 reply.read(expandedLocationCode);
514
515 if (!connectorLoc.empty())
516 {
517 expandedLocationCode += connectorLoc;
518 }
519
520 return expandedLocationCode;
521 }
522
getInventoryFromLocCode(const std::string & locationCode,uint16_t node,bool expanded) const523 std::vector<std::string> DataInterface::getInventoryFromLocCode(
524 const std::string& locationCode, uint16_t node, bool expanded) const
525 {
526 std::string methodName = expanded ? "GetFRUsByExpandedLocationCode"
527 : "GetFRUsByUnexpandedLocationCode";
528
529 // Remove the connector segment, if present, so that this method call
530 // returns an inventory path that getHWCalloutFields() can be used with.
531 // (The serial number, etc, aren't stored on the connector in the
532 // inventory, and may not even be modeled.)
533 auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode);
534
535 auto method =
536 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager,
537 interface::vpdManager, methodName.c_str());
538
539 if (expanded)
540 {
541 method.append(baseLoc);
542 }
543 else
544 {
545 method.append(addLocationCodePrefix(baseLoc), node);
546 }
547
548 auto reply = _bus.call(method, dbusTimeout);
549
550 std::vector<sdbusplus::message::object_path> entries;
551 reply.read(entries);
552
553 std::vector<std::string> paths;
554
555 // Note: The D-Bus method will fail if nothing found.
556 std::for_each(entries.begin(), entries.end(),
557 [&paths](const auto& path) { paths.push_back(path); });
558
559 return paths;
560 }
561
assertLEDGroup(const std::string & ledGroup,bool value) const562 void DataInterface::assertLEDGroup(const std::string& ledGroup,
563 bool value) const
564 {
565 DBusValue variant = value;
566 auto method =
567 _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(),
568 interface::dbusProperty, "Set");
569 method.append(interface::ledGroup, "Asserted", variant);
570 _bus.call(method, dbusTimeout);
571 }
572
setFunctional(const std::string & objectPath,bool value) const573 void DataInterface::setFunctional(const std::string& objectPath,
574 bool value) const
575 {
576 DBusPropertyMap prop{{"Functional", value}};
577 DBusInterfaceMap iface{{interface::operationalStatus, prop}};
578
579 // PIM takes a relative path like /system/chassis so remove
580 // /xyz/openbmc_project/inventory if present.
581 std::string path{objectPath};
582 if (path.starts_with(object_path::baseInv))
583 {
584 path = objectPath.substr(strlen(object_path::baseInv));
585 }
586 DBusObjectMap object{{path, iface}};
587
588 auto method = _bus.new_method_call(service_name::inventoryManager,
589 object_path::baseInv,
590 interface::inventoryManager, "Notify");
591 method.append(std::move(object));
592 _bus.call(method, dbusTimeout);
593 }
594
595 using AssociationTuple = std::tuple<std::string, std::string, std::string>;
596 using AssociationsProperty = std::vector<AssociationTuple>;
597
setCriticalAssociation(const std::string & objectPath) const598 void DataInterface::setCriticalAssociation(const std::string& objectPath) const
599 {
600 DBusValue getAssociationValue;
601
602 auto service = getService(objectPath, interface::associationDef);
603
604 getProperty(service, objectPath, interface::associationDef, "Associations",
605 getAssociationValue);
606
607 auto association = std::get<AssociationsProperty>(getAssociationValue);
608
609 AssociationTuple critAssociation{
610 "health_rollup", "critical",
611 "/xyz/openbmc_project/inventory/system/chassis"};
612
613 if (std::find(association.begin(), association.end(), critAssociation) ==
614 association.end())
615 {
616 association.push_back(critAssociation);
617 DBusValue setAssociationValue = association;
618
619 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(),
620 interface::dbusProperty, "Set");
621
622 method.append(interface::associationDef, "Associations",
623 setAssociationValue);
624 _bus.call(method, dbusTimeout);
625 }
626 }
627
getSystemNames() const628 std::vector<std::string> DataInterface::getSystemNames() const
629 {
630 DBusSubTree subtree;
631 DBusValue names;
632
633 auto method = _bus.new_method_call(service_name::objectMapper,
634 object_path::objectMapper,
635 interface::objectMapper, "GetSubTree");
636 method.append(std::string{"/"}, 0,
637 std::vector<std::string>{interface::compatible});
638 auto reply = _bus.call(method, dbusTimeout);
639
640 reply.read(subtree);
641 if (subtree.empty())
642 {
643 throw std::runtime_error("Compatible interface not on D-Bus");
644 }
645
646 for (const auto& [path, interfaceMap] : subtree)
647 {
648 auto iface = interfaceMap.find(service_name::entityManager);
649 if (iface == interfaceMap.end())
650 {
651 continue;
652 }
653
654 getProperty(iface->first, path, interface::compatible, "Names", names);
655
656 return std::get<std::vector<std::string>>(names);
657 }
658
659 throw std::runtime_error("EM Compatible interface not on D-Bus");
660 }
661
getQuiesceOnError() const662 bool DataInterface::getQuiesceOnError() const
663 {
664 bool ret = false;
665
666 try
667 {
668 auto service =
669 getService(object_path::logSetting, interface::logSetting);
670 if (!service.empty())
671 {
672 DBusValue value;
673 getProperty(service, object_path::logSetting, interface::logSetting,
674 "QuiesceOnHwError", value);
675
676 ret = std::get<bool>(value);
677 }
678 }
679 catch (const std::exception& e)
680 {
681 lg2::warning("Failed reading QuiesceOnHwError property from "
682 "interface: {IFACE} exception: {ERROR}",
683 "IFACE", interface::logSetting, "ERROR", e);
684 }
685
686 return ret;
687 }
688
689 #ifdef PEL_ENABLE_PHAL
createGuardRecord(const std::vector<uint8_t> & binPath,GardType eGardType,uint32_t plid) const690 void DataInterface::createGuardRecord(const std::vector<uint8_t>& binPath,
691 GardType eGardType, uint32_t plid) const
692 {
693 try
694 {
695 libguard::libguard_init(false);
696 libguard::create(binPath, plid, eGardType);
697 }
698 catch (libguard::exception::GuardException& e)
699 {
700 lg2::error("Exception in libguard {ERROR}", "ERROR", e);
701 }
702 }
703 #endif
704
createProgressSRC(const uint64_t & priSRC,const std::vector<uint8_t> & srcStruct) const705 void DataInterface::createProgressSRC(
706 const uint64_t& priSRC, const std::vector<uint8_t>& srcStruct) const
707 {
708 DBusValue variant = std::make_tuple(priSRC, srcStruct);
709
710 auto method = _bus.new_method_call(service_name::bootRawProgress,
711 object_path::bootRawProgress,
712 interface::dbusProperty, "Set");
713
714 method.append(interface::bootRawProgress, "Value", variant);
715
716 _bus.call(method, dbusTimeout);
717 }
718
getLogIDWithHwIsolation() const719 std::vector<uint32_t> DataInterface::getLogIDWithHwIsolation() const
720 {
721 std::vector<std::string> association = {"xyz.openbmc_project.Association"};
722 std::string hwErrorLog = "/isolated_hw_errorlog";
723 std::string errorLog = "/error_log";
724 DBusPathList paths;
725 std::vector<uint32_t> ids;
726
727 // Get all latest mapper associations
728 paths = getPaths(association);
729 for (auto& path : paths)
730 {
731 // Look for object path with hardware isolation entry if any
732 size_t pos = path.find(hwErrorLog);
733 if (pos != std::string::npos)
734 {
735 // Get the object path
736 std::string ph = path;
737 ph.erase(pos, hwErrorLog.length());
738 auto service = getService(ph, interface::hwIsolationEntry);
739 if (!service.empty())
740 {
741 bool status;
742 DBusValue value;
743
744 // Read the Resolved property from object path
745 getProperty(service, ph, interface::hwIsolationEntry,
746 "Resolved", value);
747
748 status = std::get<bool>(value);
749
750 // If the entry isn't resolved
751 if (!status)
752 {
753 auto assocService =
754 getService(path, interface::association);
755 if (!assocService.empty())
756 {
757 DBusValue endpoints;
758
759 // Read Endpoints property
760 getProperty(assocService, path, interface::association,
761 "endpoints", endpoints);
762
763 auto logPath =
764 std::get<std::vector<std::string>>(endpoints);
765 if (!logPath.empty())
766 {
767 // Get OpenBMC event log Id
768 uint32_t id = stoi(logPath[0].substr(
769 logPath[0].find_last_of('/') + 1));
770 ids.push_back(id);
771 }
772 }
773 }
774 }
775 }
776
777 // Look for object path with error_log entry if any
778 pos = path.find(errorLog);
779 if (pos != std::string::npos)
780 {
781 auto service = getService(path, interface::association);
782 if (!service.empty())
783 {
784 DBusValue value;
785
786 // Read Endpoints property
787 getProperty(service, path, interface::association, "endpoints",
788 value);
789
790 auto logPath = std::get<std::vector<std::string>>(value);
791 if (!logPath.empty())
792 {
793 // Get OpenBMC event log Id
794 uint32_t id = stoi(
795 logPath[0].substr(logPath[0].find_last_of('/') + 1));
796 ids.push_back(id);
797 }
798 }
799 }
800 }
801
802 if (ids.size() > 1)
803 {
804 // remove duplicates to have only unique ids
805 std::sort(ids.begin(), ids.end());
806 ids.erase(std::unique(ids.begin(), ids.end()), ids.end());
807 }
808 return ids;
809 }
810
getRawProgressSRC(void) const811 std::vector<uint8_t> DataInterface::getRawProgressSRC(void) const
812 {
813 using RawProgressProperty = std::tuple<uint64_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
823 std::optional<std::vector<uint8_t>>
getDIProperty(const std::string & locationCode) const824 DataInterface::getDIProperty(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
853 std::optional<bool>
isDIMMLocCode(const std::string & locCode) const854 DataInterfaceBase::isDIMMLocCode(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