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