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