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 "dbus_singleton.hpp" 19 #include "health.hpp" 20 #include "led.hpp" 21 #include "pcie.hpp" 22 #include "query.hpp" 23 #include "redfish_util.hpp" 24 #include "utils/time_utils.hpp" 25 26 #include <app.hpp> 27 #include <boost/container/flat_map.hpp> 28 #include <dbus_utility.hpp> 29 #include <registries/privilege_registry.hpp> 30 #include <sdbusplus/asio/property.hpp> 31 #include <sdbusplus/unpack_properties.hpp> 32 #include <utils/dbus_utils.hpp> 33 #include <utils/json_utils.hpp> 34 #include <utils/sw_utils.hpp> 35 36 #include <variant> 37 38 namespace redfish 39 { 40 41 /** 42 * @brief Updates the Functional State of DIMMs 43 * 44 * @param[in] aResp Shared pointer for completing asynchronous calls 45 * @param[in] dimmState Dimm's Functional state, true/false 46 * 47 * @return None. 48 */ 49 inline void 50 updateDimmProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 51 bool isDimmFunctional) 52 { 53 BMCWEB_LOG_DEBUG << "Dimm Functional: " << isDimmFunctional; 54 55 // Set it as Enabled if at least one DIMM is functional 56 // Update STATE only if previous State was DISABLED and current Dimm is 57 // ENABLED. 58 const nlohmann::json& prevMemSummary = 59 aResp->res.jsonValue["MemorySummary"]["Status"]["State"]; 60 if (prevMemSummary == "Disabled") 61 { 62 if (isDimmFunctional) 63 { 64 aResp->res.jsonValue["MemorySummary"]["Status"]["State"] = 65 "Enabled"; 66 } 67 } 68 } 69 70 /* 71 * @brief Update "ProcessorSummary" "Count" based on Cpu PresenceState 72 * 73 * @param[in] aResp Shared pointer for completing asynchronous calls 74 * @param[in] cpuPresenceState CPU present or not 75 * 76 * @return None. 77 */ 78 inline void 79 modifyCpuPresenceState(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 80 bool isCpuPresent) 81 { 82 BMCWEB_LOG_DEBUG << "Cpu Present: " << isCpuPresent; 83 84 if (isCpuPresent) 85 { 86 nlohmann::json& procCount = 87 aResp->res.jsonValue["ProcessorSummary"]["Count"]; 88 auto* procCountPtr = 89 procCount.get_ptr<nlohmann::json::number_integer_t*>(); 90 if (procCountPtr != nullptr) 91 { 92 // shouldn't be possible to be nullptr 93 *procCountPtr += 1; 94 } 95 } 96 } 97 98 /* 99 * @brief Update "ProcessorSummary" "Status" "State" based on 100 * CPU Functional State 101 * 102 * @param[in] aResp Shared pointer for completing asynchronous calls 103 * @param[in] cpuFunctionalState is CPU functional true/false 104 * 105 * @return None. 106 */ 107 inline void 108 modifyCpuFunctionalState(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 109 bool isCpuFunctional) 110 { 111 BMCWEB_LOG_DEBUG << "Cpu Functional: " << isCpuFunctional; 112 113 const nlohmann::json& prevProcState = 114 aResp->res.jsonValue["ProcessorSummary"]["Status"]["State"]; 115 116 // Set it as Enabled if at least one CPU is functional 117 // Update STATE only if previous State was Non_Functional and current CPU is 118 // Functional. 119 if (prevProcState == "Disabled") 120 { 121 if (isCpuFunctional) 122 { 123 aResp->res.jsonValue["ProcessorSummary"]["Status"]["State"] = 124 "Enabled"; 125 } 126 } 127 } 128 129 inline void getProcessorProperties( 130 const std::shared_ptr<bmcweb::AsyncResp>& aResp, 131 const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>& 132 properties) 133 { 134 135 BMCWEB_LOG_DEBUG << "Got " << properties.size() << " Cpu properties."; 136 137 // TODO: Get Model 138 139 const uint16_t* coreCount = nullptr; 140 141 const bool success = sdbusplus::unpackPropertiesNoThrow( 142 dbus_utils::UnpackErrorPrinter(), properties, "CoreCount", coreCount); 143 144 if (!success) 145 { 146 messages::internalError(aResp->res); 147 return; 148 } 149 150 if (coreCount != nullptr) 151 { 152 nlohmann::json& coreCountJson = 153 aResp->res.jsonValue["ProcessorSummary"]["CoreCount"]; 154 uint64_t* coreCountJsonPtr = coreCountJson.get_ptr<uint64_t*>(); 155 156 if (coreCountJsonPtr == nullptr) 157 { 158 coreCountJson = *coreCount; 159 } 160 else 161 { 162 *coreCountJsonPtr += *coreCount; 163 } 164 } 165 } 166 167 /* 168 * @brief Get ProcessorSummary fields 169 * 170 * @param[in] aResp Shared pointer for completing asynchronous calls 171 * @param[in] service dbus service for Cpu Information 172 * @param[in] path dbus path for Cpu 173 * 174 * @return None. 175 */ 176 inline void getProcessorSummary(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 177 const std::string& service, 178 const std::string& path) 179 { 180 181 auto getCpuPresenceState = [aResp](const boost::system::error_code ec3, 182 const bool cpuPresenceCheck) { 183 if (ec3) 184 { 185 BMCWEB_LOG_ERROR << "DBUS response error " << ec3; 186 return; 187 } 188 modifyCpuPresenceState(aResp, cpuPresenceCheck); 189 }; 190 191 auto getCpuFunctionalState = [aResp](const boost::system::error_code ec3, 192 const bool cpuFunctionalCheck) { 193 if (ec3) 194 { 195 BMCWEB_LOG_ERROR << "DBUS response error " << ec3; 196 return; 197 } 198 modifyCpuFunctionalState(aResp, cpuFunctionalCheck); 199 }; 200 201 // Get the Presence of CPU 202 sdbusplus::asio::getProperty<bool>( 203 *crow::connections::systemBus, service, path, 204 "xyz.openbmc_project.Inventory.Item", "Present", 205 std::move(getCpuPresenceState)); 206 207 // Get the Functional State 208 sdbusplus::asio::getProperty<bool>( 209 *crow::connections::systemBus, service, path, 210 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", 211 std::move(getCpuFunctionalState)); 212 213 sdbusplus::asio::getAllProperties( 214 *crow::connections::systemBus, service, path, 215 "xyz.openbmc_project.Inventory.Item.Cpu", 216 [aResp, service, 217 path](const boost::system::error_code ec2, 218 const dbus::utility::DBusPropertiesMap& properties) { 219 if (ec2) 220 { 221 BMCWEB_LOG_ERROR << "DBUS response error " << ec2; 222 messages::internalError(aResp->res); 223 return; 224 } 225 getProcessorProperties(aResp, properties); 226 }); 227 } 228 229 /* 230 * @brief Retrieves computer system properties over dbus 231 * 232 * @param[in] aResp Shared pointer for completing asynchronous calls 233 * @param[in] systemHealth Shared HealthPopulate pointer 234 * 235 * @return None. 236 */ 237 inline void 238 getComputerSystem(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 239 const std::shared_ptr<HealthPopulate>& systemHealth) 240 { 241 BMCWEB_LOG_DEBUG << "Get available system components."; 242 243 crow::connections::systemBus->async_method_call( 244 [aResp, 245 systemHealth](const boost::system::error_code ec, 246 const dbus::utility::MapperGetSubTreeResponse& subtree) { 247 if (ec) 248 { 249 BMCWEB_LOG_DEBUG << "DBUS response error"; 250 messages::internalError(aResp->res); 251 return; 252 } 253 // Iterate over all retrieved ObjectPaths. 254 for (const std::pair< 255 std::string, 256 std::vector<std::pair<std::string, std::vector<std::string>>>>& 257 object : subtree) 258 { 259 const std::string& path = object.first; 260 BMCWEB_LOG_DEBUG << "Got path: " << path; 261 const std::vector<std::pair<std::string, std::vector<std::string>>>& 262 connectionNames = object.second; 263 if (connectionNames.empty()) 264 { 265 continue; 266 } 267 268 auto memoryHealth = std::make_shared<HealthPopulate>( 269 aResp, "/MemorySummary/Status"_json_pointer); 270 271 auto cpuHealth = std::make_shared<HealthPopulate>( 272 aResp, "/ProcessorSummary/Status"_json_pointer); 273 274 systemHealth->children.emplace_back(memoryHealth); 275 systemHealth->children.emplace_back(cpuHealth); 276 277 // This is not system, so check if it's cpu, dimm, UUID or 278 // BiosVer 279 for (const auto& connection : connectionNames) 280 { 281 for (const auto& interfaceName : connection.second) 282 { 283 if (interfaceName == 284 "xyz.openbmc_project.Inventory.Item.Dimm") 285 { 286 BMCWEB_LOG_DEBUG 287 << "Found Dimm, now get its properties."; 288 289 sdbusplus::asio::getAllProperties( 290 *crow::connections::systemBus, connection.first, 291 path, "xyz.openbmc_project.Inventory.Item.Dimm", 292 [aResp, service{connection.first}, 293 path](const boost::system::error_code ec2, 294 const dbus::utility::DBusPropertiesMap& 295 properties) { 296 if (ec2) 297 { 298 BMCWEB_LOG_ERROR << "DBUS response error " 299 << ec2; 300 messages::internalError(aResp->res); 301 return; 302 } 303 BMCWEB_LOG_DEBUG << "Got " << properties.size() 304 << " Dimm properties."; 305 306 if (properties.empty()) 307 { 308 sdbusplus::asio::getProperty<bool>( 309 *crow::connections::systemBus, service, 310 path, 311 "xyz.openbmc_project.State." 312 "Decorator.OperationalStatus", 313 "Functional", 314 [aResp](const boost::system::error_code ec3, 315 bool dimmState) { 316 if (ec3) 317 { 318 BMCWEB_LOG_ERROR 319 << "DBUS response error " << ec3; 320 return; 321 } 322 updateDimmProperties(aResp, dimmState); 323 }); 324 return; 325 } 326 327 const uint32_t* memorySizeInKB = nullptr; 328 329 const bool success = 330 sdbusplus::unpackPropertiesNoThrow( 331 dbus_utils::UnpackErrorPrinter(), 332 properties, "MemorySizeInKB", 333 memorySizeInKB); 334 335 if (!success) 336 { 337 messages::internalError(aResp->res); 338 return; 339 } 340 341 if (memorySizeInKB != nullptr) 342 { 343 nlohmann::json& totalMemory = 344 aResp->res 345 .jsonValue["MemorySummary"] 346 ["TotalSystemMemoryGiB"]; 347 const uint64_t* preValue = 348 totalMemory.get_ptr<const uint64_t*>(); 349 if (preValue == nullptr) 350 { 351 aResp->res 352 .jsonValue["MemorySummary"] 353 ["TotalSystemMemoryGiB"] = 354 *memorySizeInKB / (1024 * 1024); 355 } 356 else 357 { 358 aResp->res 359 .jsonValue["MemorySummary"] 360 ["TotalSystemMemoryGiB"] = 361 *memorySizeInKB / (1024 * 1024) + 362 *preValue; 363 } 364 aResp->res.jsonValue["MemorySummary"]["Status"] 365 ["State"] = "Enabled"; 366 } 367 }); 368 369 memoryHealth->inventory.emplace_back(path); 370 } 371 else if (interfaceName == 372 "xyz.openbmc_project.Inventory.Item.Cpu") 373 { 374 BMCWEB_LOG_DEBUG 375 << "Found Cpu, now get its properties."; 376 377 getProcessorSummary(aResp, connection.first, path); 378 379 cpuHealth->inventory.emplace_back(path); 380 } 381 else if (interfaceName == "xyz.openbmc_project.Common.UUID") 382 { 383 BMCWEB_LOG_DEBUG 384 << "Found UUID, now get its properties."; 385 386 sdbusplus::asio::getAllProperties( 387 *crow::connections::systemBus, connection.first, 388 path, "xyz.openbmc_project.Common.UUID", 389 [aResp](const boost::system::error_code ec3, 390 const dbus::utility::DBusPropertiesMap& 391 properties) { 392 if (ec3) 393 { 394 BMCWEB_LOG_DEBUG << "DBUS response error " 395 << ec3; 396 messages::internalError(aResp->res); 397 return; 398 } 399 BMCWEB_LOG_DEBUG << "Got " << properties.size() 400 << " UUID properties."; 401 402 const std::string* uUID = nullptr; 403 404 const bool success = 405 sdbusplus::unpackPropertiesNoThrow( 406 dbus_utils::UnpackErrorPrinter(), 407 properties, "UUID", uUID); 408 409 if (!success) 410 { 411 messages::internalError(aResp->res); 412 return; 413 } 414 415 if (uUID != nullptr) 416 { 417 std::string valueStr = *uUID; 418 if (valueStr.size() == 32) 419 { 420 valueStr.insert(8, 1, '-'); 421 valueStr.insert(13, 1, '-'); 422 valueStr.insert(18, 1, '-'); 423 valueStr.insert(23, 1, '-'); 424 } 425 BMCWEB_LOG_DEBUG << "UUID = " << valueStr; 426 aResp->res.jsonValue["UUID"] = valueStr; 427 } 428 }); 429 } 430 else if (interfaceName == 431 "xyz.openbmc_project.Inventory.Item.System") 432 { 433 sdbusplus::asio::getAllProperties( 434 *crow::connections::systemBus, connection.first, 435 path, 436 "xyz.openbmc_project.Inventory.Decorator.Asset", 437 [aResp](const boost::system::error_code ec2, 438 const dbus::utility::DBusPropertiesMap& 439 propertiesList) { 440 if (ec2) 441 { 442 // doesn't have to include this 443 // interface 444 return; 445 } 446 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size() 447 << " properties for system"; 448 449 const std::string* partNumber = nullptr; 450 const std::string* serialNumber = nullptr; 451 const std::string* manufacturer = nullptr; 452 const std::string* model = nullptr; 453 const std::string* subModel = nullptr; 454 455 const bool success = 456 sdbusplus::unpackPropertiesNoThrow( 457 dbus_utils::UnpackErrorPrinter(), 458 propertiesList, "PartNumber", partNumber, 459 "SerialNumber", serialNumber, 460 "Manufacturer", manufacturer, "Model", 461 model, "SubModel", subModel); 462 463 if (!success) 464 { 465 messages::internalError(aResp->res); 466 return; 467 } 468 469 if (partNumber != nullptr) 470 { 471 aResp->res.jsonValue["PartNumber"] = 472 *partNumber; 473 } 474 475 if (serialNumber != nullptr) 476 { 477 aResp->res.jsonValue["SerialNumber"] = 478 *serialNumber; 479 } 480 481 if (manufacturer != nullptr) 482 { 483 aResp->res.jsonValue["Manufacturer"] = 484 *manufacturer; 485 } 486 487 if (model != nullptr) 488 { 489 aResp->res.jsonValue["Model"] = *model; 490 } 491 492 if (subModel != nullptr) 493 { 494 aResp->res.jsonValue["SubModel"] = *subModel; 495 } 496 497 // Grab the bios version 498 sw_util::populateSoftwareInformation( 499 aResp, sw_util::biosPurpose, "BiosVersion", 500 false); 501 }); 502 503 sdbusplus::asio::getProperty<std::string>( 504 *crow::connections::systemBus, connection.first, 505 path, 506 "xyz.openbmc_project.Inventory.Decorator." 507 "AssetTag", 508 "AssetTag", 509 [aResp](const boost::system::error_code ec2, 510 const std::string& value) { 511 if (ec2) 512 { 513 // doesn't have to include this 514 // interface 515 return; 516 } 517 518 aResp->res.jsonValue["AssetTag"] = value; 519 }); 520 } 521 } 522 break; 523 } 524 } 525 }, 526 "xyz.openbmc_project.ObjectMapper", 527 "/xyz/openbmc_project/object_mapper", 528 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 529 "/xyz/openbmc_project/inventory", int32_t(0), 530 std::array<const char*, 5>{ 531 "xyz.openbmc_project.Inventory.Decorator.Asset", 532 "xyz.openbmc_project.Inventory.Item.Cpu", 533 "xyz.openbmc_project.Inventory.Item.Dimm", 534 "xyz.openbmc_project.Inventory.Item.System", 535 "xyz.openbmc_project.Common.UUID", 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, const std::string& policy) { 1232 if (ec) 1233 { 1234 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1235 return; 1236 } 1237 1238 const boost::container::flat_map<std::string, std::string> policyMaps = { 1239 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn", 1240 "AlwaysOn"}, 1241 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOff", 1242 "AlwaysOff"}, 1243 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore", 1244 "LastState"}, 1245 // Return `AlwaysOff` when power restore policy set to "None" 1246 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.None", 1247 "AlwaysOff"}}; 1248 1249 auto policyMapsIt = policyMaps.find(policy); 1250 if (policyMapsIt == policyMaps.end()) 1251 { 1252 messages::internalError(aResp->res); 1253 return; 1254 } 1255 1256 aResp->res.jsonValue["PowerRestorePolicy"] = policyMapsIt->second; 1257 }); 1258 } 1259 1260 /** 1261 * @brief Get TrustedModuleRequiredToBoot property. Determines whether or not 1262 * TPM is required for booting the host. 1263 * 1264 * @param[in] aResp Shared pointer for generating response message. 1265 * 1266 * @return None. 1267 */ 1268 inline void getTrustedModuleRequiredToBoot( 1269 const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1270 { 1271 BMCWEB_LOG_DEBUG << "Get TPM required to boot."; 1272 1273 crow::connections::systemBus->async_method_call( 1274 [aResp](const boost::system::error_code ec, 1275 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1276 if (ec) 1277 { 1278 BMCWEB_LOG_DEBUG << "DBUS response error on TPM.Policy GetSubTree" 1279 << ec; 1280 // This is an optional D-Bus object so just return if 1281 // error occurs 1282 return; 1283 } 1284 if (subtree.empty()) 1285 { 1286 // As noted above, this is an optional interface so just return 1287 // if there is no instance found 1288 return; 1289 } 1290 1291 /* When there is more than one TPMEnable object... */ 1292 if (subtree.size() > 1) 1293 { 1294 BMCWEB_LOG_DEBUG 1295 << "DBUS response has more than 1 TPM Enable object:" 1296 << subtree.size(); 1297 // Throw an internal Error and return 1298 messages::internalError(aResp->res); 1299 return; 1300 } 1301 1302 // Make sure the Dbus response map has a service and objectPath 1303 // field 1304 if (subtree[0].first.empty() || subtree[0].second.size() != 1) 1305 { 1306 BMCWEB_LOG_DEBUG << "TPM.Policy mapper error!"; 1307 messages::internalError(aResp->res); 1308 return; 1309 } 1310 1311 const std::string& path = subtree[0].first; 1312 const std::string& serv = subtree[0].second.begin()->first; 1313 1314 // Valid TPM Enable object found, now reading the current value 1315 sdbusplus::asio::getProperty<bool>( 1316 *crow::connections::systemBus, serv, path, 1317 "xyz.openbmc_project.Control.TPM.Policy", "TPMEnable", 1318 [aResp](const boost::system::error_code ec2, bool tpmRequired) { 1319 if (ec2) 1320 { 1321 BMCWEB_LOG_DEBUG << "D-BUS response error on TPM.Policy Get" 1322 << ec2; 1323 messages::internalError(aResp->res); 1324 return; 1325 } 1326 1327 if (tpmRequired) 1328 { 1329 aResp->res.jsonValue["Boot"]["TrustedModuleRequiredToBoot"] = 1330 "Required"; 1331 } 1332 else 1333 { 1334 aResp->res.jsonValue["Boot"]["TrustedModuleRequiredToBoot"] = 1335 "Disabled"; 1336 } 1337 }); 1338 }, 1339 "xyz.openbmc_project.ObjectMapper", 1340 "/xyz/openbmc_project/object_mapper", 1341 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0), 1342 std::array<const char*, 1>{"xyz.openbmc_project.Control.TPM.Policy"}); 1343 } 1344 1345 /** 1346 * @brief Set TrustedModuleRequiredToBoot property. Determines whether or not 1347 * TPM is required for booting the host. 1348 * 1349 * @param[in] aResp Shared pointer for generating response message. 1350 * @param[in] tpmRequired Value to set TPM Required To Boot property to. 1351 * 1352 * @return None. 1353 */ 1354 inline void setTrustedModuleRequiredToBoot( 1355 const std::shared_ptr<bmcweb::AsyncResp>& aResp, const bool tpmRequired) 1356 { 1357 BMCWEB_LOG_DEBUG << "Set TrustedModuleRequiredToBoot."; 1358 1359 crow::connections::systemBus->async_method_call( 1360 [aResp, tpmRequired](const boost::system::error_code ec, 1361 dbus::utility::MapperGetSubTreeResponse& subtree) { 1362 if (ec) 1363 { 1364 BMCWEB_LOG_DEBUG << "DBUS response error on TPM.Policy GetSubTree" 1365 << ec; 1366 messages::internalError(aResp->res); 1367 return; 1368 } 1369 if (subtree.empty()) 1370 { 1371 messages::propertyValueNotInList(aResp->res, "ComputerSystem", 1372 "TrustedModuleRequiredToBoot"); 1373 return; 1374 } 1375 1376 /* When there is more than one TPMEnable object... */ 1377 if (subtree.size() > 1) 1378 { 1379 BMCWEB_LOG_DEBUG 1380 << "DBUS response has more than 1 TPM Enable object:" 1381 << subtree.size(); 1382 // Throw an internal Error and return 1383 messages::internalError(aResp->res); 1384 return; 1385 } 1386 1387 // Make sure the Dbus response map has a service and objectPath 1388 // field 1389 if (subtree[0].first.empty() || subtree[0].second.size() != 1) 1390 { 1391 BMCWEB_LOG_DEBUG << "TPM.Policy mapper error!"; 1392 messages::internalError(aResp->res); 1393 return; 1394 } 1395 1396 const std::string& path = subtree[0].first; 1397 const std::string& serv = subtree[0].second.begin()->first; 1398 1399 if (serv.empty()) 1400 { 1401 BMCWEB_LOG_DEBUG << "TPM.Policy service mapper error!"; 1402 messages::internalError(aResp->res); 1403 return; 1404 } 1405 1406 // Valid TPM Enable object found, now setting the value 1407 crow::connections::systemBus->async_method_call( 1408 [aResp](const boost::system::error_code ec2) { 1409 if (ec2) 1410 { 1411 BMCWEB_LOG_DEBUG 1412 << "DBUS response error: Set TrustedModuleRequiredToBoot" 1413 << ec2; 1414 messages::internalError(aResp->res); 1415 return; 1416 } 1417 BMCWEB_LOG_DEBUG << "Set TrustedModuleRequiredToBoot done."; 1418 }, 1419 serv, path, "org.freedesktop.DBus.Properties", "Set", 1420 "xyz.openbmc_project.Control.TPM.Policy", "TPMEnable", 1421 dbus::utility::DbusVariantType(tpmRequired)); 1422 }, 1423 "xyz.openbmc_project.ObjectMapper", 1424 "/xyz/openbmc_project/object_mapper", 1425 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0), 1426 std::array<const char*, 1>{"xyz.openbmc_project.Control.TPM.Policy"}); 1427 } 1428 1429 /** 1430 * @brief Sets boot properties into DBUS object(s). 1431 * 1432 * @param[in] aResp Shared pointer for generating response message. 1433 * @param[in] bootType The boot type to set. 1434 * @return Integer error code. 1435 */ 1436 inline void setBootType(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1437 const std::optional<std::string>& bootType) 1438 { 1439 std::string bootTypeStr; 1440 1441 if (!bootType) 1442 { 1443 return; 1444 } 1445 1446 // Source target specified 1447 BMCWEB_LOG_DEBUG << "Boot type: " << *bootType; 1448 // Figure out which DBUS interface and property to use 1449 if (*bootType == "Legacy") 1450 { 1451 bootTypeStr = "xyz.openbmc_project.Control.Boot.Type.Types.Legacy"; 1452 } 1453 else if (*bootType == "UEFI") 1454 { 1455 bootTypeStr = "xyz.openbmc_project.Control.Boot.Type.Types.EFI"; 1456 } 1457 else 1458 { 1459 BMCWEB_LOG_DEBUG << "Invalid property value for " 1460 "BootSourceOverrideMode: " 1461 << *bootType; 1462 messages::propertyValueNotInList(aResp->res, *bootType, 1463 "BootSourceOverrideMode"); 1464 return; 1465 } 1466 1467 // Act on validated parameters 1468 BMCWEB_LOG_DEBUG << "DBUS boot type: " << bootTypeStr; 1469 1470 crow::connections::systemBus->async_method_call( 1471 [aResp](const boost::system::error_code ec) { 1472 if (ec) 1473 { 1474 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1475 if (ec.value() == boost::asio::error::host_unreachable) 1476 { 1477 messages::resourceNotFound(aResp->res, "Set", "BootType"); 1478 return; 1479 } 1480 messages::internalError(aResp->res); 1481 return; 1482 } 1483 BMCWEB_LOG_DEBUG << "Boot type update done."; 1484 }, 1485 "xyz.openbmc_project.Settings", 1486 "/xyz/openbmc_project/control/host0/boot", 1487 "org.freedesktop.DBus.Properties", "Set", 1488 "xyz.openbmc_project.Control.Boot.Type", "BootType", 1489 dbus::utility::DbusVariantType(bootTypeStr)); 1490 } 1491 1492 /** 1493 * @brief Sets boot properties into DBUS object(s). 1494 * 1495 * @param[in] aResp Shared pointer for generating response message. 1496 * @param[in] bootType The boot type to set. 1497 * @return Integer error code. 1498 */ 1499 inline void setBootEnable(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1500 const std::optional<std::string>& bootEnable) 1501 { 1502 if (!bootEnable) 1503 { 1504 return; 1505 } 1506 // Source target specified 1507 BMCWEB_LOG_DEBUG << "Boot enable: " << *bootEnable; 1508 1509 bool bootOverrideEnable = false; 1510 bool bootOverridePersistent = false; 1511 // Figure out which DBUS interface and property to use 1512 if (*bootEnable == "Disabled") 1513 { 1514 bootOverrideEnable = false; 1515 } 1516 else if (*bootEnable == "Once") 1517 { 1518 bootOverrideEnable = true; 1519 bootOverridePersistent = false; 1520 } 1521 else if (*bootEnable == "Continuous") 1522 { 1523 bootOverrideEnable = true; 1524 bootOverridePersistent = true; 1525 } 1526 else 1527 { 1528 BMCWEB_LOG_DEBUG 1529 << "Invalid property value for BootSourceOverrideEnabled: " 1530 << *bootEnable; 1531 messages::propertyValueNotInList(aResp->res, *bootEnable, 1532 "BootSourceOverrideEnabled"); 1533 return; 1534 } 1535 1536 // Act on validated parameters 1537 BMCWEB_LOG_DEBUG << "DBUS boot override enable: " << bootOverrideEnable; 1538 1539 crow::connections::systemBus->async_method_call( 1540 [aResp](const boost::system::error_code ec2) { 1541 if (ec2) 1542 { 1543 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 1544 messages::internalError(aResp->res); 1545 return; 1546 } 1547 BMCWEB_LOG_DEBUG << "Boot override enable update done."; 1548 }, 1549 "xyz.openbmc_project.Settings", 1550 "/xyz/openbmc_project/control/host0/boot", 1551 "org.freedesktop.DBus.Properties", "Set", 1552 "xyz.openbmc_project.Object.Enable", "Enabled", 1553 dbus::utility::DbusVariantType(bootOverrideEnable)); 1554 1555 if (!bootOverrideEnable) 1556 { 1557 return; 1558 } 1559 1560 // In case boot override is enabled we need to set correct value for the 1561 // 'one_time' enable DBus interface 1562 BMCWEB_LOG_DEBUG << "DBUS boot override persistent: " 1563 << bootOverridePersistent; 1564 1565 crow::connections::systemBus->async_method_call( 1566 [aResp](const boost::system::error_code ec) { 1567 if (ec) 1568 { 1569 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1570 messages::internalError(aResp->res); 1571 return; 1572 } 1573 BMCWEB_LOG_DEBUG << "Boot one_time update done."; 1574 }, 1575 "xyz.openbmc_project.Settings", 1576 "/xyz/openbmc_project/control/host0/boot/one_time", 1577 "org.freedesktop.DBus.Properties", "Set", 1578 "xyz.openbmc_project.Object.Enable", "Enabled", 1579 dbus::utility::DbusVariantType(!bootOverridePersistent)); 1580 } 1581 1582 /** 1583 * @brief Sets boot properties into DBUS object(s). 1584 * 1585 * @param[in] aResp Shared pointer for generating response message. 1586 * @param[in] bootSource The boot source to set. 1587 * 1588 * @return Integer error code. 1589 */ 1590 inline void setBootModeOrSource(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1591 const std::optional<std::string>& bootSource) 1592 { 1593 std::string bootSourceStr; 1594 std::string bootModeStr; 1595 1596 if (!bootSource) 1597 { 1598 return; 1599 } 1600 1601 // Source target specified 1602 BMCWEB_LOG_DEBUG << "Boot source: " << *bootSource; 1603 // Figure out which DBUS interface and property to use 1604 if (assignBootParameters(aResp, *bootSource, bootSourceStr, bootModeStr) != 1605 0) 1606 { 1607 BMCWEB_LOG_DEBUG 1608 << "Invalid property value for BootSourceOverrideTarget: " 1609 << *bootSource; 1610 messages::propertyValueNotInList(aResp->res, *bootSource, 1611 "BootSourceTargetOverride"); 1612 return; 1613 } 1614 1615 // Act on validated parameters 1616 BMCWEB_LOG_DEBUG << "DBUS boot source: " << bootSourceStr; 1617 BMCWEB_LOG_DEBUG << "DBUS boot mode: " << bootModeStr; 1618 1619 crow::connections::systemBus->async_method_call( 1620 [aResp](const boost::system::error_code ec) { 1621 if (ec) 1622 { 1623 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1624 messages::internalError(aResp->res); 1625 return; 1626 } 1627 BMCWEB_LOG_DEBUG << "Boot source update done."; 1628 }, 1629 "xyz.openbmc_project.Settings", 1630 "/xyz/openbmc_project/control/host0/boot", 1631 "org.freedesktop.DBus.Properties", "Set", 1632 "xyz.openbmc_project.Control.Boot.Source", "BootSource", 1633 dbus::utility::DbusVariantType(bootSourceStr)); 1634 1635 crow::connections::systemBus->async_method_call( 1636 [aResp](const boost::system::error_code ec) { 1637 if (ec) 1638 { 1639 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1640 messages::internalError(aResp->res); 1641 return; 1642 } 1643 BMCWEB_LOG_DEBUG << "Boot mode update done."; 1644 }, 1645 "xyz.openbmc_project.Settings", 1646 "/xyz/openbmc_project/control/host0/boot", 1647 "org.freedesktop.DBus.Properties", "Set", 1648 "xyz.openbmc_project.Control.Boot.Mode", "BootMode", 1649 dbus::utility::DbusVariantType(bootModeStr)); 1650 } 1651 1652 /** 1653 * @brief Sets Boot source override properties. 1654 * 1655 * @param[in] aResp Shared pointer for generating response message. 1656 * @param[in] bootSource The boot source from incoming RF request. 1657 * @param[in] bootType The boot type from incoming RF request. 1658 * @param[in] bootEnable The boot override enable from incoming RF request. 1659 * 1660 * @return Integer error code. 1661 */ 1662 1663 inline void setBootProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1664 const std::optional<std::string>& bootSource, 1665 const std::optional<std::string>& bootType, 1666 const std::optional<std::string>& bootEnable) 1667 { 1668 BMCWEB_LOG_DEBUG << "Set boot information."; 1669 1670 setBootModeOrSource(aResp, bootSource); 1671 setBootType(aResp, bootType); 1672 setBootEnable(aResp, bootEnable); 1673 } 1674 1675 /** 1676 * @brief Sets AssetTag 1677 * 1678 * @param[in] aResp Shared pointer for generating response message. 1679 * @param[in] assetTag "AssetTag" from request. 1680 * 1681 * @return None. 1682 */ 1683 inline void setAssetTag(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1684 const std::string& assetTag) 1685 { 1686 crow::connections::systemBus->async_method_call( 1687 [aResp, 1688 assetTag](const boost::system::error_code ec, 1689 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1690 if (ec) 1691 { 1692 BMCWEB_LOG_DEBUG << "D-Bus response error on GetSubTree " << ec; 1693 messages::internalError(aResp->res); 1694 return; 1695 } 1696 if (subtree.empty()) 1697 { 1698 BMCWEB_LOG_DEBUG << "Can't find system D-Bus object!"; 1699 messages::internalError(aResp->res); 1700 return; 1701 } 1702 // Assume only 1 system D-Bus object 1703 // Throw an error if there is more than 1 1704 if (subtree.size() > 1) 1705 { 1706 BMCWEB_LOG_DEBUG << "Found more than 1 system D-Bus object!"; 1707 messages::internalError(aResp->res); 1708 return; 1709 } 1710 if (subtree[0].first.empty() || subtree[0].second.size() != 1) 1711 { 1712 BMCWEB_LOG_DEBUG << "Asset Tag Set mapper error!"; 1713 messages::internalError(aResp->res); 1714 return; 1715 } 1716 1717 const std::string& path = subtree[0].first; 1718 const std::string& service = subtree[0].second.begin()->first; 1719 1720 if (service.empty()) 1721 { 1722 BMCWEB_LOG_DEBUG << "Asset Tag Set service mapper error!"; 1723 messages::internalError(aResp->res); 1724 return; 1725 } 1726 1727 crow::connections::systemBus->async_method_call( 1728 [aResp](const boost::system::error_code ec2) { 1729 if (ec2) 1730 { 1731 BMCWEB_LOG_DEBUG << "D-Bus response error on AssetTag Set " 1732 << ec2; 1733 messages::internalError(aResp->res); 1734 return; 1735 } 1736 }, 1737 service, path, "org.freedesktop.DBus.Properties", "Set", 1738 "xyz.openbmc_project.Inventory.Decorator.AssetTag", "AssetTag", 1739 dbus::utility::DbusVariantType(assetTag)); 1740 }, 1741 "xyz.openbmc_project.ObjectMapper", 1742 "/xyz/openbmc_project/object_mapper", 1743 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 1744 "/xyz/openbmc_project/inventory", int32_t(0), 1745 std::array<const char*, 1>{ 1746 "xyz.openbmc_project.Inventory.Item.System"}); 1747 } 1748 1749 /** 1750 * @brief Sets automaticRetry (Auto Reboot) 1751 * 1752 * @param[in] aResp Shared pointer for generating response message. 1753 * @param[in] automaticRetryConfig "AutomaticRetryConfig" from request. 1754 * 1755 * @return None. 1756 */ 1757 inline void setAutomaticRetry(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1758 const std::string& automaticRetryConfig) 1759 { 1760 BMCWEB_LOG_DEBUG << "Set Automatic Retry."; 1761 1762 // OpenBMC only supports "Disabled" and "RetryAttempts". 1763 bool autoRebootEnabled = false; 1764 1765 if (automaticRetryConfig == "Disabled") 1766 { 1767 autoRebootEnabled = false; 1768 } 1769 else if (automaticRetryConfig == "RetryAttempts") 1770 { 1771 autoRebootEnabled = true; 1772 } 1773 else 1774 { 1775 BMCWEB_LOG_DEBUG << "Invalid property value for AutomaticRetryConfig: " 1776 << automaticRetryConfig; 1777 messages::propertyValueNotInList(aResp->res, automaticRetryConfig, 1778 "AutomaticRetryConfig"); 1779 return; 1780 } 1781 1782 crow::connections::systemBus->async_method_call( 1783 [aResp](const boost::system::error_code ec) { 1784 if (ec) 1785 { 1786 messages::internalError(aResp->res); 1787 return; 1788 } 1789 }, 1790 "xyz.openbmc_project.Settings", 1791 "/xyz/openbmc_project/control/host0/auto_reboot", 1792 "org.freedesktop.DBus.Properties", "Set", 1793 "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot", 1794 dbus::utility::DbusVariantType(autoRebootEnabled)); 1795 } 1796 1797 /** 1798 * @brief Sets power restore policy properties. 1799 * 1800 * @param[in] aResp Shared pointer for generating response message. 1801 * @param[in] policy power restore policy properties from request. 1802 * 1803 * @return None. 1804 */ 1805 inline void 1806 setPowerRestorePolicy(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1807 const std::string& policy) 1808 { 1809 BMCWEB_LOG_DEBUG << "Set power restore policy."; 1810 1811 const boost::container::flat_map<std::string, std::string> policyMaps = { 1812 {"AlwaysOn", 1813 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn"}, 1814 {"AlwaysOff", 1815 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOff"}, 1816 {"LastState", 1817 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore"}}; 1818 1819 std::string powerRestorPolicy; 1820 1821 auto policyMapsIt = policyMaps.find(policy); 1822 if (policyMapsIt == policyMaps.end()) 1823 { 1824 messages::propertyValueNotInList(aResp->res, policy, 1825 "PowerRestorePolicy"); 1826 return; 1827 } 1828 1829 powerRestorPolicy = policyMapsIt->second; 1830 1831 crow::connections::systemBus->async_method_call( 1832 [aResp](const boost::system::error_code ec) { 1833 if (ec) 1834 { 1835 messages::internalError(aResp->res); 1836 return; 1837 } 1838 }, 1839 "xyz.openbmc_project.Settings", 1840 "/xyz/openbmc_project/control/host0/power_restore_policy", 1841 "org.freedesktop.DBus.Properties", "Set", 1842 "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", 1843 dbus::utility::DbusVariantType(powerRestorPolicy)); 1844 } 1845 1846 #ifdef BMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE 1847 /** 1848 * @brief Retrieves provisioning status 1849 * 1850 * @param[in] aResp Shared pointer for completing asynchronous calls. 1851 * 1852 * @return None. 1853 */ 1854 inline void getProvisioningStatus(std::shared_ptr<bmcweb::AsyncResp> aResp) 1855 { 1856 BMCWEB_LOG_DEBUG << "Get OEM information."; 1857 sdbusplus::asio::getAllProperties( 1858 *crow::connections::systemBus, "xyz.openbmc_project.PFR.Manager", 1859 "/xyz/openbmc_project/pfr", "xyz.openbmc_project.PFR.Attributes", 1860 [aResp](const boost::system::error_code ec, 1861 const dbus::utility::DBusPropertiesMap& propertiesList) { 1862 nlohmann::json& oemPFR = 1863 aResp->res.jsonValue["Oem"]["OpenBmc"]["FirmwareProvisioning"]; 1864 aResp->res.jsonValue["Oem"]["OpenBmc"]["@odata.type"] = 1865 "#OemComputerSystem.OpenBmc"; 1866 oemPFR["@odata.type"] = "#OemComputerSystem.FirmwareProvisioning"; 1867 1868 if (ec) 1869 { 1870 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1871 // not an error, don't have to have the interface 1872 oemPFR["ProvisioningStatus"] = "NotProvisioned"; 1873 return; 1874 } 1875 1876 const bool* provState = nullptr; 1877 const bool* lockState = nullptr; 1878 1879 const bool success = sdbusplus::unpackPropertiesNoThrow( 1880 dbus_utils::UnpackErrorPrinter(), propertiesList, "UfmProvisioned", 1881 provState, "UfmLocked", lockState); 1882 1883 if (!success) 1884 { 1885 messages::internalError(aResp->res); 1886 return; 1887 } 1888 1889 if ((provState == nullptr) || (lockState == nullptr)) 1890 { 1891 BMCWEB_LOG_DEBUG << "Unable to get PFR attributes."; 1892 messages::internalError(aResp->res); 1893 return; 1894 } 1895 1896 if (*provState == true) 1897 { 1898 if (*lockState == true) 1899 { 1900 oemPFR["ProvisioningStatus"] = "ProvisionedAndLocked"; 1901 } 1902 else 1903 { 1904 oemPFR["ProvisioningStatus"] = "ProvisionedButNotLocked"; 1905 } 1906 } 1907 else 1908 { 1909 oemPFR["ProvisioningStatus"] = "NotProvisioned"; 1910 } 1911 }); 1912 } 1913 #endif 1914 1915 /** 1916 * @brief Translate the PowerMode to a response message. 1917 * 1918 * @param[in] aResp Shared pointer for generating response message. 1919 * @param[in] modeValue PowerMode value to be translated 1920 * 1921 * @return None. 1922 */ 1923 inline void translatePowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1924 const std::string& modeValue) 1925 { 1926 if (modeValue == "xyz.openbmc_project.Control.Power.Mode.PowerMode.Static") 1927 { 1928 aResp->res.jsonValue["PowerMode"] = "Static"; 1929 } 1930 else if ( 1931 modeValue == 1932 "xyz.openbmc_project.Control.Power.Mode.PowerMode.MaximumPerformance") 1933 { 1934 aResp->res.jsonValue["PowerMode"] = "MaximumPerformance"; 1935 } 1936 else if (modeValue == 1937 "xyz.openbmc_project.Control.Power.Mode.PowerMode.PowerSaving") 1938 { 1939 aResp->res.jsonValue["PowerMode"] = "PowerSaving"; 1940 } 1941 else if (modeValue == 1942 "xyz.openbmc_project.Control.Power.Mode.PowerMode.OEM") 1943 { 1944 aResp->res.jsonValue["PowerMode"] = "OEM"; 1945 } 1946 else 1947 { 1948 // Any other values would be invalid 1949 BMCWEB_LOG_DEBUG << "PowerMode value was not valid: " << modeValue; 1950 messages::internalError(aResp->res); 1951 } 1952 } 1953 1954 /** 1955 * @brief Retrieves system power mode 1956 * 1957 * @param[in] aResp Shared pointer for generating response message. 1958 * 1959 * @return None. 1960 */ 1961 inline void getPowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1962 { 1963 BMCWEB_LOG_DEBUG << "Get power mode."; 1964 1965 // Get Power Mode object path: 1966 crow::connections::systemBus->async_method_call( 1967 [aResp](const boost::system::error_code ec, 1968 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1969 if (ec) 1970 { 1971 BMCWEB_LOG_DEBUG << "DBUS response error on Power.Mode GetSubTree " 1972 << ec; 1973 // This is an optional D-Bus object so just return if 1974 // error occurs 1975 return; 1976 } 1977 if (subtree.empty()) 1978 { 1979 // As noted above, this is an optional interface so just return 1980 // if there is no instance found 1981 return; 1982 } 1983 if (subtree.size() > 1) 1984 { 1985 // More then one PowerMode object is not supported and is an 1986 // error 1987 BMCWEB_LOG_DEBUG 1988 << "Found more than 1 system D-Bus Power.Mode objects: " 1989 << subtree.size(); 1990 messages::internalError(aResp->res); 1991 return; 1992 } 1993 if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) 1994 { 1995 BMCWEB_LOG_DEBUG << "Power.Mode mapper error!"; 1996 messages::internalError(aResp->res); 1997 return; 1998 } 1999 const std::string& path = subtree[0].first; 2000 const std::string& service = subtree[0].second.begin()->first; 2001 if (service.empty()) 2002 { 2003 BMCWEB_LOG_DEBUG << "Power.Mode service mapper error!"; 2004 messages::internalError(aResp->res); 2005 return; 2006 } 2007 // Valid Power Mode object found, now read the current value 2008 sdbusplus::asio::getProperty<std::string>( 2009 *crow::connections::systemBus, service, path, 2010 "xyz.openbmc_project.Control.Power.Mode", "PowerMode", 2011 [aResp](const boost::system::error_code ec2, 2012 const std::string& pmode) { 2013 if (ec2) 2014 { 2015 BMCWEB_LOG_DEBUG << "DBUS response error on PowerMode Get: " 2016 << ec2; 2017 messages::internalError(aResp->res); 2018 return; 2019 } 2020 2021 aResp->res.jsonValue["PowerMode@Redfish.AllowableValues"] = { 2022 "Static", "MaximumPerformance", "PowerSaving"}; 2023 2024 BMCWEB_LOG_DEBUG << "Current power mode: " << pmode; 2025 translatePowerMode(aResp, pmode); 2026 }); 2027 }, 2028 "xyz.openbmc_project.ObjectMapper", 2029 "/xyz/openbmc_project/object_mapper", 2030 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0), 2031 std::array<const char*, 1>{"xyz.openbmc_project.Control.Power.Mode"}); 2032 } 2033 2034 /** 2035 * @brief Validate the specified mode is valid and return the PowerMode 2036 * name associated with that string 2037 * 2038 * @param[in] aResp Shared pointer for generating response message. 2039 * @param[in] modeString String representing the desired PowerMode 2040 * 2041 * @return PowerMode value or empty string if mode is not valid 2042 */ 2043 inline std::string 2044 validatePowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2045 const std::string& modeString) 2046 { 2047 std::string mode; 2048 2049 if (modeString == "Static") 2050 { 2051 mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.Static"; 2052 } 2053 else if (modeString == "MaximumPerformance") 2054 { 2055 mode = 2056 "xyz.openbmc_project.Control.Power.Mode.PowerMode.MaximumPerformance"; 2057 } 2058 else if (modeString == "PowerSaving") 2059 { 2060 mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.PowerSaving"; 2061 } 2062 else 2063 { 2064 messages::propertyValueNotInList(aResp->res, modeString, "PowerMode"); 2065 } 2066 return mode; 2067 } 2068 2069 /** 2070 * @brief Sets system power mode. 2071 * 2072 * @param[in] aResp Shared pointer for generating response message. 2073 * @param[in] pmode System power mode from request. 2074 * 2075 * @return None. 2076 */ 2077 inline void setPowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2078 const std::string& pmode) 2079 { 2080 BMCWEB_LOG_DEBUG << "Set power mode."; 2081 2082 std::string powerMode = validatePowerMode(aResp, pmode); 2083 if (powerMode.empty()) 2084 { 2085 return; 2086 } 2087 2088 // Get Power Mode object path: 2089 crow::connections::systemBus->async_method_call( 2090 [aResp, 2091 powerMode](const boost::system::error_code ec, 2092 const dbus::utility::MapperGetSubTreeResponse& subtree) { 2093 if (ec) 2094 { 2095 BMCWEB_LOG_DEBUG << "DBUS response error on Power.Mode GetSubTree " 2096 << ec; 2097 // This is an optional D-Bus object, but user attempted to patch 2098 messages::internalError(aResp->res); 2099 return; 2100 } 2101 if (subtree.empty()) 2102 { 2103 // This is an optional D-Bus object, but user attempted to patch 2104 messages::resourceNotFound(aResp->res, "ComputerSystem", 2105 "PowerMode"); 2106 return; 2107 } 2108 if (subtree.size() > 1) 2109 { 2110 // More then one PowerMode object is not supported and is an 2111 // error 2112 BMCWEB_LOG_DEBUG 2113 << "Found more than 1 system D-Bus Power.Mode objects: " 2114 << subtree.size(); 2115 messages::internalError(aResp->res); 2116 return; 2117 } 2118 if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) 2119 { 2120 BMCWEB_LOG_DEBUG << "Power.Mode mapper error!"; 2121 messages::internalError(aResp->res); 2122 return; 2123 } 2124 const std::string& path = subtree[0].first; 2125 const std::string& service = subtree[0].second.begin()->first; 2126 if (service.empty()) 2127 { 2128 BMCWEB_LOG_DEBUG << "Power.Mode service mapper error!"; 2129 messages::internalError(aResp->res); 2130 return; 2131 } 2132 2133 BMCWEB_LOG_DEBUG << "Setting power mode(" << powerMode << ") -> " 2134 << path; 2135 2136 // Set the Power Mode property 2137 crow::connections::systemBus->async_method_call( 2138 [aResp](const boost::system::error_code ec2) { 2139 if (ec2) 2140 { 2141 messages::internalError(aResp->res); 2142 return; 2143 } 2144 }, 2145 service, path, "org.freedesktop.DBus.Properties", "Set", 2146 "xyz.openbmc_project.Control.Power.Mode", "PowerMode", 2147 dbus::utility::DbusVariantType(powerMode)); 2148 }, 2149 "xyz.openbmc_project.ObjectMapper", 2150 "/xyz/openbmc_project/object_mapper", 2151 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0), 2152 std::array<const char*, 1>{"xyz.openbmc_project.Control.Power.Mode"}); 2153 } 2154 2155 /** 2156 * @brief Translates watchdog timeout action DBUS property value to redfish. 2157 * 2158 * @param[in] dbusAction The watchdog timeout action in D-BUS. 2159 * 2160 * @return Returns as a string, the timeout action in Redfish terms. If 2161 * translation cannot be done, returns an empty string. 2162 */ 2163 inline std::string dbusToRfWatchdogAction(const std::string& dbusAction) 2164 { 2165 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.None") 2166 { 2167 return "None"; 2168 } 2169 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.HardReset") 2170 { 2171 return "ResetSystem"; 2172 } 2173 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.PowerOff") 2174 { 2175 return "PowerDown"; 2176 } 2177 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.PowerCycle") 2178 { 2179 return "PowerCycle"; 2180 } 2181 2182 return ""; 2183 } 2184 2185 /** 2186 *@brief Translates timeout action from Redfish to DBUS property value. 2187 * 2188 *@param[in] rfAction The timeout action in Redfish. 2189 * 2190 *@return Returns as a string, the time_out action as expected by DBUS. 2191 *If translation cannot be done, returns an empty string. 2192 */ 2193 2194 inline std::string rfToDbusWDTTimeOutAct(const std::string& rfAction) 2195 { 2196 if (rfAction == "None") 2197 { 2198 return "xyz.openbmc_project.State.Watchdog.Action.None"; 2199 } 2200 if (rfAction == "PowerCycle") 2201 { 2202 return "xyz.openbmc_project.State.Watchdog.Action.PowerCycle"; 2203 } 2204 if (rfAction == "PowerDown") 2205 { 2206 return "xyz.openbmc_project.State.Watchdog.Action.PowerOff"; 2207 } 2208 if (rfAction == "ResetSystem") 2209 { 2210 return "xyz.openbmc_project.State.Watchdog.Action.HardReset"; 2211 } 2212 2213 return ""; 2214 } 2215 2216 /** 2217 * @brief Retrieves host watchdog timer properties over DBUS 2218 * 2219 * @param[in] aResp Shared pointer for completing asynchronous calls. 2220 * 2221 * @return None. 2222 */ 2223 inline void 2224 getHostWatchdogTimer(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 2225 { 2226 BMCWEB_LOG_DEBUG << "Get host watchodg"; 2227 sdbusplus::asio::getAllProperties( 2228 *crow::connections::systemBus, "xyz.openbmc_project.Watchdog", 2229 "/xyz/openbmc_project/watchdog/host0", 2230 "xyz.openbmc_project.State.Watchdog", 2231 [aResp](const boost::system::error_code ec, 2232 const dbus::utility::DBusPropertiesMap& properties) { 2233 if (ec) 2234 { 2235 // watchdog service is stopped 2236 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 2237 return; 2238 } 2239 2240 BMCWEB_LOG_DEBUG << "Got " << properties.size() << " wdt prop."; 2241 2242 nlohmann::json& hostWatchdogTimer = 2243 aResp->res.jsonValue["HostWatchdogTimer"]; 2244 2245 // watchdog service is running/enabled 2246 hostWatchdogTimer["Status"]["State"] = "Enabled"; 2247 2248 const bool* enabled = nullptr; 2249 const std::string* expireAction = nullptr; 2250 2251 const bool success = sdbusplus::unpackPropertiesNoThrow( 2252 dbus_utils::UnpackErrorPrinter(), properties, "Enabled", enabled, 2253 "ExpireAction", expireAction); 2254 2255 if (!success) 2256 { 2257 messages::internalError(aResp->res); 2258 return; 2259 } 2260 2261 if (enabled != nullptr) 2262 { 2263 hostWatchdogTimer["FunctionEnabled"] = *enabled; 2264 } 2265 2266 if (expireAction != nullptr) 2267 { 2268 std::string action = dbusToRfWatchdogAction(*expireAction); 2269 if (action.empty()) 2270 { 2271 messages::internalError(aResp->res); 2272 return; 2273 } 2274 hostWatchdogTimer["TimeoutAction"] = action; 2275 } 2276 }); 2277 } 2278 2279 /** 2280 * @brief Sets Host WatchDog Timer properties. 2281 * 2282 * @param[in] aResp Shared pointer for generating response message. 2283 * @param[in] wdtEnable The WDTimer Enable value (true/false) from incoming 2284 * RF request. 2285 * @param[in] wdtTimeOutAction The WDT Timeout action, from incoming RF request. 2286 * 2287 * @return None. 2288 */ 2289 inline void setWDTProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2290 const std::optional<bool> wdtEnable, 2291 const std::optional<std::string>& wdtTimeOutAction) 2292 { 2293 BMCWEB_LOG_DEBUG << "Set host watchdog"; 2294 2295 if (wdtTimeOutAction) 2296 { 2297 std::string wdtTimeOutActStr = rfToDbusWDTTimeOutAct(*wdtTimeOutAction); 2298 // check if TimeOut Action is Valid 2299 if (wdtTimeOutActStr.empty()) 2300 { 2301 BMCWEB_LOG_DEBUG << "Unsupported value for TimeoutAction: " 2302 << *wdtTimeOutAction; 2303 messages::propertyValueNotInList(aResp->res, *wdtTimeOutAction, 2304 "TimeoutAction"); 2305 return; 2306 } 2307 2308 crow::connections::systemBus->async_method_call( 2309 [aResp](const boost::system::error_code ec) { 2310 if (ec) 2311 { 2312 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 2313 messages::internalError(aResp->res); 2314 return; 2315 } 2316 }, 2317 "xyz.openbmc_project.Watchdog", 2318 "/xyz/openbmc_project/watchdog/host0", 2319 "org.freedesktop.DBus.Properties", "Set", 2320 "xyz.openbmc_project.State.Watchdog", "ExpireAction", 2321 dbus::utility::DbusVariantType(wdtTimeOutActStr)); 2322 } 2323 2324 if (wdtEnable) 2325 { 2326 crow::connections::systemBus->async_method_call( 2327 [aResp](const boost::system::error_code ec) { 2328 if (ec) 2329 { 2330 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 2331 messages::internalError(aResp->res); 2332 return; 2333 } 2334 }, 2335 "xyz.openbmc_project.Watchdog", 2336 "/xyz/openbmc_project/watchdog/host0", 2337 "org.freedesktop.DBus.Properties", "Set", 2338 "xyz.openbmc_project.State.Watchdog", "Enabled", 2339 dbus::utility::DbusVariantType(*wdtEnable)); 2340 } 2341 } 2342 2343 /** 2344 * @brief Parse the Idle Power Saver properties into json 2345 * 2346 * @param[in] aResp Shared pointer for completing asynchronous calls. 2347 * @param[in] properties IPS property data from DBus. 2348 * 2349 * @return true if successful 2350 */ 2351 inline bool 2352 parseIpsProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2353 const dbus::utility::DBusPropertiesMap& properties) 2354 { 2355 const bool* enabled = nullptr; 2356 const uint8_t* enterUtilizationPercent = nullptr; 2357 const uint64_t* enterDwellTime = nullptr; 2358 const uint8_t* exitUtilizationPercent = nullptr; 2359 const uint64_t* exitDwellTime = nullptr; 2360 2361 const bool success = sdbusplus::unpackPropertiesNoThrow( 2362 dbus_utils::UnpackErrorPrinter(), properties, "Enabled", enabled, 2363 "EnterUtilizationPercent", enterUtilizationPercent, 2364 "ExitUtilizationPercent", exitUtilizationPercent, "ExitDwellTime", 2365 exitDwellTime); 2366 2367 if (!success) 2368 { 2369 return false; 2370 } 2371 2372 if (enabled != nullptr) 2373 { 2374 aResp->res.jsonValue["IdlePowerSaver"]["Enabled"] = *enabled; 2375 } 2376 2377 if (enterUtilizationPercent != nullptr) 2378 { 2379 aResp->res.jsonValue["IdlePowerSaver"]["EnterUtilizationPercent"] = 2380 *enterUtilizationPercent; 2381 } 2382 2383 if (enterDwellTime != nullptr) 2384 { 2385 const std::chrono::duration<uint64_t, std::milli> ms(*enterDwellTime); 2386 aResp->res.jsonValue["IdlePowerSaver"]["EnterDwellTimeSeconds"] = 2387 std::chrono::duration_cast<std::chrono::duration<uint64_t>>(ms) 2388 .count(); 2389 } 2390 2391 if (exitUtilizationPercent != nullptr) 2392 { 2393 aResp->res.jsonValue["IdlePowerSaver"]["ExitUtilizationPercent"] = 2394 *exitUtilizationPercent; 2395 } 2396 2397 if (exitDwellTime != nullptr) 2398 { 2399 const std::chrono::duration<uint64_t, std::milli> ms(*exitDwellTime); 2400 aResp->res.jsonValue["IdlePowerSaver"]["ExitDwellTimeSeconds"] = 2401 std::chrono::duration_cast<std::chrono::duration<uint64_t>>(ms) 2402 .count(); 2403 } 2404 2405 return true; 2406 } 2407 2408 /** 2409 * @brief Retrieves host watchdog timer properties over DBUS 2410 * 2411 * @param[in] aResp Shared pointer for completing asynchronous calls. 2412 * 2413 * @return None. 2414 */ 2415 inline void getIdlePowerSaver(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 2416 { 2417 BMCWEB_LOG_DEBUG << "Get idle power saver parameters"; 2418 2419 // Get IdlePowerSaver object path: 2420 crow::connections::systemBus->async_method_call( 2421 [aResp](const boost::system::error_code ec, 2422 const dbus::utility::MapperGetSubTreeResponse& subtree) { 2423 if (ec) 2424 { 2425 BMCWEB_LOG_DEBUG 2426 << "DBUS response error on Power.IdlePowerSaver GetSubTree " 2427 << ec; 2428 messages::internalError(aResp->res); 2429 return; 2430 } 2431 if (subtree.empty()) 2432 { 2433 // This is an optional interface so just return 2434 // if there is no instance found 2435 BMCWEB_LOG_DEBUG << "No instances found"; 2436 return; 2437 } 2438 if (subtree.size() > 1) 2439 { 2440 // More then one PowerIdlePowerSaver object is not supported and 2441 // is an error 2442 BMCWEB_LOG_DEBUG << "Found more than 1 system D-Bus " 2443 "Power.IdlePowerSaver objects: " 2444 << subtree.size(); 2445 messages::internalError(aResp->res); 2446 return; 2447 } 2448 if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) 2449 { 2450 BMCWEB_LOG_DEBUG << "Power.IdlePowerSaver mapper error!"; 2451 messages::internalError(aResp->res); 2452 return; 2453 } 2454 const std::string& path = subtree[0].first; 2455 const std::string& service = subtree[0].second.begin()->first; 2456 if (service.empty()) 2457 { 2458 BMCWEB_LOG_DEBUG << "Power.IdlePowerSaver service mapper error!"; 2459 messages::internalError(aResp->res); 2460 return; 2461 } 2462 2463 // Valid IdlePowerSaver object found, now read the current values 2464 sdbusplus::asio::getAllProperties( 2465 *crow::connections::systemBus, service, path, 2466 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2467 [aResp](const boost::system::error_code ec2, 2468 const dbus::utility::DBusPropertiesMap& properties) { 2469 if (ec2) 2470 { 2471 BMCWEB_LOG_ERROR 2472 << "DBUS response error on IdlePowerSaver GetAll: " << ec2; 2473 messages::internalError(aResp->res); 2474 return; 2475 } 2476 2477 if (!parseIpsProperties(aResp, properties)) 2478 { 2479 messages::internalError(aResp->res); 2480 return; 2481 } 2482 }); 2483 }, 2484 "xyz.openbmc_project.ObjectMapper", 2485 "/xyz/openbmc_project/object_mapper", 2486 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0), 2487 std::array<const char*, 1>{ 2488 "xyz.openbmc_project.Control.Power.IdlePowerSaver"}); 2489 2490 BMCWEB_LOG_DEBUG << "EXIT: Get idle power saver parameters"; 2491 } 2492 2493 /** 2494 * @brief Sets Idle Power Saver properties. 2495 * 2496 * @param[in] aResp Shared pointer for generating response message. 2497 * @param[in] ipsEnable The IPS Enable value (true/false) from incoming 2498 * RF request. 2499 * @param[in] ipsEnterUtil The utilization limit to enter idle state. 2500 * @param[in] ipsEnterTime The time the utilization must be below ipsEnterUtil 2501 * before entering idle state. 2502 * @param[in] ipsExitUtil The utilization limit when exiting idle state. 2503 * @param[in] ipsExitTime The time the utilization must be above ipsExutUtil 2504 * before exiting idle state 2505 * 2506 * @return None. 2507 */ 2508 inline void setIdlePowerSaver(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2509 const std::optional<bool> ipsEnable, 2510 const std::optional<uint8_t> ipsEnterUtil, 2511 const std::optional<uint64_t> ipsEnterTime, 2512 const std::optional<uint8_t> ipsExitUtil, 2513 const std::optional<uint64_t> ipsExitTime) 2514 { 2515 BMCWEB_LOG_DEBUG << "Set idle power saver properties"; 2516 2517 // Get IdlePowerSaver object path: 2518 crow::connections::systemBus->async_method_call( 2519 [aResp, ipsEnable, ipsEnterUtil, ipsEnterTime, ipsExitUtil, 2520 ipsExitTime](const boost::system::error_code ec, 2521 const dbus::utility::MapperGetSubTreeResponse& subtree) { 2522 if (ec) 2523 { 2524 BMCWEB_LOG_DEBUG 2525 << "DBUS response error on Power.IdlePowerSaver GetSubTree " 2526 << ec; 2527 messages::internalError(aResp->res); 2528 return; 2529 } 2530 if (subtree.empty()) 2531 { 2532 // This is an optional D-Bus object, but user attempted to patch 2533 messages::resourceNotFound(aResp->res, "ComputerSystem", 2534 "IdlePowerSaver"); 2535 return; 2536 } 2537 if (subtree.size() > 1) 2538 { 2539 // More then one PowerIdlePowerSaver object is not supported and 2540 // is an error 2541 BMCWEB_LOG_DEBUG 2542 << "Found more than 1 system D-Bus Power.IdlePowerSaver objects: " 2543 << subtree.size(); 2544 messages::internalError(aResp->res); 2545 return; 2546 } 2547 if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) 2548 { 2549 BMCWEB_LOG_DEBUG << "Power.IdlePowerSaver mapper error!"; 2550 messages::internalError(aResp->res); 2551 return; 2552 } 2553 const std::string& path = subtree[0].first; 2554 const std::string& service = subtree[0].second.begin()->first; 2555 if (service.empty()) 2556 { 2557 BMCWEB_LOG_DEBUG << "Power.IdlePowerSaver service mapper error!"; 2558 messages::internalError(aResp->res); 2559 return; 2560 } 2561 2562 // Valid Power IdlePowerSaver object found, now set any values that 2563 // need to be updated 2564 2565 if (ipsEnable) 2566 { 2567 crow::connections::systemBus->async_method_call( 2568 [aResp](const boost::system::error_code ec2) { 2569 if (ec2) 2570 { 2571 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2572 messages::internalError(aResp->res); 2573 return; 2574 } 2575 }, 2576 service, path, "org.freedesktop.DBus.Properties", "Set", 2577 "xyz.openbmc_project.Control.Power.IdlePowerSaver", "Enabled", 2578 dbus::utility::DbusVariantType(*ipsEnable)); 2579 } 2580 if (ipsEnterUtil) 2581 { 2582 crow::connections::systemBus->async_method_call( 2583 [aResp](const boost::system::error_code ec2) { 2584 if (ec2) 2585 { 2586 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2587 messages::internalError(aResp->res); 2588 return; 2589 } 2590 }, 2591 service, path, "org.freedesktop.DBus.Properties", "Set", 2592 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2593 "EnterUtilizationPercent", 2594 dbus::utility::DbusVariantType(*ipsEnterUtil)); 2595 } 2596 if (ipsEnterTime) 2597 { 2598 // Convert from seconds into milliseconds for DBus 2599 const uint64_t timeMilliseconds = *ipsEnterTime * 1000; 2600 crow::connections::systemBus->async_method_call( 2601 [aResp](const boost::system::error_code ec2) { 2602 if (ec2) 2603 { 2604 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2605 messages::internalError(aResp->res); 2606 return; 2607 } 2608 }, 2609 service, path, "org.freedesktop.DBus.Properties", "Set", 2610 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2611 "EnterDwellTime", 2612 dbus::utility::DbusVariantType(timeMilliseconds)); 2613 } 2614 if (ipsExitUtil) 2615 { 2616 crow::connections::systemBus->async_method_call( 2617 [aResp](const boost::system::error_code ec2) { 2618 if (ec2) 2619 { 2620 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2621 messages::internalError(aResp->res); 2622 return; 2623 } 2624 }, 2625 service, path, "org.freedesktop.DBus.Properties", "Set", 2626 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2627 "ExitUtilizationPercent", 2628 dbus::utility::DbusVariantType(*ipsExitUtil)); 2629 } 2630 if (ipsExitTime) 2631 { 2632 // Convert from seconds into milliseconds for DBus 2633 const uint64_t timeMilliseconds = *ipsExitTime * 1000; 2634 crow::connections::systemBus->async_method_call( 2635 [aResp](const boost::system::error_code ec2) { 2636 if (ec2) 2637 { 2638 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2639 messages::internalError(aResp->res); 2640 return; 2641 } 2642 }, 2643 service, path, "org.freedesktop.DBus.Properties", "Set", 2644 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2645 "ExitDwellTime", 2646 dbus::utility::DbusVariantType(timeMilliseconds)); 2647 } 2648 }, 2649 "xyz.openbmc_project.ObjectMapper", 2650 "/xyz/openbmc_project/object_mapper", 2651 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0), 2652 std::array<const char*, 1>{ 2653 "xyz.openbmc_project.Control.Power.IdlePowerSaver"}); 2654 2655 BMCWEB_LOG_DEBUG << "EXIT: Set idle power saver parameters"; 2656 } 2657 2658 inline void handleComputerSystemHead( 2659 crow::App& app, const crow::Request& req, 2660 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 2661 { 2662 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2663 { 2664 return; 2665 } 2666 asyncResp->res.addHeader( 2667 boost::beast::http::field::link, 2668 "</redfish/v1/JsonSchemas/ComputerSystemCollection/ComputerSystemCollection.json>; rel=describedby"); 2669 } 2670 2671 /** 2672 * SystemsCollection derived class for delivering ComputerSystems Collection 2673 * Schema 2674 */ 2675 inline void requestRoutesSystemsCollection(App& app) 2676 { 2677 BMCWEB_ROUTE(app, "/redfish/v1/Systems/") 2678 .privileges(redfish::privileges::headComputerSystemCollection) 2679 .methods(boost::beast::http::verb::head)( 2680 std::bind_front(handleComputerSystemHead, std::ref(app))); 2681 2682 BMCWEB_ROUTE(app, "/redfish/v1/Systems/") 2683 .privileges(redfish::privileges::getComputerSystemCollection) 2684 .methods(boost::beast::http::verb::get)( 2685 [&app](const crow::Request& req, 2686 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2687 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2688 { 2689 return; 2690 } 2691 2692 asyncResp->res.addHeader( 2693 boost::beast::http::field::link, 2694 "</redfish/v1/JsonSchemas/ComputerSystemCollection.json>; rel=describedby"); 2695 asyncResp->res.jsonValue["@odata.type"] = 2696 "#ComputerSystemCollection.ComputerSystemCollection"; 2697 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Systems"; 2698 asyncResp->res.jsonValue["Name"] = "Computer System Collection"; 2699 2700 sdbusplus::asio::getProperty<std::string>( 2701 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 2702 "/xyz/openbmc_project/network/hypervisor", 2703 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 2704 [asyncResp](const boost::system::error_code ec2, 2705 const std::string& /*hostName*/) { 2706 nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"]; 2707 ifaceArray = nlohmann::json::array(); 2708 auto& count = asyncResp->res.jsonValue["Members@odata.count"]; 2709 2710 nlohmann::json::object_t system; 2711 system["@odata.id"] = "/redfish/v1/Systems/system"; 2712 ifaceArray.push_back(std::move(system)); 2713 count = ifaceArray.size(); 2714 if (!ec2) 2715 { 2716 BMCWEB_LOG_DEBUG << "Hypervisor is available"; 2717 nlohmann::json::object_t hypervisor; 2718 hypervisor["@odata.id"] = "/redfish/v1/Systems/hypervisor"; 2719 ifaceArray.push_back(std::move(hypervisor)); 2720 count = ifaceArray.size(); 2721 } 2722 }); 2723 }); 2724 } 2725 2726 /** 2727 * Function transceives data with dbus directly. 2728 */ 2729 inline void doNMI(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 2730 { 2731 constexpr char const* serviceName = "xyz.openbmc_project.Control.Host.NMI"; 2732 constexpr char const* objectPath = "/xyz/openbmc_project/control/host0/nmi"; 2733 constexpr char const* interfaceName = 2734 "xyz.openbmc_project.Control.Host.NMI"; 2735 constexpr char const* method = "NMI"; 2736 2737 crow::connections::systemBus->async_method_call( 2738 [asyncResp](const boost::system::error_code ec) { 2739 if (ec) 2740 { 2741 BMCWEB_LOG_ERROR << " Bad D-Bus request error: " << ec; 2742 messages::internalError(asyncResp->res); 2743 return; 2744 } 2745 messages::success(asyncResp->res); 2746 }, 2747 serviceName, objectPath, interfaceName, method); 2748 } 2749 2750 /** 2751 * SystemActionsReset class supports handle POST method for Reset action. 2752 * The class retrieves and sends data directly to D-Bus. 2753 */ 2754 inline void requestRoutesSystemActionsReset(App& app) 2755 { 2756 /** 2757 * Function handles POST method request. 2758 * Analyzes POST body message before sends Reset request data to D-Bus. 2759 */ 2760 BMCWEB_ROUTE(app, 2761 "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset/") 2762 .privileges(redfish::privileges::postComputerSystem) 2763 .methods(boost::beast::http::verb::post)( 2764 [&app](const crow::Request& req, 2765 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2766 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2767 { 2768 return; 2769 } 2770 std::string resetType; 2771 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", 2772 resetType)) 2773 { 2774 return; 2775 } 2776 2777 // Get the command and host vs. chassis 2778 std::string command; 2779 bool hostCommand = true; 2780 if ((resetType == "On") || (resetType == "ForceOn")) 2781 { 2782 command = "xyz.openbmc_project.State.Host.Transition.On"; 2783 hostCommand = true; 2784 } 2785 else if (resetType == "ForceOff") 2786 { 2787 command = "xyz.openbmc_project.State.Chassis.Transition.Off"; 2788 hostCommand = false; 2789 } 2790 else if (resetType == "ForceRestart") 2791 { 2792 command = 2793 "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot"; 2794 hostCommand = true; 2795 } 2796 else if (resetType == "GracefulShutdown") 2797 { 2798 command = "xyz.openbmc_project.State.Host.Transition.Off"; 2799 hostCommand = true; 2800 } 2801 else if (resetType == "GracefulRestart") 2802 { 2803 command = 2804 "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot"; 2805 hostCommand = true; 2806 } 2807 else if (resetType == "PowerCycle") 2808 { 2809 command = "xyz.openbmc_project.State.Host.Transition.Reboot"; 2810 hostCommand = true; 2811 } 2812 else if (resetType == "Nmi") 2813 { 2814 doNMI(asyncResp); 2815 return; 2816 } 2817 else 2818 { 2819 messages::actionParameterUnknown(asyncResp->res, "Reset", 2820 resetType); 2821 return; 2822 } 2823 2824 if (hostCommand) 2825 { 2826 crow::connections::systemBus->async_method_call( 2827 [asyncResp, resetType](const boost::system::error_code ec) { 2828 if (ec) 2829 { 2830 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 2831 if (ec.value() == boost::asio::error::invalid_argument) 2832 { 2833 messages::actionParameterNotSupported( 2834 asyncResp->res, resetType, "Reset"); 2835 } 2836 else 2837 { 2838 messages::internalError(asyncResp->res); 2839 } 2840 return; 2841 } 2842 messages::success(asyncResp->res); 2843 }, 2844 "xyz.openbmc_project.State.Host", 2845 "/xyz/openbmc_project/state/host0", 2846 "org.freedesktop.DBus.Properties", "Set", 2847 "xyz.openbmc_project.State.Host", "RequestedHostTransition", 2848 dbus::utility::DbusVariantType{command}); 2849 } 2850 else 2851 { 2852 crow::connections::systemBus->async_method_call( 2853 [asyncResp, resetType](const boost::system::error_code ec) { 2854 if (ec) 2855 { 2856 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 2857 if (ec.value() == boost::asio::error::invalid_argument) 2858 { 2859 messages::actionParameterNotSupported( 2860 asyncResp->res, resetType, "Reset"); 2861 } 2862 else 2863 { 2864 messages::internalError(asyncResp->res); 2865 } 2866 return; 2867 } 2868 messages::success(asyncResp->res); 2869 }, 2870 "xyz.openbmc_project.State.Chassis", 2871 "/xyz/openbmc_project/state/chassis0", 2872 "org.freedesktop.DBus.Properties", "Set", 2873 "xyz.openbmc_project.State.Chassis", "RequestedPowerTransition", 2874 dbus::utility::DbusVariantType{command}); 2875 } 2876 }); 2877 } 2878 2879 inline void handleComputerSystemCollectionHead( 2880 App& app, const crow::Request& req, 2881 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 2882 { 2883 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2884 { 2885 return; 2886 } 2887 2888 asyncResp->res.addHeader( 2889 boost::beast::http::field::link, 2890 "</redfish/v1/JsonSchemas/ComputerSystem/ComputerSystem.json>; rel=describedby"); 2891 } 2892 2893 /** 2894 * Systems derived class for delivering Computer Systems Schema. 2895 */ 2896 inline void requestRoutesSystems(App& app) 2897 { 2898 2899 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/") 2900 .privileges(redfish::privileges::headComputerSystem) 2901 .methods(boost::beast::http::verb::head)( 2902 std::bind_front(handleComputerSystemCollectionHead, std::ref(app))); 2903 /** 2904 * Functions triggers appropriate requests on DBus 2905 */ 2906 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/") 2907 .privileges(redfish::privileges::getComputerSystem) 2908 .methods(boost::beast::http::verb::get)( 2909 [&app](const crow::Request& req, 2910 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2911 const std::string& systemName) { 2912 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2913 { 2914 return; 2915 } 2916 if (systemName != "system") 2917 { 2918 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2919 systemName); 2920 return; 2921 } 2922 asyncResp->res.addHeader( 2923 boost::beast::http::field::link, 2924 "</redfish/v1/JsonSchemas/ComputerSystem/ComputerSystem.json>; rel=describedby"); 2925 asyncResp->res.jsonValue["@odata.type"] = 2926 "#ComputerSystem.v1_16_0.ComputerSystem"; 2927 asyncResp->res.jsonValue["Name"] = "system"; 2928 asyncResp->res.jsonValue["Id"] = "system"; 2929 asyncResp->res.jsonValue["SystemType"] = "Physical"; 2930 asyncResp->res.jsonValue["Description"] = "Computer System"; 2931 asyncResp->res.jsonValue["ProcessorSummary"]["Count"] = 0; 2932 asyncResp->res.jsonValue["ProcessorSummary"]["Status"]["State"] = 2933 "Disabled"; 2934 asyncResp->res.jsonValue["MemorySummary"]["TotalSystemMemoryGiB"] = 2935 uint64_t(0); 2936 asyncResp->res.jsonValue["MemorySummary"]["Status"]["State"] = 2937 "Disabled"; 2938 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system"; 2939 2940 asyncResp->res.jsonValue["Processors"]["@odata.id"] = 2941 "/redfish/v1/Systems/system/Processors"; 2942 asyncResp->res.jsonValue["Memory"]["@odata.id"] = 2943 "/redfish/v1/Systems/system/Memory"; 2944 asyncResp->res.jsonValue["Storage"]["@odata.id"] = 2945 "/redfish/v1/Systems/system/Storage"; 2946 2947 asyncResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"]["target"] = 2948 "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset"; 2949 asyncResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"] 2950 ["@Redfish.ActionInfo"] = 2951 "/redfish/v1/Systems/system/ResetActionInfo"; 2952 2953 asyncResp->res.jsonValue["LogServices"]["@odata.id"] = 2954 "/redfish/v1/Systems/system/LogServices"; 2955 asyncResp->res.jsonValue["Bios"]["@odata.id"] = 2956 "/redfish/v1/Systems/system/Bios"; 2957 2958 nlohmann::json::array_t managedBy; 2959 nlohmann::json& manager = managedBy.emplace_back(); 2960 manager["@odata.id"] = "/redfish/v1/Managers/bmc"; 2961 asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); 2962 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 2963 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 2964 2965 // Fill in SerialConsole info 2966 asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15; 2967 asyncResp->res.jsonValue["SerialConsole"]["IPMI"]["ServiceEnabled"] = 2968 true; 2969 2970 // TODO (Gunnar): Should look for obmc-console-ssh@2200.service 2971 asyncResp->res.jsonValue["SerialConsole"]["SSH"]["ServiceEnabled"] = 2972 true; 2973 asyncResp->res.jsonValue["SerialConsole"]["SSH"]["Port"] = 2200; 2974 asyncResp->res 2975 .jsonValue["SerialConsole"]["SSH"]["HotKeySequenceDisplay"] = 2976 "Press ~. to exit console"; 2977 2978 #ifdef BMCWEB_ENABLE_KVM 2979 // Fill in GraphicalConsole info 2980 asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true; 2981 asyncResp->res.jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 2982 4; 2983 asyncResp->res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = 2984 nlohmann::json::array_t({"KVMIP"}); 2985 2986 #endif // BMCWEB_ENABLE_KVM 2987 constexpr const std::array<const char*, 4> inventoryForSystems = { 2988 "xyz.openbmc_project.Inventory.Item.Dimm", 2989 "xyz.openbmc_project.Inventory.Item.Cpu", 2990 "xyz.openbmc_project.Inventory.Item.Drive", 2991 "xyz.openbmc_project.Inventory.Item.StorageController"}; 2992 2993 auto health = std::make_shared<HealthPopulate>(asyncResp); 2994 crow::connections::systemBus->async_method_call( 2995 [health](const boost::system::error_code ec, 2996 const std::vector<std::string>& resp) { 2997 if (ec) 2998 { 2999 // no inventory 3000 return; 3001 } 3002 3003 health->inventory = resp; 3004 }, 3005 "xyz.openbmc_project.ObjectMapper", 3006 "/xyz/openbmc_project/object_mapper", 3007 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 3008 int32_t(0), inventoryForSystems); 3009 3010 health->populate(); 3011 3012 getMainChassisId(asyncResp, 3013 [](const std::string& chassisId, 3014 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { 3015 nlohmann::json::array_t chassisArray; 3016 nlohmann::json& chassis = chassisArray.emplace_back(); 3017 chassis["@odata.id"] = "/redfish/v1/Chassis/" + chassisId; 3018 aRsp->res.jsonValue["Links"]["Chassis"] = std::move(chassisArray); 3019 }); 3020 3021 getLocationIndicatorActive(asyncResp); 3022 // TODO (Gunnar): Remove IndicatorLED after enough time has passed 3023 getIndicatorLedState(asyncResp); 3024 getComputerSystem(asyncResp, health); 3025 getHostState(asyncResp); 3026 getBootProperties(asyncResp); 3027 getBootProgress(asyncResp); 3028 getBootProgressLastStateTime(asyncResp); 3029 getPCIeDeviceList(asyncResp, "PCIeDevices"); 3030 getHostWatchdogTimer(asyncResp); 3031 getPowerRestorePolicy(asyncResp); 3032 getAutomaticRetry(asyncResp); 3033 getLastResetTime(asyncResp); 3034 #ifdef BMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE 3035 getProvisioningStatus(asyncResp); 3036 #endif 3037 getTrustedModuleRequiredToBoot(asyncResp); 3038 getPowerMode(asyncResp); 3039 getIdlePowerSaver(asyncResp); 3040 }); 3041 3042 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/") 3043 .privileges(redfish::privileges::patchComputerSystem) 3044 .methods(boost::beast::http::verb::patch)( 3045 [&app](const crow::Request& req, 3046 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3047 const std::string& systemName) { 3048 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 3049 { 3050 return; 3051 } 3052 if (systemName != "system") 3053 { 3054 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3055 systemName); 3056 return; 3057 } 3058 3059 asyncResp->res.addHeader( 3060 boost::beast::http::field::link, 3061 "</redfish/v1/JsonSchemas/ComputerSystem/ComputerSystem.json>; rel=describedby"); 3062 3063 std::optional<bool> locationIndicatorActive; 3064 std::optional<std::string> indicatorLed; 3065 std::optional<std::string> assetTag; 3066 std::optional<std::string> powerRestorePolicy; 3067 std::optional<std::string> powerMode; 3068 std::optional<bool> wdtEnable; 3069 std::optional<std::string> wdtTimeOutAction; 3070 std::optional<std::string> bootSource; 3071 std::optional<std::string> bootType; 3072 std::optional<std::string> bootEnable; 3073 std::optional<std::string> bootAutomaticRetry; 3074 std::optional<bool> bootTrustedModuleRequired; 3075 std::optional<bool> ipsEnable; 3076 std::optional<uint8_t> ipsEnterUtil; 3077 std::optional<uint64_t> ipsEnterTime; 3078 std::optional<uint8_t> ipsExitUtil; 3079 std::optional<uint64_t> ipsExitTime; 3080 3081 // clang-format off 3082 if (!json_util::readJsonPatch( 3083 req, asyncResp->res, 3084 "IndicatorLED", indicatorLed, 3085 "LocationIndicatorActive", locationIndicatorActive, 3086 "AssetTag", assetTag, 3087 "PowerRestorePolicy", powerRestorePolicy, 3088 "PowerMode", powerMode, 3089 "HostWatchdogTimer/FunctionEnabled", wdtEnable, 3090 "HostWatchdogTimer/TimeoutAction", wdtTimeOutAction, 3091 "Boot/BootSourceOverrideTarget", bootSource, 3092 "Boot/BootSourceOverrideMode", bootType, 3093 "Boot/BootSourceOverrideEnabled", bootEnable, 3094 "Boot/AutomaticRetryConfig", bootAutomaticRetry, 3095 "Boot/TrustedModuleRequiredToBoot", bootTrustedModuleRequired, 3096 "IdlePowerSaver/Enabled", ipsEnable, 3097 "IdlePowerSaver/EnterUtilizationPercent", ipsEnterUtil, 3098 "IdlePowerSaver/EnterDwellTimeSeconds", ipsEnterTime, 3099 "IdlePowerSaver/ExitUtilizationPercent", ipsExitUtil, 3100 "IdlePowerSaver/ExitDwellTimeSeconds", ipsExitTime)) 3101 { 3102 return; 3103 } 3104 // clang-format on 3105 3106 asyncResp->res.result(boost::beast::http::status::no_content); 3107 3108 if (assetTag) 3109 { 3110 setAssetTag(asyncResp, *assetTag); 3111 } 3112 3113 if (wdtEnable || wdtTimeOutAction) 3114 { 3115 setWDTProperties(asyncResp, wdtEnable, wdtTimeOutAction); 3116 } 3117 3118 if (bootSource || bootType || bootEnable) 3119 { 3120 setBootProperties(asyncResp, bootSource, bootType, bootEnable); 3121 } 3122 if (bootAutomaticRetry) 3123 { 3124 setAutomaticRetry(asyncResp, *bootAutomaticRetry); 3125 } 3126 3127 if (bootTrustedModuleRequired) 3128 { 3129 setTrustedModuleRequiredToBoot(asyncResp, 3130 *bootTrustedModuleRequired); 3131 } 3132 3133 if (locationIndicatorActive) 3134 { 3135 setLocationIndicatorActive(asyncResp, *locationIndicatorActive); 3136 } 3137 3138 // TODO (Gunnar): Remove IndicatorLED after enough time has 3139 // passed 3140 if (indicatorLed) 3141 { 3142 setIndicatorLedState(asyncResp, *indicatorLed); 3143 asyncResp->res.addHeader(boost::beast::http::field::warning, 3144 "299 - \"IndicatorLED is deprecated. Use " 3145 "LocationIndicatorActive instead.\""); 3146 } 3147 3148 if (powerRestorePolicy) 3149 { 3150 setPowerRestorePolicy(asyncResp, *powerRestorePolicy); 3151 } 3152 3153 if (powerMode) 3154 { 3155 setPowerMode(asyncResp, *powerMode); 3156 } 3157 3158 if (ipsEnable || ipsEnterUtil || ipsEnterTime || ipsExitUtil || 3159 ipsExitTime) 3160 { 3161 setIdlePowerSaver(asyncResp, ipsEnable, ipsEnterUtil, ipsEnterTime, 3162 ipsExitUtil, ipsExitTime); 3163 } 3164 }); 3165 } 3166 3167 inline void handleSystemCollectionResetActionHead( 3168 crow::App& app, const crow::Request& req, 3169 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 3170 { 3171 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 3172 { 3173 return; 3174 } 3175 asyncResp->res.addHeader( 3176 boost::beast::http::field::link, 3177 "</redfish/v1/JsonSchemas/ActionInfo/ActionInfo.json>; rel=describedby"); 3178 } 3179 3180 /** 3181 * SystemResetActionInfo derived class for delivering Computer Systems 3182 * ResetType AllowableValues using ResetInfo schema. 3183 */ 3184 inline void requestRoutesSystemResetActionInfo(App& app) 3185 { 3186 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/ResetActionInfo/") 3187 .privileges(redfish::privileges::headActionInfo) 3188 .methods(boost::beast::http::verb::head)(std::bind_front( 3189 handleSystemCollectionResetActionHead, std::ref(app))); 3190 /** 3191 * Functions triggers appropriate requests on DBus 3192 */ 3193 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/ResetActionInfo/") 3194 .privileges(redfish::privileges::getActionInfo) 3195 .methods(boost::beast::http::verb::get)( 3196 [&app](const crow::Request& req, 3197 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3198 const std::string& systemName) { 3199 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 3200 { 3201 return; 3202 } 3203 if (systemName != "system") 3204 { 3205 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3206 systemName); 3207 return; 3208 } 3209 3210 asyncResp->res.addHeader( 3211 boost::beast::http::field::link, 3212 "</redfish/v1/JsonSchemas/ActionInfo/ActionInfo.json>; rel=describedby"); 3213 3214 asyncResp->res.jsonValue["@odata.id"] = 3215 "/redfish/v1/Systems/system/ResetActionInfo"; 3216 asyncResp->res.jsonValue["@odata.type"] = 3217 "#ActionInfo.v1_1_2.ActionInfo"; 3218 asyncResp->res.jsonValue["Name"] = "Reset Action Info"; 3219 asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; 3220 3221 nlohmann::json::array_t parameters; 3222 nlohmann::json::object_t parameter; 3223 3224 parameter["Name"] = "ResetType"; 3225 parameter["Required"] = true; 3226 parameter["DataType"] = "String"; 3227 nlohmann::json::array_t allowableValues; 3228 allowableValues.emplace_back("On"); 3229 allowableValues.emplace_back("ForceOff"); 3230 allowableValues.emplace_back("ForceOn"); 3231 allowableValues.emplace_back("ForceRestart"); 3232 allowableValues.emplace_back("GracefulRestart"); 3233 allowableValues.emplace_back("GracefulShutdown"); 3234 allowableValues.emplace_back("PowerCycle"); 3235 allowableValues.emplace_back("Nmi"); 3236 parameter["AllowableValues"] = std::move(allowableValues); 3237 parameters.emplace_back(std::move(parameter)); 3238 3239 asyncResp->res.jsonValue["Parameters"] = std::move(parameters); 3240 }); 3241 } 3242 } // namespace redfish 3243