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