xref: /openbmc/phosphor-host-ipmid/chassishandler.cpp (revision ad588bf51c3acb8ec6e4fc4ee6ff81688a166045)
1 #include "config.h"
2 
3 #include "chassishandler.hpp"
4 
5 #include <arpa/inet.h>
6 #include <endian.h>
7 #include <limits.h>
8 #include <mapper.h>
9 #include <netinet/in.h>
10 
11 #include <array>
12 #include <chrono>
13 #include <cstring>
14 #include <filesystem>
15 #include <fstream>
16 #include <future>
17 #include <ipmid/api.hpp>
18 #include <ipmid/types.hpp>
19 #include <ipmid/utils.hpp>
20 #include <map>
21 #include <phosphor-logging/elog-errors.hpp>
22 #include <phosphor-logging/log.hpp>
23 #include <sdbusplus/bus.hpp>
24 #include <sdbusplus/message/types.hpp>
25 #include <sdbusplus/server/object.hpp>
26 #include <sdbusplus/timer.hpp>
27 #include <settings.hpp>
28 #include <sstream>
29 #include <string>
30 #include <xyz/openbmc_project/Common/error.hpp>
31 #include <xyz/openbmc_project/Control/Boot/Mode/server.hpp>
32 #include <xyz/openbmc_project/Control/Boot/Source/server.hpp>
33 #include <xyz/openbmc_project/Control/Boot/Type/server.hpp>
34 #include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp>
35 #include <xyz/openbmc_project/State/Chassis/server.hpp>
36 #include <xyz/openbmc_project/State/Host/server.hpp>
37 #include <xyz/openbmc_project/State/PowerOnHours/server.hpp>
38 
39 std::unique_ptr<phosphor::Timer> identifyTimer
40     __attribute__((init_priority(101)));
41 
42 static ChassisIDState chassisIDState = ChassisIDState::reserved;
43 static constexpr uint8_t setParmVersion = 0x01;
44 
45 constexpr size_t sizeVersion = 2;
46 constexpr size_t DEFAULT_IDENTIFY_TIME_OUT = 15;
47 
48 // PetiBoot-Specific
49 static constexpr uint8_t netConfInitialBytes[] = {0x80, 0x21, 0x70, 0x62,
50                                                   0x21, 0x00, 0x01, 0x06};
51 static constexpr uint8_t oemParmStart = 96;
52 static constexpr uint8_t oemParmEnd = 127;
53 
54 static constexpr size_t cookieOffset = 1;
55 static constexpr size_t versionOffset = 5;
56 static constexpr size_t addrSizeOffset = 8;
57 static constexpr size_t macOffset = 9;
58 static constexpr size_t addrTypeOffset = 16;
59 static constexpr size_t ipAddrOffset = 17;
60 
61 static constexpr size_t encIdentifyObjectsSize = 1;
62 static constexpr size_t chassisIdentifyReqLength = 2;
63 static constexpr size_t identifyIntervalPos = 0;
64 static constexpr size_t forceIdentifyPos = 1;
65 
66 namespace ipmi
67 {
68 constexpr Cc ccParmNotSupported = 0x80;
69 
70 static inline auto responseParmNotSupported()
71 {
72     return response(ccParmNotSupported);
73 }
74 } // namespace ipmi
75 
76 void register_netfn_chassis_functions() __attribute__((constructor));
77 
78 // Host settings in dbus
79 // Service name should be referenced by connection name got via object mapper
80 const char* settings_object_name = "/org/openbmc/settings/host0";
81 const char* settings_intf_name = "org.freedesktop.DBus.Properties";
82 const char* identify_led_object_name =
83     "/xyz/openbmc_project/led/groups/enclosure_identify";
84 
85 constexpr auto SETTINGS_ROOT = "/";
86 constexpr auto SETTINGS_MATCH = "host0";
87 
88 constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP";
89 constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress";
90 
91 static constexpr auto chassisStateRoot = "/xyz/openbmc_project/state";
92 static constexpr auto chassisPOHStateIntf =
93     "xyz.openbmc_project.State.PowerOnHours";
94 static constexpr auto pohCounterProperty = "POHCounter";
95 static constexpr auto match = "chassis0";
96 const static constexpr char chassisCapIntf[] =
97     "xyz.openbmc_project.Control.ChassisCapabilities";
98 const static constexpr char chassisIntrusionProp[] = "ChassisIntrusionEnabled";
99 const static constexpr char chassisFrontPanelLockoutProp[] =
100     "ChassisFrontPanelLockoutEnabled";
101 const static constexpr char chassisNMIProp[] = "ChassisNMIEnabled";
102 const static constexpr char chassisPowerInterlockProp[] =
103     "ChassisPowerInterlockEnabled";
104 const static constexpr char chassisFRUDevAddrProp[] = "FRUDeviceAddress";
105 const static constexpr char chassisSDRDevAddrProp[] = "SDRDeviceAddress";
106 const static constexpr char chassisSELDevAddrProp[] = "SELDeviceAddress";
107 const static constexpr char chassisSMDevAddrProp[] = "SMDeviceAddress";
108 const static constexpr char chassisBridgeDevAddrProp[] = "BridgeDeviceAddress";
109 static constexpr uint8_t chassisCapFlagMask = 0x0f;
110 static constexpr uint8_t chassisCapAddrMask = 0xfe;
111 static constexpr const char* powerButtonIntf =
112     "xyz.openbmc_project.Chassis.Buttons.Power";
113 static constexpr const char* powerButtonPath =
114     "/xyz/openbmc_project/Chassis/Buttons/Power0";
115 static constexpr const char* resetButtonIntf =
116     "xyz.openbmc_project.Chassis.Buttons.Reset";
117 static constexpr const char* resetButtonPath =
118     "/xyz/openbmc_project/Chassis/Buttons/Reset0";
119 
120 // Phosphor Host State manager
121 namespace State = sdbusplus::xyz::openbmc_project::State::server;
122 
123 namespace fs = std::filesystem;
124 
125 using namespace phosphor::logging;
126 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
127 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
128 
129 namespace chassis
130 {
131 namespace internal
132 {
133 
134 constexpr auto bootSettingsPath = "/xyz/openbmc_project/control/host0/boot";
135 constexpr auto bootEnableIntf = "xyz.openbmc_project.Object.Enable";
136 constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
137 constexpr auto bootTypeIntf = "xyz.openbmc_project.Control.Boot.Type";
138 constexpr auto bootSourceIntf = "xyz.openbmc_project.Control.Boot.Source";
139 constexpr auto bootSettingsOneTimePath =
140     "/xyz/openbmc_project/control/host0/boot/one_time";
141 constexpr auto bootOneTimeIntf = "xyz.openbmc_project.Object.Enable";
142 
143 constexpr auto powerRestoreIntf =
144     "xyz.openbmc_project.Control.Power.RestorePolicy";
145 sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection());
146 
147 namespace cache
148 {
149 
150 std::unique_ptr<settings::Objects> objectsPtr = nullptr;
151 
152 settings::Objects& getObjects()
153 {
154     if (objectsPtr == nullptr)
155     {
156         objectsPtr = std::make_unique<settings::Objects>(
157             dbus, std::vector<std::string>{bootModeIntf, bootTypeIntf,
158                                            bootSourceIntf, powerRestoreIntf});
159     }
160     return *objectsPtr;
161 }
162 
163 } // namespace cache
164 } // namespace internal
165 } // namespace chassis
166 
167 namespace poh
168 {
169 
170 constexpr auto minutesPerCount = 60;
171 
172 } // namespace poh
173 
174 int getHostNetworkData(ipmi::message::Payload& payload)
175 {
176     ipmi::PropertyMap properties;
177     int rc = 0;
178     uint8_t addrSize = ipmi::network::IPV4_ADDRESS_SIZE_BYTE;
179 
180     try
181     {
182         // TODO There may be cases where an interface is implemented by multiple
183         // objects,to handle such cases we are interested on that object
184         //  which are on interested busname.
185         //  Currenlty mapper doesn't give the readable busname(gives busid)
186         //  so we can't match with bus name so giving some object specific info
187         //  as SETTINGS_MATCH.
188         //  Later SETTINGS_MATCH will be replaced with busname.
189 
190         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
191 
192         auto ipObjectInfo = ipmi::getDbusObject(bus, IP_INTERFACE,
193                                                 SETTINGS_ROOT, SETTINGS_MATCH);
194 
195         auto macObjectInfo = ipmi::getDbusObject(bus, MAC_INTERFACE,
196                                                  SETTINGS_ROOT, SETTINGS_MATCH);
197 
198         properties = ipmi::getAllDbusProperties(
199             bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE);
200         auto variant = ipmi::getDbusProperty(bus, macObjectInfo.second,
201                                              macObjectInfo.first, MAC_INTERFACE,
202                                              "MACAddress");
203 
204         auto ipAddress = std::get<std::string>(properties["Address"]);
205 
206         auto gateway = std::get<std::string>(properties["Gateway"]);
207 
208         auto prefix = std::get<uint8_t>(properties["PrefixLength"]);
209 
210         uint8_t isStatic =
211             (std::get<std::string>(properties["Origin"]) ==
212              "xyz.openbmc_project.Network.IP.AddressOrigin.Static")
213                 ? 1
214                 : 0;
215 
216         auto MACAddress = std::get<std::string>(variant);
217 
218         // it is expected here that we should get the valid data
219         // but we may also get the default values.
220         // Validation of the data is done by settings.
221         //
222         // if mac address is default mac address then
223         // don't send blank override.
224         if ((MACAddress == ipmi::network::DEFAULT_MAC_ADDRESS))
225         {
226             rc = -1;
227             return rc;
228         }
229         // if addr is static then ipaddress,gateway,prefix
230         // should not be default one,don't send blank override.
231         if (isStatic)
232         {
233             if ((ipAddress == ipmi::network::DEFAULT_ADDRESS) ||
234                 (gateway == ipmi::network::DEFAULT_ADDRESS) || (!prefix))
235             {
236                 rc = -1;
237                 return rc;
238             }
239         }
240 
241         std::string token;
242         std::stringstream ss(MACAddress);
243 
244         // First pack macOffset no of bytes in payload.
245         // Latter this PetiBoot-Specific data will be populated.
246         std::vector<uint8_t> payloadInitialBytes(macOffset);
247         payload.pack(payloadInitialBytes);
248 
249         while (std::getline(ss, token, ':'))
250         {
251             payload.pack(stoi(token, nullptr, 16));
252         }
253 
254         payload.pack(0x00);
255 
256         payload.pack(isStatic);
257 
258         uint8_t addressFamily = (std::get<std::string>(properties["Type"]) ==
259                                  "xyz.openbmc_project.Network.IP.Protocol.IPv4")
260                                     ? AF_INET
261                                     : AF_INET6;
262 
263         addrSize = (addressFamily == AF_INET)
264                        ? ipmi::network::IPV4_ADDRESS_SIZE_BYTE
265                        : ipmi::network::IPV6_ADDRESS_SIZE_BYTE;
266 
267         // ipaddress and gateway would be in IPv4 format
268         std::vector<uint8_t> addrInBinary(addrSize);
269         inet_pton(addressFamily, ipAddress.c_str(),
270                   reinterpret_cast<void*>(addrInBinary.data()));
271 
272         payload.pack(addrInBinary);
273 
274         payload.pack(prefix);
275 
276         std::vector<uint8_t> gatewayDetails(addrSize);
277         inet_pton(addressFamily, gateway.c_str(),
278                   reinterpret_cast<void*>(gatewayDetails.data()));
279         payload.pack(gatewayDetails);
280     }
281     catch (const InternalFailure& e)
282     {
283         commit<InternalFailure>();
284         rc = -1;
285         return rc;
286     }
287 
288     // PetiBoot-Specific
289     // If success then copy the first 9 bytes to the payload message
290     // payload first 2 bytes contain the parameter values. Skip that 2 bytes.
291     uint8_t skipFirstTwoBytes = 2;
292     size_t payloadSize = payload.size();
293     uint8_t* configDataStartingAddress = payload.data() + skipFirstTwoBytes;
294 
295     if (payloadSize < skipFirstTwoBytes + sizeof(netConfInitialBytes))
296     {
297         log<level::ERR>("Invalid net config ");
298         rc = -1;
299         return rc;
300     }
301     std::copy(netConfInitialBytes,
302               netConfInitialBytes + sizeof(netConfInitialBytes),
303               configDataStartingAddress);
304 
305     if (payloadSize < skipFirstTwoBytes + addrSizeOffset + sizeof(addrSize))
306     {
307         log<level::ERR>("Invalid length of address size");
308         rc = -1;
309         return rc;
310     }
311     std::copy(&addrSize, &(addrSize) + sizeof(addrSize),
312               configDataStartingAddress + addrSizeOffset);
313 
314 #ifdef _IPMI_DEBUG_
315     std::printf("\n===Printing the IPMI Formatted Data========\n");
316 
317     for (uint8_t pos = 0; pos < index; pos++)
318     {
319         std::printf("%02x ", payloadStartingAddress[pos]);
320     }
321 #endif
322 
323     return rc;
324 }
325 
326 /** @brief convert IPv4 and IPv6 addresses from binary to text form.
327  *  @param[in] family - IPv4/Ipv6
328  *  @param[in] data - req data pointer.
329  *  @param[in] offset - offset in the data.
330  *  @param[in] addrSize - size of the data which needs to be read from offset.
331  *  @returns address in text form.
332  */
333 
334 std::string getAddrStr(uint8_t family, uint8_t* data, uint8_t offset,
335                        uint8_t addrSize)
336 {
337     char ipAddr[INET6_ADDRSTRLEN] = {};
338 
339     switch (family)
340     {
341         case AF_INET:
342         {
343             struct sockaddr_in addr4
344             {
345             };
346             std::memcpy(&addr4.sin_addr.s_addr, &data[offset], addrSize);
347 
348             inet_ntop(AF_INET, &addr4.sin_addr, ipAddr, INET_ADDRSTRLEN);
349 
350             break;
351         }
352         case AF_INET6:
353         {
354             struct sockaddr_in6 addr6
355             {
356             };
357             std::memcpy(&addr6.sin6_addr.s6_addr, &data[offset], addrSize);
358 
359             inet_ntop(AF_INET6, &addr6.sin6_addr, ipAddr, INET6_ADDRSTRLEN);
360 
361             break;
362         }
363         default:
364         {
365             return {};
366         }
367     }
368 
369     return ipAddr;
370 }
371 
372 ipmi::Cc setHostNetworkData(ipmi::message::Payload& data)
373 {
374     using namespace std::string_literals;
375     std::string hostNetworkConfig;
376     std::string mac("00:00:00:00:00:00");
377     std::string ipAddress, gateway;
378     std::string addrOrigin{0};
379     uint8_t addrSize{0};
380     std::string addressOrigin =
381         "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP";
382     std::string addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
383     uint8_t prefix{0};
384     uint8_t family = AF_INET;
385 
386     // cookie starts from second byte
387     // version starts from sixth byte
388 
389     try
390     {
391         do
392         {
393             // cookie ==  0x21 0x70 0x62 0x21
394             data.trailingOk = true;
395             auto msgLen = data.size();
396             std::vector<uint8_t> msgPayloadBytes(msgLen);
397             if (data.unpack(msgPayloadBytes) != 0 || !data.fullyUnpacked())
398             {
399                 log<level::ERR>(
400                     "Error in unpacking message of setHostNetworkData");
401                 return ipmi::ccReqDataLenInvalid;
402             }
403 
404             uint8_t* msgPayloadStartingPos = msgPayloadBytes.data();
405             constexpr size_t cookieSize = 4;
406             if (msgLen < cookieOffset + cookieSize)
407             {
408                 log<level::ERR>(
409                     "Error in cookie getting of setHostNetworkData");
410                 return ipmi::ccReqDataLenInvalid;
411             }
412             if (std::equal(msgPayloadStartingPos + cookieOffset,
413                            msgPayloadStartingPos + cookieOffset + cookieSize,
414                            (netConfInitialBytes + cookieOffset)) != 0)
415             {
416                 // all cookie == 0
417                 if (std::all_of(msgPayloadStartingPos + cookieOffset,
418                                 msgPayloadStartingPos + cookieOffset +
419                                     cookieSize,
420                                 [](int i) { return i == 0; }) == true)
421                 {
422                     // need to zero out the network settings.
423                     break;
424                 }
425 
426                 log<level::ERR>("Invalid Cookie");
427                 elog<InternalFailure>();
428             }
429 
430             // vesion == 0x00 0x01
431             if (msgLen < versionOffset + sizeVersion)
432             {
433                 log<level::ERR>(
434                     "Error in version getting of setHostNetworkData");
435                 return ipmi::ccReqDataLenInvalid;
436             }
437             if (std::equal(msgPayloadStartingPos + versionOffset,
438                            msgPayloadStartingPos + versionOffset + sizeVersion,
439                            (netConfInitialBytes + versionOffset)) != 0)
440             {
441                 log<level::ERR>("Invalid Version");
442                 elog<InternalFailure>();
443             }
444 
445             if (msgLen < macOffset + 6)
446             {
447                 log<level::ERR>(
448                     "Error in mac address getting of setHostNetworkData");
449                 return ipmi::ccReqDataLenInvalid;
450             }
451             std::stringstream result;
452             std::copy((msgPayloadStartingPos + macOffset),
453                       (msgPayloadStartingPos + macOffset + 5),
454                       std::ostream_iterator<int>(result, ":"));
455             mac = result.str();
456 
457             if (msgLen < addrTypeOffset + sizeof(decltype(addrOrigin)))
458             {
459                 log<level::ERR>(
460                     "Error in original address getting of setHostNetworkData");
461                 return ipmi::ccReqDataLenInvalid;
462             }
463             std::copy(msgPayloadStartingPos + addrTypeOffset,
464                       msgPayloadStartingPos + addrTypeOffset +
465                           sizeof(decltype(addrOrigin)),
466                       std::ostream_iterator<int>(result, ""));
467             addrOrigin = result.str();
468 
469             if (!addrOrigin.empty())
470             {
471                 addressOrigin =
472                     "xyz.openbmc_project.Network.IP.AddressOrigin.Static";
473             }
474 
475             if (msgLen < addrSizeOffset + sizeof(decltype(addrSize)))
476             {
477                 log<level::ERR>(
478                     "Error in address size getting of setHostNetworkData");
479                 return ipmi::ccReqDataLenInvalid;
480             }
481             // Get the address size
482             std::copy(msgPayloadStartingPos + addrSizeOffset,
483                       (msgPayloadStartingPos + addrSizeOffset +
484                        sizeof(decltype(addrSize))),
485                       &addrSize);
486 
487             uint8_t prefixOffset = ipAddrOffset + addrSize;
488             if (msgLen < prefixOffset + sizeof(decltype(prefix)))
489             {
490                 log<level::ERR>(
491                     "Error in prefix getting of setHostNetworkData");
492                 return ipmi::ccReqDataLenInvalid;
493             }
494             std::copy(msgPayloadStartingPos + prefixOffset,
495                       (msgPayloadStartingPos + prefixOffset +
496                        sizeof(decltype(prefix))),
497                       &prefix);
498 
499             uint8_t gatewayOffset = prefixOffset + sizeof(decltype(prefix));
500             if (addrSize != ipmi::network::IPV4_ADDRESS_SIZE_BYTE)
501             {
502                 addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv6";
503                 family = AF_INET6;
504             }
505 
506             if (msgLen < ipAddrOffset + addrSize)
507             {
508                 log<level::ERR>(
509                     "Error in IP address getting of setHostNetworkData");
510                 return ipmi::ccReqDataLenInvalid;
511             }
512             ipAddress = getAddrStr(family, msgPayloadStartingPos, ipAddrOffset,
513                                    addrSize);
514 
515             if (msgLen < gatewayOffset + addrSize)
516             {
517                 log<level::ERR>(
518                     "Error in gateway address getting of setHostNetworkData");
519                 return ipmi::ccReqDataLenInvalid;
520             }
521             gateway = getAddrStr(family, msgPayloadStartingPos, gatewayOffset,
522                                  addrSize);
523 
524         } while (0);
525 
526         // Cookie == 0 or it is a valid cookie
527         hostNetworkConfig += "ipaddress="s + ipAddress + ",prefix="s +
528                              std::to_string(prefix) + ",gateway="s + gateway +
529                              ",mac="s + mac + ",addressOrigin="s +
530                              addressOrigin;
531 
532         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
533 
534         auto ipObjectInfo = ipmi::getDbusObject(bus, IP_INTERFACE,
535                                                 SETTINGS_ROOT, SETTINGS_MATCH);
536         auto macObjectInfo = ipmi::getDbusObject(bus, MAC_INTERFACE,
537                                                  SETTINGS_ROOT, SETTINGS_MATCH);
538         // set the dbus property
539         ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first,
540                               IP_INTERFACE, "Address", std::string(ipAddress));
541         ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first,
542                               IP_INTERFACE, "PrefixLength", prefix);
543         ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first,
544                               IP_INTERFACE, "Origin", addressOrigin);
545         ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first,
546                               IP_INTERFACE, "Gateway", std::string(gateway));
547         ipmi::setDbusProperty(
548             bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE, "Type",
549             std::string("xyz.openbmc_project.Network.IP.Protocol.IPv4"));
550         ipmi::setDbusProperty(bus, macObjectInfo.second, macObjectInfo.first,
551                               MAC_INTERFACE, "MACAddress", std::string(mac));
552 
553         log<level::DEBUG>("Network configuration changed",
554                           entry("NETWORKCONFIG=%s", hostNetworkConfig.c_str()));
555     }
556     catch (const sdbusplus::exception_t& e)
557     {
558         commit<InternalFailure>();
559         log<level::ERR>("Error in  ipmiChassisSetSysBootOptions call");
560         return ipmi::ccUnspecifiedError;
561     }
562 
563     return ipmi::ccSuccess;
564 }
565 
566 uint32_t getPOHCounter()
567 {
568     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
569 
570     auto chassisStateObj =
571         ipmi::getDbusObject(bus, chassisPOHStateIntf, chassisStateRoot, match);
572 
573     auto service =
574         ipmi::getService(bus, chassisPOHStateIntf, chassisStateObj.first);
575 
576     auto propValue =
577         ipmi::getDbusProperty(bus, service, chassisStateObj.first,
578                               chassisPOHStateIntf, pohCounterProperty);
579 
580     return std::get<uint32_t>(propValue);
581 }
582 
583 /** @brief Implements the get chassis capabilities command
584  *
585  *  @returns IPMI completion code plus response data
586  *  chassisCapFlags        - chassis capability flag
587  *  chassisFRUInfoDevAddr  - chassis FRU info Device Address
588  *  chassisSDRDevAddr      - chassis SDR device address
589  *  chassisSELDevAddr      - chassis SEL device address
590  *  chassisSMDevAddr       - chassis system management device address
591  *  chassisBridgeDevAddr   - chassis bridge device address
592  */
593 ipmi::RspType<bool,    // chassis intrusion sensor
594               bool,    // chassis Front panel lockout
595               bool,    // chassis NMI
596               bool,    // chassis power interlock
597               uint4_t, // reserved
598               uint8_t, // chassis FRU info Device Address
599               uint8_t, // chassis SDR device address
600               uint8_t, // chassis SEL device address
601               uint8_t, // chassis system management device address
602               uint8_t  // chassis bridge device address
603               >
604     ipmiGetChassisCap()
605 {
606     ipmi::PropertyMap properties;
607     try
608     {
609         sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
610 
611         ipmi::DbusObjectInfo chassisCapObject =
612             ipmi::getDbusObject(bus, chassisCapIntf);
613 
614         // capabilities flags
615         // [7..4] - reserved
616         // [3] – 1b = provides power interlock  (IPM 1.5)
617         // [2] – 1b = provides Diagnostic Interrupt (FP NMI)
618         // [1] – 1b = provides “Front Panel Lockout” (indicates that the chassis
619         // has capabilities
620         //            to lock out external power control and reset button or
621         //            front panel interfaces and/or detect tampering with those
622         //            interfaces).
623         // [0] -1b = Chassis provides intrusion (physical security) sensor.
624         // set to default value 0x0.
625 
626         properties =
627             ipmi::getAllDbusProperties(bus, chassisCapObject.second,
628                                        chassisCapObject.first, chassisCapIntf);
629     }
630     catch (const std::exception& e)
631     {
632         log<level::ERR>("Failed to fetch Chassis Capability properties",
633                         entry("ERROR=%s", e.what()));
634         return ipmi::responseUnspecifiedError();
635     }
636 
637     bool* chassisIntrusionFlag =
638         std::get_if<bool>(&properties[chassisIntrusionProp]);
639     if (chassisIntrusionFlag == nullptr)
640     {
641         log<level::ERR>("Error to get chassis Intrusion flags");
642         return ipmi::responseUnspecifiedError();
643     }
644     bool* chassisFrontPanelFlag =
645         std::get_if<bool>(&properties[chassisFrontPanelLockoutProp]);
646     if (chassisFrontPanelFlag == nullptr)
647     {
648         log<level::ERR>("Error to get chassis intrusion flags");
649         return ipmi::responseUnspecifiedError();
650     }
651     bool* chassisNMIFlag = std::get_if<bool>(&properties[chassisNMIProp]);
652     if (chassisNMIFlag == nullptr)
653     {
654         log<level::ERR>("Error to get chassis NMI flags");
655         return ipmi::responseUnspecifiedError();
656     }
657     bool* chassisPowerInterlockFlag =
658         std::get_if<bool>(&properties[chassisPowerInterlockProp]);
659     if (chassisPowerInterlockFlag == nullptr)
660     {
661         log<level::ERR>("Error to get chassis power interlock flags");
662         return ipmi::responseUnspecifiedError();
663     }
664     uint8_t* chassisFRUInfoDevAddr =
665         std::get_if<uint8_t>(&properties[chassisFRUDevAddrProp]);
666     if (chassisFRUInfoDevAddr == nullptr)
667     {
668         log<level::ERR>("Error to get chassis FRU info device address");
669         return ipmi::responseUnspecifiedError();
670     }
671     uint8_t* chassisSDRDevAddr =
672         std::get_if<uint8_t>(&properties[chassisSDRDevAddrProp]);
673     if (chassisSDRDevAddr == nullptr)
674     {
675         log<level::ERR>("Error to get chassis SDR device address");
676         return ipmi::responseUnspecifiedError();
677     }
678     uint8_t* chassisSELDevAddr =
679         std::get_if<uint8_t>(&properties[chassisSELDevAddrProp]);
680     if (chassisSELDevAddr == nullptr)
681     {
682         log<level::ERR>("Error to get chassis SEL device address");
683         return ipmi::responseUnspecifiedError();
684     }
685     uint8_t* chassisSMDevAddr =
686         std::get_if<uint8_t>(&properties[chassisSMDevAddrProp]);
687     if (chassisSMDevAddr == nullptr)
688     {
689         log<level::ERR>("Error to get chassis SM device address");
690         return ipmi::responseUnspecifiedError();
691     }
692     uint8_t* chassisBridgeDevAddr =
693         std::get_if<uint8_t>(&properties[chassisBridgeDevAddrProp]);
694     if (chassisBridgeDevAddr == nullptr)
695     {
696         log<level::ERR>("Error to get chassis bridge device address");
697         return ipmi::responseUnspecifiedError();
698     }
699 
700     return ipmi::responseSuccess(*chassisIntrusionFlag, *chassisFrontPanelFlag,
701                                  *chassisNMIFlag, *chassisPowerInterlockFlag, 0,
702                                  *chassisFRUInfoDevAddr, *chassisSDRDevAddr,
703                                  *chassisSELDevAddr, *chassisSMDevAddr,
704                                  *chassisBridgeDevAddr);
705 }
706 
707 /** @brief implements set chassis capalibities command
708  *  @param intrusion        - chassis intrusion
709  *  @param fpLockout        - frontpannel lockout
710  *  @param reserved1        - skip one bit
711  *  @param fruDeviceAddr    - chassis FRU info Device Address
712  *  @param sdrDeviceAddr    - chassis SDR device address
713  *  @param selDeviceAddr    - chassis SEL device address
714  *  @param smDeviceAddr     - chassis system management device address
715  *  @param bridgeDeviceAddr - chassis bridge device address
716  *
717  *  @returns IPMI completion code
718  */
719 ipmi::RspType<> ipmiSetChassisCap(bool intrusion, bool fpLockout,
720                                   uint6_t reserved1,
721 
722                                   uint8_t fruDeviceAddr,
723 
724                                   uint8_t sdrDeviceAddr,
725 
726                                   uint8_t selDeviceAddr,
727 
728                                   uint8_t smDeviceAddr,
729 
730                                   uint8_t bridgeDeviceAddr)
731 {
732 
733     // check input data
734     if (reserved1 != 0)
735     {
736         log<level::ERR>("Unsupported request parameter");
737         return ipmi::responseInvalidFieldRequest();
738     }
739 
740     if ((fruDeviceAddr & ~chassisCapAddrMask) != 0)
741     {
742         log<level::ERR>("Unsupported request parameter(FRU Addr)",
743                         entry("REQ=0x%x", fruDeviceAddr));
744         return ipmi::responseInvalidFieldRequest();
745     }
746     if ((sdrDeviceAddr & ~chassisCapAddrMask) != 0)
747     {
748         log<level::ERR>("Unsupported request parameter(SDR Addr)",
749                         entry("REQ=0x%x", sdrDeviceAddr));
750         return ipmi::responseInvalidFieldRequest();
751     }
752 
753     if ((selDeviceAddr & ~chassisCapAddrMask) != 0)
754     {
755         log<level::ERR>("Unsupported request parameter(SEL Addr)",
756                         entry("REQ=0x%x", selDeviceAddr));
757         return ipmi::responseInvalidFieldRequest();
758     }
759 
760     if ((smDeviceAddr & ~chassisCapAddrMask) != 0)
761     {
762         log<level::ERR>("Unsupported request parameter(SM Addr)",
763                         entry("REQ=0x%x", smDeviceAddr));
764         return ipmi::responseInvalidFieldRequest();
765     }
766 
767     if ((bridgeDeviceAddr & ~chassisCapAddrMask) != 0)
768     {
769         log<level::ERR>("Unsupported request parameter(Bridge Addr)",
770                         entry("REQ=0x%x", bridgeDeviceAddr));
771         return ipmi::responseInvalidFieldRequest();
772     }
773 
774     try
775     {
776         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
777         ipmi::DbusObjectInfo chassisCapObject =
778             ipmi::getDbusObject(bus, chassisCapIntf);
779 
780         ipmi::setDbusProperty(bus, chassisCapObject.second,
781                               chassisCapObject.first, chassisCapIntf,
782                               chassisIntrusionProp, intrusion);
783 
784         ipmi::setDbusProperty(bus, chassisCapObject.second,
785                               chassisCapObject.first, chassisCapIntf,
786                               chassisFrontPanelLockoutProp, fpLockout);
787 
788         ipmi::setDbusProperty(bus, chassisCapObject.second,
789                               chassisCapObject.first, chassisCapIntf,
790                               chassisFRUDevAddrProp, fruDeviceAddr);
791 
792         ipmi::setDbusProperty(bus, chassisCapObject.second,
793                               chassisCapObject.first, chassisCapIntf,
794                               chassisSDRDevAddrProp, sdrDeviceAddr);
795 
796         ipmi::setDbusProperty(bus, chassisCapObject.second,
797                               chassisCapObject.first, chassisCapIntf,
798                               chassisSELDevAddrProp, selDeviceAddr);
799 
800         ipmi::setDbusProperty(bus, chassisCapObject.second,
801                               chassisCapObject.first, chassisCapIntf,
802                               chassisSMDevAddrProp, smDeviceAddr);
803 
804         ipmi::setDbusProperty(bus, chassisCapObject.second,
805                               chassisCapObject.first, chassisCapIntf,
806                               chassisBridgeDevAddrProp, bridgeDeviceAddr);
807     }
808     catch (const std::exception& e)
809     {
810         log<level::ERR>(e.what());
811         return ipmi::responseUnspecifiedError();
812     }
813     return ipmi::responseSuccess();
814 }
815 
816 //------------------------------------------
817 // Calls into Host State Manager Dbus object
818 //------------------------------------------
819 int initiateHostStateTransition(ipmi::Context::ptr& ctx,
820                                 State::Host::Transition transition)
821 {
822     // OpenBMC Host State Manager dbus framework
823     constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0";
824     constexpr auto hostStateIntf = "xyz.openbmc_project.State.Host";
825 
826     // Convert to string equivalent of the passed in transition enum.
827     auto request = State::convertForMessage(transition);
828 
829     std::string service;
830     boost::system::error_code ec =
831         ipmi::getService(ctx, hostStateIntf, hostStatePath, service);
832 
833     if (!ec)
834     {
835         ec = ipmi::setDbusProperty(ctx, service, hostStatePath, hostStateIntf,
836                                    "RequestedHostTransition", request);
837     }
838     if (ec)
839     {
840         log<level::ERR>("Failed to initiate transition",
841                         entry("EXCEPTION=%s, REQUEST=%s", ec.message().c_str(),
842                               request.c_str()));
843         return -1;
844     }
845     log<level::INFO>(
846         "Transition request initiated successfully",
847         entry("USERID=%d, REQUEST=%s", ctx->userId, request.c_str()));
848     return 0;
849 }
850 
851 //------------------------------------------
852 // Calls into Chassis State Manager Dbus object
853 //------------------------------------------
854 int initiateChassisStateTransition(ipmi::Context::ptr& ctx,
855                                    State::Chassis::Transition transition)
856 {
857     // OpenBMC Chassis State Manager dbus framework
858     constexpr auto chassisStatePath = "/xyz/openbmc_project/state/chassis0";
859     constexpr auto chassisStateIntf = "xyz.openbmc_project.State.Chassis";
860 
861     std::string service;
862     boost::system::error_code ec =
863         ipmi::getService(ctx, chassisStateIntf, chassisStatePath, service);
864 
865     // Convert to string equivalent of the passed in transition enum.
866     auto request = State::convertForMessage(transition);
867 
868     if (!ec)
869     {
870         ec = ipmi::setDbusProperty(ctx, service, chassisStatePath,
871                                    chassisStateIntf, "RequestedPowerTransition",
872                                    request);
873     }
874     if (ec)
875     {
876         log<level::ERR>("Failed to initiate transition",
877                         entry("EXCEPTION=%s, REQUEST=%s", ec.message().c_str(),
878                               request.c_str()));
879         return -1;
880     }
881 
882     return 0;
883 }
884 
885 //------------------------------------------
886 // Set Enabled property to inform NMI source
887 // handling to trigger a NMI_OUT BSOD.
888 //------------------------------------------
889 int setNmiProperty(const bool value)
890 {
891     constexpr const char* nmiSourceObjPath =
892         "/xyz/openbmc_project/Chassis/Control/NMISource";
893     constexpr const char* nmiSourceIntf =
894         "xyz.openbmc_project.Chassis.Control.NMISource";
895     std::string bmcSourceSignal = "xyz.openbmc_project.Chassis.Control."
896                                   "NMISource.BMCSourceSignal.ChassisCmd";
897     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
898 
899     try
900     {
901         auto service = ipmi::getService(*busp, nmiSourceIntf, nmiSourceObjPath);
902         ipmi::setDbusProperty(*busp, service, nmiSourceObjPath, nmiSourceIntf,
903                               "BMCSource", bmcSourceSignal);
904         ipmi::setDbusProperty(*busp, service, nmiSourceObjPath, nmiSourceIntf,
905                               "Enabled", value);
906     }
907     catch (const std::exception& e)
908     {
909         log<level::ERR>("Failed to trigger NMI_OUT",
910                         entry("EXCEPTION=%s", e.what()));
911         return -1;
912     }
913 
914     return 0;
915 }
916 
917 namespace power_policy
918 {
919 
920 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
921 using IpmiValue = uint8_t;
922 using DbusValue = RestorePolicy::Policy;
923 
924 const std::map<DbusValue, IpmiValue> dbusToIpmi = {
925     {RestorePolicy::Policy::AlwaysOff, 0x00},
926     {RestorePolicy::Policy::Restore, 0x01},
927     {RestorePolicy::Policy::AlwaysOn, 0x02}};
928 
929 static constexpr uint8_t noChange = 0x03;
930 static constexpr uint8_t allSupport = 0x01 | 0x02 | 0x04;
931 
932 /* helper function for Get Chassis Status Command
933  */
934 std::optional<uint2_t> getPowerRestorePolicy()
935 {
936     uint2_t restorePolicy = 0;
937     using namespace chassis::internal;
938 
939     settings::Objects& objects = cache::getObjects();
940 
941     try
942     {
943         const auto& powerRestoreSetting =
944             objects.map.at(powerRestoreIntf).front();
945         ipmi::Value result = ipmi::getDbusProperty(
946             *getSdBus(),
947             objects.service(powerRestoreSetting, powerRestoreIntf).c_str(),
948             powerRestoreSetting.c_str(), powerRestoreIntf,
949             "PowerRestorePolicy");
950         auto powerRestore = RestorePolicy::convertPolicyFromString(
951             std::get<std::string>(result));
952         restorePolicy = dbusToIpmi.at(powerRestore);
953     }
954     catch (const std::exception& e)
955     {
956         log<level::ERR>(
957             "Failed to fetch pgood property", entry("ERROR=%s", e.what()),
958             entry("PATH=%s", objects.map.at(powerRestoreIntf).front().c_str()),
959             entry("INTERFACE=%s", powerRestoreIntf));
960         cache::objectsPtr.reset();
961         return std::nullopt;
962     }
963     return std::make_optional(restorePolicy);
964 }
965 
966 /*
967  * getPowerStatus
968  * helper function for Get Chassis Status Command
969  * return - optional value for pgood (no value on error)
970  */
971 std::optional<bool> getPowerStatus()
972 {
973     bool powerGood = false;
974     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
975     try
976     {
977         constexpr const char* chassisStatePath =
978             "/xyz/openbmc_project/state/chassis0";
979         constexpr const char* chassisStateIntf =
980             "xyz.openbmc_project.State.Chassis";
981         auto service =
982             ipmi::getService(*busp, chassisStateIntf, chassisStatePath);
983 
984         ipmi::Value powerState =
985             ipmi::getDbusProperty(*busp, service, chassisStatePath,
986                                   chassisStateIntf, "CurrentPowerState");
987         powerGood = std::get<std::string>(powerState) ==
988                     "xyz.openbmc_project.State.Chassis.PowerState.On";
989     }
990     catch (const std::exception& e)
991     {
992         try
993         {
994             // FIXME: some legacy modules use the older path; try that next
995             constexpr const char* legacyPwrCtrlObj =
996                 "/org/openbmc/control/power0";
997             constexpr const char* legacyPwrCtrlIntf =
998                 "org.openbmc.control.Power";
999             auto service =
1000                 ipmi::getService(*busp, legacyPwrCtrlIntf, legacyPwrCtrlObj);
1001 
1002             ipmi::Value variant = ipmi::getDbusProperty(
1003                 *busp, service, legacyPwrCtrlObj, legacyPwrCtrlIntf, "pgood");
1004             powerGood = static_cast<bool>(std::get<int>(variant));
1005         }
1006         catch (const std::exception& e)
1007         {
1008             log<level::ERR>("Failed to fetch pgood property",
1009                             entry("ERROR=%s", e.what()));
1010             return std::nullopt;
1011         }
1012     }
1013     return std::make_optional(powerGood);
1014 }
1015 
1016 /*
1017  * getACFailStatus
1018  * helper function for Get Chassis Status Command
1019  * return - bool value for ACFail (false on error)
1020  */
1021 bool getACFailStatus()
1022 {
1023     constexpr const char* powerControlObj =
1024         "/xyz/openbmc_project/Chassis/Control/Power0";
1025     constexpr const char* powerControlIntf =
1026         "xyz.openbmc_project.Chassis.Control.Power";
1027     bool acFail = false;
1028     std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1029     try
1030     {
1031         auto service =
1032             ipmi::getService(*bus, powerControlIntf, powerControlObj);
1033 
1034         ipmi::Value variant = ipmi::getDbusProperty(
1035             *bus, service, powerControlObj, powerControlIntf, "PFail");
1036         acFail = std::get<bool>(variant);
1037     }
1038     catch (const std::exception& e)
1039     {
1040         log<level::ERR>("Failed to fetch PFail property",
1041                         entry("ERROR=%s", e.what()),
1042                         entry("PATH=%s", powerControlObj),
1043                         entry("INTERFACE=%s", powerControlIntf));
1044     }
1045     return acFail;
1046 }
1047 } // namespace power_policy
1048 
1049 static std::optional<bool> getButtonEnabled(const std::string& buttonPath,
1050                                             const std::string& buttonIntf)
1051 {
1052     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
1053     bool buttonDisabled = false;
1054     try
1055     {
1056         auto service = ipmi::getService(*busp, buttonIntf, buttonPath);
1057         ipmi::Value enabled = ipmi::getDbusProperty(*busp, service, buttonPath,
1058                                                     buttonIntf, "Enabled");
1059         buttonDisabled = !std::get<bool>(enabled);
1060     }
1061     catch (const sdbusplus::exception::exception& e)
1062     {
1063         log<level::ERR>("Fail to get button Enabled property",
1064                         entry("PATH=%s", buttonPath.c_str()),
1065                         entry("ERROR=%s", e.what()));
1066         return std::nullopt;
1067     }
1068     return std::make_optional(buttonDisabled);
1069 }
1070 
1071 static bool setButtonEnabled(ipmi::Context::ptr& ctx,
1072                              const std::string& buttonPath,
1073                              const std::string& buttonIntf, bool enable)
1074 {
1075     std::string service;
1076     boost::system::error_code ec;
1077     ec = ipmi::getService(ctx, buttonIntf, buttonPath, service);
1078     if (!ec)
1079     {
1080         ec = ipmi::setDbusProperty(ctx, service, buttonPath, buttonIntf,
1081                                    "Enabled", enable);
1082     }
1083     if (ec)
1084     {
1085         log<level::ERR>("Fail to set button Enabled property",
1086                         entry("SERVICE=%s", service.c_str()),
1087                         entry("PATH=%s", buttonPath.c_str()),
1088                         entry("ERROR=%s", ec.message().c_str()));
1089         return false;
1090     }
1091     return true;
1092 }
1093 
1094 //----------------------------------------------------------------------
1095 // Get Chassis Status commands
1096 //----------------------------------------------------------------------
1097 ipmi::RspType<bool,    // Power is on
1098               bool,    // Power overload
1099               bool,    // Interlock
1100               bool,    // power fault
1101               bool,    // power control fault
1102               uint2_t, // power restore policy
1103               bool,    // reserved
1104 
1105               bool, // AC failed
1106               bool, // last power down caused by a Power overload
1107               bool, // last power down caused by a power interlock
1108               bool, // last power down caused by power fault
1109               bool, // last ‘Power is on’ state was entered via IPMI command
1110               uint3_t, // reserved
1111 
1112               bool,    // Chassis intrusion active
1113               bool,    // Front Panel Lockout active
1114               bool,    // Drive Fault
1115               bool,    // Cooling/fan fault detected
1116               uint2_t, // Chassis Identify State
1117               bool,    // Chassis Identify command and state info supported
1118               bool,    // reserved
1119 
1120               bool, // Power off button disabled
1121               bool, // Reset button disabled
1122               bool, // Diagnostic Interrupt button disabled
1123               bool, // Standby (sleep) button disabled
1124               bool, // Power off button disable allowed
1125               bool, // Reset button disable allowed
1126               bool, // Diagnostic Interrupt button disable allowed
1127               bool  // Standby (sleep) button disable allowed
1128               >
1129     ipmiGetChassisStatus()
1130 {
1131     using namespace chassis::internal;
1132     std::optional<uint2_t> restorePolicy =
1133         power_policy::getPowerRestorePolicy();
1134     std::optional<bool> powerGood = power_policy::getPowerStatus();
1135     if (!restorePolicy || !powerGood)
1136     {
1137         return ipmi::responseUnspecifiedError();
1138     }
1139 
1140     //  Front Panel Button Capabilities and disable/enable status(Optional)
1141     std::optional<bool> powerButtonReading =
1142         getButtonEnabled(powerButtonPath, powerButtonIntf);
1143     // allow disable if the interface is present
1144     bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading);
1145     // default return the button is enabled (not disabled)
1146     bool powerButtonDisabled = false;
1147     if (powerButtonDisableAllow)
1148     {
1149         // return the real value of the button status, if present
1150         powerButtonDisabled = *powerButtonReading;
1151     }
1152 
1153     std::optional<bool> resetButtonReading =
1154         getButtonEnabled(resetButtonPath, resetButtonIntf);
1155     // allow disable if the interface is present
1156     bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading);
1157     // default return the button is enabled (not disabled)
1158     bool resetButtonDisabled = false;
1159     if (resetButtonDisableAllow)
1160     {
1161         // return the real value of the button status, if present
1162         resetButtonDisabled = *resetButtonReading;
1163     }
1164 
1165     bool powerDownAcFailed = power_policy::getACFailStatus();
1166 
1167     // This response has a lot of hard-coded, unsupported fields
1168     // They are set to false or 0
1169     constexpr bool powerOverload = false;
1170     constexpr bool chassisInterlock = false;
1171     constexpr bool powerFault = false;
1172     constexpr bool powerControlFault = false;
1173     constexpr bool powerDownOverload = false;
1174     constexpr bool powerDownInterlock = false;
1175     constexpr bool powerDownPowerFault = false;
1176     constexpr bool powerStatusIPMI = false;
1177     constexpr bool chassisIntrusionActive = false;
1178     constexpr bool frontPanelLockoutActive = false;
1179     constexpr bool driveFault = false;
1180     constexpr bool coolingFanFault = false;
1181     // chassisIdentifySupport set because this command is implemented
1182     constexpr bool chassisIdentifySupport = true;
1183     uint2_t chassisIdentifyState = types::enum_cast<uint2_t>(chassisIDState);
1184     constexpr bool diagButtonDisabled = false;
1185     constexpr bool sleepButtonDisabled = false;
1186     constexpr bool diagButtonDisableAllow = false;
1187     constexpr bool sleepButtonDisableAllow = false;
1188 
1189     return ipmi::responseSuccess(
1190         *powerGood, powerOverload, chassisInterlock, powerFault,
1191         powerControlFault, *restorePolicy,
1192         false, // reserved
1193 
1194         powerDownAcFailed, powerDownOverload, powerDownInterlock,
1195         powerDownPowerFault, powerStatusIPMI,
1196         uint3_t(0), // reserved
1197 
1198         chassisIntrusionActive, frontPanelLockoutActive, driveFault,
1199         coolingFanFault, chassisIdentifyState, chassisIdentifySupport,
1200         false, // reserved
1201 
1202         powerButtonDisabled, resetButtonDisabled, diagButtonDisabled,
1203         sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow,
1204         diagButtonDisableAllow, sleepButtonDisableAllow);
1205 }
1206 
1207 enum class IpmiRestartCause
1208 {
1209     Unknown = 0x0,
1210     RemoteCommand = 0x1,
1211     ResetButton = 0x2,
1212     PowerButton = 0x3,
1213     WatchdogTimer = 0x4,
1214     PowerPolicyAlwaysOn = 0x6,
1215     PowerPolicyPreviousState = 0x7,
1216     SoftReset = 0xa,
1217 };
1218 
1219 static IpmiRestartCause
1220     restartCauseToIpmiRestartCause(State::Host::RestartCause cause)
1221 {
1222     switch (cause)
1223     {
1224         case State::Host::RestartCause::Unknown:
1225         {
1226             return IpmiRestartCause::Unknown;
1227         }
1228         case State::Host::RestartCause::RemoteCommand:
1229         {
1230             return IpmiRestartCause::RemoteCommand;
1231         }
1232         case State::Host::RestartCause::ResetButton:
1233         {
1234             return IpmiRestartCause::ResetButton;
1235         }
1236         case State::Host::RestartCause::PowerButton:
1237         {
1238             return IpmiRestartCause::PowerButton;
1239         }
1240         case State::Host::RestartCause::WatchdogTimer:
1241         {
1242             return IpmiRestartCause::WatchdogTimer;
1243         }
1244         case State::Host::RestartCause::PowerPolicyAlwaysOn:
1245         {
1246             return IpmiRestartCause::PowerPolicyAlwaysOn;
1247         }
1248         case State::Host::RestartCause::PowerPolicyPreviousState:
1249         {
1250             return IpmiRestartCause::PowerPolicyPreviousState;
1251         }
1252         case State::Host::RestartCause::SoftReset:
1253         {
1254             return IpmiRestartCause::SoftReset;
1255         }
1256         default:
1257         {
1258             return IpmiRestartCause::Unknown;
1259         }
1260     }
1261 }
1262 
1263 /*
1264  * getRestartCause
1265  * helper function for Get Host restart cause Command
1266  * return - optional value for RestartCause (no value on error)
1267  */
1268 static std::optional<uint4_t> getRestartCause(ipmi::Context::ptr ctx)
1269 {
1270     constexpr const char* restartCausePath =
1271         "/xyz/openbmc_project/control/host0/restart_cause";
1272     constexpr const char* restartCauseIntf =
1273         "xyz.openbmc_project.Control.Host.RestartCause";
1274 
1275     std::string service;
1276     boost::system::error_code ec =
1277         ipmi::getService(ctx, restartCauseIntf, restartCausePath, service);
1278     if (!ec)
1279     {
1280         std::string restartCauseStr;
1281         ec = ipmi::getDbusProperty<std::string>(
1282             ctx, service, restartCausePath, restartCauseIntf, "RestartCause",
1283             restartCauseStr);
1284         if (!ec)
1285         {
1286             auto cause =
1287                 State::Host::convertRestartCauseFromString(restartCauseStr);
1288             return types::enum_cast<uint4_t>(
1289                 restartCauseToIpmiRestartCause(cause));
1290         }
1291     }
1292 
1293     log<level::ERR>("Failed to fetch RestartCause property",
1294                     entry("ERROR=%s", ec.message().c_str()),
1295                     entry("PATH=%s", restartCausePath),
1296                     entry("INTERFACE=%s", restartCauseIntf));
1297     return std::nullopt;
1298 }
1299 
1300 ipmi::RspType<uint4_t, // Restart Cause
1301               uint4_t, // reserved
1302               uint8_t  // channel number (not supported)
1303               >
1304     ipmiGetSystemRestartCause(ipmi::Context::ptr ctx)
1305 {
1306     std::optional<uint4_t> cause = getRestartCause(ctx);
1307     if (!cause)
1308     {
1309         return ipmi::responseUnspecifiedError();
1310     }
1311 
1312     constexpr uint4_t reserved = 0;
1313     auto channel = static_cast<uint8_t>(ctx->channel);
1314     return ipmi::responseSuccess(cause.value(), reserved, channel);
1315 }
1316 
1317 //-------------------------------------------------------------
1318 // Send a command to SoftPowerOff application to stop any timer
1319 //-------------------------------------------------------------
1320 int stop_soft_off_timer()
1321 {
1322     constexpr auto iface = "org.freedesktop.DBus.Properties";
1323     constexpr auto soft_off_iface = "xyz.openbmc_project.Ipmi.Internal."
1324                                     "SoftPowerOff";
1325 
1326     constexpr auto property = "ResponseReceived";
1327     constexpr auto value = "xyz.openbmc_project.Ipmi.Internal."
1328                            "SoftPowerOff.HostResponse.HostShutdown";
1329 
1330     // Get the system bus where most system services are provided.
1331     auto bus = ipmid_get_sd_bus_connection();
1332 
1333     // Get the service name
1334     // TODO openbmc/openbmc#1661 - Mapper refactor
1335     //
1336     // See openbmc/openbmc#1743 for some details but high level summary is that
1337     // for now the code will directly call the soft off interface due to a
1338     // race condition with mapper usage
1339     //
1340     // char *busname = nullptr;
1341     // auto r = mapper_get_service(bus, SOFTOFF_OBJPATH, &busname);
1342     // if (r < 0)
1343     //{
1344     //    fprintf(stderr, "Failed to get %s bus name: %s\n",
1345     //            SOFTOFF_OBJPATH, -r);
1346     //    return r;
1347     //}
1348 
1349     // No error object or reply expected.
1350     int rc = sd_bus_call_method(bus, SOFTOFF_BUSNAME, SOFTOFF_OBJPATH, iface,
1351                                 "Set", nullptr, nullptr, "ssv", soft_off_iface,
1352                                 property, "s", value);
1353     if (rc < 0)
1354     {
1355         log<level::ERR>("Failed to set property in SoftPowerOff object",
1356                         entry("ERRNO=0x%X", -rc));
1357     }
1358 
1359     // TODO openbmc/openbmc#1661 - Mapper refactor
1360     // free(busname);
1361     return rc;
1362 }
1363 
1364 //----------------------------------------------------------------------
1365 // Create file to indicate there is no need for softoff notification to host
1366 //----------------------------------------------------------------------
1367 void indicate_no_softoff_needed()
1368 {
1369     fs::path path{HOST_INBAND_REQUEST_DIR};
1370     if (!fs::is_directory(path))
1371     {
1372         fs::create_directory(path);
1373     }
1374 
1375     // Add the host instance (default 0 for now) to the file name
1376     std::string file{HOST_INBAND_REQUEST_FILE};
1377     auto size = std::snprintf(nullptr, 0, file.c_str(), 0);
1378     size++; // null
1379     std::unique_ptr<char[]> buf(new char[size]);
1380     std::snprintf(buf.get(), size, file.c_str(), 0);
1381 
1382     // Append file name to directory and create it
1383     path /= buf.get();
1384     std::ofstream(path.c_str());
1385 }
1386 
1387 /** @brief Implementation of chassis control command
1388  *
1389  *  @param - chassisControl command byte
1390  *
1391  *  @return  Success or InvalidFieldRequest.
1392  */
1393 ipmi::RspType<> ipmiChassisControl(ipmi::Context::ptr& ctx,
1394                                    uint8_t chassisControl)
1395 {
1396     int rc = 0;
1397     switch (chassisControl)
1398     {
1399         case CMD_POWER_ON:
1400             rc = initiateHostStateTransition(ctx, State::Host::Transition::On);
1401             break;
1402         case CMD_POWER_OFF:
1403             // This path would be hit in 2 conditions.
1404             // 1: When user asks for power off using ipmi chassis command 0x04
1405             // 2: Host asking for power off post shutting down.
1406 
1407             // If it's a host requested power off, then need to nudge Softoff
1408             // application that it needs to stop the watchdog timer if running.
1409             // If it is a user requested power off, then this is not really
1410             // needed. But then we need to differentiate between user and host
1411             // calling this same command
1412 
1413             // For now, we are going ahead with trying to nudge the soft off and
1414             // interpret the failure to do so as a non softoff case
1415             rc = stop_soft_off_timer();
1416 
1417             // Only request the Off transition if the soft power off
1418             // application is not running
1419             if (rc < 0)
1420             {
1421                 // First create a file to indicate to the soft off application
1422                 // that it should not run. Not doing this will result in State
1423                 // manager doing a default soft power off when asked for power
1424                 // off.
1425                 indicate_no_softoff_needed();
1426 
1427                 // Now request the shutdown
1428                 rc = initiateHostStateTransition(ctx,
1429                                                  State::Host::Transition::Off);
1430             }
1431             else
1432             {
1433                 log<level::INFO>("Soft off is running, so let shutdown target "
1434                                  "stop the host");
1435             }
1436             break;
1437 
1438         case CMD_HARD_RESET:
1439             rc = initiateHostStateTransition(
1440                 ctx, State::Host::Transition::ForceWarmReboot);
1441             break;
1442         case CMD_POWER_CYCLE:
1443             // SPEC has a section that says certain implementations can trigger
1444             // PowerOn if power is Off when a command to power cycle is
1445             // requested
1446 
1447             // First create a file to indicate to the soft off application
1448             // that it should not run since this is a direct user initiated
1449             // power reboot request (i.e. a reboot request that is not
1450             // originating via a soft power off SMS request)
1451             indicate_no_softoff_needed();
1452 
1453             rc = initiateHostStateTransition(ctx,
1454                                              State::Host::Transition::Reboot);
1455             break;
1456 
1457         case CMD_SOFT_OFF_VIA_OVER_TEMP:
1458             // Request Host State Manager to do a soft power off
1459             rc = initiateHostStateTransition(ctx, State::Host::Transition::Off);
1460             break;
1461 
1462         case CMD_PULSE_DIAGNOSTIC_INTR:
1463             rc = setNmiProperty(true);
1464             break;
1465 
1466         default:
1467         {
1468             log<level::ERR>("Invalid Chassis Control command",
1469                             entry("CMD=0x%X", chassisControl));
1470             return ipmi::responseInvalidFieldRequest();
1471         }
1472     }
1473 
1474     return ((rc < 0) ? ipmi::responseUnspecifiedError()
1475                      : ipmi::responseSuccess());
1476 }
1477 
1478 /** @brief Return D-Bus connection string to enclosure identify LED object
1479  *
1480  *  @param[in, out] connection - connection to D-Bus object
1481  *  @return a IPMI return code
1482  */
1483 std::string getEnclosureIdentifyConnection()
1484 {
1485     // lookup enclosure_identify group owner(s) in mapper
1486     auto mapperCall = chassis::internal::dbus.new_method_call(
1487         ipmi::MAPPER_BUS_NAME, ipmi::MAPPER_OBJ, ipmi::MAPPER_INTF,
1488         "GetObject");
1489 
1490     mapperCall.append(identify_led_object_name);
1491     static const std::vector<std::string> interfaces = {
1492         "xyz.openbmc_project.Led.Group"};
1493     mapperCall.append(interfaces);
1494     auto mapperReply = chassis::internal::dbus.call(mapperCall);
1495     if (mapperReply.is_method_error())
1496     {
1497         log<level::ERR>("Chassis Identify: Error communicating to mapper.");
1498         elog<InternalFailure>();
1499     }
1500     std::vector<std::pair<std::string, std::vector<std::string>>> mapperResp;
1501     mapperReply.read(mapperResp);
1502 
1503     if (mapperResp.size() != encIdentifyObjectsSize)
1504     {
1505         log<level::ERR>(
1506             "Invalid number of enclosure identify objects.",
1507             entry("ENC_IDENTITY_OBJECTS_SIZE=%d", mapperResp.size()));
1508         elog<InternalFailure>();
1509     }
1510     auto pair = mapperResp[encIdentifyObjectsSize - 1];
1511     return pair.first;
1512 }
1513 
1514 /** @brief Turn On/Off enclosure identify LED
1515  *
1516  *  @param[in] flag - true to turn on LED, false to turn off
1517  *  @return a IPMI return code
1518  */
1519 void enclosureIdentifyLed(bool flag)
1520 {
1521     using namespace chassis::internal;
1522     std::string connection = std::move(getEnclosureIdentifyConnection());
1523     auto msg = std::string("enclosureIdentifyLed(") +
1524                boost::lexical_cast<std::string>(flag) + ")";
1525     log<level::DEBUG>(msg.c_str());
1526     auto led =
1527         dbus.new_method_call(connection.c_str(), identify_led_object_name,
1528                              "org.freedesktop.DBus.Properties", "Set");
1529     led.append("xyz.openbmc_project.Led.Group", "Asserted",
1530                std::variant<bool>(flag));
1531     auto ledReply = dbus.call(led);
1532     if (ledReply.is_method_error())
1533     {
1534         log<level::ERR>("Chassis Identify: Error Setting State On/Off\n",
1535                         entry("LED_STATE=%d", flag));
1536         elog<InternalFailure>();
1537     }
1538 }
1539 
1540 /** @brief Callback method to turn off LED
1541  */
1542 void enclosureIdentifyLedOff()
1543 {
1544     try
1545     {
1546         chassisIDState = ChassisIDState::off;
1547         enclosureIdentifyLed(false);
1548     }
1549     catch (const InternalFailure& e)
1550     {
1551         report<InternalFailure>();
1552     }
1553 }
1554 
1555 /** @brief Create timer to turn on and off the enclosure LED
1556  */
1557 void createIdentifyTimer()
1558 {
1559     if (!identifyTimer)
1560     {
1561         identifyTimer =
1562             std::make_unique<phosphor::Timer>(enclosureIdentifyLedOff);
1563     }
1564 }
1565 
1566 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
1567                                     std::optional<uint8_t> force)
1568 {
1569     uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT);
1570     bool forceIdentify = force.value_or(0) & 0x01;
1571 
1572     if (identifyInterval || forceIdentify)
1573     {
1574         // stop the timer if already started;
1575         // for force identify we should not turn off LED
1576         identifyTimer->stop();
1577         try
1578         {
1579             chassisIDState = ChassisIDState::temporaryOn;
1580             enclosureIdentifyLed(true);
1581         }
1582         catch (const InternalFailure& e)
1583         {
1584             report<InternalFailure>();
1585             return ipmi::responseResponseError();
1586         }
1587 
1588         if (forceIdentify)
1589         {
1590             chassisIDState = ChassisIDState::indefiniteOn;
1591             return ipmi::responseSuccess();
1592         }
1593         // start the timer
1594         auto time = std::chrono::duration_cast<std::chrono::microseconds>(
1595             std::chrono::seconds(identifyInterval));
1596         identifyTimer->start(time);
1597     }
1598     else if (!identifyInterval)
1599     {
1600         identifyTimer->stop();
1601         enclosureIdentifyLedOff();
1602     }
1603     return ipmi::responseSuccess();
1604 }
1605 
1606 namespace boot_options
1607 {
1608 
1609 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
1610 using IpmiValue = uint8_t;
1611 constexpr auto ipmiDefault = 0;
1612 
1613 std::map<IpmiValue, Type::Types> typeIpmiToDbus = {{0x00, Type::Types::Legacy},
1614                                                    {0x01, Type::Types::EFI}};
1615 
1616 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
1617     {0x01, Source::Sources::Network},
1618     {0x02, Source::Sources::Disk},
1619     {0x05, Source::Sources::ExternalMedia},
1620     {0x0f, Source::Sources::RemovableMedia},
1621     {ipmiDefault, Source::Sources::Default}};
1622 
1623 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
1624 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT
1625     {0x03, Mode::Modes::Safe},
1626 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT
1627     {0x06, Mode::Modes::Setup},
1628     {ipmiDefault, Mode::Modes::Regular}};
1629 
1630 std::map<Type::Types, IpmiValue> typeDbusToIpmi = {{Type::Types::Legacy, 0x00},
1631                                                    {Type::Types::EFI, 0x01}};
1632 
1633 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
1634     {Source::Sources::Network, 0x01},
1635     {Source::Sources::Disk, 0x02},
1636     {Source::Sources::ExternalMedia, 0x05},
1637     {Source::Sources::RemovableMedia, 0x0f},
1638     {Source::Sources::Default, ipmiDefault}};
1639 
1640 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
1641 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT
1642     {Mode::Modes::Safe, 0x03},
1643 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT
1644     {Mode::Modes::Setup, 0x06},
1645     {Mode::Modes::Regular, ipmiDefault}};
1646 
1647 } // namespace boot_options
1648 
1649 /** @brief Get the property value for boot source
1650  *  @param[in] ctx - context pointer
1651  *  @param[out] source - boot source value
1652  *  @return On failure return IPMI error.
1653  */
1654 static ipmi::Cc getBootSource(ipmi::Context::ptr& ctx, Source::Sources& source)
1655 {
1656     using namespace chassis::internal;
1657     std::string result;
1658     std::string service;
1659     boost::system::error_code ec =
1660         getService(ctx, bootSourceIntf, bootSettingsPath, service);
1661     if (!ec)
1662     {
1663         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath,
1664                                    bootSourceIntf, "BootSource", result);
1665         if (!ec)
1666         {
1667             source = Source::convertSourcesFromString(result);
1668             return ipmi::ccSuccess;
1669         }
1670     }
1671     log<level::ERR>("Error in BootSource Get",
1672                     entry("ERROR=%s", ec.message().c_str()));
1673     return ipmi::ccUnspecifiedError;
1674 }
1675 
1676 /** @brief Set the property value for boot source
1677  *  @param[in] ctx - context pointer
1678  *  @param[in] source - boot source value
1679  *  @return On failure return IPMI error.
1680  */
1681 static ipmi::Cc setBootSource(ipmi::Context::ptr& ctx,
1682                               const Source::Sources& source)
1683 {
1684     using namespace chassis::internal;
1685     std::string service;
1686     boost::system::error_code ec =
1687         getService(ctx, bootSourceIntf, bootSettingsPath, service);
1688     if (!ec)
1689     {
1690         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath,
1691                                    bootSourceIntf, "BootSource",
1692                                    convertForMessage(source));
1693         if (!ec)
1694         {
1695             return ipmi::ccSuccess;
1696         }
1697     }
1698     log<level::ERR>("Error in BootSource Set",
1699                     entry("ERROR=%s", ec.message().c_str()));
1700     return ipmi::ccUnspecifiedError;
1701 }
1702 
1703 /** @brief Get the property value for boot mode
1704  *  @param[in] ctx - context pointer
1705  *  @param[out] mode - boot mode value
1706  *  @return On failure return IPMI error.
1707  */
1708 static ipmi::Cc getBootMode(ipmi::Context::ptr& ctx, Mode::Modes& mode)
1709 {
1710     using namespace chassis::internal;
1711     std::string result;
1712     std::string service;
1713     boost::system::error_code ec =
1714         getService(ctx, bootModeIntf, bootSettingsPath, service);
1715     if (!ec)
1716     {
1717         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootModeIntf,
1718                                    "BootMode", result);
1719         if (!ec)
1720         {
1721             mode = Mode::convertModesFromString(result);
1722             return ipmi::ccSuccess;
1723         }
1724     }
1725     log<level::ERR>("Error in BootMode Get",
1726                     entry("ERROR=%s", ec.message().c_str()));
1727     return ipmi::ccUnspecifiedError;
1728 }
1729 
1730 /** @brief Set the property value for boot mode
1731  *  @param[in] ctx - context pointer
1732  *  @param[in] mode - boot mode value
1733  *  @return On failure return IPMI error.
1734  */
1735 static ipmi::Cc setBootMode(ipmi::Context::ptr& ctx, const Mode::Modes& mode)
1736 {
1737     using namespace chassis::internal;
1738     std::string service;
1739     boost::system::error_code ec =
1740         getService(ctx, bootModeIntf, bootSettingsPath, service);
1741     if (!ec)
1742     {
1743         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootModeIntf,
1744                                    "BootMode", convertForMessage(mode));
1745         if (!ec)
1746         {
1747             return ipmi::ccSuccess;
1748         }
1749     }
1750     log<level::ERR>("Error in BootMode Set",
1751                     entry("ERROR=%s", ec.message().c_str()));
1752     return ipmi::ccUnspecifiedError;
1753 }
1754 
1755 /** @brief Get the property value for boot type
1756  *  @param[in] ctx - context pointer
1757  *  @param[out] type - boot type value
1758  *  @return On failure return IPMI error.
1759  */
1760 static ipmi::Cc getBootType(ipmi::Context::ptr& ctx, Type::Types& type)
1761 {
1762     using namespace chassis::internal;
1763     std::string result;
1764     std::string service;
1765     boost::system::error_code ec =
1766         getService(ctx, bootTypeIntf, bootSettingsPath, service);
1767 
1768     // Don't throw error if BootType interface is not present.
1769     // This interface is not relevant for some Host architectures
1770     // (for example POWER). In this case we don't won't IPMI to
1771     // return an error, but simply return bootType as EFI.
1772     type = Type::Types::EFI;
1773     if (!ec)
1774     {
1775         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootTypeIntf,
1776                                    "BootType", result);
1777         if (ec)
1778         {
1779             log<level::ERR>("Error in BootType Get",
1780                             entry("ERROR=%s", ec.message().c_str()));
1781             return ipmi::ccUnspecifiedError;
1782         }
1783         type = Type::convertTypesFromString(result);
1784     }
1785 
1786     return ipmi::ccSuccess;
1787 }
1788 
1789 /** @brief Set the property value for boot type
1790  *  @param[in] ctx - context pointer
1791  *  @param[in] type - boot type value
1792  *  @return On failure return IPMI error.
1793  */
1794 static ipmi::Cc setBootType(ipmi::Context::ptr& ctx, const Type::Types& type)
1795 {
1796     using namespace chassis::internal;
1797     std::string service;
1798     boost::system::error_code ec =
1799         getService(ctx, bootTypeIntf, bootSettingsPath, service);
1800     if (!ec)
1801     {
1802         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootTypeIntf,
1803                                    "BootType", convertForMessage(type));
1804         if (ec)
1805         {
1806             log<level::ERR>("Error in BootType Set",
1807                             entry("ERROR=%s", ec.message().c_str()));
1808             return ipmi::ccUnspecifiedError;
1809         }
1810     }
1811     // Don't throw error if BootType interface is not present.
1812     // This interface is not relevant for some Host architectures
1813     // (for example POWER). In this case we don't won't IPMI to
1814     // return an error, but want to just skip this function.
1815     return ipmi::ccSuccess;
1816 }
1817 
1818 /** @brief Get the property value for boot override enable
1819  *  @param[in] ctx - context pointer
1820  *  @param[out] enable - boot override enable
1821  *  @return On failure return IPMI error.
1822  */
1823 static ipmi::Cc getBootEnable(ipmi::Context::ptr& ctx, bool& enable)
1824 {
1825     using namespace chassis::internal;
1826     std::string result;
1827     std::string service;
1828     boost::system::error_code ec =
1829         getService(ctx, bootEnableIntf, bootSettingsPath, service);
1830     if (!ec)
1831     {
1832         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath,
1833                                    bootEnableIntf, "Enabled", enable);
1834         if (!ec)
1835         {
1836             return ipmi::ccSuccess;
1837         }
1838     }
1839     log<level::ERR>("Error in Boot Override Enable Get",
1840                     entry("ERROR=%s", ec.message().c_str()));
1841     return ipmi::ccUnspecifiedError;
1842 }
1843 
1844 /** @brief Set the property value for boot override enable
1845  *  @param[in] ctx - context pointer
1846  *  @param[in] enable - boot override enable
1847  *  @return On failure return IPMI error.
1848  */
1849 static ipmi::Cc setBootEnable(ipmi::Context::ptr& ctx, const bool& enable)
1850 {
1851     using namespace chassis::internal;
1852     std::string service;
1853     boost::system::error_code ec =
1854         getService(ctx, bootEnableIntf, bootSettingsPath, service);
1855     if (!ec)
1856     {
1857         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath,
1858                                    bootEnableIntf, "Enabled", enable);
1859         if (!ec)
1860         {
1861             return ipmi::ccSuccess;
1862         }
1863     }
1864     log<level::ERR>("Error in Boot Source Override Enable Set",
1865                     entry("ERROR=%s", ec.message().c_str()));
1866     return ipmi::ccUnspecifiedError;
1867 }
1868 
1869 /** @brief Get the property value for boot override one-time
1870  *  @param[in] ctx - context pointer
1871  *  @param[out] onetime - boot override one-time
1872  *  @return On failure return IPMI error.
1873  */
1874 static ipmi::Cc getBootOneTime(ipmi::Context::ptr& ctx, bool& onetime)
1875 {
1876     using namespace chassis::internal;
1877     std::string result;
1878     std::string service;
1879     boost::system::error_code ec =
1880         getService(ctx, bootOneTimeIntf, bootSettingsOneTimePath, service);
1881     if (!ec)
1882     {
1883         ec = ipmi::getDbusProperty(ctx, service, bootSettingsOneTimePath,
1884                                    bootOneTimeIntf, "Enabled", onetime);
1885         if (!ec)
1886         {
1887             return ipmi::ccSuccess;
1888         }
1889     }
1890     log<level::ERR>("Error in Boot Override OneTime Get",
1891                     entry("ERROR=%s", ec.message().c_str()));
1892     return ipmi::ccUnspecifiedError;
1893 }
1894 
1895 /** @brief Set the property value for boot override one-time
1896  *  @param[in] ctx - context pointer
1897  *  @param[in] onetime - boot override one-time
1898  *  @return On failure return IPMI error.
1899  */
1900 static ipmi::Cc setBootOneTime(ipmi::Context::ptr& ctx, const bool& onetime)
1901 {
1902     using namespace chassis::internal;
1903     std::string service;
1904     boost::system::error_code ec =
1905         getService(ctx, bootOneTimeIntf, bootSettingsOneTimePath, service);
1906     if (!ec)
1907     {
1908         ec = ipmi::setDbusProperty(ctx, service, bootSettingsOneTimePath,
1909                                    bootOneTimeIntf, "Enabled", onetime);
1910         if (!ec)
1911         {
1912             return ipmi::ccSuccess;
1913         }
1914     }
1915     log<level::ERR>("Error in Boot Source Override OneTime Set",
1916                     entry("ERROR=%s", ec.message().c_str()));
1917     return ipmi::ccUnspecifiedError;
1918 }
1919 
1920 static constexpr uint8_t setComplete = 0x0;
1921 static constexpr uint8_t setInProgress = 0x1;
1922 static uint8_t transferStatus = setComplete;
1923 static uint8_t bootFlagValidBitClr = 0;
1924 static uint5_t bootInitiatorAckData = 0x0;
1925 static bool cmosClear = false;
1926 
1927 /** @brief implements the Get Chassis system boot option
1928  *  @param ctx - context pointer
1929  *  @param bootOptionParameter   - boot option parameter selector
1930  *  @param reserved1    - reserved bit
1931  *  @param setSelector  - selects a particular block or set of parameters
1932  *                        under the given parameter selector
1933  *                        write as 00h if parameter doesn't use a setSelector
1934  *  @param blockSelector- selects a particular block within a set of
1935  *                        parameters write as 00h if parameter doesn't use a
1936  *                        blockSelector
1937  *
1938  *  @return IPMI completion code plus response data
1939  *  @return Payload contains below parameters:
1940  *   version             - parameter version
1941  *   bootOptionParameter - boot option parameter selector
1942  *   parmIndicator - parameter valid/invalid indicator
1943  *   data          - configuration parameter data
1944  */
1945 ipmi::RspType<ipmi::message::Payload>
1946     ipmiChassisGetSysBootOptions(ipmi::Context::ptr ctx,
1947                                  uint7_t bootOptionParameter, bool reserved1,
1948 
1949                                  uint8_t setSelector, uint8_t blockSelector)
1950 {
1951     ipmi::Cc rc;
1952     if (reserved1)
1953     {
1954         return ipmi::responseInvalidFieldRequest();
1955     }
1956 
1957     constexpr uint4_t version = 0x01;
1958     ipmi::message::Payload response;
1959     response.pack(version, uint4_t{});
1960     using namespace boot_options;
1961 
1962     IpmiValue bootOption = ipmiDefault;
1963 
1964     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1965         BootOptionParameter::setInProgress)
1966     {
1967         response.pack(bootOptionParameter, reserved1, transferStatus);
1968         return ipmi::responseSuccess(std::move(response));
1969     }
1970 
1971     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1972         BootOptionParameter::bootInfo)
1973     {
1974         constexpr uint8_t writeMask = 0;
1975         response.pack(bootOptionParameter, reserved1, writeMask,
1976                       bootInitiatorAckData);
1977         return ipmi::responseSuccess(std::move(response));
1978     }
1979 
1980     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1981         BootOptionParameter::bootFlagValidClr)
1982     {
1983         response.pack(bootOptionParameter, reserved1,
1984                       uint5_t{bootFlagValidBitClr}, uint3_t{});
1985         return ipmi::responseSuccess(std::move(response));
1986     }
1987 
1988     /*
1989      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1990      * This is the only parameter used by petitboot.
1991      */
1992     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1993         BootOptionParameter::bootFlags)
1994     {
1995         using namespace chassis::internal;
1996         using namespace chassis::internal::cache;
1997 
1998         try
1999         {
2000             Source::Sources bootSource;
2001             rc = getBootSource(ctx, bootSource);
2002             if (rc != ipmi::ccSuccess)
2003             {
2004                 return ipmi::response(rc);
2005             }
2006 
2007             Type::Types bootType;
2008             rc = getBootType(ctx, bootType);
2009             if (rc != ipmi::ccSuccess)
2010             {
2011                 return ipmi::response(rc);
2012             }
2013 
2014             Mode::Modes bootMode;
2015             rc = getBootMode(ctx, bootMode);
2016             if (rc != ipmi::ccSuccess)
2017             {
2018                 return ipmi::response(rc);
2019             }
2020 
2021             bootOption = sourceDbusToIpmi.at(bootSource);
2022             if ((Mode::Modes::Regular == bootMode) &&
2023                 (Source::Sources::Default == bootSource))
2024             {
2025                 bootOption = ipmiDefault;
2026             }
2027             else if (Source::Sources::Default == bootSource)
2028             {
2029                 bootOption = modeDbusToIpmi.at(bootMode);
2030             }
2031 
2032             IpmiValue biosBootType = typeDbusToIpmi.at(bootType);
2033 
2034             bool oneTimeEnabled;
2035             rc = getBootOneTime(ctx, oneTimeEnabled);
2036             if (rc != ipmi::ccSuccess)
2037             {
2038                 return ipmi::response(rc);
2039             }
2040 
2041             uint1_t permanent = oneTimeEnabled ? 0 : 1;
2042 
2043             bool valid;
2044             rc = getBootEnable(ctx, valid);
2045             if (rc != ipmi::ccSuccess)
2046             {
2047                 return ipmi::response(rc);
2048             }
2049 
2050             uint1_t validFlag = valid ? 1 : 0;
2051 
2052             response.pack(bootOptionParameter, reserved1, uint5_t{},
2053                           uint1_t{biosBootType}, uint1_t{permanent},
2054                           uint1_t{validFlag}, uint2_t{}, uint4_t{bootOption},
2055                           uint1_t{}, cmosClear, uint8_t{}, uint8_t{},
2056                           uint8_t{});
2057             return ipmi::responseSuccess(std::move(response));
2058         }
2059         catch (const InternalFailure& e)
2060         {
2061             cache::objectsPtr.reset();
2062             report<InternalFailure>();
2063             return ipmi::responseUnspecifiedError();
2064         }
2065     }
2066     else
2067     {
2068         if ((bootOptionParameter >= oemParmStart) &&
2069             (bootOptionParameter <= oemParmEnd))
2070         {
2071             if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
2072                 BootOptionParameter::opalNetworkSettings)
2073             {
2074                 response.pack(bootOptionParameter, reserved1);
2075                 int ret = getHostNetworkData(response);
2076                 if (ret < 0)
2077                 {
2078                     response.trailingOk = true;
2079                     log<level::ERR>(
2080                         "getHostNetworkData failed for GetSysBootOptions.");
2081                     return ipmi::responseUnspecifiedError();
2082                 }
2083                 else
2084                 {
2085                     return ipmi::responseSuccess(std::move(response));
2086                 }
2087             }
2088             else
2089             {
2090                 log<level::ERR>(
2091                     "ipmiChassisGetSysBootOptions: Unsupported parameter",
2092                     entry("PARAM=0x%x",
2093                           static_cast<uint8_t>(bootOptionParameter)));
2094                 return ipmi::responseParmNotSupported();
2095             }
2096         }
2097         else
2098         {
2099             log<level::ERR>(
2100                 "ipmiChassisGetSysBootOptions: Unsupported parameter",
2101                 entry("PARAM=0x%x", static_cast<uint8_t>(bootOptionParameter)));
2102             return ipmi::responseParmNotSupported();
2103         }
2104     }
2105     return ipmi::responseUnspecifiedError();
2106 }
2107 
2108 ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx,
2109                                              uint7_t parameterSelector,
2110                                              bool parameterIsValid,
2111                                              ipmi::message::Payload& data)
2112 {
2113     using namespace boot_options;
2114     ipmi::Cc rc;
2115 
2116     if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2117         BootOptionParameter::setInProgress)
2118     {
2119         uint2_t setInProgressFlag;
2120         uint6_t rsvd;
2121         if (data.unpack(setInProgressFlag, rsvd) != 0 || !data.fullyUnpacked())
2122         {
2123             return ipmi::responseReqDataLenInvalid();
2124         }
2125         if (rsvd)
2126         {
2127             return ipmi::responseInvalidFieldRequest();
2128         }
2129         if ((transferStatus == setInProgress) &&
2130             (static_cast<uint8_t>(setInProgressFlag) != setComplete))
2131         {
2132             return ipmi::response(IPMI_CC_FAIL_SET_IN_PROGRESS);
2133         }
2134         transferStatus = static_cast<uint8_t>(setInProgressFlag);
2135         return ipmi::responseSuccess();
2136     }
2137 
2138     /*  000101
2139      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
2140      * This is the only parameter used by petitboot.
2141      */
2142 
2143     if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2144         BootOptionParameter::bootFlags)
2145     {
2146         uint5_t rsvd;
2147         bool validFlag;
2148         bool permanent;
2149         bool biosBootType;
2150         bool lockOutResetButton;
2151         bool screenBlank;
2152         uint4_t bootDeviceSelector;
2153         bool lockKeyboard;
2154         uint8_t data3;
2155         uint4_t biosInfo;
2156         uint4_t rsvd1;
2157         uint5_t deviceInstance;
2158         uint3_t rsvd2;
2159 
2160         if (data.unpack(rsvd, biosBootType, permanent, validFlag,
2161                         lockOutResetButton, screenBlank, bootDeviceSelector,
2162                         lockKeyboard, cmosClear, data3, biosInfo, rsvd1,
2163                         deviceInstance, rsvd2) != 0 ||
2164             !data.fullyUnpacked())
2165         {
2166             return ipmi::responseReqDataLenInvalid();
2167         }
2168         if (rsvd || rsvd1 || rsvd2)
2169         {
2170             return ipmi::responseInvalidFieldRequest();
2171         }
2172 
2173         using namespace chassis::internal;
2174         using namespace chassis::internal::cache;
2175 
2176         try
2177         {
2178             rc = setBootOneTime(ctx, !permanent);
2179             if (rc != ipmi::ccSuccess)
2180             {
2181                 return ipmi::response(rc);
2182             }
2183 
2184             rc = setBootEnable(ctx, validFlag);
2185             if (rc != ipmi::ccSuccess)
2186             {
2187                 return ipmi::response(rc);
2188             }
2189 
2190             auto modeItr =
2191                 modeIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector));
2192             auto typeItr =
2193                 typeIpmiToDbus.find(static_cast<uint8_t>(biosBootType));
2194             auto sourceItr =
2195                 sourceIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector));
2196             if (sourceIpmiToDbus.end() != sourceItr)
2197             {
2198                 rc = setBootSource(ctx, sourceItr->second);
2199                 if (rc != ipmi::ccSuccess)
2200                 {
2201                     return ipmi::response(rc);
2202                 }
2203                 // If a set boot device is mapping to a boot source, then reset
2204                 // the boot mode D-Bus property to default.
2205                 // This way the ipmid code can determine which property is not
2206                 // at the default value
2207                 if (sourceItr->second != Source::Sources::Default)
2208                 {
2209                     rc = setBootMode(ctx, Mode::Modes::Regular);
2210                     if (rc != ipmi::ccSuccess)
2211                     {
2212                         return ipmi::response(rc);
2213                     }
2214                 }
2215             }
2216 
2217             if (typeIpmiToDbus.end() != typeItr)
2218             {
2219                 rc = setBootType(ctx, typeItr->second);
2220                 if (rc != ipmi::ccSuccess)
2221                 {
2222                     return ipmi::response(rc);
2223                 }
2224             }
2225 
2226             if (modeIpmiToDbus.end() != modeItr)
2227             {
2228                 rc = setBootMode(ctx, modeItr->second);
2229                 if (rc != ipmi::ccSuccess)
2230                 {
2231                     return ipmi::response(rc);
2232                 }
2233                 // If a set boot device is mapping to a boot mode, then reset
2234                 // the boot source D-Bus property to default.
2235                 // This way the ipmid code can determine which property is not
2236                 // at the default value
2237                 if (modeItr->second != Mode::Modes::Regular)
2238                 {
2239                     rc = setBootSource(ctx, Source::Sources::Default);
2240                     if (rc != ipmi::ccSuccess)
2241                     {
2242                         return ipmi::response(rc);
2243                     }
2244                 }
2245             }
2246             if ((modeIpmiToDbus.end() == modeItr) &&
2247                 (typeIpmiToDbus.end() == typeItr) &&
2248                 (sourceIpmiToDbus.end() == sourceItr))
2249             {
2250                 // return error if boot option is not supported
2251                 log<level::ERR>(
2252                     "ipmiChassisSetSysBootOptions: Boot option not supported");
2253                 return ipmi::responseInvalidFieldRequest();
2254             }
2255         }
2256         catch (const sdbusplus::exception_t& e)
2257         {
2258             objectsPtr.reset();
2259             report<InternalFailure>();
2260             log<level::ERR>(
2261                 "ipmiChassisSetSysBootOptions: Error in setting Boot "
2262                 "flag parameters");
2263             return ipmi::responseUnspecifiedError();
2264         }
2265     }
2266     else if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2267              BootOptionParameter::bootInfo)
2268     {
2269         uint8_t writeMak;
2270         uint5_t bootInfoAck;
2271         uint3_t rsvd;
2272 
2273         if (data.unpack(writeMak, bootInfoAck, rsvd) != 0 ||
2274             !data.fullyUnpacked())
2275         {
2276             return ipmi::responseReqDataLenInvalid();
2277         }
2278         if (rsvd)
2279         {
2280             return ipmi::responseInvalidFieldRequest();
2281         }
2282         bootInitiatorAckData &= ~writeMak;
2283         bootInitiatorAckData |= (writeMak & bootInfoAck);
2284         log<level::INFO>("ipmiChassisSetSysBootOptions: bootInfo parameter set "
2285                          "successfully");
2286         data.trailingOk = true;
2287         return ipmi::responseSuccess();
2288     }
2289     else if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2290              BootOptionParameter::bootFlagValidClr)
2291     {
2292         uint5_t bootFlagValidClr;
2293         uint3_t rsvd;
2294 
2295         if (data.unpack(bootFlagValidClr, rsvd) != 0 || !data.fullyUnpacked())
2296         {
2297             return ipmi::responseReqDataLenInvalid();
2298         }
2299         if (rsvd)
2300         {
2301             return ipmi::responseInvalidFieldRequest();
2302         }
2303         // store boot flag valid bits clear value
2304         bootFlagValidBitClr = static_cast<uint8_t>(bootFlagValidClr);
2305         log<level::INFO>(
2306             "ipmiChassisSetSysBootOptions: bootFlagValidBits parameter set "
2307             "successfully",
2308             entry("value=0x%x", bootFlagValidBitClr));
2309         return ipmi::responseSuccess();
2310     }
2311     else
2312     {
2313         if ((parameterSelector >= static_cast<uint7_t>(oemParmStart)) &&
2314             (parameterSelector <= static_cast<uint7_t>(oemParmEnd)))
2315         {
2316             if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2317                 BootOptionParameter::opalNetworkSettings)
2318             {
2319                 ipmi::Cc ret = setHostNetworkData(data);
2320                 if (ret != ipmi::ccSuccess)
2321                 {
2322                     log<level::ERR>("ipmiChassisSetSysBootOptions: Error in "
2323                                     "setHostNetworkData");
2324                     data.trailingOk = true;
2325                     return ipmi::response(ret);
2326                 }
2327                 data.trailingOk = true;
2328                 return ipmi::responseSuccess();
2329             }
2330             else
2331             {
2332                 log<level::ERR>(
2333                     "ipmiChassisSetSysBootOptions: Unsupported parameters",
2334                     entry("PARAM=0x%x",
2335                           static_cast<uint8_t>(parameterSelector)));
2336                 data.trailingOk = true;
2337                 return ipmi::responseParmNotSupported();
2338             }
2339         }
2340         data.trailingOk = true;
2341         return ipmi::responseParmNotSupported();
2342     }
2343     return ipmi::responseSuccess();
2344 }
2345 
2346 /** @brief implements Get POH counter command
2347  *  @parameter
2348  *   -  none
2349  *  @returns IPMI completion code plus response data
2350  *   - minPerCount - Minutes per count
2351  *   - counterReading - counter reading
2352  */
2353 ipmi::RspType<uint8_t, // Minutes per count
2354               uint32_t // Counter reading
2355               >
2356     ipmiGetPOHCounter()
2357 {
2358     // sd_bus error
2359     try
2360     {
2361         return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount),
2362                                      getPOHCounter());
2363     }
2364     catch (const std::exception& e)
2365     {
2366         log<level::ERR>(e.what());
2367         return ipmi::responseUnspecifiedError();
2368     }
2369 }
2370 
2371 ipmi::RspType<uint3_t, // policy support
2372               uint5_t  // reserved
2373               >
2374     ipmiChassisSetPowerRestorePolicy(boost::asio::yield_context yield,
2375                                      uint3_t policy, uint5_t reserved)
2376 {
2377     power_policy::DbusValue value =
2378         power_policy::RestorePolicy::Policy::AlwaysOff;
2379 
2380     if (reserved || (policy > power_policy::noChange))
2381     {
2382         phosphor::logging::log<level::ERR>(
2383             "Reserved request parameter",
2384             entry("REQ=0x%x", static_cast<int>(policy)));
2385         return ipmi::responseInvalidFieldRequest();
2386     }
2387 
2388     if (policy == power_policy::noChange)
2389     {
2390         // just return the supported policy
2391         return ipmi::responseSuccess(power_policy::allSupport, reserved);
2392     }
2393 
2394     for (auto const& it : power_policy::dbusToIpmi)
2395     {
2396         if (it.second == policy)
2397         {
2398             value = it.first;
2399             break;
2400         }
2401     }
2402 
2403     try
2404     {
2405         settings::Objects& objects = chassis::internal::cache::getObjects();
2406         const settings::Path& powerRestoreSetting =
2407             objects.map.at(chassis::internal::powerRestoreIntf).front();
2408         std::variant<std::string> property = convertForMessage(value);
2409 
2410         auto sdbusp = getSdBus();
2411         boost::system::error_code ec;
2412         sdbusp->yield_method_call<void>(
2413             yield, ec,
2414             objects
2415                 .service(powerRestoreSetting,
2416                          chassis::internal::powerRestoreIntf)
2417                 .c_str(),
2418             powerRestoreSetting, ipmi::PROP_INTF, "Set",
2419             chassis::internal::powerRestoreIntf, "PowerRestorePolicy",
2420             property);
2421         if (ec)
2422         {
2423             phosphor::logging::log<level::ERR>("Unspecified Error");
2424             return ipmi::responseUnspecifiedError();
2425         }
2426     }
2427     catch (const InternalFailure& e)
2428     {
2429         chassis::internal::cache::objectsPtr.reset();
2430         report<InternalFailure>();
2431         return ipmi::responseUnspecifiedError();
2432     }
2433 
2434     return ipmi::responseSuccess(power_policy::allSupport, reserved);
2435 }
2436 
2437 ipmi::RspType<> ipmiSetFrontPanelButtonEnables(
2438     ipmi::Context::ptr ctx, bool disablePowerButton, bool disableResetButton,
2439     bool disableDiagButton, bool disableSleepButton, uint4_t reserved)
2440 {
2441     using namespace chassis::internal;
2442 
2443     // set power button Enabled property
2444     bool success = setButtonEnabled(ctx, powerButtonPath, powerButtonIntf,
2445                                     !disablePowerButton);
2446 
2447     // set reset button Enabled property
2448     success &= setButtonEnabled(ctx, resetButtonPath, resetButtonIntf,
2449                                 !disableResetButton);
2450 
2451     if (!success)
2452     {
2453         // not all buttons were successfully set
2454         return ipmi::responseUnspecifiedError();
2455     }
2456     return ipmi::responseSuccess();
2457 }
2458 
2459 void register_netfn_chassis_functions()
2460 {
2461     createIdentifyTimer();
2462 
2463     // Get Chassis Capabilities
2464     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2465                           ipmi::chassis::cmdGetChassisCapabilities,
2466                           ipmi::Privilege::User, ipmiGetChassisCap);
2467 
2468     // Set Front Panel Button Enables
2469     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2470                           ipmi::chassis::cmdSetFrontPanelButtonEnables,
2471                           ipmi::Privilege::Admin,
2472                           ipmiSetFrontPanelButtonEnables);
2473 
2474     // Set Chassis Capabilities
2475     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2476                           ipmi::chassis::cmdSetChassisCapabilities,
2477                           ipmi::Privilege::User, ipmiSetChassisCap);
2478 
2479     // <Get System Boot Options>
2480     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2481                           ipmi::chassis::cmdGetSystemBootOptions,
2482                           ipmi::Privilege::Operator,
2483                           ipmiChassisGetSysBootOptions);
2484 
2485     // <Get Chassis Status>
2486     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2487                           ipmi::chassis::cmdGetChassisStatus,
2488                           ipmi::Privilege::User, ipmiGetChassisStatus);
2489 
2490     // <Chassis Get System Restart Cause>
2491     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2492                           ipmi::chassis::cmdGetSystemRestartCause,
2493                           ipmi::Privilege::User, ipmiGetSystemRestartCause);
2494 
2495     // <Chassis Control>
2496     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2497                           ipmi::chassis::cmdChassisControl,
2498                           ipmi::Privilege::Operator, ipmiChassisControl);
2499 
2500     // <Chassis Identify>
2501     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2502                           ipmi::chassis::cmdChassisIdentify,
2503                           ipmi::Privilege::Operator, ipmiChassisIdentify);
2504 
2505     // <Set System Boot Options>
2506     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2507                           ipmi::chassis::cmdSetSystemBootOptions,
2508                           ipmi::Privilege::Operator,
2509                           ipmiChassisSetSysBootOptions);
2510 
2511     // <Get POH Counter>
2512     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2513                           ipmi::chassis::cmdGetPohCounter,
2514                           ipmi::Privilege::User, ipmiGetPOHCounter);
2515 
2516     // <Set Power Restore Policy>
2517     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2518                           ipmi::chassis::cmdSetPowerRestorePolicy,
2519                           ipmi::Privilege::Operator,
2520                           ipmiChassisSetPowerRestorePolicy);
2521 }
2522