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