1 /* 2 // Copyright (c) 2018 Intel 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 #pragma once 17 18 #include "app.hpp" 19 #include "dbus_singleton.hpp" 20 #include "dbus_utility.hpp" 21 #include "health.hpp" 22 #include "led.hpp" 23 #include "pcie.hpp" 24 #include "query.hpp" 25 #include "redfish_util.hpp" 26 #include "registries/privilege_registry.hpp" 27 #include "utils/dbus_utils.hpp" 28 #include "utils/json_utils.hpp" 29 #include "utils/sw_utils.hpp" 30 #include "utils/time_utils.hpp" 31 32 #include <boost/container/flat_map.hpp> 33 #include <boost/system/error_code.hpp> 34 #include <sdbusplus/asio/property.hpp> 35 #include <sdbusplus/unpack_properties.hpp> 36 37 #include <array> 38 #include <string_view> 39 #include <variant> 40 41 namespace redfish 42 { 43 44 /** 45 * @brief Updates the Functional State of DIMMs 46 * 47 * @param[in] aResp Shared pointer for completing asynchronous calls 48 * @param[in] dimmState Dimm's Functional state, true/false 49 * 50 * @return None. 51 */ 52 inline void 53 updateDimmProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 54 bool isDimmFunctional) 55 { 56 BMCWEB_LOG_DEBUG << "Dimm Functional: " << isDimmFunctional; 57 58 // Set it as Enabled if at least one DIMM is functional 59 // Update STATE only if previous State was DISABLED and current Dimm is 60 // ENABLED. 61 const nlohmann::json& prevMemSummary = 62 aResp->res.jsonValue["MemorySummary"]["Status"]["State"]; 63 if (prevMemSummary == "Disabled") 64 { 65 if (isDimmFunctional) 66 { 67 aResp->res.jsonValue["MemorySummary"]["Status"]["State"] = 68 "Enabled"; 69 } 70 } 71 } 72 73 /* 74 * @brief Update "ProcessorSummary" "Count" based on Cpu PresenceState 75 * 76 * @param[in] aResp Shared pointer for completing asynchronous calls 77 * @param[in] cpuPresenceState CPU present or not 78 * 79 * @return None. 80 */ 81 inline void 82 modifyCpuPresenceState(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 83 bool isCpuPresent) 84 { 85 BMCWEB_LOG_DEBUG << "Cpu Present: " << isCpuPresent; 86 87 if (isCpuPresent) 88 { 89 nlohmann::json& procCount = 90 aResp->res.jsonValue["ProcessorSummary"]["Count"]; 91 auto* procCountPtr = 92 procCount.get_ptr<nlohmann::json::number_integer_t*>(); 93 if (procCountPtr != nullptr) 94 { 95 // shouldn't be possible to be nullptr 96 *procCountPtr += 1; 97 } 98 } 99 } 100 101 /* 102 * @brief Update "ProcessorSummary" "Status" "State" based on 103 * CPU Functional State 104 * 105 * @param[in] aResp Shared pointer for completing asynchronous calls 106 * @param[in] cpuFunctionalState is CPU functional true/false 107 * 108 * @return None. 109 */ 110 inline void 111 modifyCpuFunctionalState(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 112 bool isCpuFunctional) 113 { 114 BMCWEB_LOG_DEBUG << "Cpu Functional: " << isCpuFunctional; 115 116 const nlohmann::json& prevProcState = 117 aResp->res.jsonValue["ProcessorSummary"]["Status"]["State"]; 118 119 // Set it as Enabled if at least one CPU is functional 120 // Update STATE only if previous State was Non_Functional and current CPU is 121 // Functional. 122 if (prevProcState == "Disabled") 123 { 124 if (isCpuFunctional) 125 { 126 aResp->res.jsonValue["ProcessorSummary"]["Status"]["State"] = 127 "Enabled"; 128 } 129 } 130 } 131 132 inline void getProcessorProperties( 133 const std::shared_ptr<bmcweb::AsyncResp>& aResp, 134 const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>& 135 properties) 136 { 137 138 BMCWEB_LOG_DEBUG << "Got " << properties.size() << " Cpu properties."; 139 140 // TODO: Get Model 141 142 const uint16_t* coreCount = nullptr; 143 144 const bool success = sdbusplus::unpackPropertiesNoThrow( 145 dbus_utils::UnpackErrorPrinter(), properties, "CoreCount", coreCount); 146 147 if (!success) 148 { 149 messages::internalError(aResp->res); 150 return; 151 } 152 153 if (coreCount != nullptr) 154 { 155 nlohmann::json& coreCountJson = 156 aResp->res.jsonValue["ProcessorSummary"]["CoreCount"]; 157 uint64_t* coreCountJsonPtr = coreCountJson.get_ptr<uint64_t*>(); 158 159 if (coreCountJsonPtr == nullptr) 160 { 161 coreCountJson = *coreCount; 162 } 163 else 164 { 165 *coreCountJsonPtr += *coreCount; 166 } 167 } 168 } 169 170 /* 171 * @brief Get ProcessorSummary fields 172 * 173 * @param[in] aResp Shared pointer for completing asynchronous calls 174 * @param[in] service dbus service for Cpu Information 175 * @param[in] path dbus path for Cpu 176 * 177 * @return None. 178 */ 179 inline void getProcessorSummary(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 180 const std::string& service, 181 const std::string& path) 182 { 183 184 auto getCpuPresenceState = [aResp](const boost::system::error_code& ec3, 185 const bool cpuPresenceCheck) { 186 if (ec3) 187 { 188 BMCWEB_LOG_ERROR << "DBUS response error " << ec3; 189 return; 190 } 191 modifyCpuPresenceState(aResp, cpuPresenceCheck); 192 }; 193 194 auto getCpuFunctionalState = [aResp](const boost::system::error_code& ec3, 195 const bool cpuFunctionalCheck) { 196 if (ec3) 197 { 198 BMCWEB_LOG_ERROR << "DBUS response error " << ec3; 199 return; 200 } 201 modifyCpuFunctionalState(aResp, cpuFunctionalCheck); 202 }; 203 204 // Get the Presence of CPU 205 sdbusplus::asio::getProperty<bool>( 206 *crow::connections::systemBus, service, path, 207 "xyz.openbmc_project.Inventory.Item", "Present", 208 std::move(getCpuPresenceState)); 209 210 // Get the Functional State 211 sdbusplus::asio::getProperty<bool>( 212 *crow::connections::systemBus, service, path, 213 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", 214 std::move(getCpuFunctionalState)); 215 216 sdbusplus::asio::getAllProperties( 217 *crow::connections::systemBus, service, path, 218 "xyz.openbmc_project.Inventory.Item.Cpu", 219 [aResp, service, 220 path](const boost::system::error_code& ec2, 221 const dbus::utility::DBusPropertiesMap& properties) { 222 if (ec2) 223 { 224 BMCWEB_LOG_ERROR << "DBUS response error " << ec2; 225 messages::internalError(aResp->res); 226 return; 227 } 228 getProcessorProperties(aResp, properties); 229 }); 230 } 231 232 /* 233 * @brief Retrieves computer system properties over dbus 234 * 235 * @param[in] aResp Shared pointer for completing asynchronous calls 236 * @param[in] systemHealth Shared HealthPopulate pointer 237 * 238 * @return None. 239 */ 240 inline void 241 getComputerSystem(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 242 const std::shared_ptr<HealthPopulate>& systemHealth) 243 { 244 BMCWEB_LOG_DEBUG << "Get available system components."; 245 constexpr std::array<std::string_view, 5> interfaces = { 246 "xyz.openbmc_project.Inventory.Decorator.Asset", 247 "xyz.openbmc_project.Inventory.Item.Cpu", 248 "xyz.openbmc_project.Inventory.Item.Dimm", 249 "xyz.openbmc_project.Inventory.Item.System", 250 "xyz.openbmc_project.Common.UUID", 251 }; 252 dbus::utility::getSubTree( 253 "/xyz/openbmc_project/inventory", 0, interfaces, 254 [aResp, 255 systemHealth](const boost::system::error_code& ec, 256 const dbus::utility::MapperGetSubTreeResponse& subtree) { 257 if (ec) 258 { 259 BMCWEB_LOG_DEBUG << "DBUS response error"; 260 messages::internalError(aResp->res); 261 return; 262 } 263 // Iterate over all retrieved ObjectPaths. 264 for (const std::pair< 265 std::string, 266 std::vector<std::pair<std::string, std::vector<std::string>>>>& 267 object : subtree) 268 { 269 const std::string& path = object.first; 270 BMCWEB_LOG_DEBUG << "Got path: " << path; 271 const std::vector<std::pair<std::string, std::vector<std::string>>>& 272 connectionNames = object.second; 273 if (connectionNames.empty()) 274 { 275 continue; 276 } 277 278 auto memoryHealth = std::make_shared<HealthPopulate>( 279 aResp, "/MemorySummary/Status"_json_pointer); 280 281 auto cpuHealth = std::make_shared<HealthPopulate>( 282 aResp, "/ProcessorSummary/Status"_json_pointer); 283 284 systemHealth->children.emplace_back(memoryHealth); 285 systemHealth->children.emplace_back(cpuHealth); 286 287 // This is not system, so check if it's cpu, dimm, UUID or 288 // BiosVer 289 for (const auto& connection : connectionNames) 290 { 291 for (const auto& interfaceName : connection.second) 292 { 293 if (interfaceName == 294 "xyz.openbmc_project.Inventory.Item.Dimm") 295 { 296 BMCWEB_LOG_DEBUG 297 << "Found Dimm, now get its properties."; 298 299 sdbusplus::asio::getAllProperties( 300 *crow::connections::systemBus, connection.first, 301 path, "xyz.openbmc_project.Inventory.Item.Dimm", 302 [aResp, service{connection.first}, 303 path](const boost::system::error_code& ec2, 304 const dbus::utility::DBusPropertiesMap& 305 properties) { 306 if (ec2) 307 { 308 BMCWEB_LOG_ERROR << "DBUS response error " 309 << ec2; 310 messages::internalError(aResp->res); 311 return; 312 } 313 BMCWEB_LOG_DEBUG << "Got " << properties.size() 314 << " Dimm properties."; 315 316 if (properties.empty()) 317 { 318 sdbusplus::asio::getProperty<bool>( 319 *crow::connections::systemBus, service, 320 path, 321 "xyz.openbmc_project.State." 322 "Decorator.OperationalStatus", 323 "Functional", 324 [aResp]( 325 const boost::system::error_code& ec3, 326 bool dimmState) { 327 if (ec3) 328 { 329 BMCWEB_LOG_ERROR 330 << "DBUS response error " << ec3; 331 return; 332 } 333 updateDimmProperties(aResp, dimmState); 334 }); 335 return; 336 } 337 338 const uint32_t* memorySizeInKB = nullptr; 339 340 const bool success = 341 sdbusplus::unpackPropertiesNoThrow( 342 dbus_utils::UnpackErrorPrinter(), 343 properties, "MemorySizeInKB", 344 memorySizeInKB); 345 346 if (!success) 347 { 348 messages::internalError(aResp->res); 349 return; 350 } 351 352 if (memorySizeInKB != nullptr) 353 { 354 nlohmann::json& totalMemory = 355 aResp->res 356 .jsonValue["MemorySummary"] 357 ["TotalSystemMemoryGiB"]; 358 const uint64_t* preValue = 359 totalMemory.get_ptr<const uint64_t*>(); 360 if (preValue == nullptr) 361 { 362 aResp->res 363 .jsonValue["MemorySummary"] 364 ["TotalSystemMemoryGiB"] = 365 *memorySizeInKB / (1024 * 1024); 366 } 367 else 368 { 369 aResp->res 370 .jsonValue["MemorySummary"] 371 ["TotalSystemMemoryGiB"] = 372 *memorySizeInKB / (1024 * 1024) + 373 *preValue; 374 } 375 aResp->res.jsonValue["MemorySummary"]["Status"] 376 ["State"] = "Enabled"; 377 } 378 }); 379 380 memoryHealth->inventory.emplace_back(path); 381 } 382 else if (interfaceName == 383 "xyz.openbmc_project.Inventory.Item.Cpu") 384 { 385 BMCWEB_LOG_DEBUG 386 << "Found Cpu, now get its properties."; 387 388 getProcessorSummary(aResp, connection.first, path); 389 390 cpuHealth->inventory.emplace_back(path); 391 } 392 else if (interfaceName == "xyz.openbmc_project.Common.UUID") 393 { 394 BMCWEB_LOG_DEBUG 395 << "Found UUID, now get its properties."; 396 397 sdbusplus::asio::getAllProperties( 398 *crow::connections::systemBus, connection.first, 399 path, "xyz.openbmc_project.Common.UUID", 400 [aResp](const boost::system::error_code& ec3, 401 const dbus::utility::DBusPropertiesMap& 402 properties) { 403 if (ec3) 404 { 405 BMCWEB_LOG_DEBUG << "DBUS response error " 406 << ec3; 407 messages::internalError(aResp->res); 408 return; 409 } 410 BMCWEB_LOG_DEBUG << "Got " << properties.size() 411 << " UUID properties."; 412 413 const std::string* uUID = nullptr; 414 415 const bool success = 416 sdbusplus::unpackPropertiesNoThrow( 417 dbus_utils::UnpackErrorPrinter(), 418 properties, "UUID", uUID); 419 420 if (!success) 421 { 422 messages::internalError(aResp->res); 423 return; 424 } 425 426 if (uUID != nullptr) 427 { 428 std::string valueStr = *uUID; 429 if (valueStr.size() == 32) 430 { 431 valueStr.insert(8, 1, '-'); 432 valueStr.insert(13, 1, '-'); 433 valueStr.insert(18, 1, '-'); 434 valueStr.insert(23, 1, '-'); 435 } 436 BMCWEB_LOG_DEBUG << "UUID = " << valueStr; 437 aResp->res.jsonValue["UUID"] = valueStr; 438 } 439 }); 440 } 441 else if (interfaceName == 442 "xyz.openbmc_project.Inventory.Item.System") 443 { 444 sdbusplus::asio::getAllProperties( 445 *crow::connections::systemBus, connection.first, 446 path, 447 "xyz.openbmc_project.Inventory.Decorator.Asset", 448 [aResp](const boost::system::error_code& ec2, 449 const dbus::utility::DBusPropertiesMap& 450 propertiesList) { 451 if (ec2) 452 { 453 // doesn't have to include this 454 // interface 455 return; 456 } 457 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size() 458 << " properties for system"; 459 460 const std::string* partNumber = nullptr; 461 const std::string* serialNumber = nullptr; 462 const std::string* manufacturer = nullptr; 463 const std::string* model = nullptr; 464 const std::string* subModel = nullptr; 465 466 const bool success = 467 sdbusplus::unpackPropertiesNoThrow( 468 dbus_utils::UnpackErrorPrinter(), 469 propertiesList, "PartNumber", partNumber, 470 "SerialNumber", serialNumber, 471 "Manufacturer", manufacturer, "Model", 472 model, "SubModel", subModel); 473 474 if (!success) 475 { 476 messages::internalError(aResp->res); 477 return; 478 } 479 480 if (partNumber != nullptr) 481 { 482 aResp->res.jsonValue["PartNumber"] = 483 *partNumber; 484 } 485 486 if (serialNumber != nullptr) 487 { 488 aResp->res.jsonValue["SerialNumber"] = 489 *serialNumber; 490 } 491 492 if (manufacturer != nullptr) 493 { 494 aResp->res.jsonValue["Manufacturer"] = 495 *manufacturer; 496 } 497 498 if (model != nullptr) 499 { 500 aResp->res.jsonValue["Model"] = *model; 501 } 502 503 if (subModel != nullptr) 504 { 505 aResp->res.jsonValue["SubModel"] = *subModel; 506 } 507 508 // Grab the bios version 509 sw_util::populateSoftwareInformation( 510 aResp, sw_util::biosPurpose, "BiosVersion", 511 false); 512 }); 513 514 sdbusplus::asio::getProperty<std::string>( 515 *crow::connections::systemBus, connection.first, 516 path, 517 "xyz.openbmc_project.Inventory.Decorator." 518 "AssetTag", 519 "AssetTag", 520 [aResp](const boost::system::error_code& ec2, 521 const std::string& value) { 522 if (ec2) 523 { 524 // doesn't have to include this 525 // interface 526 return; 527 } 528 529 aResp->res.jsonValue["AssetTag"] = value; 530 }); 531 } 532 } 533 break; 534 } 535 } 536 }); 537 } 538 539 /** 540 * @brief Retrieves host state properties over dbus 541 * 542 * @param[in] aResp Shared pointer for completing asynchronous calls. 543 * 544 * @return None. 545 */ 546 inline void getHostState(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 547 { 548 BMCWEB_LOG_DEBUG << "Get host information."; 549 sdbusplus::asio::getProperty<std::string>( 550 *crow::connections::systemBus, "xyz.openbmc_project.State.Host", 551 "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host", 552 "CurrentHostState", 553 [aResp](const boost::system::error_code& ec, 554 const std::string& hostState) { 555 if (ec) 556 { 557 if (ec == boost::system::errc::host_unreachable) 558 { 559 // Service not available, no error, just don't return 560 // host state info 561 BMCWEB_LOG_DEBUG << "Service not available " << ec; 562 return; 563 } 564 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 565 messages::internalError(aResp->res); 566 return; 567 } 568 569 BMCWEB_LOG_DEBUG << "Host state: " << hostState; 570 // Verify Host State 571 if (hostState == "xyz.openbmc_project.State.Host.HostState.Running") 572 { 573 aResp->res.jsonValue["PowerState"] = "On"; 574 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 575 } 576 else if (hostState == 577 "xyz.openbmc_project.State.Host.HostState.Quiesced") 578 { 579 aResp->res.jsonValue["PowerState"] = "On"; 580 aResp->res.jsonValue["Status"]["State"] = "Quiesced"; 581 } 582 else if (hostState == 583 "xyz.openbmc_project.State.Host.HostState.DiagnosticMode") 584 { 585 aResp->res.jsonValue["PowerState"] = "On"; 586 aResp->res.jsonValue["Status"]["State"] = "InTest"; 587 } 588 else if ( 589 hostState == 590 "xyz.openbmc_project.State.Host.HostState.TransitioningToRunning") 591 { 592 aResp->res.jsonValue["PowerState"] = "PoweringOn"; 593 aResp->res.jsonValue["Status"]["State"] = "Starting"; 594 } 595 else if (hostState == 596 "xyz.openbmc_project.State.Host.HostState.TransitioningToOff") 597 { 598 aResp->res.jsonValue["PowerState"] = "PoweringOff"; 599 aResp->res.jsonValue["Status"]["State"] = "Disabled"; 600 } 601 else 602 { 603 aResp->res.jsonValue["PowerState"] = "Off"; 604 aResp->res.jsonValue["Status"]["State"] = "Disabled"; 605 } 606 }); 607 } 608 609 /** 610 * @brief Translates boot source DBUS property value to redfish. 611 * 612 * @param[in] dbusSource The boot source in DBUS speak. 613 * 614 * @return Returns as a string, the boot source in Redfish terms. If translation 615 * cannot be done, returns an empty string. 616 */ 617 inline std::string dbusToRfBootSource(const std::string& dbusSource) 618 { 619 if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.Default") 620 { 621 return "None"; 622 } 623 if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.Disk") 624 { 625 return "Hdd"; 626 } 627 if (dbusSource == 628 "xyz.openbmc_project.Control.Boot.Source.Sources.ExternalMedia") 629 { 630 return "Cd"; 631 } 632 if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.Network") 633 { 634 return "Pxe"; 635 } 636 if (dbusSource == 637 "xyz.openbmc_project.Control.Boot.Source.Sources.RemovableMedia") 638 { 639 return "Usb"; 640 } 641 return ""; 642 } 643 644 /** 645 * @brief Translates boot type DBUS property value to redfish. 646 * 647 * @param[in] dbusType The boot type in DBUS speak. 648 * 649 * @return Returns as a string, the boot type in Redfish terms. If translation 650 * cannot be done, returns an empty string. 651 */ 652 inline std::string dbusToRfBootType(const std::string& dbusType) 653 { 654 if (dbusType == "xyz.openbmc_project.Control.Boot.Type.Types.Legacy") 655 { 656 return "Legacy"; 657 } 658 if (dbusType == "xyz.openbmc_project.Control.Boot.Type.Types.EFI") 659 { 660 return "UEFI"; 661 } 662 return ""; 663 } 664 665 /** 666 * @brief Translates boot mode DBUS property value to redfish. 667 * 668 * @param[in] dbusMode The boot mode in DBUS speak. 669 * 670 * @return Returns as a string, the boot mode in Redfish terms. If translation 671 * cannot be done, returns an empty string. 672 */ 673 inline std::string dbusToRfBootMode(const std::string& dbusMode) 674 { 675 if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular") 676 { 677 return "None"; 678 } 679 if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Safe") 680 { 681 return "Diags"; 682 } 683 if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup") 684 { 685 return "BiosSetup"; 686 } 687 return ""; 688 } 689 690 /** 691 * @brief Translates boot progress DBUS property value to redfish. 692 * 693 * @param[in] dbusBootProgress The boot progress in DBUS speak. 694 * 695 * @return Returns as a string, the boot progress in Redfish terms. If 696 * translation cannot be done, returns "None". 697 */ 698 inline std::string dbusToRfBootProgress(const std::string& dbusBootProgress) 699 { 700 // Now convert the D-Bus BootProgress to the appropriate Redfish 701 // enum 702 std::string rfBpLastState = "None"; 703 if (dbusBootProgress == "xyz.openbmc_project.State.Boot.Progress." 704 "ProgressStages.Unspecified") 705 { 706 rfBpLastState = "None"; 707 } 708 else if (dbusBootProgress == 709 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 710 "PrimaryProcInit") 711 { 712 rfBpLastState = "PrimaryProcessorInitializationStarted"; 713 } 714 else if (dbusBootProgress == 715 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 716 "BusInit") 717 { 718 rfBpLastState = "BusInitializationStarted"; 719 } 720 else if (dbusBootProgress == 721 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 722 "MemoryInit") 723 { 724 rfBpLastState = "MemoryInitializationStarted"; 725 } 726 else if (dbusBootProgress == 727 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 728 "SecondaryProcInit") 729 { 730 rfBpLastState = "SecondaryProcessorInitializationStarted"; 731 } 732 else if (dbusBootProgress == 733 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 734 "PCIInit") 735 { 736 rfBpLastState = "PCIResourceConfigStarted"; 737 } 738 else if (dbusBootProgress == 739 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 740 "SystemSetup") 741 { 742 rfBpLastState = "SetupEntered"; 743 } 744 else if (dbusBootProgress == 745 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 746 "SystemInitComplete") 747 { 748 rfBpLastState = "SystemHardwareInitializationComplete"; 749 } 750 else if (dbusBootProgress == 751 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 752 "OSStart") 753 { 754 rfBpLastState = "OSBootStarted"; 755 } 756 else if (dbusBootProgress == 757 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 758 "OSRunning") 759 { 760 rfBpLastState = "OSRunning"; 761 } 762 else 763 { 764 BMCWEB_LOG_DEBUG << "Unsupported D-Bus BootProgress " 765 << dbusBootProgress; 766 // Just return the default 767 } 768 return rfBpLastState; 769 } 770 771 /** 772 * @brief Translates boot source from Redfish to the DBus boot paths. 773 * 774 * @param[in] rfSource The boot source in Redfish. 775 * @param[out] bootSource The DBus source 776 * @param[out] bootMode the DBus boot mode 777 * 778 * @return Integer error code. 779 */ 780 inline int assignBootParameters(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 781 const std::string& rfSource, 782 std::string& bootSource, std::string& bootMode) 783 { 784 bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Default"; 785 bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"; 786 787 if (rfSource == "None") 788 { 789 return 0; 790 } 791 if (rfSource == "Pxe") 792 { 793 bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Network"; 794 } 795 else if (rfSource == "Hdd") 796 { 797 bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Disk"; 798 } 799 else if (rfSource == "Diags") 800 { 801 bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Safe"; 802 } 803 else if (rfSource == "Cd") 804 { 805 bootSource = 806 "xyz.openbmc_project.Control.Boot.Source.Sources.ExternalMedia"; 807 } 808 else if (rfSource == "BiosSetup") 809 { 810 bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup"; 811 } 812 else if (rfSource == "Usb") 813 { 814 bootSource = 815 "xyz.openbmc_project.Control.Boot.Source.Sources.RemovableMedia"; 816 } 817 else 818 { 819 BMCWEB_LOG_DEBUG 820 << "Invalid property value for BootSourceOverrideTarget: " 821 << bootSource; 822 messages::propertyValueNotInList(aResp->res, rfSource, 823 "BootSourceTargetOverride"); 824 return -1; 825 } 826 return 0; 827 } 828 829 /** 830 * @brief Retrieves boot progress of the system 831 * 832 * @param[in] aResp Shared pointer for generating response message. 833 * 834 * @return None. 835 */ 836 inline void getBootProgress(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 837 { 838 sdbusplus::asio::getProperty<std::string>( 839 *crow::connections::systemBus, "xyz.openbmc_project.State.Host", 840 "/xyz/openbmc_project/state/host0", 841 "xyz.openbmc_project.State.Boot.Progress", "BootProgress", 842 [aResp](const boost::system::error_code& ec, 843 const std::string& bootProgressStr) { 844 if (ec) 845 { 846 // BootProgress is an optional object so just do nothing if 847 // not found 848 return; 849 } 850 851 BMCWEB_LOG_DEBUG << "Boot Progress: " << bootProgressStr; 852 853 aResp->res.jsonValue["BootProgress"]["LastState"] = 854 dbusToRfBootProgress(bootProgressStr); 855 }); 856 } 857 858 /** 859 * @brief Retrieves boot progress Last Update of the system 860 * 861 * @param[in] aResp Shared pointer for generating response message. 862 * 863 * @return None. 864 */ 865 inline void getBootProgressLastStateTime( 866 const std::shared_ptr<bmcweb::AsyncResp>& aResp) 867 { 868 sdbusplus::asio::getProperty<uint64_t>( 869 *crow::connections::systemBus, "xyz.openbmc_project.State.Host", 870 "/xyz/openbmc_project/state/host0", 871 "xyz.openbmc_project.State.Boot.Progress", "BootProgressLastUpdate", 872 [aResp](const boost::system::error_code& ec, 873 const uint64_t lastStateTime) { 874 if (ec) 875 { 876 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec; 877 return; 878 } 879 880 // BootProgressLastUpdate is the last time the BootProgress property 881 // was updated. The time is the Epoch time, number of microseconds 882 // since 1 Jan 1970 00::00::00 UTC." 883 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/ 884 // yaml/xyz/openbmc_project/State/Boot/Progress.interface.yaml#L11 885 886 // Convert to ISO 8601 standard 887 aResp->res.jsonValue["BootProgress"]["LastStateTime"] = 888 redfish::time_utils::getDateTimeUintUs(lastStateTime); 889 }); 890 } 891 892 /** 893 * @brief Retrieves boot override type over DBUS and fills out the response 894 * 895 * @param[in] aResp Shared pointer for generating response message. 896 * 897 * @return None. 898 */ 899 900 inline void getBootOverrideType(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 901 { 902 sdbusplus::asio::getProperty<std::string>( 903 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 904 "/xyz/openbmc_project/control/host0/boot", 905 "xyz.openbmc_project.Control.Boot.Type", "BootType", 906 [aResp](const boost::system::error_code& ec, 907 const std::string& bootType) { 908 if (ec) 909 { 910 // not an error, don't have to have the interface 911 return; 912 } 913 914 BMCWEB_LOG_DEBUG << "Boot type: " << bootType; 915 916 aResp->res.jsonValue["Boot"] 917 ["BootSourceOverrideMode@Redfish.AllowableValues"] = 918 nlohmann::json::array_t({"Legacy", "UEFI"}); 919 920 auto rfType = dbusToRfBootType(bootType); 921 if (rfType.empty()) 922 { 923 messages::internalError(aResp->res); 924 return; 925 } 926 927 aResp->res.jsonValue["Boot"]["BootSourceOverrideMode"] = rfType; 928 }); 929 } 930 931 /** 932 * @brief Retrieves boot override mode over DBUS and fills out the response 933 * 934 * @param[in] aResp Shared pointer for generating response message. 935 * 936 * @return None. 937 */ 938 939 inline void getBootOverrideMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 940 { 941 sdbusplus::asio::getProperty<std::string>( 942 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 943 "/xyz/openbmc_project/control/host0/boot", 944 "xyz.openbmc_project.Control.Boot.Mode", "BootMode", 945 [aResp](const boost::system::error_code& ec, 946 const std::string& bootModeStr) { 947 if (ec) 948 { 949 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 950 messages::internalError(aResp->res); 951 return; 952 } 953 954 BMCWEB_LOG_DEBUG << "Boot mode: " << bootModeStr; 955 956 aResp->res 957 .jsonValue["Boot"] 958 ["BootSourceOverrideTarget@Redfish.AllowableValues"] = { 959 "None", "Pxe", "Hdd", "Cd", "Diags", "BiosSetup", "Usb"}; 960 961 if (bootModeStr != 962 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular") 963 { 964 auto rfMode = dbusToRfBootMode(bootModeStr); 965 if (!rfMode.empty()) 966 { 967 aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = 968 rfMode; 969 } 970 } 971 }); 972 } 973 974 /** 975 * @brief Retrieves boot override source over DBUS 976 * 977 * @param[in] aResp Shared pointer for generating response message. 978 * 979 * @return None. 980 */ 981 982 inline void 983 getBootOverrideSource(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 984 { 985 sdbusplus::asio::getProperty<std::string>( 986 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 987 "/xyz/openbmc_project/control/host0/boot", 988 "xyz.openbmc_project.Control.Boot.Source", "BootSource", 989 [aResp](const boost::system::error_code& ec, 990 const std::string& bootSourceStr) { 991 if (ec) 992 { 993 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 994 if (ec.value() == boost::asio::error::host_unreachable) 995 { 996 return; 997 } 998 messages::internalError(aResp->res); 999 return; 1000 } 1001 1002 BMCWEB_LOG_DEBUG << "Boot source: " << bootSourceStr; 1003 1004 auto rfSource = dbusToRfBootSource(bootSourceStr); 1005 if (!rfSource.empty()) 1006 { 1007 aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = rfSource; 1008 } 1009 1010 // Get BootMode as BootSourceOverrideTarget is constructed 1011 // from both BootSource and BootMode 1012 getBootOverrideMode(aResp); 1013 }); 1014 } 1015 1016 /** 1017 * @brief This functions abstracts all the logic behind getting a 1018 * "BootSourceOverrideEnabled" property from an overall boot override enable 1019 * state 1020 * 1021 * @param[in] aResp Shared pointer for generating response message. 1022 * 1023 * @return None. 1024 */ 1025 1026 inline void 1027 processBootOverrideEnable(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1028 const bool bootOverrideEnableSetting) 1029 { 1030 if (!bootOverrideEnableSetting) 1031 { 1032 aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = "Disabled"; 1033 return; 1034 } 1035 1036 // If boot source override is enabled, we need to check 'one_time' 1037 // property to set a correct value for the "BootSourceOverrideEnabled" 1038 sdbusplus::asio::getProperty<bool>( 1039 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 1040 "/xyz/openbmc_project/control/host0/boot/one_time", 1041 "xyz.openbmc_project.Object.Enable", "Enabled", 1042 [aResp](const boost::system::error_code& ec, bool oneTimeSetting) { 1043 if (ec) 1044 { 1045 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1046 messages::internalError(aResp->res); 1047 return; 1048 } 1049 1050 if (oneTimeSetting) 1051 { 1052 aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = "Once"; 1053 } 1054 else 1055 { 1056 aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = 1057 "Continuous"; 1058 } 1059 }); 1060 } 1061 1062 /** 1063 * @brief Retrieves boot override enable over DBUS 1064 * 1065 * @param[in] aResp Shared pointer for generating response message. 1066 * 1067 * @return None. 1068 */ 1069 1070 inline void 1071 getBootOverrideEnable(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1072 { 1073 sdbusplus::asio::getProperty<bool>( 1074 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 1075 "/xyz/openbmc_project/control/host0/boot", 1076 "xyz.openbmc_project.Object.Enable", "Enabled", 1077 [aResp](const boost::system::error_code& ec, 1078 const bool bootOverrideEnable) { 1079 if (ec) 1080 { 1081 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1082 if (ec.value() == boost::asio::error::host_unreachable) 1083 { 1084 return; 1085 } 1086 messages::internalError(aResp->res); 1087 return; 1088 } 1089 1090 processBootOverrideEnable(aResp, bootOverrideEnable); 1091 }); 1092 } 1093 1094 /** 1095 * @brief Retrieves boot source override properties 1096 * 1097 * @param[in] aResp Shared pointer for generating response message. 1098 * 1099 * @return None. 1100 */ 1101 inline void getBootProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1102 { 1103 BMCWEB_LOG_DEBUG << "Get boot information."; 1104 1105 getBootOverrideSource(aResp); 1106 getBootOverrideType(aResp); 1107 getBootOverrideEnable(aResp); 1108 } 1109 1110 /** 1111 * @brief Retrieves the Last Reset Time 1112 * 1113 * "Reset" is an overloaded term in Redfish, "Reset" includes power on 1114 * and power off. Even though this is the "system" Redfish object look at the 1115 * chassis D-Bus interface for the LastStateChangeTime since this has the 1116 * last power operation time. 1117 * 1118 * @param[in] aResp Shared pointer for generating response message. 1119 * 1120 * @return None. 1121 */ 1122 inline void getLastResetTime(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1123 { 1124 BMCWEB_LOG_DEBUG << "Getting System Last Reset Time"; 1125 1126 sdbusplus::asio::getProperty<uint64_t>( 1127 *crow::connections::systemBus, "xyz.openbmc_project.State.Chassis", 1128 "/xyz/openbmc_project/state/chassis0", 1129 "xyz.openbmc_project.State.Chassis", "LastStateChangeTime", 1130 [aResp](const boost::system::error_code& ec, uint64_t lastResetTime) { 1131 if (ec) 1132 { 1133 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec; 1134 return; 1135 } 1136 1137 // LastStateChangeTime is epoch time, in milliseconds 1138 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/33e8e1dd64da53a66e888d33dc82001305cd0bf9/xyz/openbmc_project/State/Chassis.interface.yaml#L19 1139 uint64_t lastResetTimeStamp = lastResetTime / 1000; 1140 1141 // Convert to ISO 8601 standard 1142 aResp->res.jsonValue["LastResetTime"] = 1143 redfish::time_utils::getDateTimeUint(lastResetTimeStamp); 1144 }); 1145 } 1146 1147 /** 1148 * @brief Retrieves Automatic Retry properties. Known on D-Bus as AutoReboot. 1149 * 1150 * @param[in] aResp Shared pointer for generating response message. 1151 * 1152 * @return None. 1153 */ 1154 inline void getAutomaticRetry(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1155 { 1156 BMCWEB_LOG_DEBUG << "Get Automatic Retry policy"; 1157 1158 sdbusplus::asio::getProperty<bool>( 1159 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 1160 "/xyz/openbmc_project/control/host0/auto_reboot", 1161 "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot", 1162 [aResp](const boost::system::error_code& ec, bool autoRebootEnabled) { 1163 if (ec) 1164 { 1165 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec; 1166 return; 1167 } 1168 1169 BMCWEB_LOG_DEBUG << "Auto Reboot: " << autoRebootEnabled; 1170 if (autoRebootEnabled) 1171 { 1172 aResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = 1173 "RetryAttempts"; 1174 // If AutomaticRetry (AutoReboot) is enabled see how many 1175 // attempts are left 1176 sdbusplus::asio::getProperty<uint32_t>( 1177 *crow::connections::systemBus, "xyz.openbmc_project.State.Host", 1178 "/xyz/openbmc_project/state/host0", 1179 "xyz.openbmc_project.Control.Boot.RebootAttempts", 1180 "AttemptsLeft", 1181 [aResp](const boost::system::error_code& ec2, 1182 const uint32_t autoRebootAttemptsLeft) { 1183 if (ec2) 1184 { 1185 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec2; 1186 return; 1187 } 1188 1189 BMCWEB_LOG_DEBUG << "Auto Reboot Attempts Left: " 1190 << autoRebootAttemptsLeft; 1191 1192 aResp->res 1193 .jsonValue["Boot"]["RemainingAutomaticRetryAttempts"] = 1194 autoRebootAttemptsLeft; 1195 }); 1196 } 1197 else 1198 { 1199 aResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = "Disabled"; 1200 } 1201 1202 // Not on D-Bus. Hardcoded here: 1203 // https://github.com/openbmc/phosphor-state-manager/blob/1dbbef42675e94fb1f78edb87d6b11380260535a/meson_options.txt#L71 1204 aResp->res.jsonValue["Boot"]["AutomaticRetryAttempts"] = 3; 1205 1206 // "AutomaticRetryConfig" can be 3 values, Disabled, RetryAlways, 1207 // and RetryAttempts. OpenBMC only supports Disabled and 1208 // RetryAttempts. 1209 aResp->res.jsonValue["Boot"] 1210 ["AutomaticRetryConfig@Redfish.AllowableValues"] = { 1211 "Disabled", "RetryAttempts"}; 1212 }); 1213 } 1214 1215 /** 1216 * @brief Retrieves power restore policy over DBUS. 1217 * 1218 * @param[in] aResp Shared pointer for generating response message. 1219 * 1220 * @return None. 1221 */ 1222 inline void 1223 getPowerRestorePolicy(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1224 { 1225 BMCWEB_LOG_DEBUG << "Get power restore policy"; 1226 1227 sdbusplus::asio::getProperty<std::string>( 1228 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 1229 "/xyz/openbmc_project/control/host0/power_restore_policy", 1230 "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", 1231 [aResp](const boost::system::error_code& ec, 1232 const std::string& policy) { 1233 if (ec) 1234 { 1235 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1236 return; 1237 } 1238 1239 const boost::container::flat_map<std::string, std::string> policyMaps = { 1240 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn", 1241 "AlwaysOn"}, 1242 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOff", 1243 "AlwaysOff"}, 1244 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore", 1245 "LastState"}, 1246 // Return `AlwaysOff` when power restore policy set to "None" 1247 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.None", 1248 "AlwaysOff"}}; 1249 1250 auto policyMapsIt = policyMaps.find(policy); 1251 if (policyMapsIt == policyMaps.end()) 1252 { 1253 messages::internalError(aResp->res); 1254 return; 1255 } 1256 1257 aResp->res.jsonValue["PowerRestorePolicy"] = policyMapsIt->second; 1258 }); 1259 } 1260 1261 /** 1262 * @brief Get TrustedModuleRequiredToBoot property. Determines whether or not 1263 * TPM is required for booting the host. 1264 * 1265 * @param[in] aResp Shared pointer for generating response message. 1266 * 1267 * @return None. 1268 */ 1269 inline void getTrustedModuleRequiredToBoot( 1270 const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1271 { 1272 BMCWEB_LOG_DEBUG << "Get TPM required to boot."; 1273 constexpr std::array<std::string_view, 1> interfaces = { 1274 "xyz.openbmc_project.Control.TPM.Policy"}; 1275 dbus::utility::getSubTree( 1276 "/", 0, interfaces, 1277 [aResp](const boost::system::error_code& ec, 1278 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1279 if (ec) 1280 { 1281 BMCWEB_LOG_DEBUG << "DBUS response error on TPM.Policy GetSubTree" 1282 << ec; 1283 // This is an optional D-Bus object so just return if 1284 // error occurs 1285 return; 1286 } 1287 if (subtree.empty()) 1288 { 1289 // As noted above, this is an optional interface so just return 1290 // if there is no instance found 1291 return; 1292 } 1293 1294 /* When there is more than one TPMEnable object... */ 1295 if (subtree.size() > 1) 1296 { 1297 BMCWEB_LOG_DEBUG 1298 << "DBUS response has more than 1 TPM Enable object:" 1299 << subtree.size(); 1300 // Throw an internal Error and return 1301 messages::internalError(aResp->res); 1302 return; 1303 } 1304 1305 // Make sure the Dbus response map has a service and objectPath 1306 // field 1307 if (subtree[0].first.empty() || subtree[0].second.size() != 1) 1308 { 1309 BMCWEB_LOG_DEBUG << "TPM.Policy mapper error!"; 1310 messages::internalError(aResp->res); 1311 return; 1312 } 1313 1314 const std::string& path = subtree[0].first; 1315 const std::string& serv = subtree[0].second.begin()->first; 1316 1317 // Valid TPM Enable object found, now reading the current value 1318 sdbusplus::asio::getProperty<bool>( 1319 *crow::connections::systemBus, serv, path, 1320 "xyz.openbmc_project.Control.TPM.Policy", "TPMEnable", 1321 [aResp](const boost::system::error_code& ec2, bool tpmRequired) { 1322 if (ec2) 1323 { 1324 BMCWEB_LOG_DEBUG << "D-BUS response error on TPM.Policy Get" 1325 << ec2; 1326 messages::internalError(aResp->res); 1327 return; 1328 } 1329 1330 if (tpmRequired) 1331 { 1332 aResp->res.jsonValue["Boot"]["TrustedModuleRequiredToBoot"] = 1333 "Required"; 1334 } 1335 else 1336 { 1337 aResp->res.jsonValue["Boot"]["TrustedModuleRequiredToBoot"] = 1338 "Disabled"; 1339 } 1340 }); 1341 }); 1342 } 1343 1344 /** 1345 * @brief Set TrustedModuleRequiredToBoot property. Determines whether or not 1346 * TPM is required for booting the host. 1347 * 1348 * @param[in] aResp Shared pointer for generating response message. 1349 * @param[in] tpmRequired Value to set TPM Required To Boot property to. 1350 * 1351 * @return None. 1352 */ 1353 inline void setTrustedModuleRequiredToBoot( 1354 const std::shared_ptr<bmcweb::AsyncResp>& aResp, const bool tpmRequired) 1355 { 1356 BMCWEB_LOG_DEBUG << "Set TrustedModuleRequiredToBoot."; 1357 constexpr std::array<std::string_view, 1> interfaces = { 1358 "xyz.openbmc_project.Control.TPM.Policy"}; 1359 dbus::utility::getSubTree( 1360 "/", 0, interfaces, 1361 [aResp, 1362 tpmRequired](const boost::system::error_code& ec, 1363 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1364 if (ec) 1365 { 1366 BMCWEB_LOG_DEBUG << "DBUS response error on TPM.Policy GetSubTree" 1367 << ec; 1368 messages::internalError(aResp->res); 1369 return; 1370 } 1371 if (subtree.empty()) 1372 { 1373 messages::propertyValueNotInList(aResp->res, "ComputerSystem", 1374 "TrustedModuleRequiredToBoot"); 1375 return; 1376 } 1377 1378 /* When there is more than one TPMEnable object... */ 1379 if (subtree.size() > 1) 1380 { 1381 BMCWEB_LOG_DEBUG 1382 << "DBUS response has more than 1 TPM Enable object:" 1383 << subtree.size(); 1384 // Throw an internal Error and return 1385 messages::internalError(aResp->res); 1386 return; 1387 } 1388 1389 // Make sure the Dbus response map has a service and objectPath 1390 // field 1391 if (subtree[0].first.empty() || subtree[0].second.size() != 1) 1392 { 1393 BMCWEB_LOG_DEBUG << "TPM.Policy mapper error!"; 1394 messages::internalError(aResp->res); 1395 return; 1396 } 1397 1398 const std::string& path = subtree[0].first; 1399 const std::string& serv = subtree[0].second.begin()->first; 1400 1401 if (serv.empty()) 1402 { 1403 BMCWEB_LOG_DEBUG << "TPM.Policy service mapper error!"; 1404 messages::internalError(aResp->res); 1405 return; 1406 } 1407 1408 // Valid TPM Enable object found, now setting the value 1409 crow::connections::systemBus->async_method_call( 1410 [aResp](const boost::system::error_code& ec2) { 1411 if (ec2) 1412 { 1413 BMCWEB_LOG_DEBUG 1414 << "DBUS response error: Set TrustedModuleRequiredToBoot" 1415 << ec2; 1416 messages::internalError(aResp->res); 1417 return; 1418 } 1419 BMCWEB_LOG_DEBUG << "Set TrustedModuleRequiredToBoot done."; 1420 }, 1421 serv, path, "org.freedesktop.DBus.Properties", "Set", 1422 "xyz.openbmc_project.Control.TPM.Policy", "TPMEnable", 1423 dbus::utility::DbusVariantType(tpmRequired)); 1424 }); 1425 } 1426 1427 /** 1428 * @brief Sets boot properties into DBUS object(s). 1429 * 1430 * @param[in] aResp Shared pointer for generating response message. 1431 * @param[in] bootType The boot type to set. 1432 * @return Integer error code. 1433 */ 1434 inline void setBootType(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1435 const std::optional<std::string>& bootType) 1436 { 1437 std::string bootTypeStr; 1438 1439 if (!bootType) 1440 { 1441 return; 1442 } 1443 1444 // Source target specified 1445 BMCWEB_LOG_DEBUG << "Boot type: " << *bootType; 1446 // Figure out which DBUS interface and property to use 1447 if (*bootType == "Legacy") 1448 { 1449 bootTypeStr = "xyz.openbmc_project.Control.Boot.Type.Types.Legacy"; 1450 } 1451 else if (*bootType == "UEFI") 1452 { 1453 bootTypeStr = "xyz.openbmc_project.Control.Boot.Type.Types.EFI"; 1454 } 1455 else 1456 { 1457 BMCWEB_LOG_DEBUG << "Invalid property value for " 1458 "BootSourceOverrideMode: " 1459 << *bootType; 1460 messages::propertyValueNotInList(aResp->res, *bootType, 1461 "BootSourceOverrideMode"); 1462 return; 1463 } 1464 1465 // Act on validated parameters 1466 BMCWEB_LOG_DEBUG << "DBUS boot type: " << bootTypeStr; 1467 1468 crow::connections::systemBus->async_method_call( 1469 [aResp](const boost::system::error_code& ec) { 1470 if (ec) 1471 { 1472 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1473 if (ec.value() == boost::asio::error::host_unreachable) 1474 { 1475 messages::resourceNotFound(aResp->res, "Set", "BootType"); 1476 return; 1477 } 1478 messages::internalError(aResp->res); 1479 return; 1480 } 1481 BMCWEB_LOG_DEBUG << "Boot type update done."; 1482 }, 1483 "xyz.openbmc_project.Settings", 1484 "/xyz/openbmc_project/control/host0/boot", 1485 "org.freedesktop.DBus.Properties", "Set", 1486 "xyz.openbmc_project.Control.Boot.Type", "BootType", 1487 dbus::utility::DbusVariantType(bootTypeStr)); 1488 } 1489 1490 /** 1491 * @brief Sets boot properties into DBUS object(s). 1492 * 1493 * @param[in] aResp Shared pointer for generating response message. 1494 * @param[in] bootType The boot type to set. 1495 * @return Integer error code. 1496 */ 1497 inline void setBootEnable(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1498 const std::optional<std::string>& bootEnable) 1499 { 1500 if (!bootEnable) 1501 { 1502 return; 1503 } 1504 // Source target specified 1505 BMCWEB_LOG_DEBUG << "Boot enable: " << *bootEnable; 1506 1507 bool bootOverrideEnable = false; 1508 bool bootOverridePersistent = false; 1509 // Figure out which DBUS interface and property to use 1510 if (*bootEnable == "Disabled") 1511 { 1512 bootOverrideEnable = false; 1513 } 1514 else if (*bootEnable == "Once") 1515 { 1516 bootOverrideEnable = true; 1517 bootOverridePersistent = false; 1518 } 1519 else if (*bootEnable == "Continuous") 1520 { 1521 bootOverrideEnable = true; 1522 bootOverridePersistent = true; 1523 } 1524 else 1525 { 1526 BMCWEB_LOG_DEBUG 1527 << "Invalid property value for BootSourceOverrideEnabled: " 1528 << *bootEnable; 1529 messages::propertyValueNotInList(aResp->res, *bootEnable, 1530 "BootSourceOverrideEnabled"); 1531 return; 1532 } 1533 1534 // Act on validated parameters 1535 BMCWEB_LOG_DEBUG << "DBUS boot override enable: " << bootOverrideEnable; 1536 1537 crow::connections::systemBus->async_method_call( 1538 [aResp](const boost::system::error_code& ec2) { 1539 if (ec2) 1540 { 1541 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 1542 messages::internalError(aResp->res); 1543 return; 1544 } 1545 BMCWEB_LOG_DEBUG << "Boot override enable update done."; 1546 }, 1547 "xyz.openbmc_project.Settings", 1548 "/xyz/openbmc_project/control/host0/boot", 1549 "org.freedesktop.DBus.Properties", "Set", 1550 "xyz.openbmc_project.Object.Enable", "Enabled", 1551 dbus::utility::DbusVariantType(bootOverrideEnable)); 1552 1553 if (!bootOverrideEnable) 1554 { 1555 return; 1556 } 1557 1558 // In case boot override is enabled we need to set correct value for the 1559 // 'one_time' enable DBus interface 1560 BMCWEB_LOG_DEBUG << "DBUS boot override persistent: " 1561 << bootOverridePersistent; 1562 1563 crow::connections::systemBus->async_method_call( 1564 [aResp](const boost::system::error_code& ec) { 1565 if (ec) 1566 { 1567 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1568 messages::internalError(aResp->res); 1569 return; 1570 } 1571 BMCWEB_LOG_DEBUG << "Boot one_time update done."; 1572 }, 1573 "xyz.openbmc_project.Settings", 1574 "/xyz/openbmc_project/control/host0/boot/one_time", 1575 "org.freedesktop.DBus.Properties", "Set", 1576 "xyz.openbmc_project.Object.Enable", "Enabled", 1577 dbus::utility::DbusVariantType(!bootOverridePersistent)); 1578 } 1579 1580 /** 1581 * @brief Sets boot properties into DBUS object(s). 1582 * 1583 * @param[in] aResp Shared pointer for generating response message. 1584 * @param[in] bootSource The boot source to set. 1585 * 1586 * @return Integer error code. 1587 */ 1588 inline void setBootModeOrSource(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1589 const std::optional<std::string>& bootSource) 1590 { 1591 std::string bootSourceStr; 1592 std::string bootModeStr; 1593 1594 if (!bootSource) 1595 { 1596 return; 1597 } 1598 1599 // Source target specified 1600 BMCWEB_LOG_DEBUG << "Boot source: " << *bootSource; 1601 // Figure out which DBUS interface and property to use 1602 if (assignBootParameters(aResp, *bootSource, bootSourceStr, bootModeStr) != 1603 0) 1604 { 1605 BMCWEB_LOG_DEBUG 1606 << "Invalid property value for BootSourceOverrideTarget: " 1607 << *bootSource; 1608 messages::propertyValueNotInList(aResp->res, *bootSource, 1609 "BootSourceTargetOverride"); 1610 return; 1611 } 1612 1613 // Act on validated parameters 1614 BMCWEB_LOG_DEBUG << "DBUS boot source: " << bootSourceStr; 1615 BMCWEB_LOG_DEBUG << "DBUS boot mode: " << bootModeStr; 1616 1617 crow::connections::systemBus->async_method_call( 1618 [aResp](const boost::system::error_code& ec) { 1619 if (ec) 1620 { 1621 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1622 messages::internalError(aResp->res); 1623 return; 1624 } 1625 BMCWEB_LOG_DEBUG << "Boot source update done."; 1626 }, 1627 "xyz.openbmc_project.Settings", 1628 "/xyz/openbmc_project/control/host0/boot", 1629 "org.freedesktop.DBus.Properties", "Set", 1630 "xyz.openbmc_project.Control.Boot.Source", "BootSource", 1631 dbus::utility::DbusVariantType(bootSourceStr)); 1632 1633 crow::connections::systemBus->async_method_call( 1634 [aResp](const boost::system::error_code& ec) { 1635 if (ec) 1636 { 1637 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1638 messages::internalError(aResp->res); 1639 return; 1640 } 1641 BMCWEB_LOG_DEBUG << "Boot mode update done."; 1642 }, 1643 "xyz.openbmc_project.Settings", 1644 "/xyz/openbmc_project/control/host0/boot", 1645 "org.freedesktop.DBus.Properties", "Set", 1646 "xyz.openbmc_project.Control.Boot.Mode", "BootMode", 1647 dbus::utility::DbusVariantType(bootModeStr)); 1648 } 1649 1650 /** 1651 * @brief Sets Boot source override properties. 1652 * 1653 * @param[in] aResp Shared pointer for generating response message. 1654 * @param[in] bootSource The boot source from incoming RF request. 1655 * @param[in] bootType The boot type from incoming RF request. 1656 * @param[in] bootEnable The boot override enable from incoming RF request. 1657 * 1658 * @return Integer error code. 1659 */ 1660 1661 inline void setBootProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1662 const std::optional<std::string>& bootSource, 1663 const std::optional<std::string>& bootType, 1664 const std::optional<std::string>& bootEnable) 1665 { 1666 BMCWEB_LOG_DEBUG << "Set boot information."; 1667 1668 setBootModeOrSource(aResp, bootSource); 1669 setBootType(aResp, bootType); 1670 setBootEnable(aResp, bootEnable); 1671 } 1672 1673 /** 1674 * @brief Sets AssetTag 1675 * 1676 * @param[in] aResp Shared pointer for generating response message. 1677 * @param[in] assetTag "AssetTag" from request. 1678 * 1679 * @return None. 1680 */ 1681 inline void setAssetTag(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1682 const std::string& assetTag) 1683 { 1684 constexpr std::array<std::string_view, 1> interfaces = { 1685 "xyz.openbmc_project.Inventory.Item.System"}; 1686 dbus::utility::getSubTree( 1687 "/xyz/openbmc_project/inventory", 0, interfaces, 1688 [aResp, 1689 assetTag](const boost::system::error_code& ec, 1690 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1691 if (ec) 1692 { 1693 BMCWEB_LOG_DEBUG << "D-Bus response error on GetSubTree " << ec; 1694 messages::internalError(aResp->res); 1695 return; 1696 } 1697 if (subtree.empty()) 1698 { 1699 BMCWEB_LOG_DEBUG << "Can't find system D-Bus object!"; 1700 messages::internalError(aResp->res); 1701 return; 1702 } 1703 // Assume only 1 system D-Bus object 1704 // Throw an error if there is more than 1 1705 if (subtree.size() > 1) 1706 { 1707 BMCWEB_LOG_DEBUG << "Found more than 1 system D-Bus object!"; 1708 messages::internalError(aResp->res); 1709 return; 1710 } 1711 if (subtree[0].first.empty() || subtree[0].second.size() != 1) 1712 { 1713 BMCWEB_LOG_DEBUG << "Asset Tag Set mapper error!"; 1714 messages::internalError(aResp->res); 1715 return; 1716 } 1717 1718 const std::string& path = subtree[0].first; 1719 const std::string& service = subtree[0].second.begin()->first; 1720 1721 if (service.empty()) 1722 { 1723 BMCWEB_LOG_DEBUG << "Asset Tag Set service mapper error!"; 1724 messages::internalError(aResp->res); 1725 return; 1726 } 1727 1728 crow::connections::systemBus->async_method_call( 1729 [aResp](const boost::system::error_code& ec2) { 1730 if (ec2) 1731 { 1732 BMCWEB_LOG_DEBUG << "D-Bus response error on AssetTag Set " 1733 << ec2; 1734 messages::internalError(aResp->res); 1735 return; 1736 } 1737 }, 1738 service, path, "org.freedesktop.DBus.Properties", "Set", 1739 "xyz.openbmc_project.Inventory.Decorator.AssetTag", "AssetTag", 1740 dbus::utility::DbusVariantType(assetTag)); 1741 }); 1742 } 1743 1744 /** 1745 * @brief Sets automaticRetry (Auto Reboot) 1746 * 1747 * @param[in] aResp Shared pointer for generating response message. 1748 * @param[in] automaticRetryConfig "AutomaticRetryConfig" from request. 1749 * 1750 * @return None. 1751 */ 1752 inline void setAutomaticRetry(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1753 const std::string& automaticRetryConfig) 1754 { 1755 BMCWEB_LOG_DEBUG << "Set Automatic Retry."; 1756 1757 // OpenBMC only supports "Disabled" and "RetryAttempts". 1758 bool autoRebootEnabled = false; 1759 1760 if (automaticRetryConfig == "Disabled") 1761 { 1762 autoRebootEnabled = false; 1763 } 1764 else if (automaticRetryConfig == "RetryAttempts") 1765 { 1766 autoRebootEnabled = true; 1767 } 1768 else 1769 { 1770 BMCWEB_LOG_DEBUG << "Invalid property value for AutomaticRetryConfig: " 1771 << automaticRetryConfig; 1772 messages::propertyValueNotInList(aResp->res, automaticRetryConfig, 1773 "AutomaticRetryConfig"); 1774 return; 1775 } 1776 1777 crow::connections::systemBus->async_method_call( 1778 [aResp](const boost::system::error_code& ec) { 1779 if (ec) 1780 { 1781 messages::internalError(aResp->res); 1782 return; 1783 } 1784 }, 1785 "xyz.openbmc_project.Settings", 1786 "/xyz/openbmc_project/control/host0/auto_reboot", 1787 "org.freedesktop.DBus.Properties", "Set", 1788 "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot", 1789 dbus::utility::DbusVariantType(autoRebootEnabled)); 1790 } 1791 1792 /** 1793 * @brief Sets power restore policy properties. 1794 * 1795 * @param[in] aResp Shared pointer for generating response message. 1796 * @param[in] policy power restore policy properties from request. 1797 * 1798 * @return None. 1799 */ 1800 inline void 1801 setPowerRestorePolicy(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1802 const std::string& policy) 1803 { 1804 BMCWEB_LOG_DEBUG << "Set power restore policy."; 1805 1806 const boost::container::flat_map<std::string, std::string> policyMaps = { 1807 {"AlwaysOn", 1808 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn"}, 1809 {"AlwaysOff", 1810 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOff"}, 1811 {"LastState", 1812 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore"}}; 1813 1814 std::string powerRestorPolicy; 1815 1816 auto policyMapsIt = policyMaps.find(policy); 1817 if (policyMapsIt == policyMaps.end()) 1818 { 1819 messages::propertyValueNotInList(aResp->res, policy, 1820 "PowerRestorePolicy"); 1821 return; 1822 } 1823 1824 powerRestorPolicy = policyMapsIt->second; 1825 1826 crow::connections::systemBus->async_method_call( 1827 [aResp](const boost::system::error_code& ec) { 1828 if (ec) 1829 { 1830 messages::internalError(aResp->res); 1831 return; 1832 } 1833 }, 1834 "xyz.openbmc_project.Settings", 1835 "/xyz/openbmc_project/control/host0/power_restore_policy", 1836 "org.freedesktop.DBus.Properties", "Set", 1837 "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", 1838 dbus::utility::DbusVariantType(powerRestorPolicy)); 1839 } 1840 1841 #ifdef BMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE 1842 /** 1843 * @brief Retrieves provisioning status 1844 * 1845 * @param[in] aResp Shared pointer for completing asynchronous calls. 1846 * 1847 * @return None. 1848 */ 1849 inline void getProvisioningStatus(std::shared_ptr<bmcweb::AsyncResp> aResp) 1850 { 1851 BMCWEB_LOG_DEBUG << "Get OEM information."; 1852 sdbusplus::asio::getAllProperties( 1853 *crow::connections::systemBus, "xyz.openbmc_project.PFR.Manager", 1854 "/xyz/openbmc_project/pfr", "xyz.openbmc_project.PFR.Attributes", 1855 [aResp](const boost::system::error_code& ec, 1856 const dbus::utility::DBusPropertiesMap& propertiesList) { 1857 nlohmann::json& oemPFR = 1858 aResp->res.jsonValue["Oem"]["OpenBmc"]["FirmwareProvisioning"]; 1859 aResp->res.jsonValue["Oem"]["OpenBmc"]["@odata.type"] = 1860 "#OemComputerSystem.OpenBmc"; 1861 oemPFR["@odata.type"] = "#OemComputerSystem.FirmwareProvisioning"; 1862 1863 if (ec) 1864 { 1865 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1866 // not an error, don't have to have the interface 1867 oemPFR["ProvisioningStatus"] = "NotProvisioned"; 1868 return; 1869 } 1870 1871 const bool* provState = nullptr; 1872 const bool* lockState = nullptr; 1873 1874 const bool success = sdbusplus::unpackPropertiesNoThrow( 1875 dbus_utils::UnpackErrorPrinter(), propertiesList, "UfmProvisioned", 1876 provState, "UfmLocked", lockState); 1877 1878 if (!success) 1879 { 1880 messages::internalError(aResp->res); 1881 return; 1882 } 1883 1884 if ((provState == nullptr) || (lockState == nullptr)) 1885 { 1886 BMCWEB_LOG_DEBUG << "Unable to get PFR attributes."; 1887 messages::internalError(aResp->res); 1888 return; 1889 } 1890 1891 if (*provState == true) 1892 { 1893 if (*lockState == true) 1894 { 1895 oemPFR["ProvisioningStatus"] = "ProvisionedAndLocked"; 1896 } 1897 else 1898 { 1899 oemPFR["ProvisioningStatus"] = "ProvisionedButNotLocked"; 1900 } 1901 } 1902 else 1903 { 1904 oemPFR["ProvisioningStatus"] = "NotProvisioned"; 1905 } 1906 }); 1907 } 1908 #endif 1909 1910 /** 1911 * @brief Translate the PowerMode to a response message. 1912 * 1913 * @param[in] aResp Shared pointer for generating response message. 1914 * @param[in] modeValue PowerMode value to be translated 1915 * 1916 * @return None. 1917 */ 1918 inline void translatePowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1919 const std::string& modeValue) 1920 { 1921 if (modeValue == "xyz.openbmc_project.Control.Power.Mode.PowerMode.Static") 1922 { 1923 aResp->res.jsonValue["PowerMode"] = "Static"; 1924 } 1925 else if ( 1926 modeValue == 1927 "xyz.openbmc_project.Control.Power.Mode.PowerMode.MaximumPerformance") 1928 { 1929 aResp->res.jsonValue["PowerMode"] = "MaximumPerformance"; 1930 } 1931 else if (modeValue == 1932 "xyz.openbmc_project.Control.Power.Mode.PowerMode.PowerSaving") 1933 { 1934 aResp->res.jsonValue["PowerMode"] = "PowerSaving"; 1935 } 1936 else if (modeValue == 1937 "xyz.openbmc_project.Control.Power.Mode.PowerMode.OEM") 1938 { 1939 aResp->res.jsonValue["PowerMode"] = "OEM"; 1940 } 1941 else 1942 { 1943 // Any other values would be invalid 1944 BMCWEB_LOG_DEBUG << "PowerMode value was not valid: " << modeValue; 1945 messages::internalError(aResp->res); 1946 } 1947 } 1948 1949 /** 1950 * @brief Retrieves system power mode 1951 * 1952 * @param[in] aResp Shared pointer for generating response message. 1953 * 1954 * @return None. 1955 */ 1956 inline void getPowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1957 { 1958 BMCWEB_LOG_DEBUG << "Get power mode."; 1959 1960 // Get Power Mode object path: 1961 constexpr std::array<std::string_view, 1> interfaces = { 1962 "xyz.openbmc_project.Control.Power.Mode"}; 1963 dbus::utility::getSubTree( 1964 "/", 0, interfaces, 1965 [aResp](const boost::system::error_code& ec, 1966 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1967 if (ec) 1968 { 1969 BMCWEB_LOG_DEBUG << "DBUS response error on Power.Mode GetSubTree " 1970 << ec; 1971 // This is an optional D-Bus object so just return if 1972 // error occurs 1973 return; 1974 } 1975 if (subtree.empty()) 1976 { 1977 // As noted above, this is an optional interface so just return 1978 // if there is no instance found 1979 return; 1980 } 1981 if (subtree.size() > 1) 1982 { 1983 // More then one PowerMode object is not supported and is an 1984 // error 1985 BMCWEB_LOG_DEBUG 1986 << "Found more than 1 system D-Bus Power.Mode objects: " 1987 << subtree.size(); 1988 messages::internalError(aResp->res); 1989 return; 1990 } 1991 if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) 1992 { 1993 BMCWEB_LOG_DEBUG << "Power.Mode mapper error!"; 1994 messages::internalError(aResp->res); 1995 return; 1996 } 1997 const std::string& path = subtree[0].first; 1998 const std::string& service = subtree[0].second.begin()->first; 1999 if (service.empty()) 2000 { 2001 BMCWEB_LOG_DEBUG << "Power.Mode service mapper error!"; 2002 messages::internalError(aResp->res); 2003 return; 2004 } 2005 // Valid Power Mode object found, now read the current value 2006 sdbusplus::asio::getProperty<std::string>( 2007 *crow::connections::systemBus, service, path, 2008 "xyz.openbmc_project.Control.Power.Mode", "PowerMode", 2009 [aResp](const boost::system::error_code& ec2, 2010 const std::string& pmode) { 2011 if (ec2) 2012 { 2013 BMCWEB_LOG_DEBUG << "DBUS response error on PowerMode Get: " 2014 << ec2; 2015 messages::internalError(aResp->res); 2016 return; 2017 } 2018 2019 aResp->res.jsonValue["PowerMode@Redfish.AllowableValues"] = { 2020 "Static", "MaximumPerformance", "PowerSaving"}; 2021 2022 BMCWEB_LOG_DEBUG << "Current power mode: " << pmode; 2023 translatePowerMode(aResp, pmode); 2024 }); 2025 }); 2026 } 2027 2028 /** 2029 * @brief Validate the specified mode is valid and return the PowerMode 2030 * name associated with that string 2031 * 2032 * @param[in] aResp Shared pointer for generating response message. 2033 * @param[in] modeString String representing the desired PowerMode 2034 * 2035 * @return PowerMode value or empty string if mode is not valid 2036 */ 2037 inline std::string 2038 validatePowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2039 const std::string& modeString) 2040 { 2041 std::string mode; 2042 2043 if (modeString == "Static") 2044 { 2045 mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.Static"; 2046 } 2047 else if (modeString == "MaximumPerformance") 2048 { 2049 mode = 2050 "xyz.openbmc_project.Control.Power.Mode.PowerMode.MaximumPerformance"; 2051 } 2052 else if (modeString == "PowerSaving") 2053 { 2054 mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.PowerSaving"; 2055 } 2056 else 2057 { 2058 messages::propertyValueNotInList(aResp->res, modeString, "PowerMode"); 2059 } 2060 return mode; 2061 } 2062 2063 /** 2064 * @brief Sets system power mode. 2065 * 2066 * @param[in] aResp Shared pointer for generating response message. 2067 * @param[in] pmode System power mode from request. 2068 * 2069 * @return None. 2070 */ 2071 inline void setPowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2072 const std::string& pmode) 2073 { 2074 BMCWEB_LOG_DEBUG << "Set power mode."; 2075 2076 std::string powerMode = validatePowerMode(aResp, pmode); 2077 if (powerMode.empty()) 2078 { 2079 return; 2080 } 2081 2082 // Get Power Mode object path: 2083 constexpr std::array<std::string_view, 1> interfaces = { 2084 "xyz.openbmc_project.Control.Power.Mode"}; 2085 dbus::utility::getSubTree( 2086 "/", 0, interfaces, 2087 [aResp, 2088 powerMode](const boost::system::error_code& ec, 2089 const dbus::utility::MapperGetSubTreeResponse& subtree) { 2090 if (ec) 2091 { 2092 BMCWEB_LOG_DEBUG << "DBUS response error on Power.Mode GetSubTree " 2093 << ec; 2094 // This is an optional D-Bus object, but user attempted to patch 2095 messages::internalError(aResp->res); 2096 return; 2097 } 2098 if (subtree.empty()) 2099 { 2100 // This is an optional D-Bus object, but user attempted to patch 2101 messages::resourceNotFound(aResp->res, "ComputerSystem", 2102 "PowerMode"); 2103 return; 2104 } 2105 if (subtree.size() > 1) 2106 { 2107 // More then one PowerMode object is not supported and is an 2108 // error 2109 BMCWEB_LOG_DEBUG 2110 << "Found more than 1 system D-Bus Power.Mode objects: " 2111 << subtree.size(); 2112 messages::internalError(aResp->res); 2113 return; 2114 } 2115 if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) 2116 { 2117 BMCWEB_LOG_DEBUG << "Power.Mode mapper error!"; 2118 messages::internalError(aResp->res); 2119 return; 2120 } 2121 const std::string& path = subtree[0].first; 2122 const std::string& service = subtree[0].second.begin()->first; 2123 if (service.empty()) 2124 { 2125 BMCWEB_LOG_DEBUG << "Power.Mode service mapper error!"; 2126 messages::internalError(aResp->res); 2127 return; 2128 } 2129 2130 BMCWEB_LOG_DEBUG << "Setting power mode(" << powerMode << ") -> " 2131 << path; 2132 2133 // Set the Power Mode property 2134 crow::connections::systemBus->async_method_call( 2135 [aResp](const boost::system::error_code& ec2) { 2136 if (ec2) 2137 { 2138 messages::internalError(aResp->res); 2139 return; 2140 } 2141 }, 2142 service, path, "org.freedesktop.DBus.Properties", "Set", 2143 "xyz.openbmc_project.Control.Power.Mode", "PowerMode", 2144 dbus::utility::DbusVariantType(powerMode)); 2145 }); 2146 } 2147 2148 /** 2149 * @brief Translates watchdog timeout action DBUS property value to redfish. 2150 * 2151 * @param[in] dbusAction The watchdog timeout action in D-BUS. 2152 * 2153 * @return Returns as a string, the timeout action in Redfish terms. If 2154 * translation cannot be done, returns an empty string. 2155 */ 2156 inline std::string dbusToRfWatchdogAction(const std::string& dbusAction) 2157 { 2158 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.None") 2159 { 2160 return "None"; 2161 } 2162 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.HardReset") 2163 { 2164 return "ResetSystem"; 2165 } 2166 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.PowerOff") 2167 { 2168 return "PowerDown"; 2169 } 2170 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.PowerCycle") 2171 { 2172 return "PowerCycle"; 2173 } 2174 2175 return ""; 2176 } 2177 2178 /** 2179 *@brief Translates timeout action from Redfish to DBUS property value. 2180 * 2181 *@param[in] rfAction The timeout action in Redfish. 2182 * 2183 *@return Returns as a string, the time_out action as expected by DBUS. 2184 *If translation cannot be done, returns an empty string. 2185 */ 2186 2187 inline std::string rfToDbusWDTTimeOutAct(const std::string& rfAction) 2188 { 2189 if (rfAction == "None") 2190 { 2191 return "xyz.openbmc_project.State.Watchdog.Action.None"; 2192 } 2193 if (rfAction == "PowerCycle") 2194 { 2195 return "xyz.openbmc_project.State.Watchdog.Action.PowerCycle"; 2196 } 2197 if (rfAction == "PowerDown") 2198 { 2199 return "xyz.openbmc_project.State.Watchdog.Action.PowerOff"; 2200 } 2201 if (rfAction == "ResetSystem") 2202 { 2203 return "xyz.openbmc_project.State.Watchdog.Action.HardReset"; 2204 } 2205 2206 return ""; 2207 } 2208 2209 /** 2210 * @brief Retrieves host watchdog timer properties over DBUS 2211 * 2212 * @param[in] aResp Shared pointer for completing asynchronous calls. 2213 * 2214 * @return None. 2215 */ 2216 inline void 2217 getHostWatchdogTimer(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 2218 { 2219 BMCWEB_LOG_DEBUG << "Get host watchodg"; 2220 sdbusplus::asio::getAllProperties( 2221 *crow::connections::systemBus, "xyz.openbmc_project.Watchdog", 2222 "/xyz/openbmc_project/watchdog/host0", 2223 "xyz.openbmc_project.State.Watchdog", 2224 [aResp](const boost::system::error_code& ec, 2225 const dbus::utility::DBusPropertiesMap& properties) { 2226 if (ec) 2227 { 2228 // watchdog service is stopped 2229 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 2230 return; 2231 } 2232 2233 BMCWEB_LOG_DEBUG << "Got " << properties.size() << " wdt prop."; 2234 2235 nlohmann::json& hostWatchdogTimer = 2236 aResp->res.jsonValue["HostWatchdogTimer"]; 2237 2238 // watchdog service is running/enabled 2239 hostWatchdogTimer["Status"]["State"] = "Enabled"; 2240 2241 const bool* enabled = nullptr; 2242 const std::string* expireAction = nullptr; 2243 2244 const bool success = sdbusplus::unpackPropertiesNoThrow( 2245 dbus_utils::UnpackErrorPrinter(), properties, "Enabled", enabled, 2246 "ExpireAction", expireAction); 2247 2248 if (!success) 2249 { 2250 messages::internalError(aResp->res); 2251 return; 2252 } 2253 2254 if (enabled != nullptr) 2255 { 2256 hostWatchdogTimer["FunctionEnabled"] = *enabled; 2257 } 2258 2259 if (expireAction != nullptr) 2260 { 2261 std::string action = dbusToRfWatchdogAction(*expireAction); 2262 if (action.empty()) 2263 { 2264 messages::internalError(aResp->res); 2265 return; 2266 } 2267 hostWatchdogTimer["TimeoutAction"] = action; 2268 } 2269 }); 2270 } 2271 2272 /** 2273 * @brief Sets Host WatchDog Timer properties. 2274 * 2275 * @param[in] aResp Shared pointer for generating response message. 2276 * @param[in] wdtEnable The WDTimer Enable value (true/false) from incoming 2277 * RF request. 2278 * @param[in] wdtTimeOutAction The WDT Timeout action, from incoming RF request. 2279 * 2280 * @return None. 2281 */ 2282 inline void setWDTProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2283 const std::optional<bool> wdtEnable, 2284 const std::optional<std::string>& wdtTimeOutAction) 2285 { 2286 BMCWEB_LOG_DEBUG << "Set host watchdog"; 2287 2288 if (wdtTimeOutAction) 2289 { 2290 std::string wdtTimeOutActStr = rfToDbusWDTTimeOutAct(*wdtTimeOutAction); 2291 // check if TimeOut Action is Valid 2292 if (wdtTimeOutActStr.empty()) 2293 { 2294 BMCWEB_LOG_DEBUG << "Unsupported value for TimeoutAction: " 2295 << *wdtTimeOutAction; 2296 messages::propertyValueNotInList(aResp->res, *wdtTimeOutAction, 2297 "TimeoutAction"); 2298 return; 2299 } 2300 2301 crow::connections::systemBus->async_method_call( 2302 [aResp](const boost::system::error_code& ec) { 2303 if (ec) 2304 { 2305 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 2306 messages::internalError(aResp->res); 2307 return; 2308 } 2309 }, 2310 "xyz.openbmc_project.Watchdog", 2311 "/xyz/openbmc_project/watchdog/host0", 2312 "org.freedesktop.DBus.Properties", "Set", 2313 "xyz.openbmc_project.State.Watchdog", "ExpireAction", 2314 dbus::utility::DbusVariantType(wdtTimeOutActStr)); 2315 } 2316 2317 if (wdtEnable) 2318 { 2319 crow::connections::systemBus->async_method_call( 2320 [aResp](const boost::system::error_code& ec) { 2321 if (ec) 2322 { 2323 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 2324 messages::internalError(aResp->res); 2325 return; 2326 } 2327 }, 2328 "xyz.openbmc_project.Watchdog", 2329 "/xyz/openbmc_project/watchdog/host0", 2330 "org.freedesktop.DBus.Properties", "Set", 2331 "xyz.openbmc_project.State.Watchdog", "Enabled", 2332 dbus::utility::DbusVariantType(*wdtEnable)); 2333 } 2334 } 2335 2336 /** 2337 * @brief Parse the Idle Power Saver properties into json 2338 * 2339 * @param[in] aResp Shared pointer for completing asynchronous calls. 2340 * @param[in] properties IPS property data from DBus. 2341 * 2342 * @return true if successful 2343 */ 2344 inline bool 2345 parseIpsProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2346 const dbus::utility::DBusPropertiesMap& properties) 2347 { 2348 const bool* enabled = nullptr; 2349 const uint8_t* enterUtilizationPercent = nullptr; 2350 const uint64_t* enterDwellTime = nullptr; 2351 const uint8_t* exitUtilizationPercent = nullptr; 2352 const uint64_t* exitDwellTime = nullptr; 2353 2354 const bool success = sdbusplus::unpackPropertiesNoThrow( 2355 dbus_utils::UnpackErrorPrinter(), properties, "Enabled", enabled, 2356 "EnterUtilizationPercent", enterUtilizationPercent, 2357 "ExitUtilizationPercent", exitUtilizationPercent, "ExitDwellTime", 2358 exitDwellTime); 2359 2360 if (!success) 2361 { 2362 return false; 2363 } 2364 2365 if (enabled != nullptr) 2366 { 2367 aResp->res.jsonValue["IdlePowerSaver"]["Enabled"] = *enabled; 2368 } 2369 2370 if (enterUtilizationPercent != nullptr) 2371 { 2372 aResp->res.jsonValue["IdlePowerSaver"]["EnterUtilizationPercent"] = 2373 *enterUtilizationPercent; 2374 } 2375 2376 if (enterDwellTime != nullptr) 2377 { 2378 const std::chrono::duration<uint64_t, std::milli> ms(*enterDwellTime); 2379 aResp->res.jsonValue["IdlePowerSaver"]["EnterDwellTimeSeconds"] = 2380 std::chrono::duration_cast<std::chrono::duration<uint64_t>>(ms) 2381 .count(); 2382 } 2383 2384 if (exitUtilizationPercent != nullptr) 2385 { 2386 aResp->res.jsonValue["IdlePowerSaver"]["ExitUtilizationPercent"] = 2387 *exitUtilizationPercent; 2388 } 2389 2390 if (exitDwellTime != nullptr) 2391 { 2392 const std::chrono::duration<uint64_t, std::milli> ms(*exitDwellTime); 2393 aResp->res.jsonValue["IdlePowerSaver"]["ExitDwellTimeSeconds"] = 2394 std::chrono::duration_cast<std::chrono::duration<uint64_t>>(ms) 2395 .count(); 2396 } 2397 2398 return true; 2399 } 2400 2401 /** 2402 * @brief Retrieves host watchdog timer properties over DBUS 2403 * 2404 * @param[in] aResp Shared pointer for completing asynchronous calls. 2405 * 2406 * @return None. 2407 */ 2408 inline void getIdlePowerSaver(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 2409 { 2410 BMCWEB_LOG_DEBUG << "Get idle power saver parameters"; 2411 2412 // Get IdlePowerSaver object path: 2413 constexpr std::array<std::string_view, 1> interfaces = { 2414 "xyz.openbmc_project.Control.Power.IdlePowerSaver"}; 2415 dbus::utility::getSubTree( 2416 "/", 0, interfaces, 2417 [aResp](const boost::system::error_code& ec, 2418 const dbus::utility::MapperGetSubTreeResponse& subtree) { 2419 if (ec) 2420 { 2421 BMCWEB_LOG_DEBUG 2422 << "DBUS response error on Power.IdlePowerSaver GetSubTree " 2423 << ec; 2424 messages::internalError(aResp->res); 2425 return; 2426 } 2427 if (subtree.empty()) 2428 { 2429 // This is an optional interface so just return 2430 // if there is no instance found 2431 BMCWEB_LOG_DEBUG << "No instances found"; 2432 return; 2433 } 2434 if (subtree.size() > 1) 2435 { 2436 // More then one PowerIdlePowerSaver object is not supported and 2437 // is an error 2438 BMCWEB_LOG_DEBUG << "Found more than 1 system D-Bus " 2439 "Power.IdlePowerSaver objects: " 2440 << subtree.size(); 2441 messages::internalError(aResp->res); 2442 return; 2443 } 2444 if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) 2445 { 2446 BMCWEB_LOG_DEBUG << "Power.IdlePowerSaver mapper error!"; 2447 messages::internalError(aResp->res); 2448 return; 2449 } 2450 const std::string& path = subtree[0].first; 2451 const std::string& service = subtree[0].second.begin()->first; 2452 if (service.empty()) 2453 { 2454 BMCWEB_LOG_DEBUG << "Power.IdlePowerSaver service mapper error!"; 2455 messages::internalError(aResp->res); 2456 return; 2457 } 2458 2459 // Valid IdlePowerSaver object found, now read the current values 2460 sdbusplus::asio::getAllProperties( 2461 *crow::connections::systemBus, service, path, 2462 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2463 [aResp](const boost::system::error_code& ec2, 2464 const dbus::utility::DBusPropertiesMap& properties) { 2465 if (ec2) 2466 { 2467 BMCWEB_LOG_ERROR 2468 << "DBUS response error on IdlePowerSaver GetAll: " << ec2; 2469 messages::internalError(aResp->res); 2470 return; 2471 } 2472 2473 if (!parseIpsProperties(aResp, properties)) 2474 { 2475 messages::internalError(aResp->res); 2476 return; 2477 } 2478 }); 2479 }); 2480 2481 BMCWEB_LOG_DEBUG << "EXIT: Get idle power saver parameters"; 2482 } 2483 2484 /** 2485 * @brief Sets Idle Power Saver properties. 2486 * 2487 * @param[in] aResp Shared pointer for generating response message. 2488 * @param[in] ipsEnable The IPS Enable value (true/false) from incoming 2489 * RF request. 2490 * @param[in] ipsEnterUtil The utilization limit to enter idle state. 2491 * @param[in] ipsEnterTime The time the utilization must be below ipsEnterUtil 2492 * before entering idle state. 2493 * @param[in] ipsExitUtil The utilization limit when exiting idle state. 2494 * @param[in] ipsExitTime The time the utilization must be above ipsExutUtil 2495 * before exiting idle state 2496 * 2497 * @return None. 2498 */ 2499 inline void setIdlePowerSaver(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2500 const std::optional<bool> ipsEnable, 2501 const std::optional<uint8_t> ipsEnterUtil, 2502 const std::optional<uint64_t> ipsEnterTime, 2503 const std::optional<uint8_t> ipsExitUtil, 2504 const std::optional<uint64_t> ipsExitTime) 2505 { 2506 BMCWEB_LOG_DEBUG << "Set idle power saver properties"; 2507 2508 // Get IdlePowerSaver object path: 2509 constexpr std::array<std::string_view, 1> interfaces = { 2510 "xyz.openbmc_project.Control.Power.IdlePowerSaver"}; 2511 dbus::utility::getSubTree( 2512 "/", 0, interfaces, 2513 [aResp, ipsEnable, ipsEnterUtil, ipsEnterTime, ipsExitUtil, 2514 ipsExitTime](const boost::system::error_code& ec, 2515 const dbus::utility::MapperGetSubTreeResponse& subtree) { 2516 if (ec) 2517 { 2518 BMCWEB_LOG_DEBUG 2519 << "DBUS response error on Power.IdlePowerSaver GetSubTree " 2520 << ec; 2521 messages::internalError(aResp->res); 2522 return; 2523 } 2524 if (subtree.empty()) 2525 { 2526 // This is an optional D-Bus object, but user attempted to patch 2527 messages::resourceNotFound(aResp->res, "ComputerSystem", 2528 "IdlePowerSaver"); 2529 return; 2530 } 2531 if (subtree.size() > 1) 2532 { 2533 // More then one PowerIdlePowerSaver object is not supported and 2534 // is an error 2535 BMCWEB_LOG_DEBUG 2536 << "Found more than 1 system D-Bus Power.IdlePowerSaver objects: " 2537 << subtree.size(); 2538 messages::internalError(aResp->res); 2539 return; 2540 } 2541 if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) 2542 { 2543 BMCWEB_LOG_DEBUG << "Power.IdlePowerSaver mapper error!"; 2544 messages::internalError(aResp->res); 2545 return; 2546 } 2547 const std::string& path = subtree[0].first; 2548 const std::string& service = subtree[0].second.begin()->first; 2549 if (service.empty()) 2550 { 2551 BMCWEB_LOG_DEBUG << "Power.IdlePowerSaver service mapper error!"; 2552 messages::internalError(aResp->res); 2553 return; 2554 } 2555 2556 // Valid Power IdlePowerSaver object found, now set any values that 2557 // need to be updated 2558 2559 if (ipsEnable) 2560 { 2561 crow::connections::systemBus->async_method_call( 2562 [aResp](const boost::system::error_code& ec2) { 2563 if (ec2) 2564 { 2565 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2566 messages::internalError(aResp->res); 2567 return; 2568 } 2569 }, 2570 service, path, "org.freedesktop.DBus.Properties", "Set", 2571 "xyz.openbmc_project.Control.Power.IdlePowerSaver", "Enabled", 2572 dbus::utility::DbusVariantType(*ipsEnable)); 2573 } 2574 if (ipsEnterUtil) 2575 { 2576 crow::connections::systemBus->async_method_call( 2577 [aResp](const boost::system::error_code& ec2) { 2578 if (ec2) 2579 { 2580 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2581 messages::internalError(aResp->res); 2582 return; 2583 } 2584 }, 2585 service, path, "org.freedesktop.DBus.Properties", "Set", 2586 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2587 "EnterUtilizationPercent", 2588 dbus::utility::DbusVariantType(*ipsEnterUtil)); 2589 } 2590 if (ipsEnterTime) 2591 { 2592 // Convert from seconds into milliseconds for DBus 2593 const uint64_t timeMilliseconds = *ipsEnterTime * 1000; 2594 crow::connections::systemBus->async_method_call( 2595 [aResp](const boost::system::error_code& ec2) { 2596 if (ec2) 2597 { 2598 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2599 messages::internalError(aResp->res); 2600 return; 2601 } 2602 }, 2603 service, path, "org.freedesktop.DBus.Properties", "Set", 2604 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2605 "EnterDwellTime", 2606 dbus::utility::DbusVariantType(timeMilliseconds)); 2607 } 2608 if (ipsExitUtil) 2609 { 2610 crow::connections::systemBus->async_method_call( 2611 [aResp](const boost::system::error_code& ec2) { 2612 if (ec2) 2613 { 2614 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2615 messages::internalError(aResp->res); 2616 return; 2617 } 2618 }, 2619 service, path, "org.freedesktop.DBus.Properties", "Set", 2620 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2621 "ExitUtilizationPercent", 2622 dbus::utility::DbusVariantType(*ipsExitUtil)); 2623 } 2624 if (ipsExitTime) 2625 { 2626 // Convert from seconds into milliseconds for DBus 2627 const uint64_t timeMilliseconds = *ipsExitTime * 1000; 2628 crow::connections::systemBus->async_method_call( 2629 [aResp](const boost::system::error_code& ec2) { 2630 if (ec2) 2631 { 2632 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2633 messages::internalError(aResp->res); 2634 return; 2635 } 2636 }, 2637 service, path, "org.freedesktop.DBus.Properties", "Set", 2638 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2639 "ExitDwellTime", 2640 dbus::utility::DbusVariantType(timeMilliseconds)); 2641 } 2642 }); 2643 2644 BMCWEB_LOG_DEBUG << "EXIT: Set idle power saver parameters"; 2645 } 2646 2647 inline void handleComputerSystemHead( 2648 crow::App& app, const crow::Request& req, 2649 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 2650 { 2651 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2652 { 2653 return; 2654 } 2655 asyncResp->res.addHeader( 2656 boost::beast::http::field::link, 2657 "</redfish/v1/JsonSchemas/ComputerSystemCollection/ComputerSystemCollection.json>; rel=describedby"); 2658 } 2659 2660 /** 2661 * SystemsCollection derived class for delivering ComputerSystems Collection 2662 * Schema 2663 */ 2664 inline void requestRoutesSystemsCollection(App& app) 2665 { 2666 BMCWEB_ROUTE(app, "/redfish/v1/Systems/") 2667 .privileges(redfish::privileges::headComputerSystemCollection) 2668 .methods(boost::beast::http::verb::head)( 2669 std::bind_front(handleComputerSystemHead, std::ref(app))); 2670 2671 BMCWEB_ROUTE(app, "/redfish/v1/Systems/") 2672 .privileges(redfish::privileges::getComputerSystemCollection) 2673 .methods(boost::beast::http::verb::get)( 2674 [&app](const crow::Request& req, 2675 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2676 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2677 { 2678 return; 2679 } 2680 2681 asyncResp->res.addHeader( 2682 boost::beast::http::field::link, 2683 "</redfish/v1/JsonSchemas/ComputerSystemCollection.json>; rel=describedby"); 2684 asyncResp->res.jsonValue["@odata.type"] = 2685 "#ComputerSystemCollection.ComputerSystemCollection"; 2686 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Systems"; 2687 asyncResp->res.jsonValue["Name"] = "Computer System Collection"; 2688 2689 sdbusplus::asio::getProperty<std::string>( 2690 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 2691 "/xyz/openbmc_project/network/hypervisor", 2692 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 2693 [asyncResp](const boost::system::error_code& ec2, 2694 const std::string& /*hostName*/) { 2695 nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"]; 2696 ifaceArray = nlohmann::json::array(); 2697 auto& count = asyncResp->res.jsonValue["Members@odata.count"]; 2698 2699 nlohmann::json::object_t system; 2700 system["@odata.id"] = "/redfish/v1/Systems/system"; 2701 ifaceArray.push_back(std::move(system)); 2702 count = ifaceArray.size(); 2703 if (!ec2) 2704 { 2705 BMCWEB_LOG_DEBUG << "Hypervisor is available"; 2706 nlohmann::json::object_t hypervisor; 2707 hypervisor["@odata.id"] = "/redfish/v1/Systems/hypervisor"; 2708 ifaceArray.push_back(std::move(hypervisor)); 2709 count = ifaceArray.size(); 2710 } 2711 }); 2712 }); 2713 } 2714 2715 /** 2716 * Function transceives data with dbus directly. 2717 */ 2718 inline void doNMI(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 2719 { 2720 constexpr char const* serviceName = "xyz.openbmc_project.Control.Host.NMI"; 2721 constexpr char const* objectPath = "/xyz/openbmc_project/control/host0/nmi"; 2722 constexpr char const* interfaceName = 2723 "xyz.openbmc_project.Control.Host.NMI"; 2724 constexpr char const* method = "NMI"; 2725 2726 crow::connections::systemBus->async_method_call( 2727 [asyncResp](const boost::system::error_code& ec) { 2728 if (ec) 2729 { 2730 BMCWEB_LOG_ERROR << " Bad D-Bus request error: " << ec; 2731 messages::internalError(asyncResp->res); 2732 return; 2733 } 2734 messages::success(asyncResp->res); 2735 }, 2736 serviceName, objectPath, interfaceName, method); 2737 } 2738 2739 /** 2740 * SystemActionsReset class supports handle POST method for Reset action. 2741 * The class retrieves and sends data directly to D-Bus. 2742 */ 2743 inline void requestRoutesSystemActionsReset(App& app) 2744 { 2745 /** 2746 * Function handles POST method request. 2747 * Analyzes POST body message before sends Reset request data to D-Bus. 2748 */ 2749 BMCWEB_ROUTE(app, 2750 "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset/") 2751 .privileges(redfish::privileges::postComputerSystem) 2752 .methods(boost::beast::http::verb::post)( 2753 [&app](const crow::Request& req, 2754 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2755 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2756 { 2757 return; 2758 } 2759 std::string resetType; 2760 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", 2761 resetType)) 2762 { 2763 return; 2764 } 2765 2766 // Get the command and host vs. chassis 2767 std::string command; 2768 bool hostCommand = true; 2769 if ((resetType == "On") || (resetType == "ForceOn")) 2770 { 2771 command = "xyz.openbmc_project.State.Host.Transition.On"; 2772 hostCommand = true; 2773 } 2774 else if (resetType == "ForceOff") 2775 { 2776 command = "xyz.openbmc_project.State.Chassis.Transition.Off"; 2777 hostCommand = false; 2778 } 2779 else if (resetType == "ForceRestart") 2780 { 2781 command = 2782 "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot"; 2783 hostCommand = true; 2784 } 2785 else if (resetType == "GracefulShutdown") 2786 { 2787 command = "xyz.openbmc_project.State.Host.Transition.Off"; 2788 hostCommand = true; 2789 } 2790 else if (resetType == "GracefulRestart") 2791 { 2792 command = 2793 "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot"; 2794 hostCommand = true; 2795 } 2796 else if (resetType == "PowerCycle") 2797 { 2798 command = "xyz.openbmc_project.State.Host.Transition.Reboot"; 2799 hostCommand = true; 2800 } 2801 else if (resetType == "Nmi") 2802 { 2803 doNMI(asyncResp); 2804 return; 2805 } 2806 else 2807 { 2808 messages::actionParameterUnknown(asyncResp->res, "Reset", 2809 resetType); 2810 return; 2811 } 2812 2813 if (hostCommand) 2814 { 2815 crow::connections::systemBus->async_method_call( 2816 [asyncResp, resetType](const boost::system::error_code& ec) { 2817 if (ec) 2818 { 2819 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 2820 if (ec.value() == boost::asio::error::invalid_argument) 2821 { 2822 messages::actionParameterNotSupported( 2823 asyncResp->res, resetType, "Reset"); 2824 } 2825 else 2826 { 2827 messages::internalError(asyncResp->res); 2828 } 2829 return; 2830 } 2831 messages::success(asyncResp->res); 2832 }, 2833 "xyz.openbmc_project.State.Host", 2834 "/xyz/openbmc_project/state/host0", 2835 "org.freedesktop.DBus.Properties", "Set", 2836 "xyz.openbmc_project.State.Host", "RequestedHostTransition", 2837 dbus::utility::DbusVariantType{command}); 2838 } 2839 else 2840 { 2841 crow::connections::systemBus->async_method_call( 2842 [asyncResp, resetType](const boost::system::error_code& ec) { 2843 if (ec) 2844 { 2845 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 2846 if (ec.value() == boost::asio::error::invalid_argument) 2847 { 2848 messages::actionParameterNotSupported( 2849 asyncResp->res, resetType, "Reset"); 2850 } 2851 else 2852 { 2853 messages::internalError(asyncResp->res); 2854 } 2855 return; 2856 } 2857 messages::success(asyncResp->res); 2858 }, 2859 "xyz.openbmc_project.State.Chassis", 2860 "/xyz/openbmc_project/state/chassis0", 2861 "org.freedesktop.DBus.Properties", "Set", 2862 "xyz.openbmc_project.State.Chassis", "RequestedPowerTransition", 2863 dbus::utility::DbusVariantType{command}); 2864 } 2865 }); 2866 } 2867 2868 inline void handleComputerSystemCollectionHead( 2869 App& app, const crow::Request& req, 2870 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 2871 { 2872 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2873 { 2874 return; 2875 } 2876 2877 asyncResp->res.addHeader( 2878 boost::beast::http::field::link, 2879 "</redfish/v1/JsonSchemas/ComputerSystem/ComputerSystem.json>; rel=describedby"); 2880 } 2881 2882 /** 2883 * Systems derived class for delivering Computer Systems Schema. 2884 */ 2885 inline void requestRoutesSystems(App& app) 2886 { 2887 2888 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/") 2889 .privileges(redfish::privileges::headComputerSystem) 2890 .methods(boost::beast::http::verb::head)( 2891 std::bind_front(handleComputerSystemCollectionHead, std::ref(app))); 2892 /** 2893 * Functions triggers appropriate requests on DBus 2894 */ 2895 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/") 2896 .privileges(redfish::privileges::getComputerSystem) 2897 .methods(boost::beast::http::verb::get)( 2898 [&app](const crow::Request& req, 2899 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2900 const std::string& systemName) { 2901 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2902 { 2903 return; 2904 } 2905 if (systemName != "system") 2906 { 2907 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2908 systemName); 2909 return; 2910 } 2911 asyncResp->res.addHeader( 2912 boost::beast::http::field::link, 2913 "</redfish/v1/JsonSchemas/ComputerSystem/ComputerSystem.json>; rel=describedby"); 2914 asyncResp->res.jsonValue["@odata.type"] = 2915 "#ComputerSystem.v1_16_0.ComputerSystem"; 2916 asyncResp->res.jsonValue["Name"] = "system"; 2917 asyncResp->res.jsonValue["Id"] = "system"; 2918 asyncResp->res.jsonValue["SystemType"] = "Physical"; 2919 asyncResp->res.jsonValue["Description"] = "Computer System"; 2920 asyncResp->res.jsonValue["ProcessorSummary"]["Count"] = 0; 2921 asyncResp->res.jsonValue["ProcessorSummary"]["Status"]["State"] = 2922 "Disabled"; 2923 asyncResp->res.jsonValue["MemorySummary"]["TotalSystemMemoryGiB"] = 2924 uint64_t(0); 2925 asyncResp->res.jsonValue["MemorySummary"]["Status"]["State"] = 2926 "Disabled"; 2927 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system"; 2928 2929 asyncResp->res.jsonValue["Processors"]["@odata.id"] = 2930 "/redfish/v1/Systems/system/Processors"; 2931 asyncResp->res.jsonValue["Memory"]["@odata.id"] = 2932 "/redfish/v1/Systems/system/Memory"; 2933 asyncResp->res.jsonValue["Storage"]["@odata.id"] = 2934 "/redfish/v1/Systems/system/Storage"; 2935 asyncResp->res.jsonValue["FabricAdapters"]["@odata.id"] = 2936 "/redfish/v1/Systems/system/FabricAdapters"; 2937 2938 asyncResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"]["target"] = 2939 "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset"; 2940 asyncResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"] 2941 ["@Redfish.ActionInfo"] = 2942 "/redfish/v1/Systems/system/ResetActionInfo"; 2943 2944 asyncResp->res.jsonValue["LogServices"]["@odata.id"] = 2945 "/redfish/v1/Systems/system/LogServices"; 2946 asyncResp->res.jsonValue["Bios"]["@odata.id"] = 2947 "/redfish/v1/Systems/system/Bios"; 2948 2949 nlohmann::json::array_t managedBy; 2950 nlohmann::json& manager = managedBy.emplace_back(); 2951 manager["@odata.id"] = "/redfish/v1/Managers/bmc"; 2952 asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); 2953 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 2954 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 2955 2956 // Fill in SerialConsole info 2957 asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15; 2958 asyncResp->res.jsonValue["SerialConsole"]["IPMI"]["ServiceEnabled"] = 2959 true; 2960 2961 // TODO (Gunnar): Should look for obmc-console-ssh@2200.service 2962 asyncResp->res.jsonValue["SerialConsole"]["SSH"]["ServiceEnabled"] = 2963 true; 2964 asyncResp->res.jsonValue["SerialConsole"]["SSH"]["Port"] = 2200; 2965 asyncResp->res 2966 .jsonValue["SerialConsole"]["SSH"]["HotKeySequenceDisplay"] = 2967 "Press ~. to exit console"; 2968 2969 #ifdef BMCWEB_ENABLE_KVM 2970 // Fill in GraphicalConsole info 2971 asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true; 2972 asyncResp->res.jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 2973 4; 2974 asyncResp->res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = 2975 nlohmann::json::array_t({"KVMIP"}); 2976 2977 #endif // BMCWEB_ENABLE_KVM 2978 constexpr std::array<std::string_view, 4> inventoryForSystems{ 2979 "xyz.openbmc_project.Inventory.Item.Dimm", 2980 "xyz.openbmc_project.Inventory.Item.Cpu", 2981 "xyz.openbmc_project.Inventory.Item.Drive", 2982 "xyz.openbmc_project.Inventory.Item.StorageController"}; 2983 2984 auto health = std::make_shared<HealthPopulate>(asyncResp); 2985 dbus::utility::getSubTreePaths( 2986 "/", 0, inventoryForSystems, 2987 [health](const boost::system::error_code& ec, 2988 const std::vector<std::string>& resp) { 2989 if (ec) 2990 { 2991 // no inventory 2992 return; 2993 } 2994 2995 health->inventory = resp; 2996 }); 2997 2998 health->populate(); 2999 3000 getMainChassisId(asyncResp, 3001 [](const std::string& chassisId, 3002 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { 3003 nlohmann::json::array_t chassisArray; 3004 nlohmann::json& chassis = chassisArray.emplace_back(); 3005 chassis["@odata.id"] = crow::utility::urlFromPieces( 3006 "redfish", "v1", "Chassis", chassisId); 3007 aRsp->res.jsonValue["Links"]["Chassis"] = std::move(chassisArray); 3008 }); 3009 3010 getLocationIndicatorActive(asyncResp); 3011 // TODO (Gunnar): Remove IndicatorLED after enough time has passed 3012 getIndicatorLedState(asyncResp); 3013 getComputerSystem(asyncResp, health); 3014 getHostState(asyncResp); 3015 getBootProperties(asyncResp); 3016 getBootProgress(asyncResp); 3017 getBootProgressLastStateTime(asyncResp); 3018 getPCIeDeviceList(asyncResp, "PCIeDevices"); 3019 getHostWatchdogTimer(asyncResp); 3020 getPowerRestorePolicy(asyncResp); 3021 getAutomaticRetry(asyncResp); 3022 getLastResetTime(asyncResp); 3023 #ifdef BMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE 3024 getProvisioningStatus(asyncResp); 3025 #endif 3026 getTrustedModuleRequiredToBoot(asyncResp); 3027 getPowerMode(asyncResp); 3028 getIdlePowerSaver(asyncResp); 3029 }); 3030 3031 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/") 3032 .privileges(redfish::privileges::patchComputerSystem) 3033 .methods(boost::beast::http::verb::patch)( 3034 [&app](const crow::Request& req, 3035 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3036 const std::string& systemName) { 3037 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 3038 { 3039 return; 3040 } 3041 if (systemName != "system") 3042 { 3043 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3044 systemName); 3045 return; 3046 } 3047 3048 asyncResp->res.addHeader( 3049 boost::beast::http::field::link, 3050 "</redfish/v1/JsonSchemas/ComputerSystem/ComputerSystem.json>; rel=describedby"); 3051 3052 std::optional<bool> locationIndicatorActive; 3053 std::optional<std::string> indicatorLed; 3054 std::optional<std::string> assetTag; 3055 std::optional<std::string> powerRestorePolicy; 3056 std::optional<std::string> powerMode; 3057 std::optional<bool> wdtEnable; 3058 std::optional<std::string> wdtTimeOutAction; 3059 std::optional<std::string> bootSource; 3060 std::optional<std::string> bootType; 3061 std::optional<std::string> bootEnable; 3062 std::optional<std::string> bootAutomaticRetry; 3063 std::optional<bool> bootTrustedModuleRequired; 3064 std::optional<bool> ipsEnable; 3065 std::optional<uint8_t> ipsEnterUtil; 3066 std::optional<uint64_t> ipsEnterTime; 3067 std::optional<uint8_t> ipsExitUtil; 3068 std::optional<uint64_t> ipsExitTime; 3069 3070 // clang-format off 3071 if (!json_util::readJsonPatch( 3072 req, asyncResp->res, 3073 "IndicatorLED", indicatorLed, 3074 "LocationIndicatorActive", locationIndicatorActive, 3075 "AssetTag", assetTag, 3076 "PowerRestorePolicy", powerRestorePolicy, 3077 "PowerMode", powerMode, 3078 "HostWatchdogTimer/FunctionEnabled", wdtEnable, 3079 "HostWatchdogTimer/TimeoutAction", wdtTimeOutAction, 3080 "Boot/BootSourceOverrideTarget", bootSource, 3081 "Boot/BootSourceOverrideMode", bootType, 3082 "Boot/BootSourceOverrideEnabled", bootEnable, 3083 "Boot/AutomaticRetryConfig", bootAutomaticRetry, 3084 "Boot/TrustedModuleRequiredToBoot", bootTrustedModuleRequired, 3085 "IdlePowerSaver/Enabled", ipsEnable, 3086 "IdlePowerSaver/EnterUtilizationPercent", ipsEnterUtil, 3087 "IdlePowerSaver/EnterDwellTimeSeconds", ipsEnterTime, 3088 "IdlePowerSaver/ExitUtilizationPercent", ipsExitUtil, 3089 "IdlePowerSaver/ExitDwellTimeSeconds", ipsExitTime)) 3090 { 3091 return; 3092 } 3093 // clang-format on 3094 3095 asyncResp->res.result(boost::beast::http::status::no_content); 3096 3097 if (assetTag) 3098 { 3099 setAssetTag(asyncResp, *assetTag); 3100 } 3101 3102 if (wdtEnable || wdtTimeOutAction) 3103 { 3104 setWDTProperties(asyncResp, wdtEnable, wdtTimeOutAction); 3105 } 3106 3107 if (bootSource || bootType || bootEnable) 3108 { 3109 setBootProperties(asyncResp, bootSource, bootType, bootEnable); 3110 } 3111 if (bootAutomaticRetry) 3112 { 3113 setAutomaticRetry(asyncResp, *bootAutomaticRetry); 3114 } 3115 3116 if (bootTrustedModuleRequired) 3117 { 3118 setTrustedModuleRequiredToBoot(asyncResp, 3119 *bootTrustedModuleRequired); 3120 } 3121 3122 if (locationIndicatorActive) 3123 { 3124 setLocationIndicatorActive(asyncResp, *locationIndicatorActive); 3125 } 3126 3127 // TODO (Gunnar): Remove IndicatorLED after enough time has 3128 // passed 3129 if (indicatorLed) 3130 { 3131 setIndicatorLedState(asyncResp, *indicatorLed); 3132 asyncResp->res.addHeader(boost::beast::http::field::warning, 3133 "299 - \"IndicatorLED is deprecated. Use " 3134 "LocationIndicatorActive instead.\""); 3135 } 3136 3137 if (powerRestorePolicy) 3138 { 3139 setPowerRestorePolicy(asyncResp, *powerRestorePolicy); 3140 } 3141 3142 if (powerMode) 3143 { 3144 setPowerMode(asyncResp, *powerMode); 3145 } 3146 3147 if (ipsEnable || ipsEnterUtil || ipsEnterTime || ipsExitUtil || 3148 ipsExitTime) 3149 { 3150 setIdlePowerSaver(asyncResp, ipsEnable, ipsEnterUtil, ipsEnterTime, 3151 ipsExitUtil, ipsExitTime); 3152 } 3153 }); 3154 } 3155 3156 inline void handleSystemCollectionResetActionHead( 3157 crow::App& app, const crow::Request& req, 3158 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 3159 { 3160 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 3161 { 3162 return; 3163 } 3164 asyncResp->res.addHeader( 3165 boost::beast::http::field::link, 3166 "</redfish/v1/JsonSchemas/ActionInfo/ActionInfo.json>; rel=describedby"); 3167 } 3168 3169 /** 3170 * SystemResetActionInfo derived class for delivering Computer Systems 3171 * ResetType AllowableValues using ResetInfo schema. 3172 */ 3173 inline void requestRoutesSystemResetActionInfo(App& app) 3174 { 3175 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/ResetActionInfo/") 3176 .privileges(redfish::privileges::headActionInfo) 3177 .methods(boost::beast::http::verb::head)(std::bind_front( 3178 handleSystemCollectionResetActionHead, std::ref(app))); 3179 /** 3180 * Functions triggers appropriate requests on DBus 3181 */ 3182 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/ResetActionInfo/") 3183 .privileges(redfish::privileges::getActionInfo) 3184 .methods(boost::beast::http::verb::get)( 3185 [&app](const crow::Request& req, 3186 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3187 const std::string& systemName) { 3188 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 3189 { 3190 return; 3191 } 3192 if (systemName != "system") 3193 { 3194 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3195 systemName); 3196 return; 3197 } 3198 3199 asyncResp->res.addHeader( 3200 boost::beast::http::field::link, 3201 "</redfish/v1/JsonSchemas/ActionInfo/ActionInfo.json>; rel=describedby"); 3202 3203 asyncResp->res.jsonValue["@odata.id"] = 3204 "/redfish/v1/Systems/system/ResetActionInfo"; 3205 asyncResp->res.jsonValue["@odata.type"] = 3206 "#ActionInfo.v1_1_2.ActionInfo"; 3207 asyncResp->res.jsonValue["Name"] = "Reset Action Info"; 3208 asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; 3209 3210 nlohmann::json::array_t parameters; 3211 nlohmann::json::object_t parameter; 3212 3213 parameter["Name"] = "ResetType"; 3214 parameter["Required"] = true; 3215 parameter["DataType"] = "String"; 3216 nlohmann::json::array_t allowableValues; 3217 allowableValues.emplace_back("On"); 3218 allowableValues.emplace_back("ForceOff"); 3219 allowableValues.emplace_back("ForceOn"); 3220 allowableValues.emplace_back("ForceRestart"); 3221 allowableValues.emplace_back("GracefulRestart"); 3222 allowableValues.emplace_back("GracefulShutdown"); 3223 allowableValues.emplace_back("PowerCycle"); 3224 allowableValues.emplace_back("Nmi"); 3225 parameter["AllowableValues"] = std::move(allowableValues); 3226 parameters.emplace_back(std::move(parameter)); 3227 3228 asyncResp->res.jsonValue["Parameters"] = std::move(parameters); 3229 }); 3230 } 3231 } // namespace redfish 3232