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