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