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 biosConfigMgr = "xyz.openbmc_project.BIOSConfigManager"; 49 constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw"; 50 constexpr auto pldm = "xyz.openbmc_project.PLDM"; 51 constexpr auto inventoryManager = "xyz.openbmc_project.Inventory.Manager"; 52 constexpr auto entityManager = "xyz.openbmc_project.EntityManager"; 53 constexpr auto systemd = "org.freedesktop.systemd1"; 54 } // namespace service_name 55 56 namespace object_path 57 { 58 constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper"; 59 constexpr auto systemInv = "/xyz/openbmc_project/inventory/system"; 60 constexpr auto motherBoardInv = 61 "/xyz/openbmc_project/inventory/system/chassis/motherboard"; 62 constexpr auto baseInv = "/xyz/openbmc_project/inventory"; 63 constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0"; 64 constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0"; 65 constexpr auto hostState = "/xyz/openbmc_project/state/host0"; 66 constexpr auto enableHostPELs = 67 "/xyz/openbmc_project/logging/send_event_logs_to_host"; 68 constexpr auto vpdManager = "/com/ibm/VPD/Manager"; 69 constexpr auto logSetting = "/xyz/openbmc_project/logging/settings"; 70 constexpr auto biosConfigMgr = "/xyz/openbmc_project/bios_config/manager"; 71 constexpr auto bootRawProgress = "/xyz/openbmc_project/state/boot/raw0"; 72 constexpr auto systemd = "/org/freedesktop/systemd1"; 73 } // namespace object_path 74 75 namespace interface 76 { 77 constexpr auto dbusProperty = "org.freedesktop.DBus.Properties"; 78 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper"; 79 constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset"; 80 constexpr auto bootProgress = "xyz.openbmc_project.State.Boot.Progress"; 81 constexpr auto enable = "xyz.openbmc_project.Object.Enable"; 82 constexpr auto bmcState = "xyz.openbmc_project.State.BMC"; 83 constexpr auto chassisState = "xyz.openbmc_project.State.Chassis"; 84 constexpr auto hostState = "xyz.openbmc_project.State.Host"; 85 constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI"; 86 constexpr auto vsbpRecordVPD = "com.ibm.ipzvpd.VSBP"; 87 constexpr auto locCode = "xyz.openbmc_project.Inventory.Decorator.LocationCode"; 88 constexpr auto compatible = 89 "xyz.openbmc_project.Inventory.Decorator.Compatible"; 90 constexpr auto vpdManager = "com.ibm.VPD.Manager"; 91 constexpr auto ledGroup = "xyz.openbmc_project.Led.Group"; 92 constexpr auto operationalStatus = 93 "xyz.openbmc_project.State.Decorator.OperationalStatus"; 94 constexpr auto logSetting = "xyz.openbmc_project.Logging.Settings"; 95 constexpr auto associationDef = "xyz.openbmc_project.Association.Definitions"; 96 constexpr auto hwIsolationEntry = "xyz.openbmc_project.HardwareIsolation.Entry"; 97 constexpr auto association = "xyz.openbmc_project.Association"; 98 constexpr auto biosConfigMgr = "xyz.openbmc_project.BIOSConfig.Manager"; 99 constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw"; 100 constexpr auto invItem = "xyz.openbmc_project.Inventory.Item"; 101 constexpr auto invFan = "xyz.openbmc_project.Inventory.Item.Fan"; 102 constexpr auto invPowerSupply = 103 "xyz.openbmc_project.Inventory.Item.PowerSupply"; 104 constexpr auto inventoryManager = "xyz.openbmc_project.Inventory.Manager"; 105 constexpr auto systemdMgr = "org.freedesktop.systemd1.Manager"; 106 } // namespace interface 107 108 using namespace sdbusplus::server::xyz::openbmc_project::state::boot; 109 using namespace sdbusplus::server::xyz::openbmc_project::state; 110 namespace match_rules = sdbusplus::bus::match::rules; 111 112 const DBusInterfaceList hotplugInterfaces{interface::invFan, 113 interface::invPowerSupply}; 114 static constexpr auto PDBG_DTB_PATH = 115 "/var/lib/phosphor-software-manager/hostfw/running/DEVTREE"; 116 117 std::pair<std::string, std::string> 118 DataInterfaceBase::extractConnectorFromLocCode( 119 const std::string& locationCode) 120 { 121 auto base = locationCode; 122 std::string connector{}; 123 124 auto pos = base.find("-T"); 125 if (pos != std::string::npos) 126 { 127 connector = base.substr(pos); 128 base = base.substr(0, pos); 129 } 130 131 return {base, connector}; 132 } 133 134 DataInterface::DataInterface(sdbusplus::bus_t& bus) : 135 _bus(bus), _systemdSlot(nullptr) 136 { 137 readBMCFWVersion(); 138 readServerFWVersion(); 139 readBMCFWVersionID(); 140 141 // Watch the BootProgress property 142 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 143 bus, object_path::hostState, interface::bootProgress, "BootProgress", 144 *this, [this](const auto& value) { 145 this->_bootState = std::get<std::string>(value); 146 auto status = Progress::convertProgressStagesFromString( 147 std::get<std::string>(value)); 148 149 if ((status == Progress::ProgressStages::SystemInitComplete) || 150 (status == Progress::ProgressStages::OSRunning)) 151 { 152 setHostUp(true); 153 } 154 else 155 { 156 setHostUp(false); 157 } 158 })); 159 160 // Watch the host PEL enable property 161 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 162 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this, 163 [this](const auto& value) { 164 if (std::get<bool>(value) != this->_sendPELsToHost) 165 { 166 lg2::info("The send PELs to host setting changed to {VAL}", 167 "VAL", std::get<bool>(value)); 168 } 169 this->_sendPELsToHost = std::get<bool>(value); 170 })); 171 172 // Watch the BMCState property 173 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 174 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState", 175 *this, [this](const auto& value) { 176 const auto& state = std::get<std::string>(value); 177 this->_bmcState = state; 178 179 // Wait for BMC ready to start watching for 180 // plugs so things calm down first. 181 if (BMC::convertBMCStateFromString(state) == BMC::BMCState::Ready) 182 { 183 startFruPlugWatch(); 184 } 185 })); 186 187 // Watch the chassis current and requested power state properties 188 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>( 189 bus, object_path::chassisState, interface::chassisState, *this, 190 [this](const auto& properties) { 191 auto state = properties.find("CurrentPowerState"); 192 if (state != properties.end()) 193 { 194 this->_chassisState = std::get<std::string>(state->second); 195 } 196 197 auto trans = properties.find("RequestedPowerTransition"); 198 if (trans != properties.end()) 199 { 200 this->_chassisTransition = std::get<std::string>(trans->second); 201 } 202 })); 203 204 // Watch the CurrentHostState property 205 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 206 bus, object_path::hostState, interface::hostState, "CurrentHostState", 207 *this, [this](const auto& value) { 208 this->_hostState = std::get<std::string>(value); 209 })); 210 211 // Watch the BaseBIOSTable property for the hmc managed attribute 212 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 213 bus, object_path::biosConfigMgr, interface::biosConfigMgr, 214 "BaseBIOSTable", service_name::biosConfigMgr, *this, 215 [this](const auto& value) { 216 const auto& attributes = std::get<BiosAttributes>(value); 217 218 auto it = attributes.find("pvm_hmc_managed"); 219 if (it != attributes.end()) 220 { 221 const auto& currentValVariant = std::get<5>(it->second); 222 auto currentVal = std::get_if<std::string>(¤tValVariant); 223 if (currentVal) 224 { 225 this->_hmcManaged = 226 (*currentVal == "Enabled") ? true : false; 227 } 228 } 229 })); 230 231 if (isPHALDevTreeExist()) 232 { 233 #ifdef PEL_ENABLE_PHAL 234 initPHAL(); 235 #endif 236 } 237 else 238 { 239 // Watch the "openpower-update-bios-attr-table" service to init 240 // PHAL libraries 241 subscribeToSystemdSignals(); 242 } 243 } 244 245 DBusPropertyMap DataInterface::getAllProperties( 246 const std::string& service, const std::string& objectPath, 247 const std::string& interface) const 248 { 249 DBusPropertyMap properties; 250 251 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 252 interface::dbusProperty, "GetAll"); 253 method.append(interface); 254 auto reply = _bus.call(method, dbusTimeout); 255 256 reply.read(properties); 257 258 return properties; 259 } 260 261 void DataInterface::getProperty( 262 const std::string& service, const std::string& objectPath, 263 const std::string& interface, const std::string& property, 264 DBusValue& value) const 265 { 266 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 267 interface::dbusProperty, "Get"); 268 method.append(interface, property); 269 auto reply = _bus.call(method, dbusTimeout); 270 271 reply.read(value); 272 } 273 274 DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const 275 { 276 auto method = _bus.new_method_call( 277 service_name::objectMapper, object_path::objectMapper, 278 interface::objectMapper, "GetSubTreePaths"); 279 280 method.append(std::string{"/"}, 0, interfaces); 281 282 auto reply = _bus.call(method, dbusTimeout); 283 284 DBusPathList paths; 285 reply.read(paths); 286 287 return paths; 288 } 289 290 DBusService DataInterface::getService(const std::string& objectPath, 291 const std::string& interface) const 292 { 293 auto method = _bus.new_method_call(service_name::objectMapper, 294 object_path::objectMapper, 295 interface::objectMapper, "GetObject"); 296 297 method.append(objectPath, std::vector<std::string>({interface})); 298 299 auto reply = _bus.call(method, dbusTimeout); 300 301 std::map<DBusService, DBusInterfaceList> response; 302 reply.read(response); 303 304 if (!response.empty()) 305 { 306 return response.begin()->first; 307 } 308 309 return std::string{}; 310 } 311 312 void DataInterface::readBMCFWVersion() 313 { 314 _bmcFWVersion = 315 phosphor::logging::util::getOSReleaseValue("VERSION").value_or(""); 316 } 317 318 void DataInterface::readServerFWVersion() 319 { 320 auto value = 321 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or(""); 322 if ((value != "") && (value.find_last_of(')') != std::string::npos)) 323 { 324 std::size_t pos = value.find_first_of('(') + 1; 325 _serverFWVersion = value.substr(pos, value.find_last_of(')') - pos); 326 } 327 } 328 329 void DataInterface::readBMCFWVersionID() 330 { 331 _bmcFWVersionID = 332 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or(""); 333 } 334 335 std::string DataInterface::getMachineTypeModel() const 336 { 337 std::string model; 338 try 339 { 340 auto service = getService(object_path::systemInv, interface::invAsset); 341 if (!service.empty()) 342 { 343 DBusValue value; 344 getProperty(service, object_path::systemInv, interface::invAsset, 345 "Model", value); 346 347 model = std::get<std::string>(value); 348 } 349 } 350 catch (const std::exception& e) 351 { 352 lg2::warning("Failed reading Model property from " 353 "interface: {IFACE} exception: {ERROR}", 354 "IFACE", interface::invAsset, "ERROR", e); 355 } 356 357 return model; 358 } 359 360 std::string DataInterface::getMachineSerialNumber() const 361 { 362 std::string sn; 363 try 364 { 365 auto service = getService(object_path::systemInv, interface::invAsset); 366 if (!service.empty()) 367 { 368 DBusValue value; 369 getProperty(service, object_path::systemInv, interface::invAsset, 370 "SerialNumber", value); 371 372 sn = std::get<std::string>(value); 373 } 374 } 375 catch (const std::exception& e) 376 { 377 lg2::warning("Failed reading SerialNumber property from " 378 "interface: {IFACE} exception: {ERROR}", 379 "IFACE", interface::invAsset, "ERROR", e); 380 } 381 382 return sn; 383 } 384 385 std::string DataInterface::getMotherboardCCIN() const 386 { 387 std::string ccin; 388 389 try 390 { 391 auto service = 392 getService(object_path::motherBoardInv, interface::viniRecordVPD); 393 if (!service.empty()) 394 { 395 DBusValue value; 396 getProperty(service, object_path::motherBoardInv, 397 interface::viniRecordVPD, "CC", value); 398 399 auto cc = std::get<std::vector<uint8_t>>(value); 400 ccin = std::string{cc.begin(), cc.end()}; 401 } 402 } 403 catch (const std::exception& e) 404 { 405 lg2::warning("Failed reading Motherboard CCIN property from " 406 "interface: {IFACE} exception: {ERROR}", 407 "IFACE", interface::viniRecordVPD, "ERROR", e); 408 } 409 410 return ccin; 411 } 412 413 std::vector<uint8_t> DataInterface::getSystemIMKeyword() const 414 { 415 std::vector<uint8_t> systemIM; 416 417 try 418 { 419 auto service = 420 getService(object_path::motherBoardInv, interface::vsbpRecordVPD); 421 if (!service.empty()) 422 { 423 DBusValue value; 424 getProperty(service, object_path::motherBoardInv, 425 interface::vsbpRecordVPD, "IM", value); 426 427 systemIM = std::get<std::vector<uint8_t>>(value); 428 } 429 } 430 catch (const std::exception& e) 431 { 432 lg2::warning("Failed reading System IM property from " 433 "interface: {IFACE} exception: {ERROR}", 434 "IFACE", interface::vsbpRecordVPD, "ERROR", e); 435 } 436 437 return systemIM; 438 } 439 440 void DataInterface::getHWCalloutFields( 441 const std::string& inventoryPath, std::string& fruPartNumber, 442 std::string& ccin, std::string& serialNumber) const 443 { 444 // For now, attempt to get all of the properties directly on the path 445 // passed in. In the future, may need to make use of an algorithm 446 // to figure out which inventory objects actually hold these 447 // interfaces in the case of non FRUs, or possibly another service 448 // will provide this info. Any missing interfaces will result 449 // in exceptions being thrown. 450 451 auto service = getService(inventoryPath, interface::viniRecordVPD); 452 453 auto properties = 454 getAllProperties(service, inventoryPath, interface::viniRecordVPD); 455 456 auto value = std::get<std::vector<uint8_t>>(properties["FN"]); 457 fruPartNumber = std::string{value.begin(), value.end()}; 458 459 value = std::get<std::vector<uint8_t>>(properties["CC"]); 460 ccin = std::string{value.begin(), value.end()}; 461 462 value = std::get<std::vector<uint8_t>>(properties["SN"]); 463 serialNumber = std::string{value.begin(), value.end()}; 464 } 465 466 std::string DataInterface::getLocationCode( 467 const std::string& inventoryPath) const 468 { 469 auto service = getService(inventoryPath, interface::locCode); 470 471 DBusValue locCode; 472 getProperty(service, inventoryPath, interface::locCode, "LocationCode", 473 locCode); 474 475 return std::get<std::string>(locCode); 476 } 477 478 std::string DataInterface::addLocationCodePrefix( 479 const std::string& locationCode) 480 { 481 static const std::string locationCodePrefix{"Ufcs-"}; 482 483 // Technically there are 2 location code prefixes, Ufcs and Umts, so 484 // if it already starts with a U then don't need to do anything. 485 if (locationCode.front() != 'U') 486 { 487 return locationCodePrefix + locationCode; 488 } 489 490 return locationCode; 491 } 492 493 std::string DataInterface::expandLocationCode(const std::string& locationCode, 494 uint16_t /*node*/) const 495 { 496 // Location codes for connectors are the location code of the FRU they are 497 // on, plus a '-Tx' segment. Remove this last segment before expanding it 498 // and then add it back in afterwards. This way, the connector doesn't have 499 // to be in the model just so that it can be expanded. 500 auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode); 501 502 auto method = 503 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager, 504 interface::vpdManager, "GetExpandedLocationCode"); 505 506 method.append(addLocationCodePrefix(baseLoc), static_cast<uint16_t>(0)); 507 508 auto reply = _bus.call(method, dbusTimeout); 509 510 std::string expandedLocationCode; 511 reply.read(expandedLocationCode); 512 513 if (!connectorLoc.empty()) 514 { 515 expandedLocationCode += connectorLoc; 516 } 517 518 return expandedLocationCode; 519 } 520 521 std::vector<std::string> DataInterface::getInventoryFromLocCode( 522 const std::string& locationCode, uint16_t node, bool expanded) const 523 { 524 std::string methodName = expanded ? "GetFRUsByExpandedLocationCode" 525 : "GetFRUsByUnexpandedLocationCode"; 526 527 // Remove the connector segment, if present, so that this method call 528 // returns an inventory path that getHWCalloutFields() can be used with. 529 // (The serial number, etc, aren't stored on the connector in the 530 // inventory, and may not even be modeled.) 531 auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode); 532 533 auto method = 534 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager, 535 interface::vpdManager, methodName.c_str()); 536 537 if (expanded) 538 { 539 method.append(baseLoc); 540 } 541 else 542 { 543 method.append(addLocationCodePrefix(baseLoc), node); 544 } 545 546 auto reply = _bus.call(method, dbusTimeout); 547 548 std::vector<sdbusplus::message::object_path> entries; 549 reply.read(entries); 550 551 std::vector<std::string> paths; 552 553 // Note: The D-Bus method will fail if nothing found. 554 std::for_each(entries.begin(), entries.end(), 555 [&paths](const auto& path) { paths.push_back(path); }); 556 557 return paths; 558 } 559 560 void DataInterface::assertLEDGroup(const std::string& ledGroup, 561 bool value) const 562 { 563 DBusValue variant = value; 564 auto method = 565 _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(), 566 interface::dbusProperty, "Set"); 567 method.append(interface::ledGroup, "Asserted", variant); 568 _bus.call(method, dbusTimeout); 569 } 570 571 void DataInterface::setFunctional(const std::string& objectPath, 572 bool value) const 573 { 574 DBusPropertyMap prop{{"Functional", value}}; 575 DBusInterfaceMap iface{{interface::operationalStatus, prop}}; 576 577 // PIM takes a relative path like /system/chassis so remove 578 // /xyz/openbmc_project/inventory if present. 579 std::string path{objectPath}; 580 if (path.starts_with(object_path::baseInv)) 581 { 582 path = objectPath.substr(strlen(object_path::baseInv)); 583 } 584 DBusObjectMap object{{path, iface}}; 585 586 auto method = _bus.new_method_call(service_name::inventoryManager, 587 object_path::baseInv, 588 interface::inventoryManager, "Notify"); 589 method.append(std::move(object)); 590 _bus.call(method, dbusTimeout); 591 } 592 593 using AssociationTuple = std::tuple<std::string, std::string, std::string>; 594 using AssociationsProperty = std::vector<AssociationTuple>; 595 596 void DataInterface::setCriticalAssociation(const std::string& objectPath) const 597 { 598 DBusValue getAssociationValue; 599 600 auto service = getService(objectPath, interface::associationDef); 601 602 getProperty(service, objectPath, interface::associationDef, "Associations", 603 getAssociationValue); 604 605 auto association = std::get<AssociationsProperty>(getAssociationValue); 606 607 AssociationTuple critAssociation{ 608 "health_rollup", "critical", 609 "/xyz/openbmc_project/inventory/system/chassis"}; 610 611 if (std::find(association.begin(), association.end(), critAssociation) == 612 association.end()) 613 { 614 association.push_back(critAssociation); 615 DBusValue setAssociationValue = association; 616 617 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 618 interface::dbusProperty, "Set"); 619 620 method.append(interface::associationDef, "Associations", 621 setAssociationValue); 622 _bus.call(method, dbusTimeout); 623 } 624 } 625 626 std::vector<std::string> DataInterface::getSystemNames() const 627 { 628 DBusSubTree subtree; 629 DBusValue names; 630 631 auto method = _bus.new_method_call(service_name::objectMapper, 632 object_path::objectMapper, 633 interface::objectMapper, "GetSubTree"); 634 method.append(std::string{"/"}, 0, 635 std::vector<std::string>{interface::compatible}); 636 auto reply = _bus.call(method, dbusTimeout); 637 638 reply.read(subtree); 639 if (subtree.empty()) 640 { 641 throw std::runtime_error("Compatible interface not on D-Bus"); 642 } 643 644 for (const auto& [path, interfaceMap] : subtree) 645 { 646 auto iface = interfaceMap.find(service_name::entityManager); 647 if (iface == interfaceMap.end()) 648 { 649 continue; 650 } 651 652 getProperty(iface->first, path, interface::compatible, "Names", names); 653 654 return std::get<std::vector<std::string>>(names); 655 } 656 657 throw std::runtime_error("EM Compatible interface not on D-Bus"); 658 } 659 660 bool DataInterface::getQuiesceOnError() const 661 { 662 bool ret = false; 663 664 try 665 { 666 auto service = 667 getService(object_path::logSetting, interface::logSetting); 668 if (!service.empty()) 669 { 670 DBusValue value; 671 getProperty(service, object_path::logSetting, interface::logSetting, 672 "QuiesceOnHwError", value); 673 674 ret = std::get<bool>(value); 675 } 676 } 677 catch (const std::exception& e) 678 { 679 lg2::warning("Failed reading QuiesceOnHwError property from " 680 "interface: {IFACE} exception: {ERROR}", 681 "IFACE", interface::logSetting, "ERROR", e); 682 } 683 684 return ret; 685 } 686 687 #ifdef PEL_ENABLE_PHAL 688 void DataInterface::createGuardRecord(const std::vector<uint8_t>& binPath, 689 GardType eGardType, uint32_t plid) const 690 { 691 try 692 { 693 libguard::libguard_init(false); 694 libguard::create(binPath, plid, eGardType); 695 } 696 catch (libguard::exception::GuardException& e) 697 { 698 lg2::error("Exception in libguard {ERROR}", "ERROR", e); 699 } 700 } 701 #endif 702 703 void DataInterface::createProgressSRC( 704 const std::vector<uint8_t>& priSRC, 705 const std::vector<uint8_t>& srcStruct) const 706 { 707 DBusValue variant = std::make_tuple(priSRC, srcStruct); 708 709 auto method = _bus.new_method_call(service_name::bootRawProgress, 710 object_path::bootRawProgress, 711 interface::dbusProperty, "Set"); 712 713 method.append(interface::bootRawProgress, "Value", variant); 714 715 _bus.call(method, dbusTimeout); 716 } 717 718 std::vector<uint32_t> DataInterface::getLogIDWithHwIsolation() const 719 { 720 std::vector<std::string> association = {"xyz.openbmc_project.Association"}; 721 std::string hwErrorLog = "/isolated_hw_errorlog"; 722 std::string errorLog = "/error_log"; 723 DBusPathList paths; 724 std::vector<uint32_t> ids; 725 726 // Get all latest mapper associations 727 paths = getPaths(association); 728 for (auto& path : paths) 729 { 730 // Look for object path with hardware isolation entry if any 731 size_t pos = path.find(hwErrorLog); 732 if (pos != std::string::npos) 733 { 734 // Get the object path 735 std::string ph = path; 736 ph.erase(pos, hwErrorLog.length()); 737 auto service = getService(ph, interface::hwIsolationEntry); 738 if (!service.empty()) 739 { 740 bool status; 741 DBusValue value; 742 743 // Read the Resolved property from object path 744 getProperty(service, ph, interface::hwIsolationEntry, 745 "Resolved", value); 746 747 status = std::get<bool>(value); 748 749 // If the entry isn't resolved 750 if (!status) 751 { 752 auto assocService = 753 getService(path, interface::association); 754 if (!assocService.empty()) 755 { 756 DBusValue endpoints; 757 758 // Read Endpoints property 759 getProperty(assocService, path, interface::association, 760 "endpoints", endpoints); 761 762 auto logPath = 763 std::get<std::vector<std::string>>(endpoints); 764 if (!logPath.empty()) 765 { 766 // Get OpenBMC event log Id 767 uint32_t id = stoi(logPath[0].substr( 768 logPath[0].find_last_of('/') + 1)); 769 ids.push_back(id); 770 } 771 } 772 } 773 } 774 } 775 776 // Look for object path with error_log entry if any 777 pos = path.find(errorLog); 778 if (pos != std::string::npos) 779 { 780 auto service = getService(path, interface::association); 781 if (!service.empty()) 782 { 783 DBusValue value; 784 785 // Read Endpoints property 786 getProperty(service, path, interface::association, "endpoints", 787 value); 788 789 auto logPath = std::get<std::vector<std::string>>(value); 790 if (!logPath.empty()) 791 { 792 // Get OpenBMC event log Id 793 uint32_t id = stoi( 794 logPath[0].substr(logPath[0].find_last_of('/') + 1)); 795 ids.push_back(id); 796 } 797 } 798 } 799 } 800 801 if (ids.size() > 1) 802 { 803 // remove duplicates to have only unique ids 804 std::sort(ids.begin(), ids.end()); 805 ids.erase(std::unique(ids.begin(), ids.end()), ids.end()); 806 } 807 return ids; 808 } 809 810 std::vector<uint8_t> DataInterface::getRawProgressSRC(void) const 811 { 812 using RawProgressProperty = 813 std::tuple<std::vector<uint8_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>> DataInterface::getDIProperty( 824 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> DataInterfaceBase::isDIMMLocCode( 854 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 == "obmc-recover-pnor.service") && 1141 (jobUnitResult == "done")) 1142 { 1143 #ifdef PEL_ENABLE_PHAL 1144 this->initPHAL(); 1145 #endif 1146 // Invoke unsubscribe method to stop monitoring for 1147 // JobRemoved signals. 1148 this->unsubscribeFromSystemdSignals(); 1149 } 1150 }); 1151 }); 1152 } 1153 catch (const sdbusplus::exception_t& e) 1154 { 1155 lg2::error( 1156 "Exception occured while handling JobRemoved systemd signal, " 1157 "exception: {ERROR}", 1158 "ERROR", e); 1159 } 1160 } 1161 1162 void DataInterface::unsubscribeFromSystemdSignals() 1163 { 1164 try 1165 { 1166 auto method = 1167 _bus.new_method_call(service_name::systemd, object_path::systemd, 1168 interface::systemdMgr, "Unsubscribe"); 1169 _systemdSlot = method.call_async([this](sdbusplus::message_t&& msg) { 1170 // Unsubscribing the _systemdSlot from the subscribed signal 1171 this->_systemdSlot = sdbusplus::slot_t(nullptr); 1172 if (msg.is_method_error()) 1173 { 1174 auto* error = msg.get_error(); 1175 lg2::error( 1176 "Failed to unsubscribe from JobRemoved systemd signal, " 1177 "errorName: {ERR_NAME}, errorMsg: {ERR_MSG} ", 1178 "ERR_NAME", error->name, "ERR_MSG", error->message); 1179 return; 1180 } 1181 // Reset _systemdMatch to avoid reception of further JobRemoved 1182 // signals 1183 this->_systemdMatch.reset(); 1184 }); 1185 } 1186 catch (const sdbusplus::exception_t& e) 1187 { 1188 lg2::error( 1189 "Exception occured while unsubscribing from JobRemoved systemd signal, " 1190 "exception: {ERROR}", 1191 "ERROR", e); 1192 } 1193 } 1194 1195 } // namespace pels 1196 } // namespace openpower 1197