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