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 (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 (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 (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 (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 (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 (sdbusplus::exception::SdBusError& 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         case CMD_POWER_CYCLE:
1426             // SPEC has a section that says certain implementations can trigger
1427             // PowerOn if power is Off when a command to power cycle is
1428             // requested
1429 
1430             // First create a file to indicate to the soft off application
1431             // that it should not run since this is a direct user initiated
1432             // power reboot request (i.e. a reboot request that is not
1433             // originating via a soft power off SMS request)
1434             indicate_no_softoff_needed();
1435 
1436             rc = initiate_state_transition(State::Host::Transition::Reboot);
1437             break;
1438 
1439         case CMD_SOFT_OFF_VIA_OVER_TEMP:
1440             // Request Host State Manager to do a soft power off
1441             rc = initiate_state_transition(State::Host::Transition::Off);
1442             break;
1443 
1444         case CMD_PULSE_DIAGNOSTIC_INTR:
1445             rc = setNmiProperty(true);
1446             break;
1447 
1448         default:
1449         {
1450             log<level::ERR>("Invalid Chassis Control command",
1451                             entry("CMD=0x%X", chassisControl));
1452             return ipmi::responseInvalidFieldRequest();
1453         }
1454     }
1455 
1456     return ((rc < 0) ? ipmi::responseUnspecifiedError()
1457                      : ipmi::responseSuccess());
1458 }
1459 
1460 /** @brief Return D-Bus connection string to enclosure identify LED object
1461  *
1462  *  @param[in, out] connection - connection to D-Bus object
1463  *  @return a IPMI return code
1464  */
1465 std::string getEnclosureIdentifyConnection()
1466 {
1467     // lookup enclosure_identify group owner(s) in mapper
1468     auto mapperCall = chassis::internal::dbus.new_method_call(
1469         ipmi::MAPPER_BUS_NAME, ipmi::MAPPER_OBJ, ipmi::MAPPER_INTF,
1470         "GetObject");
1471 
1472     mapperCall.append(identify_led_object_name);
1473     static const std::vector<std::string> interfaces = {
1474         "xyz.openbmc_project.Led.Group"};
1475     mapperCall.append(interfaces);
1476     auto mapperReply = chassis::internal::dbus.call(mapperCall);
1477     if (mapperReply.is_method_error())
1478     {
1479         log<level::ERR>("Chassis Identify: Error communicating to mapper.");
1480         elog<InternalFailure>();
1481     }
1482     std::vector<std::pair<std::string, std::vector<std::string>>> mapperResp;
1483     mapperReply.read(mapperResp);
1484 
1485     if (mapperResp.size() != encIdentifyObjectsSize)
1486     {
1487         log<level::ERR>(
1488             "Invalid number of enclosure identify objects.",
1489             entry("ENC_IDENTITY_OBJECTS_SIZE=%d", mapperResp.size()));
1490         elog<InternalFailure>();
1491     }
1492     auto pair = mapperResp[encIdentifyObjectsSize - 1];
1493     return pair.first;
1494 }
1495 
1496 /** @brief Turn On/Off enclosure identify LED
1497  *
1498  *  @param[in] flag - true to turn on LED, false to turn off
1499  *  @return a IPMI return code
1500  */
1501 void enclosureIdentifyLed(bool flag)
1502 {
1503     using namespace chassis::internal;
1504     std::string connection = std::move(getEnclosureIdentifyConnection());
1505     auto msg = std::string("enclosureIdentifyLed(") +
1506                boost::lexical_cast<std::string>(flag) + ")";
1507     log<level::DEBUG>(msg.c_str());
1508     auto led =
1509         dbus.new_method_call(connection.c_str(), identify_led_object_name,
1510                              "org.freedesktop.DBus.Properties", "Set");
1511     led.append("xyz.openbmc_project.Led.Group", "Asserted",
1512                std::variant<bool>(flag));
1513     auto ledReply = dbus.call(led);
1514     if (ledReply.is_method_error())
1515     {
1516         log<level::ERR>("Chassis Identify: Error Setting State On/Off\n",
1517                         entry("LED_STATE=%d", flag));
1518         elog<InternalFailure>();
1519     }
1520 }
1521 
1522 /** @brief Callback method to turn off LED
1523  */
1524 void enclosureIdentifyLedOff()
1525 {
1526     try
1527     {
1528         chassisIDState = ChassisIDState::off;
1529         enclosureIdentifyLed(false);
1530     }
1531     catch (const InternalFailure& e)
1532     {
1533         report<InternalFailure>();
1534     }
1535 }
1536 
1537 /** @brief Create timer to turn on and off the enclosure LED
1538  */
1539 void createIdentifyTimer()
1540 {
1541     if (!identifyTimer)
1542     {
1543         identifyTimer =
1544             std::make_unique<phosphor::Timer>(enclosureIdentifyLedOff);
1545     }
1546 }
1547 
1548 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
1549                                     std::optional<uint8_t> force)
1550 {
1551     uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT);
1552     bool forceIdentify = force.value_or(0) & 0x01;
1553 
1554     if (identifyInterval || forceIdentify)
1555     {
1556         // stop the timer if already started;
1557         // for force identify we should not turn off LED
1558         identifyTimer->stop();
1559         try
1560         {
1561             chassisIDState = ChassisIDState::temporaryOn;
1562             enclosureIdentifyLed(true);
1563         }
1564         catch (const InternalFailure& e)
1565         {
1566             report<InternalFailure>();
1567             return ipmi::responseResponseError();
1568         }
1569 
1570         if (forceIdentify)
1571         {
1572             chassisIDState = ChassisIDState::indefiniteOn;
1573             return ipmi::responseSuccess();
1574         }
1575         // start the timer
1576         auto time = std::chrono::duration_cast<std::chrono::microseconds>(
1577             std::chrono::seconds(identifyInterval));
1578         identifyTimer->start(time);
1579     }
1580     else if (!identifyInterval)
1581     {
1582         identifyTimer->stop();
1583         enclosureIdentifyLedOff();
1584     }
1585     return ipmi::responseSuccess();
1586 }
1587 
1588 namespace boot_options
1589 {
1590 
1591 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
1592 using IpmiValue = uint8_t;
1593 constexpr auto ipmiDefault = 0;
1594 
1595 std::map<IpmiValue, Type::Types> typeIpmiToDbus = {{0x00, Type::Types::Legacy},
1596                                                    {0x01, Type::Types::EFI}};
1597 
1598 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
1599     {0x01, Source::Sources::Network},
1600     {0x02, Source::Sources::Disk},
1601     {0x05, Source::Sources::ExternalMedia},
1602     {0x0f, Source::Sources::RemovableMedia},
1603     {ipmiDefault, Source::Sources::Default}};
1604 
1605 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
1606 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT
1607     {0x03, Mode::Modes::Safe},
1608 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT
1609     {0x06, Mode::Modes::Setup},
1610     {ipmiDefault, Mode::Modes::Regular}};
1611 
1612 std::map<Type::Types, IpmiValue> typeDbusToIpmi = {{Type::Types::Legacy, 0x00},
1613                                                    {Type::Types::EFI, 0x01}};
1614 
1615 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
1616     {Source::Sources::Network, 0x01},
1617     {Source::Sources::Disk, 0x02},
1618     {Source::Sources::ExternalMedia, 0x05},
1619     {Source::Sources::RemovableMedia, 0x0f},
1620     {Source::Sources::Default, ipmiDefault}};
1621 
1622 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
1623 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT
1624     {Mode::Modes::Safe, 0x03},
1625 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT
1626     {Mode::Modes::Setup, 0x06},
1627     {Mode::Modes::Regular, ipmiDefault}};
1628 
1629 } // namespace boot_options
1630 
1631 /** @brief Get the property value for boot source
1632  *  @param[in] ctx - context pointer
1633  *  @param[out] source - boot source value
1634  *  @return On failure return IPMI error.
1635  */
1636 static ipmi::Cc getBootSource(ipmi::Context::ptr& ctx, Source::Sources& source)
1637 {
1638     using namespace chassis::internal;
1639     std::string result;
1640     std::string service;
1641     boost::system::error_code ec =
1642         getService(ctx, bootSourceIntf, bootSettingsPath, service);
1643     if (!ec)
1644     {
1645         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath,
1646                                    bootSourceIntf, "BootSource", result);
1647         if (!ec)
1648         {
1649             source = Source::convertSourcesFromString(result);
1650             return ipmi::ccSuccess;
1651         }
1652     }
1653     log<level::ERR>("Error in BootSource Get",
1654                     entry("ERROR=%s", ec.message().c_str()));
1655     return ipmi::ccUnspecifiedError;
1656 }
1657 
1658 /** @brief Set the property value for boot source
1659  *  @param[in] ctx - context pointer
1660  *  @param[in] source - boot source value
1661  *  @return On failure return IPMI error.
1662  */
1663 static ipmi::Cc setBootSource(ipmi::Context::ptr& ctx,
1664                               const Source::Sources& source)
1665 {
1666     using namespace chassis::internal;
1667     std::string service;
1668     boost::system::error_code ec =
1669         getService(ctx, bootSourceIntf, bootSettingsPath, service);
1670     if (!ec)
1671     {
1672         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath,
1673                                    bootSourceIntf, "BootSource",
1674                                    convertForMessage(source));
1675         if (!ec)
1676         {
1677             return ipmi::ccSuccess;
1678         }
1679     }
1680     log<level::ERR>("Error in BootSource Set",
1681                     entry("ERROR=%s", ec.message().c_str()));
1682     return ipmi::ccUnspecifiedError;
1683 }
1684 
1685 /** @brief Get the property value for boot mode
1686  *  @param[in] ctx - context pointer
1687  *  @param[out] mode - boot mode value
1688  *  @return On failure return IPMI error.
1689  */
1690 static ipmi::Cc getBootMode(ipmi::Context::ptr& ctx, Mode::Modes& mode)
1691 {
1692     using namespace chassis::internal;
1693     std::string result;
1694     std::string service;
1695     boost::system::error_code ec =
1696         getService(ctx, bootModeIntf, bootSettingsPath, service);
1697     if (!ec)
1698     {
1699         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootModeIntf,
1700                                    "BootMode", result);
1701         if (!ec)
1702         {
1703             mode = Mode::convertModesFromString(result);
1704             return ipmi::ccSuccess;
1705         }
1706     }
1707     log<level::ERR>("Error in BootMode Get",
1708                     entry("ERROR=%s", ec.message().c_str()));
1709     return ipmi::ccUnspecifiedError;
1710 }
1711 
1712 /** @brief Set the property value for boot mode
1713  *  @param[in] ctx - context pointer
1714  *  @param[in] mode - boot mode value
1715  *  @return On failure return IPMI error.
1716  */
1717 static ipmi::Cc setBootMode(ipmi::Context::ptr& ctx, const Mode::Modes& mode)
1718 {
1719     using namespace chassis::internal;
1720     std::string service;
1721     boost::system::error_code ec =
1722         getService(ctx, bootModeIntf, bootSettingsPath, service);
1723     if (!ec)
1724     {
1725         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootModeIntf,
1726                                    "BootMode", convertForMessage(mode));
1727         if (!ec)
1728         {
1729             return ipmi::ccSuccess;
1730         }
1731     }
1732     log<level::ERR>("Error in BootMode Set",
1733                     entry("ERROR=%s", ec.message().c_str()));
1734     return ipmi::ccUnspecifiedError;
1735 }
1736 
1737 /** @brief Get the property value for boot type
1738  *  @param[in] ctx - context pointer
1739  *  @param[out] type - boot type value
1740  *  @return On failure return IPMI error.
1741  */
1742 static ipmi::Cc getBootType(ipmi::Context::ptr& ctx, Type::Types& type)
1743 {
1744     using namespace chassis::internal;
1745     std::string result;
1746     std::string service;
1747     boost::system::error_code ec =
1748         getService(ctx, bootTypeIntf, bootSettingsPath, service);
1749 
1750     // Don't throw error if BootType interface is not present.
1751     // This interface is not relevant for some Host architectures
1752     // (for example POWER). In this case we don't won't IPMI to
1753     // return an error, but simply return bootType as EFI.
1754     type = Type::Types::EFI;
1755     if (!ec)
1756     {
1757         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootTypeIntf,
1758                                    "BootType", result);
1759         if (ec)
1760         {
1761             log<level::ERR>("Error in BootType Get",
1762                             entry("ERROR=%s", ec.message().c_str()));
1763             return ipmi::ccUnspecifiedError;
1764         }
1765         type = Type::convertTypesFromString(result);
1766     }
1767 
1768     return ipmi::ccSuccess;
1769 }
1770 
1771 /** @brief Set the property value for boot type
1772  *  @param[in] ctx - context pointer
1773  *  @param[in] type - boot type value
1774  *  @return On failure return IPMI error.
1775  */
1776 static ipmi::Cc setBootType(ipmi::Context::ptr& ctx, const Type::Types& type)
1777 {
1778     using namespace chassis::internal;
1779     std::string service;
1780     boost::system::error_code ec =
1781         getService(ctx, bootTypeIntf, bootSettingsPath, service);
1782     if (!ec)
1783     {
1784         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootTypeIntf,
1785                                    "BootType", convertForMessage(type));
1786         if (ec)
1787         {
1788             log<level::ERR>("Error in BootType Set",
1789                             entry("ERROR=%s", ec.message().c_str()));
1790             return ipmi::ccUnspecifiedError;
1791         }
1792     }
1793     // Don't throw error if BootType interface is not present.
1794     // This interface is not relevant for some Host architectures
1795     // (for example POWER). In this case we don't won't IPMI to
1796     // return an error, but want to just skip this function.
1797     return ipmi::ccSuccess;
1798 }
1799 
1800 /** @brief Get the property value for boot override enable
1801  *  @param[in] ctx - context pointer
1802  *  @param[out] enable - boot override enable
1803  *  @return On failure return IPMI error.
1804  */
1805 static ipmi::Cc getBootEnable(ipmi::Context::ptr& ctx, bool& enable)
1806 {
1807     using namespace chassis::internal;
1808     std::string result;
1809     std::string service;
1810     boost::system::error_code ec =
1811         getService(ctx, bootEnableIntf, bootSettingsPath, service);
1812     if (!ec)
1813     {
1814         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath,
1815                                    bootEnableIntf, "Enabled", enable);
1816         if (!ec)
1817         {
1818             return ipmi::ccSuccess;
1819         }
1820     }
1821     log<level::ERR>("Error in Boot Override Enable Get",
1822                     entry("ERROR=%s", ec.message().c_str()));
1823     return ipmi::ccUnspecifiedError;
1824 }
1825 
1826 /** @brief Set the property value for boot override enable
1827  *  @param[in] ctx - context pointer
1828  *  @param[in] enable - boot override enable
1829  *  @return On failure return IPMI error.
1830  */
1831 static ipmi::Cc setBootEnable(ipmi::Context::ptr& ctx, const bool& enable)
1832 {
1833     using namespace chassis::internal;
1834     std::string service;
1835     boost::system::error_code ec =
1836         getService(ctx, bootEnableIntf, bootSettingsPath, service);
1837     if (!ec)
1838     {
1839         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath,
1840                                    bootEnableIntf, "Enabled", enable);
1841         if (!ec)
1842         {
1843             return ipmi::ccSuccess;
1844         }
1845     }
1846     log<level::ERR>("Error in Boot Source Override Enable Set",
1847                     entry("ERROR=%s", ec.message().c_str()));
1848     return ipmi::ccUnspecifiedError;
1849 }
1850 
1851 /** @brief Get the property value for boot override one-time
1852  *  @param[in] ctx - context pointer
1853  *  @param[out] onetime - boot override one-time
1854  *  @return On failure return IPMI error.
1855  */
1856 static ipmi::Cc getBootOneTime(ipmi::Context::ptr& ctx, bool& onetime)
1857 {
1858     using namespace chassis::internal;
1859     std::string result;
1860     std::string service;
1861     boost::system::error_code ec =
1862         getService(ctx, bootOneTimeIntf, bootSettingsOneTimePath, service);
1863     if (!ec)
1864     {
1865         ec = ipmi::getDbusProperty(ctx, service, bootSettingsOneTimePath,
1866                                    bootOneTimeIntf, "Enabled", onetime);
1867         if (!ec)
1868         {
1869             return ipmi::ccSuccess;
1870         }
1871     }
1872     log<level::ERR>("Error in Boot Override OneTime Get",
1873                     entry("ERROR=%s", ec.message().c_str()));
1874     return ipmi::ccUnspecifiedError;
1875 }
1876 
1877 /** @brief Set the property value for boot override one-time
1878  *  @param[in] ctx - context pointer
1879  *  @param[in] onetime - boot override one-time
1880  *  @return On failure return IPMI error.
1881  */
1882 static ipmi::Cc setBootOneTime(ipmi::Context::ptr& ctx, const bool& onetime)
1883 {
1884     using namespace chassis::internal;
1885     std::string service;
1886     boost::system::error_code ec =
1887         getService(ctx, bootOneTimeIntf, bootSettingsOneTimePath, service);
1888     if (!ec)
1889     {
1890         ec = ipmi::setDbusProperty(ctx, service, bootSettingsOneTimePath,
1891                                    bootOneTimeIntf, "Enabled", onetime);
1892         if (!ec)
1893         {
1894             return ipmi::ccSuccess;
1895         }
1896     }
1897     log<level::ERR>("Error in Boot Source Override OneTime Set",
1898                     entry("ERROR=%s", ec.message().c_str()));
1899     return ipmi::ccUnspecifiedError;
1900 }
1901 
1902 static constexpr uint8_t setComplete = 0x0;
1903 static constexpr uint8_t setInProgress = 0x1;
1904 static uint8_t transferStatus = setComplete;
1905 static uint8_t bootFlagValidBitClr = 0;
1906 
1907 /** @brief implements the Get Chassis system boot option
1908  *  @param ctx - context pointer
1909  *  @param bootOptionParameter   - boot option parameter selector
1910  *  @param reserved1    - reserved bit
1911  *  @param setSelector  - selects a particular block or set of parameters
1912  *                        under the given parameter selector
1913  *                        write as 00h if parameter doesn't use a setSelector
1914  *  @param blockSelector- selects a particular block within a set of
1915  *                        parameters write as 00h if parameter doesn't use a
1916  *                        blockSelector
1917  *
1918  *  @return IPMI completion code plus response data
1919  *  @return Payload contains below parameters:
1920  *   version             - parameter version
1921  *   bootOptionParameter - boot option parameter selector
1922  *   parmIndicator - parameter valid/invalid indicator
1923  *   data          - configuration parameter data
1924  */
1925 ipmi::RspType<ipmi::message::Payload>
1926     ipmiChassisGetSysBootOptions(ipmi::Context::ptr ctx,
1927                                  uint7_t bootOptionParameter, bool reserved1,
1928 
1929                                  uint8_t setSelector, uint8_t blockSelector)
1930 {
1931     ipmi::Cc rc;
1932     if (reserved1)
1933     {
1934         return ipmi::responseInvalidFieldRequest();
1935     }
1936 
1937     constexpr uint4_t version = 0x01;
1938     ipmi::message::Payload response;
1939     response.pack(version, uint4_t{});
1940     using namespace boot_options;
1941 
1942     IpmiValue bootOption = ipmiDefault;
1943 
1944     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1945         BootOptionParameter::setInProgress)
1946     {
1947         response.pack(bootOptionParameter, reserved1, transferStatus);
1948         return ipmi::responseSuccess(std::move(response));
1949     }
1950 
1951     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1952         BootOptionParameter::bootInfo)
1953     {
1954         constexpr uint8_t writeMask = 0;
1955         constexpr uint8_t bootInfoAck = 0;
1956         response.pack(bootOptionParameter, writeMask, bootInfoAck);
1957         return ipmi::responseSuccess(std::move(response));
1958     }
1959 
1960     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1961         BootOptionParameter::bootFlagValidClr)
1962     {
1963         response.pack(bootOptionParameter, reserved1,
1964                       uint5_t{bootFlagValidBitClr}, uint3_t{});
1965         return ipmi::responseSuccess(std::move(response));
1966     }
1967 
1968     /*
1969      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1970      * This is the only parameter used by petitboot.
1971      */
1972     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1973         BootOptionParameter::bootFlags)
1974     {
1975         using namespace chassis::internal;
1976         using namespace chassis::internal::cache;
1977 
1978         try
1979         {
1980             Source::Sources bootSource;
1981             rc = getBootSource(ctx, bootSource);
1982             if (rc != ipmi::ccSuccess)
1983             {
1984                 return ipmi::response(rc);
1985             }
1986 
1987             Type::Types bootType;
1988             rc = getBootType(ctx, bootType);
1989             if (rc != ipmi::ccSuccess)
1990             {
1991                 return ipmi::response(rc);
1992             }
1993 
1994             Mode::Modes bootMode;
1995             rc = getBootMode(ctx, bootMode);
1996             if (rc != ipmi::ccSuccess)
1997             {
1998                 return ipmi::response(rc);
1999             }
2000 
2001             bootOption = sourceDbusToIpmi.at(bootSource);
2002             if ((Mode::Modes::Regular == bootMode) &&
2003                 (Source::Sources::Default == bootSource))
2004             {
2005                 bootOption = ipmiDefault;
2006             }
2007             else if (Source::Sources::Default == bootSource)
2008             {
2009                 bootOption = modeDbusToIpmi.at(bootMode);
2010             }
2011 
2012             IpmiValue biosBootType = typeDbusToIpmi.at(bootType);
2013 
2014             bool oneTimeEnabled;
2015             rc = getBootOneTime(ctx, oneTimeEnabled);
2016             if (rc != ipmi::ccSuccess)
2017             {
2018                 return ipmi::response(rc);
2019             }
2020 
2021             uint1_t permanent = oneTimeEnabled ? 0 : 1;
2022 
2023             bool valid;
2024             rc = getBootEnable(ctx, valid);
2025             if (rc != ipmi::ccSuccess)
2026             {
2027                 return ipmi::response(rc);
2028             }
2029 
2030             uint1_t validFlag = valid ? 1 : 0;
2031 
2032             response.pack(bootOptionParameter, reserved1, uint5_t{},
2033                           uint1_t{biosBootType}, uint1_t{permanent},
2034                           uint1_t{validFlag}, uint2_t{}, uint4_t{bootOption},
2035                           uint2_t{}, uint8_t{}, uint8_t{}, uint8_t{});
2036             return ipmi::responseSuccess(std::move(response));
2037         }
2038         catch (InternalFailure& e)
2039         {
2040             cache::objectsPtr.reset();
2041             report<InternalFailure>();
2042             return ipmi::responseUnspecifiedError();
2043         }
2044     }
2045     else
2046     {
2047         if ((bootOptionParameter >= oemParmStart) &&
2048             (bootOptionParameter <= oemParmEnd))
2049         {
2050             if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
2051                 BootOptionParameter::opalNetworkSettings)
2052             {
2053                 response.pack(bootOptionParameter, reserved1);
2054                 int ret = getHostNetworkData(response);
2055                 if (ret < 0)
2056                 {
2057                     response.trailingOk = true;
2058                     log<level::ERR>(
2059                         "getHostNetworkData failed for GetSysBootOptions.");
2060                     return ipmi::responseUnspecifiedError();
2061                 }
2062                 else
2063                 {
2064                     return ipmi::responseSuccess(std::move(response));
2065                 }
2066             }
2067             else
2068             {
2069                 log<level::ERR>(
2070                     "ipmiChassisGetSysBootOptions: Unsupported parameter",
2071                     entry("PARAM=0x%x",
2072                           static_cast<uint8_t>(bootOptionParameter)));
2073                 return ipmi::responseParmNotSupported();
2074             }
2075         }
2076         else
2077         {
2078             log<level::ERR>(
2079                 "ipmiChassisGetSysBootOptions: Unsupported parameter",
2080                 entry("PARAM=0x%x", static_cast<uint8_t>(bootOptionParameter)));
2081             return ipmi::responseParmNotSupported();
2082         }
2083     }
2084     return ipmi::responseUnspecifiedError();
2085 }
2086 
2087 ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx,
2088                                              uint7_t parameterSelector,
2089                                              bool parameterIsValid,
2090                                              ipmi::message::Payload& data)
2091 {
2092     using namespace boot_options;
2093     ipmi::Cc rc;
2094 
2095     if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2096         BootOptionParameter::setInProgress)
2097     {
2098         uint2_t setInProgressFlag;
2099         uint6_t rsvd;
2100         if (data.unpack(setInProgressFlag, rsvd) != 0 || !data.fullyUnpacked())
2101         {
2102             return ipmi::responseReqDataLenInvalid();
2103         }
2104         if (rsvd)
2105         {
2106             return ipmi::responseInvalidFieldRequest();
2107         }
2108         if ((transferStatus == setInProgress) &&
2109             (static_cast<uint8_t>(setInProgressFlag) != setComplete))
2110         {
2111             return ipmi::response(IPMI_CC_FAIL_SET_IN_PROGRESS);
2112         }
2113         transferStatus = static_cast<uint8_t>(setInProgressFlag);
2114         return ipmi::responseSuccess();
2115     }
2116 
2117     /*  000101
2118      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
2119      * This is the only parameter used by petitboot.
2120      */
2121 
2122     if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2123         BootOptionParameter::bootFlags)
2124     {
2125         uint5_t rsvd;
2126         bool validFlag;
2127         bool permanent;
2128         bool biosBootType;
2129         bool lockOutResetButton;
2130         bool screenBlank;
2131         uint4_t bootDeviceSelector;
2132         bool lockKeyboard;
2133         bool cmosClear;
2134         uint8_t data3;
2135         uint4_t biosInfo;
2136         uint4_t rsvd1;
2137         uint5_t deviceInstance;
2138         uint3_t rsvd2;
2139 
2140         if (data.unpack(rsvd, biosBootType, permanent, validFlag,
2141                         lockOutResetButton, screenBlank, bootDeviceSelector,
2142                         lockKeyboard, cmosClear, data3, biosInfo, rsvd1,
2143                         deviceInstance, rsvd2) != 0 ||
2144             !data.fullyUnpacked())
2145         {
2146             return ipmi::responseReqDataLenInvalid();
2147         }
2148         if (rsvd || rsvd1 || rsvd2)
2149         {
2150             return ipmi::responseInvalidFieldRequest();
2151         }
2152 
2153         using namespace chassis::internal;
2154         using namespace chassis::internal::cache;
2155 
2156         try
2157         {
2158             rc = setBootOneTime(ctx, !permanent);
2159             if (rc != ipmi::ccSuccess)
2160             {
2161                 return ipmi::response(rc);
2162             }
2163 
2164             rc = setBootEnable(ctx, validFlag);
2165             if (rc != ipmi::ccSuccess)
2166             {
2167                 return ipmi::response(rc);
2168             }
2169 
2170             auto modeItr =
2171                 modeIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector));
2172             auto typeItr =
2173                 typeIpmiToDbus.find(static_cast<uint8_t>(biosBootType));
2174             auto sourceItr =
2175                 sourceIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector));
2176             if (sourceIpmiToDbus.end() != sourceItr)
2177             {
2178                 rc = setBootSource(ctx, sourceItr->second);
2179                 if (rc != ipmi::ccSuccess)
2180                 {
2181                     return ipmi::response(rc);
2182                 }
2183                 // If a set boot device is mapping to a boot source, then reset
2184                 // the boot mode D-Bus property to default.
2185                 // This way the ipmid code can determine which property is not
2186                 // at the default value
2187                 if (sourceItr->second != Source::Sources::Default)
2188                 {
2189                     rc = setBootMode(ctx, Mode::Modes::Regular);
2190                     if (rc != ipmi::ccSuccess)
2191                     {
2192                         return ipmi::response(rc);
2193                     }
2194                 }
2195             }
2196 
2197             if (typeIpmiToDbus.end() != typeItr)
2198             {
2199                 rc = setBootType(ctx, typeItr->second);
2200                 if (rc != ipmi::ccSuccess)
2201                 {
2202                     return ipmi::response(rc);
2203                 }
2204             }
2205 
2206             if (modeIpmiToDbus.end() != modeItr)
2207             {
2208                 rc = setBootMode(ctx, modeItr->second);
2209                 if (rc != ipmi::ccSuccess)
2210                 {
2211                     return ipmi::response(rc);
2212                 }
2213                 // If a set boot device is mapping to a boot mode, then reset
2214                 // the boot source D-Bus property to default.
2215                 // This way the ipmid code can determine which property is not
2216                 // at the default value
2217                 if (modeItr->second != Mode::Modes::Regular)
2218                 {
2219                     rc = setBootSource(ctx, Source::Sources::Default);
2220                     if (rc != ipmi::ccSuccess)
2221                     {
2222                         return ipmi::response(rc);
2223                     }
2224                 }
2225             }
2226             if ((modeIpmiToDbus.end() == modeItr) &&
2227                 (typeIpmiToDbus.end() == typeItr) &&
2228                 (sourceIpmiToDbus.end() == sourceItr))
2229             {
2230                 // return error if boot option is not supported
2231                 log<level::ERR>(
2232                     "ipmiChassisSetSysBootOptions: Boot option not supported");
2233                 return ipmi::responseInvalidFieldRequest();
2234             }
2235         }
2236         catch (sdbusplus::exception_t& e)
2237         {
2238             objectsPtr.reset();
2239             report<InternalFailure>();
2240             log<level::ERR>(
2241                 "ipmiChassisSetSysBootOptions: Error in setting Boot "
2242                 "flag parameters");
2243             return ipmi::responseUnspecifiedError();
2244         }
2245     }
2246     else if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2247              BootOptionParameter::bootInfo)
2248     {
2249         uint8_t writeMak;
2250         uint5_t bootInitiatorAckData;
2251         uint3_t rsvd;
2252 
2253         if (data.unpack(writeMak, bootInitiatorAckData, rsvd) != 0 ||
2254             !data.fullyUnpacked())
2255         {
2256             return ipmi::responseReqDataLenInvalid();
2257         }
2258         if (rsvd)
2259         {
2260             return ipmi::responseInvalidFieldRequest();
2261         }
2262         // (ccSuccess). There is no implementation in OpenBMC for this
2263         // parameter. This is added to support the ipmitool command `chassis
2264         // bootdev` which sends set on parameter #4, before setting the boot
2265         // flags.
2266         log<level::INFO>("ipmiChassisSetSysBootOptions: bootInfo parameter set "
2267                          "successfully");
2268         data.trailingOk = true;
2269         return ipmi::responseSuccess();
2270     }
2271     else if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2272              BootOptionParameter::bootFlagValidClr)
2273     {
2274         uint5_t bootFlagValidClr;
2275         uint3_t rsvd;
2276 
2277         if (data.unpack(bootFlagValidClr, rsvd) != 0 || !data.fullyUnpacked())
2278         {
2279             return ipmi::responseReqDataLenInvalid();
2280         }
2281         if (rsvd)
2282         {
2283             return ipmi::responseInvalidFieldRequest();
2284         }
2285         // store boot flag valid bits clear value
2286         bootFlagValidBitClr = static_cast<uint8_t>(bootFlagValidClr);
2287         log<level::INFO>(
2288             "ipmiChassisSetSysBootOptions: bootFlagValidBits parameter set "
2289             "successfully",
2290             entry("value=0x%x", bootFlagValidBitClr));
2291         return ipmi::responseSuccess();
2292     }
2293     else
2294     {
2295         if ((parameterSelector >= static_cast<uint7_t>(oemParmStart)) &&
2296             (parameterSelector <= static_cast<uint7_t>(oemParmEnd)))
2297         {
2298             if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2299                 BootOptionParameter::opalNetworkSettings)
2300             {
2301                 ipmi::Cc ret = setHostNetworkData(data);
2302                 if (ret != ipmi::ccSuccess)
2303                 {
2304                     log<level::ERR>("ipmiChassisSetSysBootOptions: Error in "
2305                                     "setHostNetworkData");
2306                     data.trailingOk = true;
2307                     return ipmi::response(ret);
2308                 }
2309                 data.trailingOk = true;
2310                 return ipmi::responseSuccess();
2311             }
2312             else
2313             {
2314                 log<level::ERR>(
2315                     "ipmiChassisSetSysBootOptions: Unsupported parameters",
2316                     entry("PARAM=0x%x",
2317                           static_cast<uint8_t>(parameterSelector)));
2318                 data.trailingOk = true;
2319                 return ipmi::responseParmNotSupported();
2320             }
2321         }
2322         data.trailingOk = true;
2323         return ipmi::responseParmNotSupported();
2324     }
2325     return ipmi::responseSuccess();
2326 }
2327 
2328 /** @brief implements Get POH counter command
2329  *  @parameter
2330  *   -  none
2331  *  @returns IPMI completion code plus response data
2332  *   - minPerCount - Minutes per count
2333  *   - counterReading - counter reading
2334  */
2335 ipmi::RspType<uint8_t, // Minutes per count
2336               uint32_t // Counter reading
2337               >
2338     ipmiGetPOHCounter()
2339 {
2340     // sd_bus error
2341     try
2342     {
2343         return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount),
2344                                      getPOHCounter());
2345     }
2346     catch (std::exception& e)
2347     {
2348         log<level::ERR>(e.what());
2349         return ipmi::responseUnspecifiedError();
2350     }
2351 }
2352 
2353 ipmi::RspType<uint3_t, // policy support
2354               uint5_t  // reserved
2355               >
2356     ipmiChassisSetPowerRestorePolicy(boost::asio::yield_context yield,
2357                                      uint3_t policy, uint5_t reserved)
2358 {
2359     power_policy::DbusValue value =
2360         power_policy::RestorePolicy::Policy::AlwaysOff;
2361 
2362     if (reserved || (policy > power_policy::noChange))
2363     {
2364         phosphor::logging::log<level::ERR>(
2365             "Reserved request parameter",
2366             entry("REQ=0x%x", static_cast<int>(policy)));
2367         return ipmi::responseInvalidFieldRequest();
2368     }
2369 
2370     if (policy == power_policy::noChange)
2371     {
2372         // just return the supported policy
2373         return ipmi::responseSuccess(power_policy::allSupport, reserved);
2374     }
2375 
2376     for (auto const& it : power_policy::dbusToIpmi)
2377     {
2378         if (it.second == policy)
2379         {
2380             value = it.first;
2381             break;
2382         }
2383     }
2384 
2385     try
2386     {
2387         settings::Objects& objects = chassis::internal::cache::getObjects();
2388         const settings::Path& powerRestoreSetting =
2389             objects.map.at(chassis::internal::powerRestoreIntf).front();
2390         std::variant<std::string> property = convertForMessage(value);
2391 
2392         auto sdbusp = getSdBus();
2393         boost::system::error_code ec;
2394         sdbusp->yield_method_call<void>(
2395             yield, ec,
2396             objects
2397                 .service(powerRestoreSetting,
2398                          chassis::internal::powerRestoreIntf)
2399                 .c_str(),
2400             powerRestoreSetting, ipmi::PROP_INTF, "Set",
2401             chassis::internal::powerRestoreIntf, "PowerRestorePolicy",
2402             property);
2403         if (ec)
2404         {
2405             phosphor::logging::log<level::ERR>("Unspecified Error");
2406             return ipmi::responseUnspecifiedError();
2407         }
2408     }
2409     catch (InternalFailure& e)
2410     {
2411         chassis::internal::cache::objectsPtr.reset();
2412         report<InternalFailure>();
2413         return ipmi::responseUnspecifiedError();
2414     }
2415 
2416     return ipmi::responseSuccess(power_policy::allSupport, reserved);
2417 }
2418 
2419 ipmi::RspType<> ipmiSetFrontPanelButtonEnables(
2420     ipmi::Context::ptr ctx, bool disablePowerButton, bool disableResetButton,
2421     bool disableDiagButton, bool disableSleepButton, uint4_t reserved)
2422 {
2423     using namespace chassis::internal;
2424 
2425     // set power button Enabled property
2426     bool success = setButtonEnabled(ctx, powerButtonPath, powerButtonIntf,
2427                                     !disablePowerButton);
2428 
2429     // set reset button Enabled property
2430     success &= setButtonEnabled(ctx, resetButtonPath, resetButtonIntf,
2431                                 !disableResetButton);
2432 
2433     if (!success)
2434     {
2435         // not all buttons were successfully set
2436         return ipmi::responseUnspecifiedError();
2437     }
2438     return ipmi::responseSuccess();
2439 }
2440 
2441 void register_netfn_chassis_functions()
2442 {
2443     createIdentifyTimer();
2444 
2445     // Get Chassis Capabilities
2446     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2447                           ipmi::chassis::cmdGetChassisCapabilities,
2448                           ipmi::Privilege::User, ipmiGetChassisCap);
2449 
2450     // Set Front Panel Button Enables
2451     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2452                           ipmi::chassis::cmdSetFrontPanelButtonEnables,
2453                           ipmi::Privilege::Admin,
2454                           ipmiSetFrontPanelButtonEnables);
2455 
2456     // Set Chassis Capabilities
2457     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2458                           ipmi::chassis::cmdSetChassisCapabilities,
2459                           ipmi::Privilege::User, ipmiSetChassisCap);
2460 
2461     // <Get System Boot Options>
2462     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2463                           ipmi::chassis::cmdGetSystemBootOptions,
2464                           ipmi::Privilege::Operator,
2465                           ipmiChassisGetSysBootOptions);
2466 
2467     // <Get Chassis Status>
2468     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2469                           ipmi::chassis::cmdGetChassisStatus,
2470                           ipmi::Privilege::User, ipmiGetChassisStatus);
2471 
2472     // <Chassis Get System Restart Cause>
2473     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2474                           ipmi::chassis::cmdGetSystemRestartCause,
2475                           ipmi::Privilege::User, ipmiGetSystemRestartCause);
2476 
2477     // <Chassis Control>
2478     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2479                           ipmi::chassis::cmdChassisControl,
2480                           ipmi::Privilege::Operator, ipmiChassisControl);
2481 
2482     // <Chassis Identify>
2483     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2484                           ipmi::chassis::cmdChassisIdentify,
2485                           ipmi::Privilege::Operator, ipmiChassisIdentify);
2486 
2487     // <Set System Boot Options>
2488     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2489                           ipmi::chassis::cmdSetSystemBootOptions,
2490                           ipmi::Privilege::Operator,
2491                           ipmiChassisSetSysBootOptions);
2492 
2493     // <Get POH Counter>
2494     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2495                           ipmi::chassis::cmdGetPohCounter,
2496                           ipmi::Privilege::User, ipmiGetPOHCounter);
2497 
2498     // <Set Power Restore Policy>
2499     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2500                           ipmi::chassis::cmdSetPowerRestorePolicy,
2501                           ipmi::Privilege::Operator,
2502                           ipmiChassisSetPowerRestorePolicy);
2503 }
2504