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 "health.hpp" 19 #include "led.hpp" 20 #include "pcie.hpp" 21 #include "redfish_util.hpp" 22 23 #include <boost/container/flat_map.hpp> 24 #include <node.hpp> 25 #include <utils/fw_utils.hpp> 26 #include <utils/json_utils.hpp> 27 28 #include <variant> 29 30 namespace redfish 31 { 32 33 /** 34 * @brief Updates the Functional State of DIMMs 35 * 36 * @param[in] aResp Shared pointer for completing asynchronous calls 37 * @param[in] dimmState Dimm's Functional state, true/false 38 * 39 * @return None. 40 */ 41 inline void updateDimmProperties(std::shared_ptr<AsyncResp> aResp, 42 const std::variant<bool>& dimmState) 43 { 44 const bool* isDimmFunctional = std::get_if<bool>(&dimmState); 45 if (isDimmFunctional == nullptr) 46 { 47 messages::internalError(aResp->res); 48 return; 49 } 50 BMCWEB_LOG_DEBUG << "Dimm Functional: " << *isDimmFunctional; 51 52 // Set it as Enabled if at least one DIMM is functional 53 // Update STATE only if previous State was DISABLED and current Dimm is 54 // ENABLED. 55 nlohmann::json& prevMemSummary = 56 aResp->res.jsonValue["MemorySummary"]["Status"]["State"]; 57 if (prevMemSummary == "Disabled") 58 { 59 if (*isDimmFunctional == true) 60 { 61 aResp->res.jsonValue["MemorySummary"]["Status"]["State"] = 62 "Enabled"; 63 } 64 } 65 } 66 67 /* 68 * @brief Update "ProcessorSummary" "Count" based on Cpu PresenceState 69 * 70 * @param[in] aResp Shared pointer for completing asynchronous calls 71 * @param[in] cpuPresenceState CPU present or not 72 * 73 * @return None. 74 */ 75 inline void modifyCpuPresenceState(std::shared_ptr<AsyncResp> aResp, 76 const std::variant<bool>& cpuPresenceState) 77 { 78 const bool* isCpuPresent = std::get_if<bool>(&cpuPresenceState); 79 80 if (isCpuPresent == nullptr) 81 { 82 messages::internalError(aResp->res); 83 return; 84 } 85 BMCWEB_LOG_DEBUG << "Cpu Present: " << *isCpuPresent; 86 87 if (*isCpuPresent == true) 88 { 89 nlohmann::json& procCount = 90 aResp->res.jsonValue["ProcessorSummary"]["Count"]; 91 auto procCountPtr = 92 procCount.get_ptr<nlohmann::json::number_integer_t*>(); 93 if (procCountPtr != nullptr) 94 { 95 // shouldn't be possible to be nullptr 96 *procCountPtr += 1; 97 } 98 } 99 } 100 101 /* 102 * @brief Update "ProcessorSummary" "Status" "State" based on 103 * CPU Functional State 104 * 105 * @param[in] aResp Shared pointer for completing asynchronous calls 106 * @param[in] cpuFunctionalState is CPU functional true/false 107 * 108 * @return None. 109 */ 110 inline void 111 modifyCpuFunctionalState(std::shared_ptr<AsyncResp> aResp, 112 const std::variant<bool>& cpuFunctionalState) 113 { 114 const bool* isCpuFunctional = std::get_if<bool>(&cpuFunctionalState); 115 116 if (isCpuFunctional == nullptr) 117 { 118 messages::internalError(aResp->res); 119 return; 120 } 121 BMCWEB_LOG_DEBUG << "Cpu Functional: " << *isCpuFunctional; 122 123 nlohmann::json& prevProcState = 124 aResp->res.jsonValue["ProcessorSummary"]["Status"]["State"]; 125 126 // Set it as Enabled if at least one CPU is functional 127 // Update STATE only if previous State was Non_Functional and current CPU is 128 // Functional. 129 if (prevProcState == "Disabled") 130 { 131 if (*isCpuFunctional == true) 132 { 133 aResp->res.jsonValue["ProcessorSummary"]["Status"]["State"] = 134 "Enabled"; 135 } 136 } 137 } 138 139 /* 140 * @brief Retrieves computer system properties over dbus 141 * 142 * @param[in] aResp Shared pointer for completing asynchronous calls 143 * @param[in] name Computer system name from request 144 * 145 * @return None. 146 */ 147 inline void getComputerSystem(std::shared_ptr<AsyncResp> aResp, 148 std::shared_ptr<HealthPopulate> systemHealth) 149 { 150 BMCWEB_LOG_DEBUG << "Get available system components."; 151 152 crow::connections::systemBus->async_method_call( 153 [aResp, systemHealth]( 154 const boost::system::error_code ec, 155 const std::vector<std::pair< 156 std::string, 157 std::vector<std::pair<std::string, std::vector<std::string>>>>>& 158 subtree) { 159 if (ec) 160 { 161 BMCWEB_LOG_DEBUG << "DBUS response error"; 162 messages::internalError(aResp->res); 163 return; 164 } 165 // Iterate over all retrieved ObjectPaths. 166 for (const std::pair<std::string, 167 std::vector<std::pair< 168 std::string, std::vector<std::string>>>>& 169 object : subtree) 170 { 171 const std::string& path = object.first; 172 BMCWEB_LOG_DEBUG << "Got path: " << path; 173 const std::vector< 174 std::pair<std::string, std::vector<std::string>>>& 175 connectionNames = object.second; 176 if (connectionNames.size() < 1) 177 { 178 continue; 179 } 180 181 auto memoryHealth = std::make_shared<HealthPopulate>( 182 aResp, aResp->res.jsonValue["MemorySummary"]["Status"]); 183 184 auto cpuHealth = std::make_shared<HealthPopulate>( 185 aResp, aResp->res.jsonValue["ProcessorSummary"]["Status"]); 186 187 systemHealth->children.emplace_back(memoryHealth); 188 systemHealth->children.emplace_back(cpuHealth); 189 190 // This is not system, so check if it's cpu, dimm, UUID or 191 // BiosVer 192 for (const auto& connection : connectionNames) 193 { 194 for (const auto& interfaceName : connection.second) 195 { 196 if (interfaceName == 197 "xyz.openbmc_project.Inventory.Item.Dimm") 198 { 199 BMCWEB_LOG_DEBUG 200 << "Found Dimm, now get its properties."; 201 202 crow::connections::systemBus->async_method_call( 203 [aResp, service{connection.first}, 204 path(std::move(path))]( 205 const boost::system::error_code ec2, 206 const std::vector< 207 std::pair<std::string, VariantType>>& 208 properties) { 209 if (ec2) 210 { 211 BMCWEB_LOG_ERROR 212 << "DBUS response error " << ec2; 213 messages::internalError(aResp->res); 214 return; 215 } 216 BMCWEB_LOG_DEBUG << "Got " 217 << properties.size() 218 << " Dimm properties."; 219 220 if (properties.size() > 0) 221 { 222 for (const std::pair<std::string, 223 VariantType>& 224 property : properties) 225 { 226 if (property.first != 227 "MemorySizeInKB") 228 { 229 continue; 230 } 231 const uint32_t* value = 232 std::get_if<uint32_t>( 233 &property.second); 234 if (value == nullptr) 235 { 236 BMCWEB_LOG_DEBUG 237 << "Find incorrect type of " 238 "MemorySize"; 239 continue; 240 } 241 nlohmann::json& totalMemory = 242 aResp->res 243 .jsonValue["MemorySummar" 244 "y"] 245 ["TotalSystemMe" 246 "moryGiB"]; 247 uint64_t* preValue = 248 totalMemory 249 .get_ptr<uint64_t*>(); 250 if (preValue == nullptr) 251 { 252 continue; 253 } 254 aResp->res 255 .jsonValue["MemorySummary"] 256 ["TotalSystemMemoryGi" 257 "B"] = 258 *value / (1024 * 1024) + 259 *preValue; 260 aResp->res 261 .jsonValue["MemorySummary"] 262 ["Status"]["State"] = 263 "Enabled"; 264 } 265 } 266 else 267 { 268 auto getDimmProperties = 269 [aResp]( 270 const boost::system::error_code 271 ec3, 272 const std::variant<bool>& 273 dimmState) { 274 if (ec3) 275 { 276 BMCWEB_LOG_ERROR 277 << "DBUS response " 278 "error " 279 << ec3; 280 return; 281 } 282 updateDimmProperties(aResp, 283 dimmState); 284 }; 285 crow::connections::systemBus 286 ->async_method_call( 287 std::move(getDimmProperties), 288 service, path, 289 "org.freedesktop.DBus." 290 "Properties", 291 "Get", 292 "xyz.openbmc_project.State." 293 "Decorator.OperationalStatus", 294 "Functional"); 295 } 296 }, 297 connection.first, path, 298 "org.freedesktop.DBus.Properties", "GetAll", 299 "xyz.openbmc_project.Inventory.Item.Dimm"); 300 301 memoryHealth->inventory.emplace_back(path); 302 } 303 else if (interfaceName == 304 "xyz.openbmc_project.Inventory.Item.Cpu") 305 { 306 BMCWEB_LOG_DEBUG 307 << "Found Cpu, now get its properties."; 308 309 crow::connections::systemBus->async_method_call( 310 [aResp, service{connection.first}, 311 path(std::move(path))]( 312 const boost::system::error_code ec2, 313 const std::vector< 314 std::pair<std::string, VariantType>>& 315 properties) { 316 if (ec2) 317 { 318 BMCWEB_LOG_ERROR 319 << "DBUS response error " << ec2; 320 messages::internalError(aResp->res); 321 return; 322 } 323 BMCWEB_LOG_DEBUG << "Got " 324 << properties.size() 325 << " Cpu properties."; 326 327 if (properties.size() > 0) 328 { 329 const uint64_t* processorId = nullptr; 330 const std::string* procFamily = nullptr; 331 nlohmann::json& procSummary = 332 aResp->res.jsonValue["ProcessorSumm" 333 "ary"]; 334 nlohmann::json& procCount = 335 procSummary["Count"]; 336 337 auto procCountPtr = procCount.get_ptr< 338 nlohmann::json:: 339 number_integer_t*>(); 340 if (procCountPtr == nullptr) 341 { 342 messages::internalError(aResp->res); 343 return; 344 } 345 for (const auto& property : properties) 346 { 347 348 if (property.first == "Id") 349 { 350 processorId = 351 std::get_if<uint64_t>( 352 &property.second); 353 if (nullptr != procFamily) 354 break; 355 continue; 356 } 357 358 if (property.first == "Family") 359 { 360 procFamily = 361 std::get_if<std::string>( 362 &property.second); 363 if (nullptr != processorId) 364 break; 365 continue; 366 } 367 } 368 369 if (procFamily != nullptr && 370 processorId != nullptr) 371 { 372 if (procCountPtr != nullptr && 373 *processorId != 0) 374 { 375 *procCountPtr += 1; 376 procSummary["Status"]["State"] = 377 "Enabled"; 378 379 procSummary["Model"] = 380 *procFamily; 381 } 382 } 383 } 384 else 385 { 386 auto getCpuPresenceState = 387 [aResp]( 388 const boost::system::error_code 389 ec3, 390 const std::variant<bool>& 391 cpuPresenceCheck) { 392 if (ec3) 393 { 394 BMCWEB_LOG_ERROR 395 << "DBUS response " 396 "error " 397 << ec3; 398 return; 399 } 400 modifyCpuPresenceState( 401 aResp, cpuPresenceCheck); 402 }; 403 404 auto getCpuFunctionalState = 405 [aResp]( 406 const boost::system::error_code 407 ec3, 408 const std::variant<bool>& 409 cpuFunctionalCheck) { 410 if (ec3) 411 { 412 BMCWEB_LOG_ERROR 413 << "DBUS response " 414 "error " 415 << ec3; 416 return; 417 } 418 modifyCpuFunctionalState( 419 aResp, cpuFunctionalCheck); 420 }; 421 // Get the Presence of CPU 422 crow::connections::systemBus 423 ->async_method_call( 424 std::move(getCpuPresenceState), 425 service, path, 426 "org.freedesktop.DBus." 427 "Properties", 428 "Get", 429 "xyz.openbmc_project.Inventory." 430 "Item", 431 "Present"); 432 433 // Get the Functional State 434 crow::connections::systemBus 435 ->async_method_call( 436 std::move( 437 getCpuFunctionalState), 438 service, path, 439 "org.freedesktop.DBus." 440 "Properties", 441 "Get", 442 "xyz.openbmc_project.State." 443 "Decorator." 444 "OperationalStatus", 445 "Functional"); 446 447 // Get the MODEL from 448 // xyz.openbmc_project.Inventory.Decorator.Asset 449 // support it later as Model is Empty 450 // currently. 451 } 452 }, 453 connection.first, path, 454 "org.freedesktop.DBus.Properties", "GetAll", 455 "xyz.openbmc_project.Inventory.Item.Cpu"); 456 457 cpuHealth->inventory.emplace_back(path); 458 } 459 else if (interfaceName == 460 "xyz.openbmc_project.Common.UUID") 461 { 462 BMCWEB_LOG_DEBUG 463 << "Found UUID, now get its properties."; 464 crow::connections::systemBus->async_method_call( 465 [aResp]( 466 const boost::system::error_code ec3, 467 const std::vector< 468 std::pair<std::string, VariantType>>& 469 properties) { 470 if (ec3) 471 { 472 BMCWEB_LOG_DEBUG 473 << "DBUS response error " << ec3; 474 messages::internalError(aResp->res); 475 return; 476 } 477 BMCWEB_LOG_DEBUG << "Got " 478 << properties.size() 479 << " UUID properties."; 480 for (const std::pair<std::string, 481 VariantType>& 482 property : properties) 483 { 484 if (property.first == "UUID") 485 { 486 const std::string* value = 487 std::get_if<std::string>( 488 &property.second); 489 490 if (value != nullptr) 491 { 492 std::string valueStr = *value; 493 if (valueStr.size() == 32) 494 { 495 valueStr.insert(8, 1, '-'); 496 valueStr.insert(13, 1, '-'); 497 valueStr.insert(18, 1, '-'); 498 valueStr.insert(23, 1, '-'); 499 } 500 BMCWEB_LOG_DEBUG << "UUID = " 501 << valueStr; 502 aResp->res.jsonValue["UUID"] = 503 valueStr; 504 } 505 } 506 } 507 }, 508 connection.first, path, 509 "org.freedesktop.DBus.Properties", "GetAll", 510 "xyz.openbmc_project.Common.UUID"); 511 } 512 else if (interfaceName == 513 "xyz.openbmc_project.Inventory.Item.System") 514 { 515 crow::connections::systemBus->async_method_call( 516 [aResp]( 517 const boost::system::error_code ec2, 518 const std::vector< 519 std::pair<std::string, VariantType>>& 520 propertiesList) { 521 if (ec2) 522 { 523 // doesn't have to include this 524 // interface 525 return; 526 } 527 BMCWEB_LOG_DEBUG 528 << "Got " << propertiesList.size() 529 << " properties for system"; 530 for (const std::pair<std::string, 531 VariantType>& 532 property : propertiesList) 533 { 534 const std::string& propertyName = 535 property.first; 536 if ((propertyName == "PartNumber") || 537 (propertyName == "SerialNumber") || 538 (propertyName == "Manufacturer") || 539 (propertyName == "Model")) 540 { 541 const std::string* value = 542 std::get_if<std::string>( 543 &property.second); 544 if (value != nullptr) 545 { 546 aResp->res 547 .jsonValue[propertyName] = 548 *value; 549 } 550 } 551 } 552 553 // Grab the bios version 554 fw_util::populateFirmwareInformation( 555 aResp, fw_util::biosPurpose, 556 "BiosVersion", false); 557 }, 558 connection.first, path, 559 "org.freedesktop.DBus.Properties", "GetAll", 560 "xyz.openbmc_project.Inventory.Decorator." 561 "Asset"); 562 563 crow::connections::systemBus->async_method_call( 564 [aResp]( 565 const boost::system::error_code ec2, 566 const std::variant<std::string>& property) { 567 if (ec2) 568 { 569 // doesn't have to include this 570 // interface 571 return; 572 } 573 574 const std::string* value = 575 std::get_if<std::string>(&property); 576 if (value != nullptr) 577 { 578 aResp->res.jsonValue["AssetTag"] = 579 *value; 580 } 581 }, 582 connection.first, path, 583 "org.freedesktop.DBus.Properties", "Get", 584 "xyz.openbmc_project.Inventory.Decorator." 585 "AssetTag", 586 "AssetTag"); 587 } 588 } 589 } 590 } 591 }, 592 "xyz.openbmc_project.ObjectMapper", 593 "/xyz/openbmc_project/object_mapper", 594 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 595 "/xyz/openbmc_project/inventory", int32_t(0), 596 std::array<const char*, 5>{ 597 "xyz.openbmc_project.Inventory.Decorator.Asset", 598 "xyz.openbmc_project.Inventory.Item.Cpu", 599 "xyz.openbmc_project.Inventory.Item.Dimm", 600 "xyz.openbmc_project.Inventory.Item.System", 601 "xyz.openbmc_project.Common.UUID", 602 }); 603 } 604 605 /** 606 * @brief Retrieves host state properties over dbus 607 * 608 * @param[in] aResp Shared pointer for completing asynchronous calls. 609 * 610 * @return None. 611 */ 612 inline void getHostState(std::shared_ptr<AsyncResp> aResp) 613 { 614 BMCWEB_LOG_DEBUG << "Get host information."; 615 crow::connections::systemBus->async_method_call( 616 [aResp](const boost::system::error_code ec, 617 const std::variant<std::string>& hostState) { 618 if (ec) 619 { 620 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 621 messages::internalError(aResp->res); 622 return; 623 } 624 625 const std::string* s = std::get_if<std::string>(&hostState); 626 BMCWEB_LOG_DEBUG << "Host state: " << *s; 627 if (s != nullptr) 628 { 629 // Verify Host State 630 if (*s == "xyz.openbmc_project.State.Host.HostState.Running") 631 { 632 aResp->res.jsonValue["PowerState"] = "On"; 633 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 634 } 635 else if (*s == "xyz.openbmc_project.State.Host.HostState." 636 "Quiesced") 637 { 638 aResp->res.jsonValue["PowerState"] = "On"; 639 aResp->res.jsonValue["Status"]["State"] = "Quiesced"; 640 } 641 else if (*s == "xyz.openbmc_project.State.Host.HostState." 642 "DiagnosticMode") 643 { 644 aResp->res.jsonValue["PowerState"] = "On"; 645 aResp->res.jsonValue["Status"]["State"] = "InTest"; 646 } 647 else 648 { 649 aResp->res.jsonValue["PowerState"] = "Off"; 650 aResp->res.jsonValue["Status"]["State"] = "Disabled"; 651 } 652 } 653 }, 654 "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0", 655 "org.freedesktop.DBus.Properties", "Get", 656 "xyz.openbmc_project.State.Host", "CurrentHostState"); 657 } 658 659 /** 660 * @brief Translates boot source DBUS property value to redfish. 661 * 662 * @param[in] dbusSource The boot source in DBUS speak. 663 * 664 * @return Returns as a string, the boot source in Redfish terms. If translation 665 * cannot be done, returns an empty string. 666 */ 667 inline std::string dbusToRfBootSource(const std::string& dbusSource) 668 { 669 if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.Default") 670 { 671 return "None"; 672 } 673 else if (dbusSource == 674 "xyz.openbmc_project.Control.Boot.Source.Sources.Disk") 675 { 676 return "Hdd"; 677 } 678 else if (dbusSource == 679 "xyz.openbmc_project.Control.Boot.Source.Sources.ExternalMedia") 680 { 681 return "Cd"; 682 } 683 else if (dbusSource == 684 "xyz.openbmc_project.Control.Boot.Source.Sources.Network") 685 { 686 return "Pxe"; 687 } 688 else if (dbusSource == 689 "xyz.openbmc_project.Control.Boot.Source.Sources.RemovableMedia") 690 { 691 return "Usb"; 692 } 693 else 694 { 695 return ""; 696 } 697 } 698 699 /** 700 * @brief Translates boot mode DBUS property value to redfish. 701 * 702 * @param[in] dbusMode The boot mode in DBUS speak. 703 * 704 * @return Returns as a string, the boot mode in Redfish terms. If translation 705 * cannot be done, returns an empty string. 706 */ 707 inline std::string dbusToRfBootMode(const std::string& dbusMode) 708 { 709 if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular") 710 { 711 return "None"; 712 } 713 else if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Safe") 714 { 715 return "Diags"; 716 } 717 else if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup") 718 { 719 return "BiosSetup"; 720 } 721 else 722 { 723 return ""; 724 } 725 } 726 727 /** 728 * @brief Translates boot source from Redfish to the DBus boot paths. 729 * 730 * @param[in] rfSource The boot source in Redfish. 731 * @param[out] bootSource The DBus source 732 * @param[out] bootMode the DBus boot mode 733 * 734 * @return Integer error code. 735 */ 736 inline int assignBootParameters(std::shared_ptr<AsyncResp> aResp, 737 const std::string& rfSource, 738 std::string& bootSource, std::string& bootMode) 739 { 740 // The caller has initialized the bootSource and bootMode to: 741 // bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"; 742 // bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Default"; 743 // Only modify the bootSource/bootMode variable needed to achieve the 744 // desired boot action. 745 746 if (rfSource == "None") 747 { 748 return 0; 749 } 750 else if (rfSource == "Pxe") 751 { 752 bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Network"; 753 } 754 else if (rfSource == "Hdd") 755 { 756 bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Disk"; 757 } 758 else if (rfSource == "Diags") 759 { 760 bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Safe"; 761 } 762 else if (rfSource == "Cd") 763 { 764 bootSource = 765 "xyz.openbmc_project.Control.Boot.Source.Sources.ExternalMedia"; 766 } 767 else if (rfSource == "BiosSetup") 768 { 769 bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup"; 770 } 771 else if (rfSource == "Usb") 772 { 773 bootSource = 774 "xyz.openbmc_project.Control.Boot.Source.Sources.RemovableMedia"; 775 } 776 else 777 { 778 BMCWEB_LOG_DEBUG << "Invalid property value for " 779 "BootSourceOverrideTarget: " 780 << bootSource; 781 messages::propertyValueNotInList(aResp->res, rfSource, 782 "BootSourceTargetOverride"); 783 return -1; 784 } 785 return 0; 786 } 787 788 /** 789 * @brief Retrieves boot mode over DBUS and fills out the response 790 * 791 * @param[in] aResp Shared pointer for generating response message. 792 * @param[in] bootDbusObj The dbus object to query for boot properties. 793 * 794 * @return None. 795 */ 796 inline void getBootMode(std::shared_ptr<AsyncResp> aResp, 797 std::string bootDbusObj) 798 { 799 crow::connections::systemBus->async_method_call( 800 [aResp](const boost::system::error_code ec, 801 const std::variant<std::string>& bootMode) { 802 if (ec) 803 { 804 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 805 messages::internalError(aResp->res); 806 return; 807 } 808 809 const std::string* bootModeStr = 810 std::get_if<std::string>(&bootMode); 811 812 if (!bootModeStr) 813 { 814 messages::internalError(aResp->res); 815 return; 816 } 817 818 BMCWEB_LOG_DEBUG << "Boot mode: " << *bootModeStr; 819 820 // TODO (Santosh): Do we need to support override mode? 821 aResp->res.jsonValue["Boot"]["BootSourceOverrideMode"] = "Legacy"; 822 aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget@Redfish." 823 "AllowableValues"] = { 824 "None", "Pxe", "Hdd", "Cd", "Diags", "BiosSetup", "Usb"}; 825 826 if (*bootModeStr != 827 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular") 828 { 829 auto rfMode = dbusToRfBootMode(*bootModeStr); 830 if (!rfMode.empty()) 831 { 832 aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = 833 rfMode; 834 } 835 } 836 837 // If the BootSourceOverrideTarget is still "None" at the end, 838 // reset the BootSourceOverrideEnabled to indicate that 839 // overrides are disabled 840 if (aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] == 841 "None") 842 { 843 aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = 844 "Disabled"; 845 } 846 }, 847 "xyz.openbmc_project.Settings", bootDbusObj, 848 "org.freedesktop.DBus.Properties", "Get", 849 "xyz.openbmc_project.Control.Boot.Mode", "BootMode"); 850 } 851 852 /** 853 * @brief Retrieves boot source over DBUS 854 * 855 * @param[in] aResp Shared pointer for generating response message. 856 * @param[in] oneTimeEnable Boolean to indicate boot properties are one-time. 857 * 858 * @return None. 859 */ 860 inline void getBootSource(std::shared_ptr<AsyncResp> aResp, bool oneTimeEnabled) 861 { 862 std::string bootDbusObj = 863 oneTimeEnabled ? "/xyz/openbmc_project/control/host0/boot/one_time" 864 : "/xyz/openbmc_project/control/host0/boot"; 865 866 BMCWEB_LOG_DEBUG << "Is one time: " << oneTimeEnabled; 867 aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = 868 (oneTimeEnabled) ? "Once" : "Continuous"; 869 870 crow::connections::systemBus->async_method_call( 871 [aResp, bootDbusObj](const boost::system::error_code ec, 872 const std::variant<std::string>& bootSource) { 873 if (ec) 874 { 875 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 876 messages::internalError(aResp->res); 877 return; 878 } 879 880 const std::string* bootSourceStr = 881 std::get_if<std::string>(&bootSource); 882 883 if (!bootSourceStr) 884 { 885 messages::internalError(aResp->res); 886 return; 887 } 888 BMCWEB_LOG_DEBUG << "Boot source: " << *bootSourceStr; 889 890 auto rfSource = dbusToRfBootSource(*bootSourceStr); 891 if (!rfSource.empty()) 892 { 893 aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = 894 rfSource; 895 } 896 }, 897 "xyz.openbmc_project.Settings", bootDbusObj, 898 "org.freedesktop.DBus.Properties", "Get", 899 "xyz.openbmc_project.Control.Boot.Source", "BootSource"); 900 getBootMode(std::move(aResp), std::move(bootDbusObj)); 901 } 902 903 /** 904 * @brief Retrieves "One time" enabled setting over DBUS and calls function to 905 * get boot source and boot mode. 906 * 907 * @param[in] aResp Shared pointer for generating response message. 908 * 909 * @return None. 910 */ 911 inline void getBootProperties(std::shared_ptr<AsyncResp> aResp) 912 { 913 BMCWEB_LOG_DEBUG << "Get boot information."; 914 915 crow::connections::systemBus->async_method_call( 916 [aResp](const boost::system::error_code ec, 917 const std::variant<bool>& oneTime) { 918 if (ec) 919 { 920 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 921 // not an error, don't have to have the interface 922 return; 923 } 924 925 const bool* oneTimePtr = std::get_if<bool>(&oneTime); 926 927 if (!oneTimePtr) 928 { 929 messages::internalError(aResp->res); 930 return; 931 } 932 getBootSource(aResp, *oneTimePtr); 933 }, 934 "xyz.openbmc_project.Settings", 935 "/xyz/openbmc_project/control/host0/boot/one_time", 936 "org.freedesktop.DBus.Properties", "Get", 937 "xyz.openbmc_project.Object.Enable", "Enabled"); 938 } 939 940 /** 941 * @brief Retrieves the Last Reset Time 942 * 943 * "Reset" is an overloaded term in Redfish, "Reset" includes power on 944 * and power off. Even though this is the "system" Redfish object look at the 945 * chassis D-Bus interface for the LastStateChangeTime since this has the 946 * last power operation time. 947 * 948 * @param[in] aResp Shared pointer for generating response message. 949 * 950 * @return None. 951 */ 952 inline void getLastResetTime(std::shared_ptr<AsyncResp> aResp) 953 { 954 BMCWEB_LOG_DEBUG << "Getting System Last Reset Time"; 955 956 crow::connections::systemBus->async_method_call( 957 [aResp](const boost::system::error_code ec, 958 std::variant<uint64_t>& lastResetTime) { 959 if (ec) 960 { 961 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec; 962 return; 963 } 964 965 const uint64_t* lastResetTimePtr = 966 std::get_if<uint64_t>(&lastResetTime); 967 968 if (!lastResetTimePtr) 969 { 970 messages::internalError(aResp->res); 971 return; 972 } 973 // LastStateChangeTime is epoch time, in milliseconds 974 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/33e8e1dd64da53a66e888d33dc82001305cd0bf9/xyz/openbmc_project/State/Chassis.interface.yaml#L19 975 time_t lastResetTimeStamp = 976 static_cast<time_t>(*lastResetTimePtr / 1000); 977 978 // Convert to ISO 8601 standard 979 aResp->res.jsonValue["LastResetTime"] = 980 crow::utility::getDateTime(lastResetTimeStamp); 981 }, 982 "xyz.openbmc_project.State.Chassis", 983 "/xyz/openbmc_project/state/chassis0", 984 "org.freedesktop.DBus.Properties", "Get", 985 "xyz.openbmc_project.State.Chassis", "LastStateChangeTime"); 986 } 987 988 /** 989 * @brief Retrieves Automatic Retry properties. Known on D-Bus as AutoReboot. 990 * 991 * @param[in] aResp Shared pointer for generating response message. 992 * 993 * @return None. 994 */ 995 inline void getAutomaticRetry(std::shared_ptr<AsyncResp> aResp) 996 { 997 BMCWEB_LOG_DEBUG << "Get Automatic Retry policy"; 998 999 crow::connections::systemBus->async_method_call( 1000 [aResp](const boost::system::error_code ec, 1001 std::variant<bool>& autoRebootEnabled) { 1002 if (ec) 1003 { 1004 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec; 1005 return; 1006 } 1007 1008 const bool* autoRebootEnabledPtr = 1009 std::get_if<bool>(&autoRebootEnabled); 1010 1011 if (!autoRebootEnabledPtr) 1012 { 1013 messages::internalError(aResp->res); 1014 return; 1015 } 1016 1017 BMCWEB_LOG_DEBUG << "Auto Reboot: " << *autoRebootEnabledPtr; 1018 if (*autoRebootEnabledPtr == true) 1019 { 1020 aResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = 1021 "RetryAttempts"; 1022 // If AutomaticRetry (AutoReboot) is enabled see how many 1023 // attempts are left 1024 crow::connections::systemBus->async_method_call( 1025 [aResp](const boost::system::error_code ec2, 1026 std::variant<uint32_t>& autoRebootAttemptsLeft) { 1027 if (ec2) 1028 { 1029 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec2; 1030 return; 1031 } 1032 1033 const uint32_t* autoRebootAttemptsLeftPtr = 1034 std::get_if<uint32_t>(&autoRebootAttemptsLeft); 1035 1036 if (!autoRebootAttemptsLeftPtr) 1037 { 1038 messages::internalError(aResp->res); 1039 return; 1040 } 1041 1042 BMCWEB_LOG_DEBUG << "Auto Reboot Attempts Left: " 1043 << *autoRebootAttemptsLeftPtr; 1044 1045 aResp->res 1046 .jsonValue["Boot"] 1047 ["RemainingAutomaticRetryAttempts"] = 1048 *autoRebootAttemptsLeftPtr; 1049 }, 1050 "xyz.openbmc_project.State.Host", 1051 "/xyz/openbmc_project/state/host0", 1052 "org.freedesktop.DBus.Properties", "Get", 1053 "xyz.openbmc_project.Control.Boot.RebootAttempts", 1054 "AttemptsLeft"); 1055 } 1056 else 1057 { 1058 aResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = 1059 "Disabled"; 1060 } 1061 1062 // Not on D-Bus. Hardcoded here: 1063 // https://github.com/openbmc/phosphor-state-manager/blob/1dbbef42675e94fb1f78edb87d6b11380260535a/meson_options.txt#L71 1064 aResp->res.jsonValue["Boot"]["AutomaticRetryAttempts"] = 3; 1065 1066 // "AutomaticRetryConfig" can be 3 values, Disabled, RetryAlways, 1067 // and RetryAttempts. OpenBMC only supports Disabled and 1068 // RetryAttempts. 1069 aResp->res.jsonValue["Boot"]["AutomaticRetryConfig@Redfish." 1070 "AllowableValues"] = {"Disabled", 1071 "RetryAttempts"}; 1072 }, 1073 "xyz.openbmc_project.Settings", 1074 "/xyz/openbmc_project/control/host0/auto_reboot", 1075 "org.freedesktop.DBus.Properties", "Get", 1076 "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot"); 1077 } 1078 1079 /** 1080 * @brief Retrieves power restore policy over DBUS. 1081 * 1082 * @param[in] aResp Shared pointer for generating response message. 1083 * 1084 * @return None. 1085 */ 1086 inline void getPowerRestorePolicy(std::shared_ptr<AsyncResp> aResp) 1087 { 1088 BMCWEB_LOG_DEBUG << "Get power restore policy"; 1089 1090 crow::connections::systemBus->async_method_call( 1091 [aResp](const boost::system::error_code ec, 1092 std::variant<std::string>& policy) { 1093 if (ec) 1094 { 1095 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1096 return; 1097 } 1098 1099 const boost::container::flat_map<std::string, std::string> 1100 policyMaps = { 1101 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy." 1102 "AlwaysOn", 1103 "AlwaysOn"}, 1104 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy." 1105 "AlwaysOff", 1106 "AlwaysOff"}, 1107 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy." 1108 "LastState", 1109 "LastState"}}; 1110 1111 const std::string* policyPtr = std::get_if<std::string>(&policy); 1112 1113 if (!policyPtr) 1114 { 1115 messages::internalError(aResp->res); 1116 return; 1117 } 1118 1119 auto policyMapsIt = policyMaps.find(*policyPtr); 1120 if (policyMapsIt == policyMaps.end()) 1121 { 1122 messages::internalError(aResp->res); 1123 return; 1124 } 1125 1126 aResp->res.jsonValue["PowerRestorePolicy"] = policyMapsIt->second; 1127 }, 1128 "xyz.openbmc_project.Settings", 1129 "/xyz/openbmc_project/control/host0/power_restore_policy", 1130 "org.freedesktop.DBus.Properties", "Get", 1131 "xyz.openbmc_project.Control.Power.RestorePolicy", 1132 "PowerRestorePolicy"); 1133 } 1134 1135 /** 1136 * @brief Sets boot properties into DBUS object(s). 1137 * 1138 * @param[in] aResp Shared pointer for generating response message. 1139 * @param[in] oneTimeEnabled Is "one-time" setting already enabled. 1140 * @param[in] bootSource The boot source to set. 1141 * @param[in] bootEnable The source override "enable" to set. 1142 * 1143 * @return Integer error code. 1144 */ 1145 inline void setBootModeOrSource(std::shared_ptr<AsyncResp> aResp, 1146 bool oneTimeEnabled, 1147 std::optional<std::string> bootSource, 1148 std::optional<std::string> bootEnable) 1149 { 1150 std::string bootSourceStr = 1151 "xyz.openbmc_project.Control.Boot.Source.Sources.Default"; 1152 std::string bootModeStr = 1153 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"; 1154 bool oneTimeSetting = oneTimeEnabled; 1155 bool useBootSource = true; 1156 1157 // Validate incoming parameters 1158 if (bootEnable) 1159 { 1160 if (*bootEnable == "Once") 1161 { 1162 oneTimeSetting = true; 1163 } 1164 else if (*bootEnable == "Continuous") 1165 { 1166 oneTimeSetting = false; 1167 } 1168 else if (*bootEnable == "Disabled") 1169 { 1170 BMCWEB_LOG_DEBUG << "Boot source override will be disabled"; 1171 oneTimeSetting = false; 1172 useBootSource = false; 1173 } 1174 else 1175 { 1176 BMCWEB_LOG_DEBUG << "Unsupported value for " 1177 "BootSourceOverrideEnabled: " 1178 << *bootEnable; 1179 messages::propertyValueNotInList(aResp->res, *bootEnable, 1180 "BootSourceOverrideEnabled"); 1181 return; 1182 } 1183 } 1184 1185 if (bootSource && useBootSource) 1186 { 1187 // Source target specified 1188 BMCWEB_LOG_DEBUG << "Boot source: " << *bootSource; 1189 // Figure out which DBUS interface and property to use 1190 if (assignBootParameters(aResp, *bootSource, bootSourceStr, 1191 bootModeStr)) 1192 { 1193 BMCWEB_LOG_DEBUG 1194 << "Invalid property value for BootSourceOverrideTarget: " 1195 << *bootSource; 1196 messages::propertyValueNotInList(aResp->res, *bootSource, 1197 "BootSourceTargetOverride"); 1198 return; 1199 } 1200 } 1201 1202 // Act on validated parameters 1203 BMCWEB_LOG_DEBUG << "DBUS boot source: " << bootSourceStr; 1204 BMCWEB_LOG_DEBUG << "DBUS boot mode: " << bootModeStr; 1205 const char* bootObj = 1206 oneTimeSetting ? "/xyz/openbmc_project/control/host0/boot/one_time" 1207 : "/xyz/openbmc_project/control/host0/boot"; 1208 1209 crow::connections::systemBus->async_method_call( 1210 [aResp](const boost::system::error_code ec) { 1211 if (ec) 1212 { 1213 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1214 messages::internalError(aResp->res); 1215 return; 1216 } 1217 BMCWEB_LOG_DEBUG << "Boot source update done."; 1218 }, 1219 "xyz.openbmc_project.Settings", bootObj, 1220 "org.freedesktop.DBus.Properties", "Set", 1221 "xyz.openbmc_project.Control.Boot.Source", "BootSource", 1222 std::variant<std::string>(bootSourceStr)); 1223 1224 crow::connections::systemBus->async_method_call( 1225 [aResp](const boost::system::error_code ec) { 1226 if (ec) 1227 { 1228 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1229 messages::internalError(aResp->res); 1230 return; 1231 } 1232 BMCWEB_LOG_DEBUG << "Boot mode update done."; 1233 }, 1234 "xyz.openbmc_project.Settings", bootObj, 1235 "org.freedesktop.DBus.Properties", "Set", 1236 "xyz.openbmc_project.Control.Boot.Mode", "BootMode", 1237 std::variant<std::string>(bootModeStr)); 1238 1239 crow::connections::systemBus->async_method_call( 1240 [aResp{std::move(aResp)}](const boost::system::error_code ec) { 1241 if (ec) 1242 { 1243 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1244 messages::internalError(aResp->res); 1245 return; 1246 } 1247 BMCWEB_LOG_DEBUG << "Boot enable update done."; 1248 }, 1249 "xyz.openbmc_project.Settings", 1250 "/xyz/openbmc_project/control/host0/boot/one_time", 1251 "org.freedesktop.DBus.Properties", "Set", 1252 "xyz.openbmc_project.Object.Enable", "Enabled", 1253 std::variant<bool>(oneTimeSetting)); 1254 } 1255 1256 /** 1257 * @brief Retrieves "One time" enabled setting over DBUS and calls function to 1258 * set boot source/boot mode properties. 1259 * 1260 * @param[in] aResp Shared pointer for generating response message. 1261 * @param[in] bootSource The boot source from incoming RF request. 1262 * @param[in] bootEnable The boot override enable from incoming RF request. 1263 * 1264 * @return Integer error code. 1265 */ 1266 inline void setBootSourceProperties(std::shared_ptr<AsyncResp> aResp, 1267 std::optional<std::string> bootSource, 1268 std::optional<std::string> bootEnable) 1269 { 1270 BMCWEB_LOG_DEBUG << "Set boot information."; 1271 1272 crow::connections::systemBus->async_method_call( 1273 [aResp, bootSource{std::move(bootSource)}, 1274 bootEnable{std::move(bootEnable)}](const boost::system::error_code ec, 1275 const std::variant<bool>& oneTime) { 1276 if (ec) 1277 { 1278 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1279 messages::internalError(aResp->res); 1280 return; 1281 } 1282 1283 const bool* oneTimePtr = std::get_if<bool>(&oneTime); 1284 1285 if (!oneTimePtr) 1286 { 1287 messages::internalError(aResp->res); 1288 return; 1289 } 1290 1291 BMCWEB_LOG_DEBUG << "Got one time: " << *oneTimePtr; 1292 1293 setBootModeOrSource(aResp, *oneTimePtr, std::move(bootSource), 1294 std::move(bootEnable)); 1295 }, 1296 "xyz.openbmc_project.Settings", 1297 "/xyz/openbmc_project/control/host0/boot/one_time", 1298 "org.freedesktop.DBus.Properties", "Get", 1299 "xyz.openbmc_project.Object.Enable", "Enabled"); 1300 } 1301 1302 /** 1303 * @brief Sets automaticRetry (Auto Reboot) 1304 * 1305 * @param[in] aResp Shared pointer for generating response message. 1306 * @param[in] automaticRetryConfig "AutomaticRetryConfig" from request. 1307 * 1308 * @return None. 1309 */ 1310 inline void setAutomaticRetry(std::shared_ptr<AsyncResp> aResp, 1311 const std::string&& automaticRetryConfig) 1312 { 1313 BMCWEB_LOG_DEBUG << "Set Automatic Retry."; 1314 1315 // OpenBMC only supports "Disabled" and "RetryAttempts". 1316 bool autoRebootEnabled; 1317 1318 if (automaticRetryConfig == "Disabled") 1319 { 1320 autoRebootEnabled = false; 1321 } 1322 else if (automaticRetryConfig == "RetryAttempts") 1323 { 1324 autoRebootEnabled = true; 1325 } 1326 else 1327 { 1328 BMCWEB_LOG_DEBUG << "Invalid property value for " 1329 "AutomaticRetryConfig: " 1330 << automaticRetryConfig; 1331 messages::propertyValueNotInList(aResp->res, automaticRetryConfig, 1332 "AutomaticRetryConfig"); 1333 return; 1334 } 1335 1336 crow::connections::systemBus->async_method_call( 1337 [aResp](const boost::system::error_code ec) { 1338 if (ec) 1339 { 1340 messages::internalError(aResp->res); 1341 return; 1342 } 1343 }, 1344 "xyz.openbmc_project.Settings", 1345 "/xyz/openbmc_project/control/host0/auto_reboot", 1346 "org.freedesktop.DBus.Properties", "Set", 1347 "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot", 1348 std::variant<bool>(autoRebootEnabled)); 1349 } 1350 1351 /** 1352 * @brief Sets power restore policy properties. 1353 * 1354 * @param[in] aResp Shared pointer for generating response message. 1355 * @param[in] policy power restore policy properties from request. 1356 * 1357 * @return None. 1358 */ 1359 inline void setPowerRestorePolicy(std::shared_ptr<AsyncResp> aResp, 1360 std::optional<std::string> policy) 1361 { 1362 BMCWEB_LOG_DEBUG << "Set power restore policy."; 1363 1364 const boost::container::flat_map<std::string, std::string> policyMaps = { 1365 {"AlwaysOn", "xyz.openbmc_project.Control.Power.RestorePolicy.Policy." 1366 "AlwaysOn"}, 1367 {"AlwaysOff", "xyz.openbmc_project.Control.Power.RestorePolicy.Policy." 1368 "AlwaysOff"}, 1369 {"LastState", "xyz.openbmc_project.Control.Power.RestorePolicy.Policy." 1370 "LastState"}}; 1371 1372 std::string powerRestorPolicy; 1373 1374 auto policyMapsIt = policyMaps.find(*policy); 1375 if (policyMapsIt == policyMaps.end()) 1376 { 1377 messages::internalError(aResp->res); 1378 return; 1379 } 1380 1381 powerRestorPolicy = policyMapsIt->second; 1382 1383 crow::connections::systemBus->async_method_call( 1384 [aResp](const boost::system::error_code ec) { 1385 if (ec) 1386 { 1387 messages::internalError(aResp->res); 1388 return; 1389 } 1390 }, 1391 "xyz.openbmc_project.Settings", 1392 "/xyz/openbmc_project/control/host0/power_restore_policy", 1393 "org.freedesktop.DBus.Properties", "Set", 1394 "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", 1395 std::variant<std::string>(powerRestorPolicy)); 1396 } 1397 1398 #ifdef BMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE 1399 /** 1400 * @brief Retrieves provisioning status 1401 * 1402 * @param[in] aResp Shared pointer for completing asynchronous calls. 1403 * 1404 * @return None. 1405 */ 1406 inline void getProvisioningStatus(std::shared_ptr<AsyncResp> aResp) 1407 { 1408 BMCWEB_LOG_DEBUG << "Get OEM information."; 1409 crow::connections::systemBus->async_method_call( 1410 [aResp](const boost::system::error_code ec, 1411 const std::vector<std::pair<std::string, VariantType>>& 1412 propertiesList) { 1413 nlohmann::json& oemPFR = 1414 aResp->res.jsonValue["Oem"]["OpenBmc"]["FirmwareProvisioning"]; 1415 if (ec) 1416 { 1417 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1418 // not an error, don't have to have the interface 1419 oemPFR["ProvisioningStatus"] = "NotProvisioned"; 1420 return; 1421 } 1422 1423 const bool* provState = nullptr; 1424 const bool* lockState = nullptr; 1425 for (const std::pair<std::string, VariantType>& property : 1426 propertiesList) 1427 { 1428 if (property.first == "UfmProvisioned") 1429 { 1430 provState = std::get_if<bool>(&property.second); 1431 } 1432 else if (property.first == "UfmLocked") 1433 { 1434 lockState = std::get_if<bool>(&property.second); 1435 } 1436 } 1437 1438 if ((provState == nullptr) || (lockState == nullptr)) 1439 { 1440 BMCWEB_LOG_DEBUG << "Unable to get PFR attributes."; 1441 messages::internalError(aResp->res); 1442 return; 1443 } 1444 1445 if (*provState == true) 1446 { 1447 if (*lockState == true) 1448 { 1449 oemPFR["ProvisioningStatus"] = "ProvisionedAndLocked"; 1450 } 1451 else 1452 { 1453 oemPFR["ProvisioningStatus"] = "ProvisionedButNotLocked"; 1454 } 1455 } 1456 else 1457 { 1458 oemPFR["ProvisioningStatus"] = "NotProvisioned"; 1459 } 1460 }, 1461 "xyz.openbmc_project.PFR.Manager", "/xyz/openbmc_project/pfr", 1462 "org.freedesktop.DBus.Properties", "GetAll", 1463 "xyz.openbmc_project.PFR.Attributes"); 1464 } 1465 #endif 1466 1467 /** 1468 * @brief Translates watchdog timeout action DBUS property value to redfish. 1469 * 1470 * @param[in] dbusAction The watchdog timeout action in D-BUS. 1471 * 1472 * @return Returns as a string, the timeout action in Redfish terms. If 1473 * translation cannot be done, returns an empty string. 1474 */ 1475 inline std::string dbusToRfWatchdogAction(const std::string& dbusAction) 1476 { 1477 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.None") 1478 { 1479 return "None"; 1480 } 1481 else if (dbusAction == 1482 "xyz.openbmc_project.State.Watchdog.Action.HardReset") 1483 { 1484 return "ResetSystem"; 1485 } 1486 else if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.PowerOff") 1487 { 1488 return "PowerDown"; 1489 } 1490 else if (dbusAction == 1491 "xyz.openbmc_project.State.Watchdog.Action.PowerCycle") 1492 { 1493 return "PowerCycle"; 1494 } 1495 1496 return ""; 1497 } 1498 1499 /** 1500 *@brief Translates timeout action from Redfish to DBUS property value. 1501 * 1502 *@param[in] rfAction The timeout action in Redfish. 1503 * 1504 *@return Returns as a string, the time_out action as expected by DBUS. 1505 *If translation cannot be done, returns an empty string. 1506 */ 1507 1508 inline std::string rfToDbusWDTTimeOutAct(const std::string& rfAction) 1509 { 1510 if (rfAction == "None") 1511 { 1512 return "xyz.openbmc_project.State.Watchdog.Action.None"; 1513 } 1514 else if (rfAction == "PowerCycle") 1515 { 1516 return "xyz.openbmc_project.State.Watchdog.Action.PowerCycle"; 1517 } 1518 else if (rfAction == "PowerDown") 1519 { 1520 return "xyz.openbmc_project.State.Watchdog.Action.PowerOff"; 1521 } 1522 else if (rfAction == "ResetSystem") 1523 { 1524 return "xyz.openbmc_project.State.Watchdog.Action.HardReset"; 1525 } 1526 1527 return ""; 1528 } 1529 1530 /** 1531 * @brief Retrieves host watchdog timer properties over DBUS 1532 * 1533 * @param[in] aResp Shared pointer for completing asynchronous calls. 1534 * 1535 * @return None. 1536 */ 1537 inline void getHostWatchdogTimer(std::shared_ptr<AsyncResp> aResp) 1538 { 1539 BMCWEB_LOG_DEBUG << "Get host watchodg"; 1540 crow::connections::systemBus->async_method_call( 1541 [aResp](const boost::system::error_code ec, 1542 PropertiesType& properties) { 1543 if (ec) 1544 { 1545 // watchdog service is stopped 1546 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1547 return; 1548 } 1549 1550 BMCWEB_LOG_DEBUG << "Got " << properties.size() << " wdt prop."; 1551 1552 nlohmann::json& hostWatchdogTimer = 1553 aResp->res.jsonValue["HostWatchdogTimer"]; 1554 1555 // watchdog service is running/enabled 1556 hostWatchdogTimer["Status"]["State"] = "Enabled"; 1557 1558 for (const auto& property : properties) 1559 { 1560 BMCWEB_LOG_DEBUG << "prop=" << property.first; 1561 if (property.first == "Enabled") 1562 { 1563 const bool* state = std::get_if<bool>(&property.second); 1564 1565 if (!state) 1566 { 1567 messages::internalError(aResp->res); 1568 continue; 1569 } 1570 1571 hostWatchdogTimer["FunctionEnabled"] = *state; 1572 } 1573 else if (property.first == "ExpireAction") 1574 { 1575 const std::string* s = 1576 std::get_if<std::string>(&property.second); 1577 if (!s) 1578 { 1579 messages::internalError(aResp->res); 1580 continue; 1581 } 1582 1583 std::string action = dbusToRfWatchdogAction(*s); 1584 if (action.empty()) 1585 { 1586 messages::internalError(aResp->res); 1587 continue; 1588 } 1589 hostWatchdogTimer["TimeoutAction"] = action; 1590 } 1591 } 1592 }, 1593 "xyz.openbmc_project.Watchdog", "/xyz/openbmc_project/watchdog/host0", 1594 "org.freedesktop.DBus.Properties", "GetAll", 1595 "xyz.openbmc_project.State.Watchdog"); 1596 } 1597 1598 /** 1599 * @brief Sets Host WatchDog Timer properties. 1600 * 1601 * @param[in] aResp Shared pointer for generating response message. 1602 * @param[in] wdtEnable The WDTimer Enable value (true/false) from incoming 1603 * RF request. 1604 * @param[in] wdtTimeOutAction The WDT Timeout action, from incoming RF request. 1605 * 1606 * @return None. 1607 */ 1608 inline void setWDTProperties(std::shared_ptr<AsyncResp> aResp, 1609 const std::optional<bool> wdtEnable, 1610 const std::optional<std::string>& wdtTimeOutAction) 1611 { 1612 BMCWEB_LOG_DEBUG << "Set host watchdog"; 1613 1614 if (wdtTimeOutAction) 1615 { 1616 std::string wdtTimeOutActStr = rfToDbusWDTTimeOutAct(*wdtTimeOutAction); 1617 // check if TimeOut Action is Valid 1618 if (wdtTimeOutActStr.empty()) 1619 { 1620 BMCWEB_LOG_DEBUG << "Unsupported value for TimeoutAction: " 1621 << *wdtTimeOutAction; 1622 messages::propertyValueNotInList(aResp->res, *wdtTimeOutAction, 1623 "TimeoutAction"); 1624 return; 1625 } 1626 1627 crow::connections::systemBus->async_method_call( 1628 [aResp](const boost::system::error_code ec) { 1629 if (ec) 1630 { 1631 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1632 messages::internalError(aResp->res); 1633 return; 1634 } 1635 }, 1636 "xyz.openbmc_project.Watchdog", 1637 "/xyz/openbmc_project/watchdog/host0", 1638 "org.freedesktop.DBus.Properties", "Set", 1639 "xyz.openbmc_project.State.Watchdog", "ExpireAction", 1640 std::variant<std::string>(wdtTimeOutActStr)); 1641 } 1642 1643 if (wdtEnable) 1644 { 1645 crow::connections::systemBus->async_method_call( 1646 [aResp](const boost::system::error_code ec) { 1647 if (ec) 1648 { 1649 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1650 messages::internalError(aResp->res); 1651 return; 1652 } 1653 }, 1654 "xyz.openbmc_project.Watchdog", 1655 "/xyz/openbmc_project/watchdog/host0", 1656 "org.freedesktop.DBus.Properties", "Set", 1657 "xyz.openbmc_project.State.Watchdog", "Enabled", 1658 std::variant<bool>(*wdtEnable)); 1659 } 1660 } 1661 1662 /** 1663 * SystemsCollection derived class for delivering ComputerSystems Collection 1664 * Schema 1665 */ 1666 class SystemsCollection : public Node 1667 { 1668 public: 1669 SystemsCollection(App& app) : Node(app, "/redfish/v1/Systems/") 1670 { 1671 entityPrivileges = { 1672 {boost::beast::http::verb::get, {{"Login"}}}, 1673 {boost::beast::http::verb::head, {{"Login"}}}, 1674 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1675 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1676 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1677 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1678 } 1679 1680 private: 1681 void doGet(crow::Response& res, const crow::Request&, 1682 const std::vector<std::string>&) override 1683 { 1684 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1685 res.jsonValue["@odata.type"] = 1686 "#ComputerSystemCollection.ComputerSystemCollection"; 1687 res.jsonValue["@odata.id"] = "/redfish/v1/Systems"; 1688 res.jsonValue["Name"] = "Computer System Collection"; 1689 1690 crow::connections::systemBus->async_method_call( 1691 [asyncResp](const boost::system::error_code ec, 1692 const std::variant<std::string>& /*hostName*/) { 1693 nlohmann::json& iface_array = 1694 asyncResp->res.jsonValue["Members"]; 1695 iface_array = nlohmann::json::array(); 1696 auto& count = asyncResp->res.jsonValue["Members@odata.count"]; 1697 count = 0; 1698 iface_array.push_back( 1699 {{"@odata.id", "/redfish/v1/Systems/system"}}); 1700 if (!ec) 1701 { 1702 BMCWEB_LOG_DEBUG << "Hypervisor is available"; 1703 iface_array.push_back( 1704 {{"@odata.id", "/redfish/v1/Systems/hypervisor"}}); 1705 count = iface_array.size(); 1706 return; 1707 } 1708 }, 1709 "xyz.openbmc_project.Settings", 1710 "/xyz/openbmc_project/network/hypervisor", 1711 "org.freedesktop.DBus.Properties", "Get", 1712 "xyz.openbmc_project.Network.SystemConfiguration", "HostName"); 1713 } 1714 }; 1715 1716 /** 1717 * SystemActionsReset class supports handle POST method for Reset action. 1718 * The class retrieves and sends data directly to D-Bus. 1719 */ 1720 class SystemActionsReset : public Node 1721 { 1722 public: 1723 SystemActionsReset(App& app) : 1724 Node(app, "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset/") 1725 { 1726 entityPrivileges = { 1727 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1728 } 1729 1730 private: 1731 /** 1732 * Function handles POST method request. 1733 * Analyzes POST body message before sends Reset request data to D-Bus. 1734 */ 1735 void doPost(crow::Response& res, const crow::Request& req, 1736 const std::vector<std::string>&) override 1737 { 1738 auto asyncResp = std::make_shared<AsyncResp>(res); 1739 1740 std::string resetType; 1741 if (!json_util::readJson(req, res, "ResetType", resetType)) 1742 { 1743 return; 1744 } 1745 1746 // Get the command and host vs. chassis 1747 std::string command; 1748 bool hostCommand; 1749 if (resetType == "On") 1750 { 1751 command = "xyz.openbmc_project.State.Host.Transition.On"; 1752 hostCommand = true; 1753 } 1754 else if (resetType == "ForceOff") 1755 { 1756 command = "xyz.openbmc_project.State.Chassis.Transition.Off"; 1757 hostCommand = false; 1758 } 1759 else if (resetType == "ForceOn") 1760 { 1761 command = "xyz.openbmc_project.State.Host.Transition.On"; 1762 hostCommand = true; 1763 } 1764 else if (resetType == "ForceRestart") 1765 { 1766 command = 1767 "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot"; 1768 hostCommand = true; 1769 } 1770 else if (resetType == "GracefulShutdown") 1771 { 1772 command = "xyz.openbmc_project.State.Host.Transition.Off"; 1773 hostCommand = true; 1774 } 1775 else if (resetType == "GracefulRestart") 1776 { 1777 command = 1778 "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot"; 1779 hostCommand = true; 1780 } 1781 else if (resetType == "PowerCycle") 1782 { 1783 command = "xyz.openbmc_project.State.Host.Transition.Reboot"; 1784 hostCommand = true; 1785 } 1786 else if (resetType == "Nmi") 1787 { 1788 doNMI(asyncResp); 1789 return; 1790 } 1791 else 1792 { 1793 messages::actionParameterUnknown(res, "Reset", resetType); 1794 return; 1795 } 1796 1797 if (hostCommand) 1798 { 1799 crow::connections::systemBus->async_method_call( 1800 [asyncResp, resetType](const boost::system::error_code ec) { 1801 if (ec) 1802 { 1803 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 1804 if (ec.value() == boost::asio::error::invalid_argument) 1805 { 1806 messages::actionParameterNotSupported( 1807 asyncResp->res, resetType, "Reset"); 1808 } 1809 else 1810 { 1811 messages::internalError(asyncResp->res); 1812 } 1813 return; 1814 } 1815 messages::success(asyncResp->res); 1816 }, 1817 "xyz.openbmc_project.State.Host", 1818 "/xyz/openbmc_project/state/host0", 1819 "org.freedesktop.DBus.Properties", "Set", 1820 "xyz.openbmc_project.State.Host", "RequestedHostTransition", 1821 std::variant<std::string>{command}); 1822 } 1823 else 1824 { 1825 crow::connections::systemBus->async_method_call( 1826 [asyncResp, resetType](const boost::system::error_code ec) { 1827 if (ec) 1828 { 1829 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 1830 if (ec.value() == boost::asio::error::invalid_argument) 1831 { 1832 messages::actionParameterNotSupported( 1833 asyncResp->res, resetType, "Reset"); 1834 } 1835 else 1836 { 1837 messages::internalError(asyncResp->res); 1838 } 1839 return; 1840 } 1841 messages::success(asyncResp->res); 1842 }, 1843 "xyz.openbmc_project.State.Chassis", 1844 "/xyz/openbmc_project/state/chassis0", 1845 "org.freedesktop.DBus.Properties", "Set", 1846 "xyz.openbmc_project.State.Chassis", "RequestedPowerTransition", 1847 std::variant<std::string>{command}); 1848 } 1849 } 1850 /** 1851 * Function transceives data with dbus directly. 1852 */ 1853 void doNMI(const std::shared_ptr<AsyncResp>& asyncResp) 1854 { 1855 constexpr char const* serviceName = 1856 "xyz.openbmc_project.Control.Host.NMI"; 1857 constexpr char const* objectPath = 1858 "/xyz/openbmc_project/control/host0/nmi"; 1859 constexpr char const* interfaceName = 1860 "xyz.openbmc_project.Control.Host.NMI"; 1861 constexpr char const* method = "NMI"; 1862 1863 crow::connections::systemBus->async_method_call( 1864 [asyncResp](const boost::system::error_code ec) { 1865 if (ec) 1866 { 1867 BMCWEB_LOG_ERROR << " Bad D-Bus request error: " << ec; 1868 messages::internalError(asyncResp->res); 1869 return; 1870 } 1871 messages::success(asyncResp->res); 1872 }, 1873 serviceName, objectPath, interfaceName, method); 1874 } 1875 }; 1876 1877 /** 1878 * Systems derived class for delivering Computer Systems Schema. 1879 */ 1880 class Systems : public Node 1881 { 1882 public: 1883 /* 1884 * Default Constructor 1885 */ 1886 Systems(App& app) : Node(app, "/redfish/v1/Systems/system/") 1887 { 1888 entityPrivileges = { 1889 {boost::beast::http::verb::get, {{"Login"}}}, 1890 {boost::beast::http::verb::head, {{"Login"}}}, 1891 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1892 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1893 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1894 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1895 } 1896 1897 private: 1898 /** 1899 * Functions triggers appropriate requests on DBus 1900 */ 1901 void doGet(crow::Response& res, const crow::Request&, 1902 const std::vector<std::string>&) override 1903 { 1904 res.jsonValue["@odata.type"] = "#ComputerSystem.v1_12_0.ComputerSystem"; 1905 res.jsonValue["Name"] = "system"; 1906 res.jsonValue["Id"] = "system"; 1907 res.jsonValue["SystemType"] = "Physical"; 1908 res.jsonValue["Description"] = "Computer System"; 1909 res.jsonValue["ProcessorSummary"]["Count"] = 0; 1910 res.jsonValue["ProcessorSummary"]["Status"]["State"] = "Disabled"; 1911 res.jsonValue["MemorySummary"]["TotalSystemMemoryGiB"] = uint64_t(0); 1912 res.jsonValue["MemorySummary"]["Status"]["State"] = "Disabled"; 1913 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system"; 1914 1915 res.jsonValue["Processors"] = { 1916 {"@odata.id", "/redfish/v1/Systems/system/Processors"}}; 1917 res.jsonValue["Memory"] = { 1918 {"@odata.id", "/redfish/v1/Systems/system/Memory"}}; 1919 res.jsonValue["Storage"] = { 1920 {"@odata.id", "/redfish/v1/Systems/system/Storage"}}; 1921 1922 res.jsonValue["Actions"]["#ComputerSystem.Reset"] = { 1923 {"target", 1924 "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset"}, 1925 {"@Redfish.ActionInfo", 1926 "/redfish/v1/Systems/system/ResetActionInfo"}}; 1927 1928 res.jsonValue["LogServices"] = { 1929 {"@odata.id", "/redfish/v1/Systems/system/LogServices"}}; 1930 1931 res.jsonValue["Bios"] = { 1932 {"@odata.id", "/redfish/v1/Systems/system/Bios"}}; 1933 1934 res.jsonValue["Links"]["ManagedBy"] = { 1935 {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 1936 1937 res.jsonValue["Status"] = { 1938 {"Health", "OK"}, 1939 {"State", "Enabled"}, 1940 }; 1941 auto asyncResp = std::make_shared<AsyncResp>(res); 1942 1943 constexpr const std::array<const char*, 4> inventoryForSystems = { 1944 "xyz.openbmc_project.Inventory.Item.Dimm", 1945 "xyz.openbmc_project.Inventory.Item.Cpu", 1946 "xyz.openbmc_project.Inventory.Item.Drive", 1947 "xyz.openbmc_project.Inventory.Item.StorageController"}; 1948 1949 auto health = std::make_shared<HealthPopulate>(asyncResp); 1950 crow::connections::systemBus->async_method_call( 1951 [health](const boost::system::error_code ec, 1952 std::vector<std::string>& resp) { 1953 if (ec) 1954 { 1955 // no inventory 1956 return; 1957 } 1958 1959 health->inventory = std::move(resp); 1960 }, 1961 "xyz.openbmc_project.ObjectMapper", 1962 "/xyz/openbmc_project/object_mapper", 1963 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 1964 int32_t(0), inventoryForSystems); 1965 1966 health->populate(); 1967 1968 getMainChassisId(asyncResp, [](const std::string& chassisId, 1969 std::shared_ptr<AsyncResp> aRsp) { 1970 aRsp->res.jsonValue["Links"]["Chassis"] = { 1971 {{"@odata.id", "/redfish/v1/Chassis/" + chassisId}}}; 1972 }); 1973 1974 getIndicatorLedState(asyncResp); 1975 getComputerSystem(asyncResp, health); 1976 getHostState(asyncResp); 1977 getBootProperties(asyncResp); 1978 getPCIeDeviceList(asyncResp, "PCIeDevices"); 1979 getHostWatchdogTimer(asyncResp); 1980 getPowerRestorePolicy(asyncResp); 1981 getAutomaticRetry(asyncResp); 1982 getLastResetTime(asyncResp); 1983 #ifdef BMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE 1984 getProvisioningStatus(asyncResp); 1985 #endif 1986 } 1987 1988 void doPatch(crow::Response& res, const crow::Request& req, 1989 const std::vector<std::string>&) override 1990 { 1991 std::optional<std::string> indicatorLed; 1992 std::optional<nlohmann::json> bootProps; 1993 std::optional<nlohmann::json> wdtTimerProps; 1994 std::optional<std::string> powerRestorePolicy; 1995 auto asyncResp = std::make_shared<AsyncResp>(res); 1996 1997 if (!json_util::readJson(req, res, "IndicatorLED", indicatorLed, "Boot", 1998 bootProps, "WatchdogTimer", wdtTimerProps, 1999 "PowerRestorePolicy", powerRestorePolicy)) 2000 { 2001 return; 2002 } 2003 2004 res.result(boost::beast::http::status::no_content); 2005 2006 if (wdtTimerProps) 2007 { 2008 std::optional<bool> wdtEnable; 2009 std::optional<std::string> wdtTimeOutAction; 2010 2011 if (!json_util::readJson(*wdtTimerProps, asyncResp->res, 2012 "FunctionEnabled", wdtEnable, 2013 "TimeoutAction", wdtTimeOutAction)) 2014 { 2015 return; 2016 } 2017 setWDTProperties(asyncResp, std::move(wdtEnable), 2018 std::move(wdtTimeOutAction)); 2019 } 2020 2021 if (bootProps) 2022 { 2023 std::optional<std::string> bootSource; 2024 std::optional<std::string> bootEnable; 2025 std::optional<std::string> automaticRetryConfig; 2026 2027 if (!json_util::readJson( 2028 *bootProps, asyncResp->res, "BootSourceOverrideTarget", 2029 bootSource, "BootSourceOverrideEnabled", bootEnable, 2030 "AutomaticRetryConfig", automaticRetryConfig)) 2031 { 2032 return; 2033 } 2034 if (bootSource || bootEnable) 2035 { 2036 setBootSourceProperties(asyncResp, std::move(bootSource), 2037 std::move(bootEnable)); 2038 } 2039 if (automaticRetryConfig) 2040 { 2041 setAutomaticRetry(asyncResp, std::move(*automaticRetryConfig)); 2042 } 2043 } 2044 2045 if (indicatorLed) 2046 { 2047 setIndicatorLedState(asyncResp, std::move(*indicatorLed)); 2048 } 2049 2050 if (powerRestorePolicy) 2051 { 2052 setPowerRestorePolicy(asyncResp, std::move(*powerRestorePolicy)); 2053 } 2054 } 2055 }; 2056 2057 /** 2058 * SystemResetActionInfo derived class for delivering Computer Systems 2059 * ResetType AllowableValues using ResetInfo schema. 2060 */ 2061 class SystemResetActionInfo : public Node 2062 { 2063 public: 2064 /* 2065 * Default Constructor 2066 */ 2067 SystemResetActionInfo(App& app) : 2068 Node(app, "/redfish/v1/Systems/system/ResetActionInfo/") 2069 { 2070 entityPrivileges = { 2071 {boost::beast::http::verb::get, {{"Login"}}}, 2072 {boost::beast::http::verb::head, {{"Login"}}}, 2073 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 2074 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 2075 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 2076 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 2077 } 2078 2079 private: 2080 /** 2081 * Functions triggers appropriate requests on DBus 2082 */ 2083 void doGet(crow::Response& res, const crow::Request&, 2084 const std::vector<std::string>&) override 2085 { 2086 res.jsonValue = { 2087 {"@odata.type", "#ActionInfo.v1_1_2.ActionInfo"}, 2088 {"@odata.id", "/redfish/v1/Systems/system/ResetActionInfo"}, 2089 {"Name", "Reset Action Info"}, 2090 {"Id", "ResetActionInfo"}, 2091 {"Parameters", 2092 {{{"Name", "ResetType"}, 2093 {"Required", true}, 2094 {"DataType", "String"}, 2095 {"AllowableValues", 2096 {"On", "ForceOff", "ForceOn", "ForceRestart", "GracefulRestart", 2097 "GracefulShutdown", "PowerCycle", "Nmi"}}}}}}; 2098 res.end(); 2099 } 2100 }; 2101 } // namespace redfish 2102