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