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 #include "config.h" 17 18 #include "data_interface.hpp" 19 20 #include "util.hpp" 21 22 #include <phosphor-logging/lg2.hpp> 23 #include <xyz/openbmc_project/State/BMC/server.hpp> 24 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp> 25 26 #include <fstream> 27 #include <iterator> 28 29 // Use a timeout of 10s for D-Bus calls so if there are 30 // timeouts the callers of the PEL creation method won't 31 // also timeout. 32 constexpr auto dbusTimeout = 10000000; 33 34 namespace openpower 35 { 36 namespace pels 37 { 38 39 namespace service_name 40 { 41 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper"; 42 constexpr auto vpdManager = "com.ibm.VPD.Manager"; 43 constexpr auto ledGroupManager = "xyz.openbmc_project.LED.GroupManager"; 44 constexpr auto logSetting = "xyz.openbmc_project.Settings"; 45 constexpr auto hwIsolation = "org.open_power.HardwareIsolation"; 46 constexpr auto biosConfigMgr = "xyz.openbmc_project.BIOSConfigManager"; 47 constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw"; 48 constexpr auto pldm = "xyz.openbmc_project.PLDM"; 49 constexpr auto inventoryManager = "xyz.openbmc_project.Inventory.Manager"; 50 } // namespace service_name 51 52 namespace object_path 53 { 54 constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper"; 55 constexpr auto systemInv = "/xyz/openbmc_project/inventory/system"; 56 constexpr auto chassisInv = "/xyz/openbmc_project/inventory/system/chassis"; 57 constexpr auto motherBoardInv = 58 "/xyz/openbmc_project/inventory/system/chassis/motherboard"; 59 constexpr auto baseInv = "/xyz/openbmc_project/inventory"; 60 constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0"; 61 constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0"; 62 constexpr auto hostState = "/xyz/openbmc_project/state/host0"; 63 constexpr auto pldm = "/xyz/openbmc_project/pldm"; 64 constexpr auto enableHostPELs = 65 "/xyz/openbmc_project/logging/send_event_logs_to_host"; 66 constexpr auto vpdManager = "/com/ibm/VPD/Manager"; 67 constexpr auto logSetting = "/xyz/openbmc_project/logging/settings"; 68 constexpr auto hwIsolation = "/xyz/openbmc_project/hardware_isolation"; 69 constexpr auto biosConfigMgr = "/xyz/openbmc_project/bios_config/manager"; 70 constexpr auto bootRawProgress = "/xyz/openbmc_project/state/boot/raw0"; 71 } // namespace object_path 72 73 namespace interface 74 { 75 constexpr auto dbusProperty = "org.freedesktop.DBus.Properties"; 76 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper"; 77 constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset"; 78 constexpr auto bootProgress = "xyz.openbmc_project.State.Boot.Progress"; 79 constexpr auto pldmRequester = "xyz.openbmc_project.PLDM.Requester"; 80 constexpr auto enable = "xyz.openbmc_project.Object.Enable"; 81 constexpr auto bmcState = "xyz.openbmc_project.State.BMC"; 82 constexpr auto chassisState = "xyz.openbmc_project.State.Chassis"; 83 constexpr auto hostState = "xyz.openbmc_project.State.Host"; 84 constexpr auto invMotherboard = 85 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"; 86 constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI"; 87 constexpr auto vsbpRecordVPD = "com.ibm.ipzvpd.VSBP"; 88 constexpr auto locCode = "xyz.openbmc_project.Inventory.Decorator.LocationCode"; 89 constexpr auto compatible = 90 "xyz.openbmc_project.Configuration.IBMCompatibleSystem"; 91 constexpr auto vpdManager = "com.ibm.VPD.Manager"; 92 constexpr auto ledGroup = "xyz.openbmc_project.Led.Group"; 93 constexpr auto operationalStatus = 94 "xyz.openbmc_project.State.Decorator.OperationalStatus"; 95 constexpr auto logSetting = "xyz.openbmc_project.Logging.Settings"; 96 constexpr auto associationDef = "xyz.openbmc_project.Association.Definitions"; 97 constexpr auto dumpEntry = "xyz.openbmc_project.Dump.Entry"; 98 constexpr auto dumpProgress = "xyz.openbmc_project.Common.Progress"; 99 constexpr auto hwIsolationCreate = "org.open_power.HardwareIsolation.Create"; 100 constexpr auto hwIsolationEntry = "xyz.openbmc_project.HardwareIsolation.Entry"; 101 constexpr auto association = "xyz.openbmc_project.Association"; 102 constexpr auto biosConfigMgr = "xyz.openbmc_project.BIOSConfig.Manager"; 103 constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw"; 104 constexpr auto invItem = "xyz.openbmc_project.Inventory.Item"; 105 constexpr auto invFan = "xyz.openbmc_project.Inventory.Item.Fan"; 106 constexpr auto invPowerSupply = 107 "xyz.openbmc_project.Inventory.Item.PowerSupply"; 108 constexpr auto inventoryManager = "xyz.openbmc_project.Inventory.Manager"; 109 } // namespace interface 110 111 using namespace sdbusplus::server::xyz::openbmc_project::state::boot; 112 using namespace sdbusplus::server::xyz::openbmc_project::state; 113 namespace match_rules = sdbusplus::bus::match::rules; 114 115 const DBusInterfaceList hotplugInterfaces{interface::invFan, 116 interface::invPowerSupply}; 117 118 std::pair<std::string, std::string> 119 DataInterfaceBase::extractConnectorFromLocCode( 120 const std::string& locationCode) 121 { 122 auto base = locationCode; 123 std::string connector{}; 124 125 auto pos = base.find("-T"); 126 if (pos != std::string::npos) 127 { 128 connector = base.substr(pos); 129 base = base.substr(0, pos); 130 } 131 132 return {base, connector}; 133 } 134 135 DataInterface::DataInterface(sdbusplus::bus_t& bus) : _bus(bus) 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}", "VAL", 167 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 = (*currentVal == "Enabled") ? true : false; 226 } 227 } 228 })); 229 } 230 231 DBusPropertyMap 232 DataInterface::getAllProperties(const std::string& service, 233 const std::string& objectPath, 234 const std::string& interface) const 235 { 236 DBusPropertyMap properties; 237 238 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 239 interface::dbusProperty, "GetAll"); 240 method.append(interface); 241 auto reply = _bus.call(method, dbusTimeout); 242 243 reply.read(properties); 244 245 return properties; 246 } 247 248 void DataInterface::getProperty(const std::string& service, 249 const std::string& objectPath, 250 const std::string& interface, 251 const std::string& property, 252 DBusValue& value) const 253 { 254 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 255 interface::dbusProperty, "Get"); 256 method.append(interface, property); 257 auto reply = _bus.call(method, dbusTimeout); 258 259 reply.read(value); 260 } 261 262 DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const 263 { 264 auto method = _bus.new_method_call( 265 service_name::objectMapper, object_path::objectMapper, 266 interface::objectMapper, "GetSubTreePaths"); 267 268 method.append(std::string{"/"}, 0, interfaces); 269 270 auto reply = _bus.call(method, dbusTimeout); 271 272 DBusPathList paths; 273 reply.read(paths); 274 275 return paths; 276 } 277 278 DBusService DataInterface::getService(const std::string& objectPath, 279 const std::string& interface) const 280 { 281 auto method = _bus.new_method_call(service_name::objectMapper, 282 object_path::objectMapper, 283 interface::objectMapper, "GetObject"); 284 285 method.append(objectPath, std::vector<std::string>({interface})); 286 287 auto reply = _bus.call(method, dbusTimeout); 288 289 std::map<DBusService, DBusInterfaceList> response; 290 reply.read(response); 291 292 if (!response.empty()) 293 { 294 return response.begin()->first; 295 } 296 297 return std::string{}; 298 } 299 300 void DataInterface::readBMCFWVersion() 301 { 302 _bmcFWVersion = 303 phosphor::logging::util::getOSReleaseValue("VERSION").value_or(""); 304 } 305 306 void DataInterface::readServerFWVersion() 307 { 308 auto value = 309 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or(""); 310 if ((value != "") && (value.find_last_of(')') != std::string::npos)) 311 { 312 std::size_t pos = value.find_first_of('(') + 1; 313 _serverFWVersion = value.substr(pos, value.find_last_of(')') - pos); 314 } 315 } 316 317 void DataInterface::readBMCFWVersionID() 318 { 319 _bmcFWVersionID = 320 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or(""); 321 } 322 323 std::string DataInterface::getMachineTypeModel() const 324 { 325 std::string model; 326 try 327 { 328 auto service = getService(object_path::systemInv, interface::invAsset); 329 if (!service.empty()) 330 { 331 DBusValue value; 332 getProperty(service, object_path::systemInv, interface::invAsset, 333 "Model", value); 334 335 model = std::get<std::string>(value); 336 } 337 } 338 catch (const std::exception& e) 339 { 340 lg2::warning("Failed reading Model property from " 341 "interface: {IFACE} exception: {ERROR}", 342 "IFACE", interface::invAsset, "ERROR", e); 343 } 344 345 return model; 346 } 347 348 std::string DataInterface::getMachineSerialNumber() const 349 { 350 std::string sn; 351 try 352 { 353 auto service = getService(object_path::systemInv, interface::invAsset); 354 if (!service.empty()) 355 { 356 DBusValue value; 357 getProperty(service, object_path::systemInv, interface::invAsset, 358 "SerialNumber", value); 359 360 sn = std::get<std::string>(value); 361 } 362 } 363 catch (const std::exception& e) 364 { 365 lg2::warning("Failed reading SerialNumber property from " 366 "interface: {IFACE} exception: {ERROR}", 367 "IFACE", interface::invAsset, "ERROR", e); 368 } 369 370 return sn; 371 } 372 373 std::string DataInterface::getMotherboardCCIN() const 374 { 375 std::string ccin; 376 377 try 378 { 379 auto service = getService(object_path::motherBoardInv, 380 interface::viniRecordVPD); 381 if (!service.empty()) 382 { 383 DBusValue value; 384 getProperty(service, object_path::motherBoardInv, 385 interface::viniRecordVPD, "CC", value); 386 387 auto cc = std::get<std::vector<uint8_t>>(value); 388 ccin = std::string{cc.begin(), cc.end()}; 389 } 390 } 391 catch (const std::exception& e) 392 { 393 lg2::warning("Failed reading Motherboard CCIN property from " 394 "interface: {IFACE} exception: {ERROR}", 395 "IFACE", interface::viniRecordVPD, "ERROR", e); 396 } 397 398 return ccin; 399 } 400 401 std::vector<uint8_t> DataInterface::getSystemIMKeyword() const 402 { 403 std::vector<uint8_t> systemIM; 404 405 try 406 { 407 auto service = getService(object_path::motherBoardInv, 408 interface::vsbpRecordVPD); 409 if (!service.empty()) 410 { 411 DBusValue value; 412 getProperty(service, object_path::motherBoardInv, 413 interface::vsbpRecordVPD, "IM", value); 414 415 systemIM = std::get<std::vector<uint8_t>>(value); 416 } 417 } 418 catch (const std::exception& e) 419 { 420 lg2::warning("Failed reading System IM property from " 421 "interface: {IFACE} exception: {ERROR}", 422 "IFACE", interface::vsbpRecordVPD, "ERROR", e); 423 } 424 425 return systemIM; 426 } 427 428 void DataInterface::getHWCalloutFields(const std::string& inventoryPath, 429 std::string& fruPartNumber, 430 std::string& ccin, 431 std::string& serialNumber) const 432 { 433 // For now, attempt to get all of the properties directly on the path 434 // passed in. In the future, may need to make use of an algorithm 435 // to figure out which inventory objects actually hold these 436 // interfaces in the case of non FRUs, or possibly another service 437 // will provide this info. Any missing interfaces will result 438 // in exceptions being thrown. 439 440 auto service = getService(inventoryPath, interface::viniRecordVPD); 441 442 auto properties = getAllProperties(service, inventoryPath, 443 interface::viniRecordVPD); 444 445 auto value = std::get<std::vector<uint8_t>>(properties["FN"]); 446 fruPartNumber = std::string{value.begin(), value.end()}; 447 448 value = std::get<std::vector<uint8_t>>(properties["CC"]); 449 ccin = std::string{value.begin(), value.end()}; 450 451 value = std::get<std::vector<uint8_t>>(properties["SN"]); 452 serialNumber = std::string{value.begin(), value.end()}; 453 } 454 455 std::string 456 DataInterface::getLocationCode(const std::string& inventoryPath) const 457 { 458 auto service = getService(inventoryPath, interface::locCode); 459 460 DBusValue locCode; 461 getProperty(service, inventoryPath, interface::locCode, "LocationCode", 462 locCode); 463 464 return std::get<std::string>(locCode); 465 } 466 467 std::string 468 DataInterface::addLocationCodePrefix(const std::string& locationCode) 469 { 470 static const std::string locationCodePrefix{"Ufcs-"}; 471 472 // Technically there are 2 location code prefixes, Ufcs and Umts, so 473 // if it already starts with a U then don't need to do anything. 474 if (locationCode.front() != 'U') 475 { 476 return locationCodePrefix + locationCode; 477 } 478 479 return locationCode; 480 } 481 482 std::string DataInterface::expandLocationCode(const std::string& locationCode, 483 uint16_t /*node*/) const 484 { 485 // Location codes for connectors are the location code of the FRU they are 486 // on, plus a '-Tx' segment. Remove this last segment before expanding it 487 // and then add it back in afterwards. This way, the connector doesn't have 488 // to be in the model just so that it can be expanded. 489 auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode); 490 491 auto method = 492 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager, 493 interface::vpdManager, "GetExpandedLocationCode"); 494 495 method.append(addLocationCodePrefix(baseLoc), static_cast<uint16_t>(0)); 496 497 auto reply = _bus.call(method, dbusTimeout); 498 499 std::string expandedLocationCode; 500 reply.read(expandedLocationCode); 501 502 if (!connectorLoc.empty()) 503 { 504 expandedLocationCode += connectorLoc; 505 } 506 507 return expandedLocationCode; 508 } 509 510 std::vector<std::string> 511 DataInterface::getInventoryFromLocCode(const std::string& locationCode, 512 uint16_t node, bool expanded) const 513 { 514 std::string methodName = expanded ? "GetFRUsByExpandedLocationCode" 515 : "GetFRUsByUnexpandedLocationCode"; 516 517 // Remove the connector segment, if present, so that this method call 518 // returns an inventory path that getHWCalloutFields() can be used with. 519 // (The serial number, etc, aren't stored on the connector in the 520 // inventory, and may not even be modeled.) 521 auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode); 522 523 auto method = 524 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager, 525 interface::vpdManager, methodName.c_str()); 526 527 if (expanded) 528 { 529 method.append(baseLoc); 530 } 531 else 532 { 533 method.append(addLocationCodePrefix(baseLoc), node); 534 } 535 536 auto reply = _bus.call(method, dbusTimeout); 537 538 std::vector<sdbusplus::message::object_path> entries; 539 reply.read(entries); 540 541 std::vector<std::string> paths; 542 543 // Note: The D-Bus method will fail if nothing found. 544 std::for_each(entries.begin(), entries.end(), 545 [&paths](const auto& path) { paths.push_back(path); }); 546 547 return paths; 548 } 549 550 void DataInterface::assertLEDGroup(const std::string& ledGroup, 551 bool value) const 552 { 553 DBusValue variant = value; 554 auto method = _bus.new_method_call(service_name::ledGroupManager, 555 ledGroup.c_str(), 556 interface::dbusProperty, "Set"); 557 method.append(interface::ledGroup, "Asserted", variant); 558 _bus.call(method, dbusTimeout); 559 } 560 561 void DataInterface::setFunctional(const std::string& objectPath, 562 bool value) const 563 { 564 DBusPropertyMap prop{{"Functional", value}}; 565 DBusInterfaceMap iface{{interface::operationalStatus, prop}}; 566 567 // PIM takes a relative path like /system/chassis so remove 568 // /xyz/openbmc_project/inventory if present. 569 std::string path{objectPath}; 570 if (path.starts_with(object_path::baseInv)) 571 { 572 path = objectPath.substr(strlen(object_path::baseInv)); 573 } 574 DBusObjectMap object{{path, iface}}; 575 576 auto method = _bus.new_method_call(service_name::inventoryManager, 577 object_path::baseInv, 578 interface::inventoryManager, "Notify"); 579 method.append(std::move(object)); 580 _bus.call(method, dbusTimeout); 581 } 582 583 using AssociationTuple = std::tuple<std::string, std::string, std::string>; 584 using AssociationsProperty = std::vector<AssociationTuple>; 585 586 void DataInterface::setCriticalAssociation(const std::string& objectPath) const 587 { 588 DBusValue getAssociationValue; 589 590 auto service = getService(objectPath, interface::associationDef); 591 592 getProperty(service, objectPath, interface::associationDef, "Associations", 593 getAssociationValue); 594 595 auto association = std::get<AssociationsProperty>(getAssociationValue); 596 597 AssociationTuple critAssociation{ 598 "health_rollup", "critical", 599 "/xyz/openbmc_project/inventory/system/chassis"}; 600 601 if (std::find(association.begin(), association.end(), critAssociation) == 602 association.end()) 603 { 604 association.push_back(critAssociation); 605 DBusValue setAssociationValue = association; 606 607 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 608 interface::dbusProperty, "Set"); 609 610 method.append(interface::associationDef, "Associations", 611 setAssociationValue); 612 _bus.call(method, dbusTimeout); 613 } 614 } 615 616 std::vector<std::string> DataInterface::getSystemNames() const 617 { 618 DBusSubTree subtree; 619 DBusValue names; 620 621 auto method = _bus.new_method_call(service_name::objectMapper, 622 object_path::objectMapper, 623 interface::objectMapper, "GetSubTree"); 624 method.append(std::string{"/"}, 0, 625 std::vector<std::string>{interface::compatible}); 626 auto reply = _bus.call(method, dbusTimeout); 627 628 reply.read(subtree); 629 if (subtree.empty()) 630 { 631 throw std::runtime_error("Compatible interface not on D-Bus"); 632 } 633 634 const auto& object = *(subtree.begin()); 635 const auto& path = object.first; 636 const auto& service = object.second.begin()->first; 637 638 getProperty(service, path, interface::compatible, "Names", names); 639 640 return std::get<std::vector<std::string>>(names); 641 } 642 643 bool DataInterface::getQuiesceOnError() const 644 { 645 bool ret = false; 646 647 try 648 { 649 auto service = getService(object_path::logSetting, 650 interface::logSetting); 651 if (!service.empty()) 652 { 653 DBusValue value; 654 getProperty(service, object_path::logSetting, interface::logSetting, 655 "QuiesceOnHwError", value); 656 657 ret = std::get<bool>(value); 658 } 659 } 660 catch (const std::exception& e) 661 { 662 lg2::warning("Failed reading QuiesceOnHwError property from " 663 "interface: {IFACE} exception: {ERROR}", 664 "IFACE", interface::logSetting, "ERROR", e); 665 } 666 667 return ret; 668 } 669 670 std::vector<bool> 671 DataInterface::checkDumpStatus(const std::vector<std::string>& type) const 672 { 673 DBusSubTree subtree; 674 std::vector<bool> result(type.size(), false); 675 676 // Query GetSubTree for the availability of dump interface 677 auto method = _bus.new_method_call(service_name::objectMapper, 678 object_path::objectMapper, 679 interface::objectMapper, "GetSubTree"); 680 method.append(std::string{"/"}, 0, 681 std::vector<std::string>{interface::dumpEntry}); 682 auto reply = _bus.call(method, dbusTimeout); 683 684 reply.read(subtree); 685 686 if (subtree.empty()) 687 { 688 return result; 689 } 690 691 std::vector<bool>::iterator itDumpStatus = result.begin(); 692 uint8_t count = 0; 693 for (const auto& [path, serviceInfo] : subtree) 694 { 695 const auto& service = serviceInfo.begin()->first; 696 // Check for dump type on the object path 697 for (const auto& it : type) 698 { 699 if (path.find(it) != std::string::npos) 700 { 701 DBusValue value, progress; 702 703 // If dump type status is already available go for next path 704 if (*itDumpStatus) 705 { 706 break; 707 } 708 709 // Check for valid dump to be available if following 710 // conditions are met for the dump entry path - 711 // Offloaded == false and Status == Completed 712 getProperty(service, path, interface::dumpEntry, "Offloaded", 713 value); 714 getProperty(service, path, interface::dumpProgress, "Status", 715 progress); 716 auto offload = std::get<bool>(value); 717 auto status = std::get<std::string>(progress); 718 if (!offload && (status.find("Completed") != std::string::npos)) 719 { 720 *itDumpStatus = true; 721 count++; 722 if (count >= type.size()) 723 { 724 return result; 725 } 726 break; 727 } 728 } 729 ++itDumpStatus; 730 } 731 itDumpStatus = result.begin(); 732 } 733 734 return result; 735 } 736 737 void DataInterface::createGuardRecord(const std::vector<uint8_t>& binPath, 738 const std::string& type, 739 const std::string& logPath) const 740 { 741 try 742 { 743 auto method = _bus.new_method_call( 744 service_name::hwIsolation, object_path::hwIsolation, 745 interface::hwIsolationCreate, "CreateWithEntityPath"); 746 method.append(binPath, type, sdbusplus::message::object_path(logPath)); 747 // Note: hw isolation "CreateWithEntityPath" got dependency on logging 748 // api's. Making d-bus call no reply type to avoid cyclic dependency. 749 // Added minimal timeout to catch initial failures. 750 // Need to revisit this design later to avoid cyclic dependency. 751 constexpr auto hwIsolationTimeout = 100000; // in micro seconds 752 _bus.call_noreply(method, hwIsolationTimeout); 753 } 754 755 catch (const sdbusplus::exception_t& e) 756 { 757 std::string errName = e.name(); 758 // SD_BUS_ERROR_TIMEOUT error is expected, due to PEL api dependency 759 // mentioned above. Ignoring the error. 760 if (errName != SD_BUS_ERROR_TIMEOUT) 761 { 762 lg2::error("GUARD D-Bus call exception. Path={PATH}, " 763 "interface = {IFACE}, exception = {ERROR}", 764 "PATH", object_path::hwIsolation, "IFACE", 765 interface::hwIsolationCreate, "ERROR", e); 766 } 767 } 768 } 769 770 void DataInterface::createProgressSRC( 771 const uint64_t& priSRC, const std::vector<uint8_t>& srcStruct) const 772 { 773 DBusValue variant = std::make_tuple(priSRC, srcStruct); 774 775 auto method = _bus.new_method_call(service_name::bootRawProgress, 776 object_path::bootRawProgress, 777 interface::dbusProperty, "Set"); 778 779 method.append(interface::bootRawProgress, "Value", variant); 780 781 _bus.call(method, dbusTimeout); 782 } 783 784 std::vector<uint32_t> DataInterface::getLogIDWithHwIsolation() const 785 { 786 std::vector<std::string> association = {"xyz.openbmc_project.Association"}; 787 std::string hwErrorLog = "/isolated_hw_errorlog"; 788 std::string errorLog = "/error_log"; 789 DBusPathList paths; 790 std::vector<uint32_t> ids; 791 792 // Get all latest mapper associations 793 paths = getPaths(association); 794 for (auto& path : paths) 795 { 796 // Look for object path with hardware isolation entry if any 797 size_t pos = path.find(hwErrorLog); 798 if (pos != std::string::npos) 799 { 800 // Get the object path 801 std::string ph = path; 802 ph.erase(pos, hwErrorLog.length()); 803 auto service = getService(ph, interface::hwIsolationEntry); 804 if (!service.empty()) 805 { 806 bool status; 807 DBusValue value; 808 809 // Read the Resolved property from object path 810 getProperty(service, ph, interface::hwIsolationEntry, 811 "Resolved", value); 812 813 status = std::get<bool>(value); 814 815 // If the entry isn't resolved 816 if (!status) 817 { 818 auto assocService = getService(path, 819 interface::association); 820 if (!assocService.empty()) 821 { 822 DBusValue endpoints; 823 824 // Read Endpoints property 825 getProperty(assocService, path, interface::association, 826 "endpoints", endpoints); 827 828 auto logPath = 829 std::get<std::vector<std::string>>(endpoints); 830 if (!logPath.empty()) 831 { 832 // Get OpenBMC event log Id 833 uint32_t id = stoi(logPath[0].substr( 834 logPath[0].find_last_of('/') + 1)); 835 ids.push_back(id); 836 } 837 } 838 } 839 } 840 } 841 842 // Look for object path with error_log entry if any 843 pos = path.find(errorLog); 844 if (pos != std::string::npos) 845 { 846 auto service = getService(path, interface::association); 847 if (!service.empty()) 848 { 849 DBusValue value; 850 851 // Read Endpoints property 852 getProperty(service, path, interface::association, "endpoints", 853 value); 854 855 auto logPath = std::get<std::vector<std::string>>(value); 856 if (!logPath.empty()) 857 { 858 // Get OpenBMC event log Id 859 uint32_t id = stoi( 860 logPath[0].substr(logPath[0].find_last_of('/') + 1)); 861 ids.push_back(id); 862 } 863 } 864 } 865 } 866 867 if (ids.size() > 1) 868 { 869 // remove duplicates to have only unique ids 870 std::sort(ids.begin(), ids.end()); 871 ids.erase(std::unique(ids.begin(), ids.end()), ids.end()); 872 } 873 return ids; 874 } 875 876 std::vector<uint8_t> DataInterface::getRawProgressSRC(void) const 877 { 878 using RawProgressProperty = std::tuple<uint64_t, std::vector<uint8_t>>; 879 880 DBusValue value; 881 getProperty(service_name::bootRawProgress, object_path::bootRawProgress, 882 interface::bootRawProgress, "Value", value); 883 884 const auto& rawProgress = std::get<RawProgressProperty>(value); 885 return std::get<1>(rawProgress); 886 } 887 888 void DataInterface::startFruPlugWatch() 889 { 890 // Add a watch on inventory InterfacesAdded and then find all 891 // existing hotpluggable interfaces and add propertiesChanged 892 // watches on them. 893 894 _invIaMatch = std::make_unique<sdbusplus::bus::match_t>( 895 _bus, match_rules::interfacesAdded(object_path::baseInv), 896 std::bind(&DataInterface::inventoryIfaceAdded, this, 897 std::placeholders::_1)); 898 try 899 { 900 auto paths = getPaths(hotplugInterfaces); 901 902 _invPresentMatches.clear(); 903 904 std::for_each(paths.begin(), paths.end(), 905 [this](const auto& path) { addHotplugWatch(path); }); 906 } 907 catch (const sdbusplus::exception_t& e) 908 { 909 lg2::warning("Failed getting FRU paths to watch: {ERROR}", "ERROR", e); 910 } 911 } 912 913 void DataInterface::addHotplugWatch(const std::string& path) 914 { 915 if (!_invPresentMatches.contains(path)) 916 { 917 _invPresentMatches.emplace( 918 path, 919 std::make_unique<sdbusplus::bus::match_t>( 920 _bus, match_rules::propertiesChanged(path, interface::invItem), 921 std::bind(&DataInterface::presenceChanged, this, 922 std::placeholders::_1))); 923 } 924 } 925 926 void DataInterface::inventoryIfaceAdded(sdbusplus::message_t& msg) 927 { 928 sdbusplus::message::object_path path; 929 DBusInterfaceMap interfaces; 930 931 msg.read(path, interfaces); 932 933 // Check if any of the new interfaces are for hot pluggable FRUs. 934 if (std::find_if(interfaces.begin(), interfaces.end(), 935 [](const auto& interfacePair) { 936 return std::find(hotplugInterfaces.begin(), hotplugInterfaces.end(), 937 interfacePair.first) != hotplugInterfaces.end(); 938 }) == interfaces.end()) 939 { 940 return; 941 } 942 943 addHotplugWatch(path.str); 944 945 // If an Inventory.Item interface was also added, check presence now. 946 947 // Notes: 948 // * This assumes the Inv.Item and Inv.Fan/PS are added together which 949 // is currently the case. 950 // * If the code ever switches to something without a Present 951 // property, then the IA signal itself would probably indicate presence. 952 953 auto itemIt = interfaces.find(interface::invItem); 954 if (itemIt != interfaces.end()) 955 { 956 notifyPresenceSubsribers(path.str, itemIt->second); 957 } 958 } 959 960 void DataInterface::presenceChanged(sdbusplus::message_t& msg) 961 { 962 DBusInterface interface; 963 DBusPropertyMap properties; 964 965 msg.read(interface, properties); 966 if (interface != interface::invItem) 967 { 968 return; 969 } 970 971 std::string path = msg.get_path(); 972 notifyPresenceSubsribers(path, properties); 973 } 974 975 void DataInterface::notifyPresenceSubsribers(const std::string& path, 976 const DBusPropertyMap& properties) 977 { 978 auto prop = properties.find("Present"); 979 if ((prop == properties.end()) || (!std::get<bool>(prop->second))) 980 { 981 return; 982 } 983 984 std::string locCode; 985 986 try 987 { 988 auto service = getService(path, interface::locCode); 989 990 // If the hotplugged FRU is hosted by PLDM, then it is 991 // in an IO expansion drawer and we don't care about it. 992 if (service == service_name::pldm) 993 { 994 return; 995 } 996 997 locCode = getLocationCode(path); 998 } 999 catch (const sdbusplus::exception_t& e) 1000 { 1001 lg2::debug("Could not get location code for {PATH}: {ERROR}", "PATH", 1002 path, "ERROR", e); 1003 return; 1004 } 1005 1006 lg2::debug("Detected FRU {PATH} ({LOC}) present ", "PATH", path, "LOC", 1007 locCode); 1008 1009 // Tell the subscribers. 1010 setFruPresent(locCode); 1011 } 1012 } // namespace pels 1013 } // namespace openpower 1014