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 <fstream> 25 #include <iterator> 26 #include <phosphor-logging/log.hpp> 27 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp> 28 29 namespace openpower 30 { 31 namespace pels 32 { 33 34 namespace service_name 35 { 36 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper"; 37 constexpr auto vpdManager = "com.ibm.VPD.Manager"; 38 constexpr auto ledGroupManager = "xyz.openbmc_project.LED.GroupManager"; 39 constexpr auto logSetting = "xyz.openbmc_project.Settings"; 40 constexpr auto hwIsolation = "org.open_power.HardwareIsolation"; 41 constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw"; 42 } // namespace service_name 43 44 namespace object_path 45 { 46 constexpr auto objectMapper = "/xyz/openbmc_project/object_mapper"; 47 constexpr auto systemInv = "/xyz/openbmc_project/inventory/system"; 48 constexpr auto chassisInv = "/xyz/openbmc_project/inventory/system/chassis"; 49 constexpr auto motherBoardInv = 50 "/xyz/openbmc_project/inventory/system/chassis/motherboard"; 51 constexpr auto baseInv = "/xyz/openbmc_project/inventory"; 52 constexpr auto bmcState = "/xyz/openbmc_project/state/bmc0"; 53 constexpr auto chassisState = "/xyz/openbmc_project/state/chassis0"; 54 constexpr auto hostState = "/xyz/openbmc_project/state/host0"; 55 constexpr auto pldm = "/xyz/openbmc_project/pldm"; 56 constexpr auto enableHostPELs = 57 "/xyz/openbmc_project/logging/send_event_logs_to_host"; 58 constexpr auto vpdManager = "/com/ibm/VPD/Manager"; 59 constexpr auto logSetting = "/xyz/openbmc_project/logging/settings"; 60 constexpr auto hwIsolation = "/xyz/openbmc_project/hardware_isolation"; 61 constexpr auto bootRawSetting = "/xyz/openbmc_project/state/boot/raw0"; 62 } // namespace object_path 63 64 namespace interface 65 { 66 constexpr auto dbusProperty = "org.freedesktop.DBus.Properties"; 67 constexpr auto objectMapper = "xyz.openbmc_project.ObjectMapper"; 68 constexpr auto invAsset = "xyz.openbmc_project.Inventory.Decorator.Asset"; 69 constexpr auto bootProgress = "xyz.openbmc_project.State.Boot.Progress"; 70 constexpr auto pldmRequester = "xyz.openbmc_project.PLDM.Requester"; 71 constexpr auto enable = "xyz.openbmc_project.Object.Enable"; 72 constexpr auto bmcState = "xyz.openbmc_project.State.BMC"; 73 constexpr auto chassisState = "xyz.openbmc_project.State.Chassis"; 74 constexpr auto hostState = "xyz.openbmc_project.State.Host"; 75 constexpr auto invMotherboard = 76 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"; 77 constexpr auto viniRecordVPD = "com.ibm.ipzvpd.VINI"; 78 constexpr auto vsbpRecordVPD = "com.ibm.ipzvpd.VSBP"; 79 constexpr auto locCode = "com.ibm.ipzvpd.Location"; 80 constexpr auto compatible = 81 "xyz.openbmc_project.Configuration.IBMCompatibleSystem"; 82 constexpr auto vpdManager = "com.ibm.VPD.Manager"; 83 constexpr auto ledGroup = "xyz.openbmc_project.Led.Group"; 84 constexpr auto operationalStatus = 85 "xyz.openbmc_project.State.Decorator.OperationalStatus"; 86 constexpr auto logSetting = "xyz.openbmc_project.Logging.Settings"; 87 constexpr auto association = "xyz.openbmc_project.Association.Definitions"; 88 constexpr auto dumpEntry = "xyz.openbmc_project.Dump.Entry"; 89 constexpr auto dumpProgress = "xyz.openbmc_project.Common.Progress"; 90 constexpr auto hwIsolationCreate = "org.open_power.HardwareIsolation.Create"; 91 constexpr auto bootRawProgress = "xyz.openbmc_project.State.Boot.Raw"; 92 } // namespace interface 93 94 using namespace sdbusplus::xyz::openbmc_project::State::Boot::server; 95 using namespace phosphor::logging; 96 97 std::pair<std::string, std::string> 98 DataInterfaceBase::extractConnectorFromLocCode( 99 const std::string& locationCode) 100 { 101 auto base = locationCode; 102 std::string connector{}; 103 104 auto pos = base.find("-T"); 105 if (pos != std::string::npos) 106 { 107 connector = base.substr(pos); 108 base = base.substr(0, pos); 109 } 110 111 return {base, connector}; 112 } 113 114 DataInterface::DataInterface(sdbusplus::bus::bus& bus) : _bus(bus) 115 { 116 readBMCFWVersion(); 117 readServerFWVersion(); 118 readBMCFWVersionID(); 119 120 // Watch the BootProgress property 121 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 122 bus, object_path::hostState, interface::bootProgress, "BootProgress", 123 *this, [this](const auto& value) { 124 this->_bootState = std::get<std::string>(value); 125 auto status = Progress::convertProgressStagesFromString( 126 std::get<std::string>(value)); 127 128 if ((status == Progress::ProgressStages::SystemInitComplete) || 129 (status == Progress::ProgressStages::OSStart) || 130 (status == Progress::ProgressStages::OSRunning)) 131 { 132 setHostUp(true); 133 } 134 else 135 { 136 setHostUp(false); 137 } 138 })); 139 140 // Watch the host PEL enable property 141 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 142 bus, object_path::enableHostPELs, interface::enable, "Enabled", *this, 143 [this](const auto& value) { 144 this->_sendPELsToHost = std::get<bool>(value); 145 })); 146 147 // Watch the BMCState property 148 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 149 bus, object_path::bmcState, interface::bmcState, "CurrentBMCState", 150 *this, [this](const auto& value) { 151 this->_bmcState = std::get<std::string>(value); 152 })); 153 154 // Watch the chassis current and requested power state properties 155 _properties.emplace_back(std::make_unique<InterfaceWatcher<DataInterface>>( 156 bus, object_path::chassisState, interface::chassisState, *this, 157 [this](const auto& properties) { 158 auto state = properties.find("CurrentPowerState"); 159 if (state != properties.end()) 160 { 161 this->_chassisState = std::get<std::string>(state->second); 162 } 163 164 auto trans = properties.find("RequestedPowerTransition"); 165 if (trans != properties.end()) 166 { 167 this->_chassisTransition = std::get<std::string>(trans->second); 168 } 169 })); 170 171 // Watch the CurrentHostState property 172 _properties.emplace_back(std::make_unique<PropertyWatcher<DataInterface>>( 173 bus, object_path::hostState, interface::hostState, "CurrentHostState", 174 *this, [this](const auto& value) { 175 this->_hostState = std::get<std::string>(value); 176 })); 177 } 178 179 DBusPropertyMap 180 DataInterface::getAllProperties(const std::string& service, 181 const std::string& objectPath, 182 const std::string& interface) const 183 { 184 DBusPropertyMap properties; 185 186 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 187 interface::dbusProperty, "GetAll"); 188 method.append(interface); 189 auto reply = _bus.call(method); 190 191 reply.read(properties); 192 193 return properties; 194 } 195 196 void DataInterface::getProperty(const std::string& service, 197 const std::string& objectPath, 198 const std::string& interface, 199 const std::string& property, 200 DBusValue& value) const 201 { 202 203 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 204 interface::dbusProperty, "Get"); 205 method.append(interface, property); 206 auto reply = _bus.call(method); 207 208 reply.read(value); 209 } 210 211 DBusPathList DataInterface::getPaths(const DBusInterfaceList& interfaces) const 212 { 213 214 auto method = _bus.new_method_call( 215 service_name::objectMapper, object_path::objectMapper, 216 interface::objectMapper, "GetSubTreePaths"); 217 218 method.append(std::string{"/"}, 0, interfaces); 219 220 auto reply = _bus.call(method); 221 222 DBusPathList paths; 223 reply.read(paths); 224 225 return paths; 226 } 227 228 DBusService DataInterface::getService(const std::string& objectPath, 229 const std::string& interface) const 230 { 231 auto method = _bus.new_method_call(service_name::objectMapper, 232 object_path::objectMapper, 233 interface::objectMapper, "GetObject"); 234 235 method.append(objectPath, std::vector<std::string>({interface})); 236 237 auto reply = _bus.call(method); 238 239 std::map<DBusService, DBusInterfaceList> response; 240 reply.read(response); 241 242 if (!response.empty()) 243 { 244 return response.begin()->first; 245 } 246 247 return std::string{}; 248 } 249 250 void DataInterface::readBMCFWVersion() 251 { 252 _bmcFWVersion = 253 phosphor::logging::util::getOSReleaseValue("VERSION").value_or(""); 254 } 255 256 void DataInterface::readServerFWVersion() 257 { 258 auto value = 259 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or(""); 260 if ((value != "") && (value.find_last_of(')') != std::string::npos)) 261 { 262 std::size_t pos = value.find_first_of('(') + 1; 263 _serverFWVersion = value.substr(pos, value.find_last_of(')') - pos); 264 } 265 } 266 267 void DataInterface::readBMCFWVersionID() 268 { 269 _bmcFWVersionID = 270 phosphor::logging::util::getOSReleaseValue("VERSION_ID").value_or(""); 271 } 272 273 std::string DataInterface::getMachineTypeModel() const 274 { 275 std::string model; 276 try 277 { 278 279 auto service = getService(object_path::systemInv, interface::invAsset); 280 if (!service.empty()) 281 { 282 DBusValue value; 283 getProperty(service, object_path::systemInv, interface::invAsset, 284 "Model", value); 285 286 model = std::get<std::string>(value); 287 } 288 } 289 catch (const std::exception& e) 290 { 291 log<level::WARNING>(fmt::format("Failed reading Model property from " 292 "Interface: {} exception: {}", 293 interface::invAsset, e.what()) 294 .c_str()); 295 } 296 297 return model; 298 } 299 300 std::string DataInterface::getMachineSerialNumber() const 301 { 302 std::string sn; 303 try 304 { 305 306 auto service = getService(object_path::systemInv, interface::invAsset); 307 if (!service.empty()) 308 { 309 DBusValue value; 310 getProperty(service, object_path::systemInv, interface::invAsset, 311 "SerialNumber", value); 312 313 sn = std::get<std::string>(value); 314 } 315 } 316 catch (const std::exception& e) 317 { 318 log<level::WARNING>( 319 fmt::format("Failed reading SerialNumber property from " 320 "Interface: {} exception: {}", 321 interface::invAsset, e.what()) 322 .c_str()); 323 } 324 325 return sn; 326 } 327 328 std::string DataInterface::getMotherboardCCIN() const 329 { 330 std::string ccin; 331 332 try 333 { 334 auto service = 335 getService(object_path::motherBoardInv, interface::viniRecordVPD); 336 if (!service.empty()) 337 { 338 DBusValue value; 339 getProperty(service, object_path::motherBoardInv, 340 interface::viniRecordVPD, "CC", value); 341 342 auto cc = std::get<std::vector<uint8_t>>(value); 343 ccin = std::string{cc.begin(), cc.end()}; 344 } 345 } 346 catch (const std::exception& e) 347 { 348 log<level::WARNING>( 349 fmt::format("Failed reading Motherboard CCIN property from " 350 "Interface: {} exception: {}", 351 interface::viniRecordVPD, e.what()) 352 .c_str()); 353 } 354 355 return ccin; 356 } 357 358 std::vector<uint8_t> DataInterface::getSystemIMKeyword() const 359 { 360 std::vector<uint8_t> systemIM; 361 362 try 363 { 364 auto service = 365 getService(object_path::motherBoardInv, interface::vsbpRecordVPD); 366 if (!service.empty()) 367 { 368 DBusValue value; 369 getProperty(service, object_path::motherBoardInv, 370 interface::vsbpRecordVPD, "IM", value); 371 372 systemIM = std::get<std::vector<uint8_t>>(value); 373 } 374 } 375 catch (const std::exception& e) 376 { 377 log<level::WARNING>( 378 fmt::format("Failed reading System IM property from " 379 "Interface: {} exception: {}", 380 interface::vsbpRecordVPD, e.what()) 381 .c_str()); 382 } 383 384 return systemIM; 385 } 386 387 void DataInterface::getHWCalloutFields(const std::string& inventoryPath, 388 std::string& fruPartNumber, 389 std::string& ccin, 390 std::string& serialNumber) const 391 { 392 // For now, attempt to get all of the properties directly on the path 393 // passed in. In the future, may need to make use of an algorithm 394 // to figure out which inventory objects actually hold these 395 // interfaces in the case of non FRUs, or possibly another service 396 // will provide this info. Any missing interfaces will result 397 // in exceptions being thrown. 398 399 auto service = getService(inventoryPath, interface::viniRecordVPD); 400 401 auto properties = 402 getAllProperties(service, inventoryPath, interface::viniRecordVPD); 403 404 auto value = std::get<std::vector<uint8_t>>(properties["FN"]); 405 fruPartNumber = std::string{value.begin(), value.end()}; 406 407 value = std::get<std::vector<uint8_t>>(properties["CC"]); 408 ccin = std::string{value.begin(), value.end()}; 409 410 value = std::get<std::vector<uint8_t>>(properties["SN"]); 411 serialNumber = std::string{value.begin(), value.end()}; 412 } 413 414 std::string 415 DataInterface::getLocationCode(const std::string& inventoryPath) const 416 { 417 auto service = getService(inventoryPath, interface::locCode); 418 419 DBusValue locCode; 420 getProperty(service, inventoryPath, interface::locCode, "LocationCode", 421 locCode); 422 423 return std::get<std::string>(locCode); 424 } 425 426 std::string 427 DataInterface::addLocationCodePrefix(const std::string& locationCode) 428 { 429 static const std::string locationCodePrefix{"Ufcs-"}; 430 431 // Technically there are 2 location code prefixes, Ufcs and Umts, so 432 // if it already starts with a U then don't need to do anything. 433 if (locationCode.front() != 'U') 434 { 435 return locationCodePrefix + locationCode; 436 } 437 438 return locationCode; 439 } 440 441 std::string DataInterface::expandLocationCode(const std::string& locationCode, 442 uint16_t /*node*/) const 443 { 444 // Location codes for connectors are the location code of the FRU they are 445 // on, plus a '-Tx' segment. Remove this last segment before expanding it 446 // and then add it back in afterwards. This way, the connector doesn't have 447 // to be in the model just so that it can be expanded. 448 auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode); 449 450 auto method = 451 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager, 452 interface::vpdManager, "GetExpandedLocationCode"); 453 454 method.append(addLocationCodePrefix(baseLoc), static_cast<uint16_t>(0)); 455 456 auto reply = _bus.call(method); 457 458 std::string expandedLocationCode; 459 reply.read(expandedLocationCode); 460 461 if (!connectorLoc.empty()) 462 { 463 expandedLocationCode += connectorLoc; 464 } 465 466 return expandedLocationCode; 467 } 468 469 std::string 470 DataInterface::getInventoryFromLocCode(const std::string& locationCode, 471 uint16_t node, bool expanded) const 472 { 473 std::string methodName = expanded ? "GetFRUsByExpandedLocationCode" 474 : "GetFRUsByUnexpandedLocationCode"; 475 476 // Remove the connector segment, if present, so that this method call 477 // returns an inventory path that getHWCalloutFields() can be used with. 478 // (The serial number, etc, aren't stored on the connector in the 479 // inventory, and may not even be modeled.) 480 auto [baseLoc, connectorLoc] = extractConnectorFromLocCode(locationCode); 481 482 auto method = 483 _bus.new_method_call(service_name::vpdManager, object_path::vpdManager, 484 interface::vpdManager, methodName.c_str()); 485 486 if (expanded) 487 { 488 method.append(baseLoc); 489 } 490 else 491 { 492 method.append(addLocationCodePrefix(baseLoc), node); 493 } 494 495 auto reply = _bus.call(method); 496 497 std::vector<sdbusplus::message::object_path> entries; 498 reply.read(entries); 499 500 // Get the shortest entry from the paths received, as this 501 // would be the path furthest up the inventory hierarchy so 502 // would be the parent FRU. There is guaranteed to at least 503 // be one entry if the call didn't fail. 504 std::string shortest{entries[0]}; 505 506 std::for_each(entries.begin(), entries.end(), 507 [&shortest](const auto& path) { 508 if (path.str.size() < shortest.size()) 509 { 510 shortest = path; 511 } 512 }); 513 514 return shortest; 515 } 516 517 void DataInterface::assertLEDGroup(const std::string& ledGroup, 518 bool value) const 519 { 520 DBusValue variant = value; 521 auto method = 522 _bus.new_method_call(service_name::ledGroupManager, ledGroup.c_str(), 523 interface::dbusProperty, "Set"); 524 method.append(interface::ledGroup, "Asserted", variant); 525 _bus.call(method); 526 } 527 528 void DataInterface::setFunctional(const std::string& objectPath, 529 bool value) const 530 { 531 DBusValue variant = value; 532 auto service = getService(objectPath, interface::operationalStatus); 533 534 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 535 interface::dbusProperty, "Set"); 536 537 method.append(interface::operationalStatus, "Functional", variant); 538 _bus.call(method); 539 } 540 541 using AssociationTuple = std::tuple<std::string, std::string, std::string>; 542 using AssociationsProperty = std::vector<AssociationTuple>; 543 544 void DataInterface::setCriticalAssociation(const std::string& objectPath) const 545 { 546 DBusValue getAssociationValue; 547 548 auto service = getService(objectPath, interface::association); 549 550 getProperty(service, objectPath, interface::association, "Associations", 551 getAssociationValue); 552 553 auto association = std::get<AssociationsProperty>(getAssociationValue); 554 555 AssociationTuple critAssociation{ 556 "health_rollup", "critical", 557 "/xyz/openbmc_project/inventory/system/chassis"}; 558 559 if (std::find(association.begin(), association.end(), critAssociation) == 560 association.end()) 561 { 562 association.push_back(critAssociation); 563 DBusValue setAssociationValue = association; 564 565 auto method = _bus.new_method_call(service.c_str(), objectPath.c_str(), 566 interface::dbusProperty, "Set"); 567 568 method.append(interface::association, "Associations", 569 setAssociationValue); 570 _bus.call(method); 571 } 572 } 573 574 std::vector<std::string> DataInterface::getSystemNames() const 575 { 576 DBusSubTree subtree; 577 DBusValue names; 578 579 auto method = _bus.new_method_call(service_name::objectMapper, 580 object_path::objectMapper, 581 interface::objectMapper, "GetSubTree"); 582 method.append(std::string{"/"}, 0, 583 std::vector<std::string>{interface::compatible}); 584 auto reply = _bus.call(method); 585 586 reply.read(subtree); 587 if (subtree.empty()) 588 { 589 throw std::runtime_error("Compatible interface not on D-Bus"); 590 } 591 592 const auto& object = *(subtree.begin()); 593 const auto& path = object.first; 594 const auto& service = object.second.begin()->first; 595 596 getProperty(service, path, interface::compatible, "Names", names); 597 598 return std::get<std::vector<std::string>>(names); 599 } 600 601 bool DataInterface::getQuiesceOnError() const 602 { 603 bool ret = false; 604 605 try 606 { 607 auto service = 608 getService(object_path::logSetting, interface::logSetting); 609 if (!service.empty()) 610 { 611 DBusValue value; 612 getProperty(service, object_path::logSetting, interface::logSetting, 613 "QuiesceOnHwError", value); 614 615 ret = std::get<bool>(value); 616 } 617 } 618 catch (const std::exception& e) 619 { 620 log<level::WARNING>( 621 fmt::format("Failed reading QuiesceOnHwError property from " 622 "Interface: {} exception: {}", 623 interface::logSetting, e.what()) 624 .c_str()); 625 } 626 627 return ret; 628 } 629 630 std::vector<bool> 631 DataInterface::checkDumpStatus(const std::vector<std::string>& type) const 632 { 633 DBusSubTree subtree; 634 std::vector<bool> result(type.size(), false); 635 636 // Query GetSubTree for the availability of dump interface 637 auto method = _bus.new_method_call(service_name::objectMapper, 638 object_path::objectMapper, 639 interface::objectMapper, "GetSubTree"); 640 method.append(std::string{"/"}, 0, 641 std::vector<std::string>{interface::dumpEntry}); 642 auto reply = _bus.call(method); 643 644 reply.read(subtree); 645 646 if (subtree.empty()) 647 { 648 return result; 649 } 650 651 std::vector<bool>::iterator itDumpStatus = result.begin(); 652 uint8_t count = 0; 653 for (const auto& [path, serviceInfo] : subtree) 654 { 655 const auto& service = serviceInfo.begin()->first; 656 // Check for dump type on the object path 657 for (const auto& it : type) 658 { 659 if (path.find(it) != std::string::npos) 660 { 661 DBusValue value, progress; 662 663 // If dump type status is already available go for next path 664 if (*itDumpStatus) 665 { 666 break; 667 } 668 669 // Check for valid dump to be available if following 670 // conditions are met for the dump entry path - 671 // Offloaded == false and Status == Completed 672 getProperty(service, path, interface::dumpEntry, "Offloaded", 673 value); 674 getProperty(service, path, interface::dumpProgress, "Status", 675 progress); 676 auto offload = std::get<bool>(value); 677 auto status = std::get<std::string>(progress); 678 if (!offload && (status.find("Completed") != std::string::npos)) 679 { 680 *itDumpStatus = true; 681 count++; 682 if (count >= type.size()) 683 { 684 return result; 685 } 686 break; 687 } 688 } 689 itDumpStatus++; 690 } 691 itDumpStatus = result.begin(); 692 } 693 694 return result; 695 } 696 697 void DataInterface::createGuardRecord(const std::vector<uint8_t>& binPath, 698 const std::string& type, 699 const std::string& logPath) const 700 { 701 try 702 { 703 auto method = _bus.new_method_call( 704 service_name::hwIsolation, object_path::hwIsolation, 705 interface::hwIsolationCreate, "CreateWithEntityPath"); 706 method.append(binPath, type, sdbusplus::message::object_path(logPath)); 707 // Note: hw isolation "CreateWithEntityPath" got dependency on logging 708 // api's. Making d-bus call no reply type to avoid cyclic dependency. 709 // Added minimal timeout to catch initial failures. 710 // Need to revisit this design later to avoid cyclic dependency. 711 constexpr auto dbusTimeout = 100000; // in micro seconds 712 _bus.call_noreply(method, dbusTimeout); 713 } 714 715 catch (const sdbusplus::exception::exception& e) 716 { 717 std::string errName = e.name(); 718 // SD_BUS_ERROR_TIMEOUT error is expected, due to PEL api dependency 719 // mentioned above. Ignoring the error. 720 if (errName != SD_BUS_ERROR_TIMEOUT) 721 { 722 log<level::ERR>( 723 fmt::format("GUARD D-Bus call exception" 724 "OBJPATH={}, INTERFACE={}, EXCEPTION={}", 725 object_path::hwIsolation, 726 interface::hwIsolationCreate, e.what()) 727 .c_str()); 728 } 729 } 730 } 731 732 void DataInterface::createProgressSRC( 733 const uint64_t& priSRC, const std::vector<uint8_t>& srcStruct) const 734 { 735 DBusValue variant = std::make_tuple(priSRC, srcStruct); 736 737 auto method = _bus.new_method_call(service_name::bootRawProgress, 738 object_path::bootRawSetting, 739 interface::dbusProperty, "Set"); 740 741 method.append(interface::bootRawProgress, "Value", variant); 742 743 _bus.call(method); 744 } 745 } // namespace pels 746 } // namespace openpower 747