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