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