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