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/Power/RestorePolicy/server.hpp>
34 #include <xyz/openbmc_project/State/Host/server.hpp>
35 #include <xyz/openbmc_project/State/PowerOnHours/server.hpp>
36 
37 // Defines
38 #define SET_PARM_VERSION 0x01
39 #define SET_PARM_BOOT_FLAGS_PERMANENT 0x40
40 #define SET_PARM_BOOT_FLAGS_VALID_ONE_TIME 0x80
41 #define SET_PARM_BOOT_FLAGS_VALID_PERMANENT 0xC0
42 
43 std::unique_ptr<phosphor::Timer> identifyTimer
44     __attribute__((init_priority(101)));
45 
46 static ChassisIDState chassisIDState = ChassisIDState::reserved;
47 
48 constexpr size_t SIZE_MAC = 18;
49 constexpr size_t SIZE_BOOT_OPTION = (uint8_t)
50     BootOptionResponseSize::OPAL_NETWORK_SETTINGS; // Maximum size of the boot
51                                                    // option parametrs
52 constexpr size_t SIZE_PREFIX = 7;
53 constexpr size_t MAX_PREFIX_VALUE = 32;
54 constexpr size_t SIZE_COOKIE = 4;
55 constexpr size_t SIZE_VERSION = 2;
56 constexpr size_t DEFAULT_IDENTIFY_TIME_OUT = 15;
57 
58 // PetiBoot-Specific
59 static constexpr uint8_t net_conf_initial_bytes[] = {0x80, 0x21, 0x70, 0x62,
60                                                      0x21, 0x00, 0x01, 0x06};
61 
62 static constexpr size_t COOKIE_OFFSET = 1;
63 static constexpr size_t VERSION_OFFSET = 5;
64 static constexpr size_t ADDR_SIZE_OFFSET = 8;
65 static constexpr size_t MAC_OFFSET = 9;
66 static constexpr size_t ADDRTYPE_OFFSET = 16;
67 static constexpr size_t IPADDR_OFFSET = 17;
68 
69 static constexpr size_t encIdentifyObjectsSize = 1;
70 static constexpr size_t chassisIdentifyReqLength = 2;
71 static constexpr size_t identifyIntervalPos = 0;
72 static constexpr size_t forceIdentifyPos = 1;
73 
74 void register_netfn_chassis_functions() __attribute__((constructor));
75 
76 // Host settings in dbus
77 // Service name should be referenced by connection name got via object mapper
78 const char* settings_object_name = "/org/openbmc/settings/host0";
79 const char* settings_intf_name = "org.freedesktop.DBus.Properties";
80 const char* identify_led_object_name =
81     "/xyz/openbmc_project/led/groups/enclosure_identify";
82 
83 constexpr auto SETTINGS_ROOT = "/";
84 constexpr auto SETTINGS_MATCH = "host0";
85 
86 constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP";
87 constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress";
88 
89 static constexpr auto chassisStateRoot = "/xyz/openbmc_project/state";
90 static constexpr auto chassisPOHStateIntf =
91     "xyz.openbmc_project.State.PowerOnHours";
92 static constexpr auto pOHCounterProperty = "POHCounter";
93 static constexpr auto match = "chassis0";
94 const static constexpr char chassisCapIntf[] =
95     "xyz.openbmc_project.Control.ChassisCapabilities";
96 const static constexpr char chassisIntrusionProp[] = "ChassisIntrusionEnabled";
97 const static constexpr char chassisFrontPanelLockoutProp[] =
98     "ChassisFrontPanelLockoutEnabled";
99 const static constexpr char chassisNMIProp[] = "ChassisNMIEnabled";
100 const static constexpr char chassisPowerInterlockProp[] =
101     "ChassisPowerInterlockEnabled";
102 const static constexpr char chassisFRUDevAddrProp[] = "FRUDeviceAddress";
103 const static constexpr char chassisSDRDevAddrProp[] = "SDRDeviceAddress";
104 const static constexpr char chassisSELDevAddrProp[] = "SELDeviceAddress";
105 const static constexpr char chassisSMDevAddrProp[] = "SMDeviceAddress";
106 const static constexpr char chassisBridgeDevAddrProp[] = "BridgeDeviceAddress";
107 static constexpr uint8_t chassisCapFlagMask = 0x0f;
108 static constexpr uint8_t chassisCapAddrMask = 0xfe;
109 static constexpr const char* powerButtonIntf =
110     "xyz.openbmc_project.Chassis.Buttons.Power";
111 static constexpr const char* powerButtonPath =
112     "/xyz/openbmc_project/Chassis/Buttons/Power0";
113 static constexpr const char* resetButtonIntf =
114     "xyz.openbmc_project.Chassis.Buttons.Reset";
115 static constexpr const char* resetButtonPath =
116     "/xyz/openbmc_project/Chassis/Buttons/Reset0";
117 
118 // Phosphor Host State manager
119 namespace State = sdbusplus::xyz::openbmc_project::State::server;
120 
121 namespace fs = std::filesystem;
122 
123 using namespace phosphor::logging;
124 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
125 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
126 
127 namespace chassis
128 {
129 namespace internal
130 {
131 
132 constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
133 constexpr auto bootSourceIntf = "xyz.openbmc_project.Control.Boot.Source";
134 constexpr auto powerRestoreIntf =
135     "xyz.openbmc_project.Control.Power.RestorePolicy";
136 sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection());
137 
138 namespace cache
139 {
140 
141 std::unique_ptr<settings::Objects> objectsPtr = nullptr;
142 
143 settings::Objects& getObjects()
144 {
145     if (objectsPtr == nullptr)
146     {
147         objectsPtr = std::make_unique<settings::Objects>(
148             dbus, std::vector<std::string>{bootModeIntf, bootSourceIntf,
149                                            powerRestoreIntf});
150     }
151     return *objectsPtr;
152 }
153 
154 } // namespace cache
155 } // namespace internal
156 } // namespace chassis
157 
158 namespace poh
159 {
160 
161 constexpr auto minutesPerCount = 60;
162 
163 } // namespace poh
164 
165 struct get_sys_boot_options_t
166 {
167     uint8_t parameter;
168     uint8_t set;
169     uint8_t block;
170 } __attribute__((packed));
171 
172 struct get_sys_boot_options_response_t
173 {
174     uint8_t version;
175     uint8_t parm;
176     uint8_t data[SIZE_BOOT_OPTION];
177 } __attribute__((packed));
178 
179 struct set_sys_boot_options_t
180 {
181     uint8_t parameter;
182     uint8_t data[SIZE_BOOT_OPTION];
183 } __attribute__((packed));
184 
185 int getHostNetworkData(get_sys_boot_options_response_t* respptr)
186 {
187     ipmi::PropertyMap properties;
188     int rc = 0;
189     uint8_t addrSize = ipmi::network::IPV4_ADDRESS_SIZE_BYTE;
190 
191     try
192     {
193         // TODO There may be cases where an interface is implemented by multiple
194         // objects,to handle such cases we are interested on that object
195         //  which are on interested busname.
196         //  Currenlty mapper doesn't give the readable busname(gives busid)
197         //  so we can't match with bus name so giving some object specific info
198         //  as SETTINGS_MATCH.
199         //  Later SETTINGS_MATCH will be replaced with busname.
200 
201         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
202 
203         auto ipObjectInfo = ipmi::getDbusObject(bus, IP_INTERFACE,
204                                                 SETTINGS_ROOT, SETTINGS_MATCH);
205 
206         auto macObjectInfo = ipmi::getDbusObject(bus, MAC_INTERFACE,
207                                                  SETTINGS_ROOT, SETTINGS_MATCH);
208 
209         properties = ipmi::getAllDbusProperties(
210             bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE);
211         auto variant = ipmi::getDbusProperty(bus, macObjectInfo.second,
212                                              macObjectInfo.first, MAC_INTERFACE,
213                                              "MACAddress");
214 
215         auto ipAddress = std::get<std::string>(properties["Address"]);
216 
217         auto gateway = std::get<std::string>(properties["Gateway"]);
218 
219         auto prefix = std::get<uint8_t>(properties["PrefixLength"]);
220 
221         uint8_t isStatic =
222             (std::get<std::string>(properties["Origin"]) ==
223              "xyz.openbmc_project.Network.IP.AddressOrigin.Static")
224                 ? 1
225                 : 0;
226 
227         auto MACAddress = std::get<std::string>(variant);
228 
229         // it is expected here that we should get the valid data
230         // but we may also get the default values.
231         // Validation of the data is done by settings.
232         //
233         // if mac address is default mac address then
234         // don't send blank override.
235         if ((MACAddress == ipmi::network::DEFAULT_MAC_ADDRESS))
236         {
237             std::memset(respptr->data, 0, SIZE_BOOT_OPTION);
238             rc = -1;
239             return rc;
240         }
241         // if addr is static then ipaddress,gateway,prefix
242         // should not be default one,don't send blank override.
243         if (isStatic)
244         {
245             if ((ipAddress == ipmi::network::DEFAULT_ADDRESS) ||
246                 (gateway == ipmi::network::DEFAULT_ADDRESS) || (!prefix))
247             {
248                 std::memset(respptr->data, 0, SIZE_BOOT_OPTION);
249                 rc = -1;
250                 return rc;
251             }
252         }
253 
254         sscanf(
255             MACAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT,
256             (respptr->data + MAC_OFFSET), (respptr->data + MAC_OFFSET + 1),
257             (respptr->data + MAC_OFFSET + 2), (respptr->data + MAC_OFFSET + 3),
258             (respptr->data + MAC_OFFSET + 4), (respptr->data + MAC_OFFSET + 5));
259 
260         respptr->data[MAC_OFFSET + 6] = 0x00;
261 
262         std::memcpy(respptr->data + ADDRTYPE_OFFSET, &isStatic,
263                     sizeof(isStatic));
264 
265         uint8_t addressFamily = (std::get<std::string>(properties["Type"]) ==
266                                  "xyz.openbmc_project.Network.IP.Protocol.IPv4")
267                                     ? AF_INET
268                                     : AF_INET6;
269 
270         addrSize = (addressFamily == AF_INET)
271                        ? ipmi::network::IPV4_ADDRESS_SIZE_BYTE
272                        : ipmi::network::IPV6_ADDRESS_SIZE_BYTE;
273 
274         // ipaddress and gateway would be in IPv4 format
275         inet_pton(addressFamily, ipAddress.c_str(),
276                   (respptr->data + IPADDR_OFFSET));
277 
278         uint8_t prefixOffset = IPADDR_OFFSET + addrSize;
279 
280         std::memcpy(respptr->data + prefixOffset, &prefix, sizeof(prefix));
281 
282         uint8_t gatewayOffset = prefixOffset + sizeof(decltype(prefix));
283 
284         inet_pton(addressFamily, gateway.c_str(),
285                   (respptr->data + gatewayOffset));
286     }
287     catch (InternalFailure& e)
288     {
289         commit<InternalFailure>();
290         std::memset(respptr->data, 0, SIZE_BOOT_OPTION);
291         rc = -1;
292         return rc;
293     }
294 
295     // PetiBoot-Specific
296     // If success then copy the first 9 bytes to the data
297     std::memcpy(respptr->data, net_conf_initial_bytes,
298                 sizeof(net_conf_initial_bytes));
299 
300     std::memcpy(respptr->data + ADDR_SIZE_OFFSET, &addrSize, sizeof(addrSize));
301 
302 #ifdef _IPMI_DEBUG_
303     std::printf("\n===Printing the IPMI Formatted Data========\n");
304 
305     for (uint8_t pos = 0; pos < index; pos++)
306     {
307         std::printf("%02x ", respptr->data[pos]);
308     }
309 #endif
310 
311     return rc;
312 }
313 
314 /** @brief convert IPv4 and IPv6 addresses from binary to text form.
315  *  @param[in] family - IPv4/Ipv6
316  *  @param[in] data - req data pointer.
317  *  @param[in] offset - offset in the data.
318  *  @param[in] addrSize - size of the data which needs to be read from offset.
319  *  @returns address in text form.
320  */
321 
322 std::string getAddrStr(uint8_t family, uint8_t* data, uint8_t offset,
323                        uint8_t addrSize)
324 {
325     char ipAddr[INET6_ADDRSTRLEN] = {};
326 
327     switch (family)
328     {
329         case AF_INET:
330         {
331             struct sockaddr_in addr4
332             {
333             };
334             std::memcpy(&addr4.sin_addr.s_addr, &data[offset], addrSize);
335 
336             inet_ntop(AF_INET, &addr4.sin_addr, ipAddr, INET_ADDRSTRLEN);
337 
338             break;
339         }
340         case AF_INET6:
341         {
342             struct sockaddr_in6 addr6
343             {
344             };
345             std::memcpy(&addr6.sin6_addr.s6_addr, &data[offset], addrSize);
346 
347             inet_ntop(AF_INET6, &addr6.sin6_addr, ipAddr, INET6_ADDRSTRLEN);
348 
349             break;
350         }
351         default:
352         {
353             return {};
354         }
355     }
356 
357     return ipAddr;
358 }
359 
360 int setHostNetworkData(set_sys_boot_options_t* reqptr)
361 {
362     using namespace std::string_literals;
363     std::string host_network_config;
364     char mac[]{"00:00:00:00:00:00"};
365     std::string ipAddress, gateway;
366     char addrOrigin{0};
367     uint8_t addrSize{0};
368     std::string addressOrigin =
369         "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP";
370     std::string addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
371     uint8_t prefix{0};
372     uint32_t zeroCookie = 0;
373     uint8_t family = AF_INET;
374 
375     // cookie starts from second byte
376     // version starts from sixth byte
377 
378     try
379     {
380         do
381         {
382             // cookie ==  0x21 0x70 0x62 0x21
383             if (memcmp(&(reqptr->data[COOKIE_OFFSET]),
384                        (net_conf_initial_bytes + COOKIE_OFFSET),
385                        SIZE_COOKIE) != 0)
386             {
387                 // cookie == 0
388                 if (memcmp(&(reqptr->data[COOKIE_OFFSET]), &zeroCookie,
389                            SIZE_COOKIE) == 0)
390                 {
391                     // need to zero out the network settings.
392                     break;
393                 }
394 
395                 log<level::ERR>("Invalid Cookie");
396                 elog<InternalFailure>();
397             }
398 
399             // vesion == 0x00 0x01
400             if (memcmp(&(reqptr->data[VERSION_OFFSET]),
401                        (net_conf_initial_bytes + VERSION_OFFSET),
402                        SIZE_VERSION) != 0)
403             {
404 
405                 log<level::ERR>("Invalid Version");
406                 elog<InternalFailure>();
407             }
408 
409             std::snprintf(
410                 mac, SIZE_MAC, ipmi::network::MAC_ADDRESS_FORMAT,
411                 reqptr->data[MAC_OFFSET], reqptr->data[MAC_OFFSET + 1],
412                 reqptr->data[MAC_OFFSET + 2], reqptr->data[MAC_OFFSET + 3],
413                 reqptr->data[MAC_OFFSET + 4], reqptr->data[MAC_OFFSET + 5]);
414 
415             std::memcpy(&addrOrigin, &(reqptr->data[ADDRTYPE_OFFSET]),
416                         sizeof(decltype(addrOrigin)));
417 
418             if (addrOrigin)
419             {
420                 addressOrigin =
421                     "xyz.openbmc_project.Network.IP.AddressOrigin.Static";
422             }
423 
424             // Get the address size
425             std::memcpy(&addrSize, &reqptr->data[ADDR_SIZE_OFFSET],
426                         sizeof(addrSize));
427 
428             uint8_t prefixOffset = IPADDR_OFFSET + addrSize;
429 
430             std::memcpy(&prefix, &(reqptr->data[prefixOffset]),
431                         sizeof(decltype(prefix)));
432 
433             uint8_t gatewayOffset = prefixOffset + sizeof(decltype(prefix));
434 
435             if (addrSize != ipmi::network::IPV4_ADDRESS_SIZE_BYTE)
436             {
437                 addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv6";
438                 family = AF_INET6;
439             }
440 
441             ipAddress =
442                 getAddrStr(family, reqptr->data, IPADDR_OFFSET, addrSize);
443 
444             gateway = getAddrStr(family, reqptr->data, gatewayOffset, addrSize);
445 
446         } while (0);
447 
448         // Cookie == 0 or it is a valid cookie
449         host_network_config += "ipaddress="s + ipAddress + ",prefix="s +
450                                std::to_string(prefix) + ",gateway="s + gateway +
451                                ",mac="s + mac + ",addressOrigin="s +
452                                addressOrigin;
453 
454         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
455 
456         auto ipObjectInfo = ipmi::getDbusObject(bus, IP_INTERFACE,
457                                                 SETTINGS_ROOT, SETTINGS_MATCH);
458         auto macObjectInfo = ipmi::getDbusObject(bus, MAC_INTERFACE,
459                                                  SETTINGS_ROOT, SETTINGS_MATCH);
460         // set the dbus property
461         ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first,
462                               IP_INTERFACE, "Address", std::string(ipAddress));
463         ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first,
464                               IP_INTERFACE, "PrefixLength", prefix);
465         ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first,
466                               IP_INTERFACE, "Origin", addressOrigin);
467         ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first,
468                               IP_INTERFACE, "Gateway", std::string(gateway));
469         ipmi::setDbusProperty(
470             bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE, "Type",
471             std::string("xyz.openbmc_project.Network.IP.Protocol.IPv4"));
472         ipmi::setDbusProperty(bus, macObjectInfo.second, macObjectInfo.first,
473                               MAC_INTERFACE, "MACAddress", std::string(mac));
474 
475         log<level::DEBUG>(
476             "Network configuration changed",
477             entry("NETWORKCONFIG=%s", host_network_config.c_str()));
478     }
479     catch (InternalFailure& e)
480     {
481         commit<InternalFailure>();
482         return -1;
483     }
484 
485     return 0;
486 }
487 
488 uint32_t getPOHCounter()
489 {
490     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
491 
492     auto chassisStateObj =
493         ipmi::getDbusObject(bus, chassisPOHStateIntf, chassisStateRoot, match);
494 
495     auto service =
496         ipmi::getService(bus, chassisPOHStateIntf, chassisStateObj.first);
497 
498     auto propValue =
499         ipmi::getDbusProperty(bus, service, chassisStateObj.first,
500                               chassisPOHStateIntf, pOHCounterProperty);
501 
502     return std::get<uint32_t>(propValue);
503 }
504 
505 /** @brief Implements the get chassis capabilities command
506  *
507  *  @returns IPMI completion code plus response data
508  *  chassisCapFlags        - chassis capability flag
509  *  chassisFRUInfoDevAddr  - chassis FRU info Device Address
510  *  chassisSDRDevAddr      - chassis SDR device address
511  *  chassisSELDevAddr      - chassis SEL device address
512  *  chassisSMDevAddr       - chassis system management device address
513  *  chassisBridgeDevAddr   - chassis bridge device address
514  */
515 ipmi::RspType<bool,    // chassis intrusion sensor
516               bool,    // chassis Front panel lockout
517               bool,    // chassis NMI
518               bool,    // chassis power interlock
519               uint4_t, // reserved
520               uint8_t, // chassis FRU info Device Address
521               uint8_t, // chassis SDR device address
522               uint8_t, // chassis SEL device address
523               uint8_t, // chassis system management device address
524               uint8_t  // chassis bridge device address
525               >
526     ipmiGetChassisCap()
527 {
528     ipmi::PropertyMap properties;
529     try
530     {
531         sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
532 
533         ipmi::DbusObjectInfo chassisCapObject =
534             ipmi::getDbusObject(bus, chassisCapIntf);
535 
536         // capabilities flags
537         // [7..4] - reserved
538         // [3] – 1b = provides power interlock  (IPM 1.5)
539         // [2] – 1b = provides Diagnostic Interrupt (FP NMI)
540         // [1] – 1b = provides “Front Panel Lockout” (indicates that the chassis
541         // has capabilities
542         //            to lock out external power control and reset button or
543         //            front panel interfaces and/or detect tampering with those
544         //            interfaces).
545         // [0] -1b = Chassis provides intrusion (physical security) sensor.
546         // set to default value 0x0.
547 
548         properties =
549             ipmi::getAllDbusProperties(bus, chassisCapObject.second,
550                                        chassisCapObject.first, chassisCapIntf);
551     }
552     catch (std::exception& e)
553     {
554         log<level::ERR>("Failed to fetch Chassis Capability properties",
555                         entry("ERROR=%s", e.what()));
556         return ipmi::responseUnspecifiedError();
557     }
558 
559     bool* chassisIntrusionFlag =
560         std::get_if<bool>(&properties[chassisIntrusionProp]);
561     if (chassisIntrusionFlag == nullptr)
562     {
563         log<level::ERR>("Error to get chassis Intrusion flags");
564         return ipmi::responseUnspecifiedError();
565     }
566     bool* chassisFrontPanelFlag =
567         std::get_if<bool>(&properties[chassisFrontPanelLockoutProp]);
568     if (chassisFrontPanelFlag == nullptr)
569     {
570         log<level::ERR>("Error to get chassis intrusion flags");
571         return ipmi::responseUnspecifiedError();
572     }
573     bool* chassisNMIFlag = std::get_if<bool>(&properties[chassisNMIProp]);
574     if (chassisNMIFlag == nullptr)
575     {
576         log<level::ERR>("Error to get chassis NMI flags");
577         return ipmi::responseUnspecifiedError();
578     }
579     bool* chassisPowerInterlockFlag =
580         std::get_if<bool>(&properties[chassisPowerInterlockProp]);
581     if (chassisPowerInterlockFlag == nullptr)
582     {
583         log<level::ERR>("Error to get chassis power interlock flags");
584         return ipmi::responseUnspecifiedError();
585     }
586     uint8_t* chassisFRUInfoDevAddr =
587         std::get_if<uint8_t>(&properties[chassisFRUDevAddrProp]);
588     if (chassisFRUInfoDevAddr == nullptr)
589     {
590         log<level::ERR>("Error to get chassis FRU info device address");
591         return ipmi::responseUnspecifiedError();
592     }
593     uint8_t* chassisSDRDevAddr =
594         std::get_if<uint8_t>(&properties[chassisSDRDevAddrProp]);
595     if (chassisSDRDevAddr == nullptr)
596     {
597         log<level::ERR>("Error to get chassis SDR device address");
598         return ipmi::responseUnspecifiedError();
599     }
600     uint8_t* chassisSELDevAddr =
601         std::get_if<uint8_t>(&properties[chassisSELDevAddrProp]);
602     if (chassisSELDevAddr == nullptr)
603     {
604         log<level::ERR>("Error to get chassis SEL device address");
605         return ipmi::responseUnspecifiedError();
606     }
607     uint8_t* chassisSMDevAddr =
608         std::get_if<uint8_t>(&properties[chassisSMDevAddrProp]);
609     if (chassisSMDevAddr == nullptr)
610     {
611         log<level::ERR>("Error to get chassis SM device address");
612         return ipmi::responseUnspecifiedError();
613     }
614     uint8_t* chassisBridgeDevAddr =
615         std::get_if<uint8_t>(&properties[chassisBridgeDevAddrProp]);
616     if (chassisBridgeDevAddr == nullptr)
617     {
618         log<level::ERR>("Error to get chassis bridge device address");
619         return ipmi::responseUnspecifiedError();
620     }
621 
622     return ipmi::responseSuccess(*chassisIntrusionFlag, *chassisFrontPanelFlag,
623                                  *chassisNMIFlag, *chassisPowerInterlockFlag, 0,
624                                  *chassisFRUInfoDevAddr, *chassisSDRDevAddr,
625                                  *chassisSELDevAddr, *chassisSMDevAddr,
626                                  *chassisBridgeDevAddr);
627 }
628 
629 /** @brief implements set chassis capalibities command
630  *  @param intrusion        - chassis intrusion
631  *  @param fpLockout        - frontpannel lockout
632  *  @param reserved1        - skip one bit
633  *  @param fruDeviceAddr    - chassis FRU info Device Address
634  *  @param sdrDeviceAddr    - chassis SDR device address
635  *  @param selDeviceAddr    - chassis SEL device address
636  *  @param smDeviceAddr     - chassis system management device address
637  *  @param bridgeDeviceAddr - chassis bridge device address
638  *
639  *  @returns IPMI completion code
640  */
641 ipmi::RspType<> ipmiSetChassisCap(bool intrusion, bool fpLockout,
642                                   uint6_t reserved1,
643 
644                                   uint8_t fruDeviceAddr,
645 
646                                   uint8_t sdrDeviceAddr,
647 
648                                   uint8_t selDeviceAddr,
649 
650                                   uint8_t smDeviceAddr,
651 
652                                   uint8_t bridgeDeviceAddr)
653 {
654 
655     // check input data
656     if (reserved1 != 0)
657     {
658         log<level::ERR>("Unsupported request parameter");
659         return ipmi::responseInvalidFieldRequest();
660     }
661 
662     if ((fruDeviceAddr & ~chassisCapAddrMask) != 0)
663     {
664         log<level::ERR>("Unsupported request parameter(FRU Addr)",
665                         entry("REQ=0x%x", fruDeviceAddr));
666         return ipmi::responseInvalidFieldRequest();
667     }
668     if ((sdrDeviceAddr & ~chassisCapAddrMask) != 0)
669     {
670         log<level::ERR>("Unsupported request parameter(SDR Addr)",
671                         entry("REQ=0x%x", sdrDeviceAddr));
672         return ipmi::responseInvalidFieldRequest();
673     }
674 
675     if ((selDeviceAddr & ~chassisCapAddrMask) != 0)
676     {
677         log<level::ERR>("Unsupported request parameter(SEL Addr)",
678                         entry("REQ=0x%x", selDeviceAddr));
679         return ipmi::responseInvalidFieldRequest();
680     }
681 
682     if ((smDeviceAddr & ~chassisCapAddrMask) != 0)
683     {
684         log<level::ERR>("Unsupported request parameter(SM Addr)",
685                         entry("REQ=0x%x", smDeviceAddr));
686         return ipmi::responseInvalidFieldRequest();
687     }
688 
689     if ((bridgeDeviceAddr & ~chassisCapAddrMask) != 0)
690     {
691         log<level::ERR>("Unsupported request parameter(Bridge Addr)",
692                         entry("REQ=0x%x", bridgeDeviceAddr));
693         return ipmi::responseInvalidFieldRequest();
694     }
695 
696     try
697     {
698         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
699         ipmi::DbusObjectInfo chassisCapObject =
700             ipmi::getDbusObject(bus, chassisCapIntf);
701 
702         ipmi::setDbusProperty(bus, chassisCapObject.second,
703                               chassisCapObject.first, chassisCapIntf,
704                               chassisIntrusionProp, intrusion);
705 
706         ipmi::setDbusProperty(bus, chassisCapObject.second,
707                               chassisCapObject.first, chassisCapIntf,
708                               chassisFrontPanelLockoutProp, fpLockout);
709 
710         ipmi::setDbusProperty(bus, chassisCapObject.second,
711                               chassisCapObject.first, chassisCapIntf,
712                               chassisFRUDevAddrProp, fruDeviceAddr);
713 
714         ipmi::setDbusProperty(bus, chassisCapObject.second,
715                               chassisCapObject.first, chassisCapIntf,
716                               chassisSDRDevAddrProp, sdrDeviceAddr);
717 
718         ipmi::setDbusProperty(bus, chassisCapObject.second,
719                               chassisCapObject.first, chassisCapIntf,
720                               chassisSELDevAddrProp, selDeviceAddr);
721 
722         ipmi::setDbusProperty(bus, chassisCapObject.second,
723                               chassisCapObject.first, chassisCapIntf,
724                               chassisSMDevAddrProp, smDeviceAddr);
725 
726         ipmi::setDbusProperty(bus, chassisCapObject.second,
727                               chassisCapObject.first, chassisCapIntf,
728                               chassisBridgeDevAddrProp, bridgeDeviceAddr);
729     }
730     catch (std::exception& e)
731     {
732         log<level::ERR>(e.what());
733         return ipmi::responseUnspecifiedError();
734     }
735     return ipmi::responseSuccess();
736 }
737 
738 //------------------------------------------
739 // Calls into Host State Manager Dbus object
740 //------------------------------------------
741 int initiate_state_transition(State::Host::Transition transition)
742 {
743     // OpenBMC Host State Manager dbus framework
744     constexpr auto HOST_STATE_MANAGER_ROOT = "/xyz/openbmc_project/state/host0";
745     constexpr auto HOST_STATE_MANAGER_IFACE = "xyz.openbmc_project.State.Host";
746     constexpr auto DBUS_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
747     constexpr auto PROPERTY = "RequestedHostTransition";
748 
749     // sd_bus error
750     int rc = 0;
751     char* busname = NULL;
752 
753     // SD Bus error report mechanism.
754     sd_bus_error bus_error = SD_BUS_ERROR_NULL;
755 
756     // Gets a hook onto either a SYSTEM or SESSION bus
757     sd_bus* bus_type = ipmid_get_sd_bus_connection();
758     rc = mapper_get_service(bus_type, HOST_STATE_MANAGER_ROOT, &busname);
759     if (rc < 0)
760     {
761         log<level::ERR>(
762             "Failed to get bus name",
763             entry("ERRNO=0x%X, OBJPATH=%s", -rc, HOST_STATE_MANAGER_ROOT));
764         return rc;
765     }
766 
767     // Convert to string equivalent of the passed in transition enum.
768     auto request = State::convertForMessage(transition);
769 
770     rc = sd_bus_call_method(bus_type,                // On the system bus
771                             busname,                 // Service to contact
772                             HOST_STATE_MANAGER_ROOT, // Object path
773                             DBUS_PROPERTY_IFACE,     // Interface name
774                             "Set",                   // Method to be called
775                             &bus_error,              // object to return error
776                             nullptr,                 // Response buffer if any
777                             "ssv",                   // Takes 3 arguments
778                             HOST_STATE_MANAGER_IFACE, PROPERTY, "s",
779                             request.c_str());
780     if (rc < 0)
781     {
782         log<level::ERR>("Failed to initiate transition",
783                         entry("ERRNO=0x%X, REQUEST=%s", -rc, request.c_str()));
784     }
785     else
786     {
787         log<level::INFO>("Transition request initiated successfully");
788     }
789 
790     sd_bus_error_free(&bus_error);
791     free(busname);
792 
793     return rc;
794 }
795 
796 //------------------------------------------
797 // Set Enabled property to inform NMI source
798 // handling to trigger a NMI_OUT BSOD.
799 //------------------------------------------
800 int setNmiProperty(const bool value)
801 {
802     constexpr const char* nmiSourceObjPath =
803         "/xyz/openbmc_project/Chassis/Control/NMISource";
804     constexpr const char* nmiSourceIntf =
805         "xyz.openbmc_project.Chassis.Control.NMISource";
806     std::string bmcSourceSignal = "xyz.openbmc_project.Chassis.Control."
807                                   "NMISource.BMCSourceSignal.ChassisCmd";
808     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
809 
810     try
811     {
812         auto service = ipmi::getService(*busp, nmiSourceIntf, nmiSourceObjPath);
813         ipmi::setDbusProperty(*busp, service, nmiSourceObjPath, nmiSourceIntf,
814                               "BMCSource", bmcSourceSignal);
815         ipmi::setDbusProperty(*busp, service, nmiSourceObjPath, nmiSourceIntf,
816                               "Enabled", value);
817     }
818     catch (std::exception& e)
819     {
820         log<level::ERR>("Failed to trigger NMI_OUT",
821                         entry("EXCEPTION=%s", e.what()));
822         return -1;
823     }
824 
825     return 0;
826 }
827 
828 namespace power_policy
829 {
830 
831 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
832 using IpmiValue = uint8_t;
833 using DbusValue = RestorePolicy::Policy;
834 
835 const std::map<DbusValue, IpmiValue> dbusToIpmi = {
836     {RestorePolicy::Policy::AlwaysOff, 0x00},
837     {RestorePolicy::Policy::Restore, 0x01},
838     {RestorePolicy::Policy::AlwaysOn, 0x02}};
839 
840 static constexpr uint8_t noChange = 0x03;
841 static constexpr uint8_t allSupport = 0x01 | 0x02 | 0x04;
842 
843 /* helper function for Get Chassis Status Command
844  */
845 std::optional<uint2_t> getPowerRestorePolicy()
846 {
847     uint2_t restorePolicy = 0;
848     using namespace chassis::internal;
849 
850     settings::Objects& objects = cache::getObjects();
851 
852     try
853     {
854         const auto& powerRestoreSetting =
855             objects.map.at(powerRestoreIntf).front();
856         ipmi::Value result = ipmi::getDbusProperty(
857             *getSdBus(),
858             objects.service(powerRestoreSetting, powerRestoreIntf).c_str(),
859             powerRestoreSetting.c_str(), powerRestoreIntf,
860             "PowerRestorePolicy");
861         auto powerRestore = RestorePolicy::convertPolicyFromString(
862             std::get<std::string>(result));
863         restorePolicy = dbusToIpmi.at(powerRestore);
864     }
865     catch (const std::exception& e)
866     {
867         log<level::ERR>(
868             "Failed to fetch pgood property", entry("ERROR=%s", e.what()),
869             entry("PATH=%s", objects.map.at(powerRestoreIntf).front().c_str()),
870             entry("INTERFACE=%s", powerRestoreIntf));
871         cache::objectsPtr.reset();
872         return std::nullopt;
873     }
874     return std::make_optional(restorePolicy);
875 }
876 
877 /*
878  * getPowerStatus
879  * helper function for Get Chassis Status Command
880  * return - optional value for pgood (no value on error)
881  */
882 std::optional<bool> getPowerStatus()
883 {
884     bool powerGood = false;
885     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
886     try
887     {
888         constexpr const char* chassisStatePath =
889             "/xyz/openbmc_project/state/chassis0";
890         constexpr const char* chassisStateIntf =
891             "xyz.openbmc_project.State.Chassis";
892         auto service =
893             ipmi::getService(*busp, chassisStateIntf, chassisStatePath);
894 
895         ipmi::Value powerState =
896             ipmi::getDbusProperty(*busp, service, chassisStatePath,
897                                   chassisStateIntf, "CurrentPowerState");
898         powerGood = std::get<std::string>(powerState) ==
899                     "xyz.openbmc_project.State.Chassis.PowerState.On";
900     }
901     catch (const std::exception& e)
902     {
903         try
904         {
905             // FIXME: some legacy modules use the older path; try that next
906             constexpr const char* legacyPwrCtrlObj =
907                 "/org/openbmc/control/power0";
908             constexpr const char* legacyPwrCtrlIntf =
909                 "org.openbmc.control.Power";
910             auto service =
911                 ipmi::getService(*busp, legacyPwrCtrlIntf, legacyPwrCtrlObj);
912 
913             ipmi::Value variant = ipmi::getDbusProperty(
914                 *busp, service, legacyPwrCtrlObj, legacyPwrCtrlIntf, "pgood");
915             powerGood = static_cast<bool>(std::get<int>(variant));
916         }
917         catch (const std::exception& e)
918         {
919             log<level::ERR>("Failed to fetch pgood property",
920                             entry("ERROR=%s", e.what()));
921             return std::nullopt;
922         }
923     }
924     return std::make_optional(powerGood);
925 }
926 
927 /*
928  * getACFailStatus
929  * helper function for Get Chassis Status Command
930  * return - bool value for ACFail (false on error)
931  */
932 bool getACFailStatus()
933 {
934     constexpr const char* powerControlObj =
935         "/xyz/openbmc_project/Chassis/Control/Power0";
936     constexpr const char* powerControlIntf =
937         "xyz.openbmc_project.Chassis.Control.Power";
938     bool acFail = false;
939     std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
940     try
941     {
942         auto service =
943             ipmi::getService(*bus, powerControlIntf, powerControlObj);
944 
945         ipmi::Value variant = ipmi::getDbusProperty(
946             *bus, service, powerControlObj, powerControlIntf, "PFail");
947         acFail = std::get<bool>(variant);
948     }
949     catch (const std::exception& e)
950     {
951         log<level::ERR>("Failed to fetch PFail property",
952                         entry("ERROR=%s", e.what()),
953                         entry("PATH=%s", powerControlObj),
954                         entry("INTERFACE=%s", powerControlIntf));
955     }
956     return acFail;
957 }
958 } // namespace power_policy
959 
960 static std::optional<bool> getButtonEnabled(const std::string& buttonPath,
961                                             const std::string& buttonIntf)
962 {
963     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
964     bool buttonDisabled = false;
965     try
966     {
967         auto service = ipmi::getService(*busp, buttonIntf, buttonPath);
968         ipmi::Value enabled = ipmi::getDbusProperty(*busp, service, buttonPath,
969                                                     buttonIntf, "Enabled");
970         buttonDisabled = !std::get<bool>(enabled);
971     }
972     catch (sdbusplus::exception::SdBusError& e)
973     {
974         log<level::ERR>("Fail to get button Enabled property",
975                         entry("PATH=%s", buttonPath.c_str()),
976                         entry("ERROR=%s", e.what()));
977         return std::nullopt;
978     }
979     return std::make_optional(buttonDisabled);
980 }
981 
982 //----------------------------------------------------------------------
983 // Get Chassis Status commands
984 //----------------------------------------------------------------------
985 ipmi::RspType<bool,    // Power is on
986               bool,    // Power overload
987               bool,    // Interlock
988               bool,    // power fault
989               bool,    // power control fault
990               uint2_t, // power restore policy
991               bool,    // reserved
992 
993               bool, // AC failed
994               bool, // last power down caused by a Power overload
995               bool, // last power down caused by a power interlock
996               bool, // last power down caused by power fault
997               bool, // last ‘Power is on’ state was entered via IPMI command
998               uint3_t, // reserved
999 
1000               bool,    // Chassis intrusion active
1001               bool,    // Front Panel Lockout active
1002               bool,    // Drive Fault
1003               bool,    // Cooling/fan fault detected
1004               uint2_t, // Chassis Identify State
1005               bool,    // Chassis Identify command and state info supported
1006               bool,    // reserved
1007 
1008               bool, // Power off button disabled
1009               bool, // Reset button disabled
1010               bool, // Diagnostic Interrupt button disabled
1011               bool, // Standby (sleep) button disabled
1012               bool, // Power off button disable allowed
1013               bool, // Reset button disable allowed
1014               bool, // Diagnostic Interrupt button disable allowed
1015               bool  // Standby (sleep) button disable allowed
1016               >
1017     ipmiGetChassisStatus()
1018 {
1019     using namespace chassis::internal;
1020     std::optional<uint2_t> restorePolicy =
1021         power_policy::getPowerRestorePolicy();
1022     std::optional<bool> powerGood = power_policy::getPowerStatus();
1023     if (!restorePolicy || !powerGood)
1024     {
1025         return ipmi::responseUnspecifiedError();
1026     }
1027 
1028     //  Front Panel Button Capabilities and disable/enable status(Optional)
1029     std::optional<bool> powerButtonReading =
1030         getButtonEnabled(powerButtonPath, powerButtonIntf);
1031     // allow disable if the interface is present
1032     bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading);
1033     // default return the button is enabled (not disabled)
1034     bool powerButtonDisabled = false;
1035     if (powerButtonDisableAllow)
1036     {
1037         // return the real value of the button status, if present
1038         powerButtonDisabled = *powerButtonReading;
1039     }
1040 
1041     std::optional<bool> resetButtonReading =
1042         getButtonEnabled(resetButtonPath, resetButtonIntf);
1043     // allow disable if the interface is present
1044     bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading);
1045     // default return the button is enabled (not disabled)
1046     bool resetButtonDisabled = false;
1047     if (resetButtonDisableAllow)
1048     {
1049         // return the real value of the button status, if present
1050         resetButtonDisabled = *resetButtonReading;
1051     }
1052 
1053     bool powerDownAcFailed = power_policy::getACFailStatus();
1054 
1055     // This response has a lot of hard-coded, unsupported fields
1056     // They are set to false or 0
1057     constexpr bool powerOverload = false;
1058     constexpr bool chassisInterlock = false;
1059     constexpr bool powerFault = false;
1060     constexpr bool powerControlFault = false;
1061     constexpr bool powerDownOverload = false;
1062     constexpr bool powerDownInterlock = false;
1063     constexpr bool powerDownPowerFault = false;
1064     constexpr bool powerStatusIPMI = false;
1065     constexpr bool chassisIntrusionActive = false;
1066     constexpr bool frontPanelLockoutActive = false;
1067     constexpr bool driveFault = false;
1068     constexpr bool coolingFanFault = false;
1069     // chassisIdentifySupport set because this command is implemented
1070     constexpr bool chassisIdentifySupport = true;
1071     uint2_t chassisIdentifyState = static_cast<uint2_t>(chassisIDState);
1072     constexpr bool diagButtonDisabled = false;
1073     constexpr bool sleepButtonDisabled = false;
1074     constexpr bool diagButtonDisableAllow = false;
1075     constexpr bool sleepButtonDisableAllow = false;
1076 
1077     return ipmi::responseSuccess(
1078         *powerGood, powerOverload, chassisInterlock, powerFault,
1079         powerControlFault, *restorePolicy,
1080         false, // reserved
1081 
1082         powerDownAcFailed, powerDownOverload, powerDownInterlock,
1083         powerDownPowerFault, powerStatusIPMI,
1084         uint3_t(0), // reserved
1085 
1086         chassisIntrusionActive, frontPanelLockoutActive, driveFault,
1087         coolingFanFault, chassisIdentifyState, chassisIdentifySupport,
1088         false, // reserved
1089 
1090         powerButtonDisabled, resetButtonDisabled, diagButtonDisabled,
1091         sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow,
1092         diagButtonDisableAllow, sleepButtonDisableAllow);
1093 }
1094 
1095 //-------------------------------------------------------------
1096 // Send a command to SoftPowerOff application to stop any timer
1097 //-------------------------------------------------------------
1098 int stop_soft_off_timer()
1099 {
1100     constexpr auto iface = "org.freedesktop.DBus.Properties";
1101     constexpr auto soft_off_iface = "xyz.openbmc_project.Ipmi.Internal."
1102                                     "SoftPowerOff";
1103 
1104     constexpr auto property = "ResponseReceived";
1105     constexpr auto value = "xyz.openbmc_project.Ipmi.Internal."
1106                            "SoftPowerOff.HostResponse.HostShutdown";
1107 
1108     // Get the system bus where most system services are provided.
1109     auto bus = ipmid_get_sd_bus_connection();
1110 
1111     // Get the service name
1112     // TODO openbmc/openbmc#1661 - Mapper refactor
1113     //
1114     // See openbmc/openbmc#1743 for some details but high level summary is that
1115     // for now the code will directly call the soft off interface due to a
1116     // race condition with mapper usage
1117     //
1118     // char *busname = nullptr;
1119     // auto r = mapper_get_service(bus, SOFTOFF_OBJPATH, &busname);
1120     // if (r < 0)
1121     //{
1122     //    fprintf(stderr, "Failed to get %s bus name: %s\n",
1123     //            SOFTOFF_OBJPATH, -r);
1124     //    return r;
1125     //}
1126 
1127     // No error object or reply expected.
1128     int rc = sd_bus_call_method(bus, SOFTOFF_BUSNAME, SOFTOFF_OBJPATH, iface,
1129                                 "Set", nullptr, nullptr, "ssv", soft_off_iface,
1130                                 property, "s", value);
1131     if (rc < 0)
1132     {
1133         log<level::ERR>("Failed to set property in SoftPowerOff object",
1134                         entry("ERRNO=0x%X", -rc));
1135     }
1136 
1137     // TODO openbmc/openbmc#1661 - Mapper refactor
1138     // free(busname);
1139     return rc;
1140 }
1141 
1142 //----------------------------------------------------------------------
1143 // Create file to indicate there is no need for softoff notification to host
1144 //----------------------------------------------------------------------
1145 void indicate_no_softoff_needed()
1146 {
1147     fs::path path{HOST_INBAND_REQUEST_DIR};
1148     if (!fs::is_directory(path))
1149     {
1150         fs::create_directory(path);
1151     }
1152 
1153     // Add the host instance (default 0 for now) to the file name
1154     std::string file{HOST_INBAND_REQUEST_FILE};
1155     auto size = std::snprintf(nullptr, 0, file.c_str(), 0);
1156     size++; // null
1157     std::unique_ptr<char[]> buf(new char[size]);
1158     std::snprintf(buf.get(), size, file.c_str(), 0);
1159 
1160     // Append file name to directory and create it
1161     path /= buf.get();
1162     std::ofstream(path.c_str());
1163 }
1164 
1165 /** @brief Implementation of chassis control command
1166  *
1167  *  @param - chassisControl command byte
1168  *
1169  *  @return  Success or InvalidFieldRequest.
1170  */
1171 ipmi::RspType<> ipmiChassisControl(uint8_t chassisControl)
1172 {
1173     int rc = 0;
1174     switch (chassisControl)
1175     {
1176         case CMD_POWER_ON:
1177             rc = initiate_state_transition(State::Host::Transition::On);
1178             break;
1179         case CMD_POWER_OFF:
1180             // This path would be hit in 2 conditions.
1181             // 1: When user asks for power off using ipmi chassis command 0x04
1182             // 2: Host asking for power off post shutting down.
1183 
1184             // If it's a host requested power off, then need to nudge Softoff
1185             // application that it needs to stop the watchdog timer if running.
1186             // If it is a user requested power off, then this is not really
1187             // needed. But then we need to differentiate between user and host
1188             // calling this same command
1189 
1190             // For now, we are going ahead with trying to nudge the soft off and
1191             // interpret the failure to do so as a non softoff case
1192             rc = stop_soft_off_timer();
1193 
1194             // Only request the Off transition if the soft power off
1195             // application is not running
1196             if (rc < 0)
1197             {
1198                 // First create a file to indicate to the soft off application
1199                 // that it should not run. Not doing this will result in State
1200                 // manager doing a default soft power off when asked for power
1201                 // off.
1202                 indicate_no_softoff_needed();
1203 
1204                 // Now request the shutdown
1205                 rc = initiate_state_transition(State::Host::Transition::Off);
1206             }
1207             else
1208             {
1209                 log<level::INFO>("Soft off is running, so let shutdown target "
1210                                  "stop the host");
1211             }
1212             break;
1213 
1214         case CMD_HARD_RESET:
1215         case CMD_POWER_CYCLE:
1216             // SPEC has a section that says certain implementations can trigger
1217             // PowerOn if power is Off when a command to power cycle is
1218             // requested
1219 
1220             // First create a file to indicate to the soft off application
1221             // that it should not run since this is a direct user initiated
1222             // power reboot request (i.e. a reboot request that is not
1223             // originating via a soft power off SMS request)
1224             indicate_no_softoff_needed();
1225 
1226             rc = initiate_state_transition(State::Host::Transition::Reboot);
1227             break;
1228 
1229         case CMD_SOFT_OFF_VIA_OVER_TEMP:
1230             // Request Host State Manager to do a soft power off
1231             rc = initiate_state_transition(State::Host::Transition::Off);
1232             break;
1233 
1234         case CMD_PULSE_DIAGNOSTIC_INTR:
1235             rc = setNmiProperty(true);
1236             break;
1237 
1238         default:
1239         {
1240             log<level::ERR>("Invalid Chassis Control command",
1241                             entry("CMD=0x%X", chassisControl));
1242             return ipmi::responseInvalidFieldRequest();
1243         }
1244     }
1245 
1246     return ((rc < 0) ? ipmi::responseUnspecifiedError()
1247                      : ipmi::responseSuccess());
1248 }
1249 
1250 /** @brief Return D-Bus connection string to enclosure identify LED object
1251  *
1252  *  @param[in, out] connection - connection to D-Bus object
1253  *  @return a IPMI return code
1254  */
1255 std::string getEnclosureIdentifyConnection()
1256 {
1257     // lookup enclosure_identify group owner(s) in mapper
1258     auto mapperCall = chassis::internal::dbus.new_method_call(
1259         ipmi::MAPPER_BUS_NAME, ipmi::MAPPER_OBJ, ipmi::MAPPER_INTF,
1260         "GetObject");
1261 
1262     mapperCall.append(identify_led_object_name);
1263     static const std::vector<std::string> interfaces = {
1264         "xyz.openbmc_project.Led.Group"};
1265     mapperCall.append(interfaces);
1266     auto mapperReply = chassis::internal::dbus.call(mapperCall);
1267     if (mapperReply.is_method_error())
1268     {
1269         log<level::ERR>("Chassis Identify: Error communicating to mapper.");
1270         elog<InternalFailure>();
1271     }
1272     std::vector<std::pair<std::string, std::vector<std::string>>> mapperResp;
1273     mapperReply.read(mapperResp);
1274 
1275     if (mapperResp.size() != encIdentifyObjectsSize)
1276     {
1277         log<level::ERR>(
1278             "Invalid number of enclosure identify objects.",
1279             entry("ENC_IDENTITY_OBJECTS_SIZE=%d", mapperResp.size()));
1280         elog<InternalFailure>();
1281     }
1282     auto pair = mapperResp[encIdentifyObjectsSize - 1];
1283     return pair.first;
1284 }
1285 
1286 /** @brief Turn On/Off enclosure identify LED
1287  *
1288  *  @param[in] flag - true to turn on LED, false to turn off
1289  *  @return a IPMI return code
1290  */
1291 void enclosureIdentifyLed(bool flag)
1292 {
1293     using namespace chassis::internal;
1294     std::string connection = std::move(getEnclosureIdentifyConnection());
1295     auto msg = std::string("enclosureIdentifyLed(") +
1296                boost::lexical_cast<std::string>(flag) + ")";
1297     log<level::DEBUG>(msg.c_str());
1298     auto led =
1299         dbus.new_method_call(connection.c_str(), identify_led_object_name,
1300                              "org.freedesktop.DBus.Properties", "Set");
1301     led.append("xyz.openbmc_project.Led.Group", "Asserted",
1302                std::variant<bool>(flag));
1303     auto ledReply = dbus.call(led);
1304     if (ledReply.is_method_error())
1305     {
1306         log<level::ERR>("Chassis Identify: Error Setting State On/Off\n",
1307                         entry("LED_STATE=%d", flag));
1308         elog<InternalFailure>();
1309     }
1310 }
1311 
1312 /** @brief Callback method to turn off LED
1313  */
1314 void enclosureIdentifyLedOff()
1315 {
1316     try
1317     {
1318         chassisIDState = ChassisIDState::off;
1319         enclosureIdentifyLed(false);
1320     }
1321     catch (const InternalFailure& e)
1322     {
1323         report<InternalFailure>();
1324     }
1325 }
1326 
1327 /** @brief Create timer to turn on and off the enclosure LED
1328  */
1329 void createIdentifyTimer()
1330 {
1331     if (!identifyTimer)
1332     {
1333         identifyTimer =
1334             std::make_unique<phosphor::Timer>(enclosureIdentifyLedOff);
1335     }
1336 }
1337 
1338 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
1339                                     std::optional<uint8_t> force)
1340 {
1341     uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT);
1342     bool forceIdentify = force.value_or(0) & 0x01;
1343 
1344     if (identifyInterval || forceIdentify)
1345     {
1346         // stop the timer if already started;
1347         // for force identify we should not turn off LED
1348         identifyTimer->stop();
1349         try
1350         {
1351             chassisIDState = ChassisIDState::temporaryOn;
1352             enclosureIdentifyLed(true);
1353         }
1354         catch (const InternalFailure& e)
1355         {
1356             report<InternalFailure>();
1357             return ipmi::responseResponseError();
1358         }
1359 
1360         if (forceIdentify)
1361         {
1362             chassisIDState = ChassisIDState::indefiniteOn;
1363             return ipmi::responseSuccess();
1364         }
1365         // start the timer
1366         auto time = std::chrono::duration_cast<std::chrono::microseconds>(
1367             std::chrono::seconds(identifyInterval));
1368         identifyTimer->start(time);
1369     }
1370     else if (!identifyInterval)
1371     {
1372         identifyTimer->stop();
1373         enclosureIdentifyLedOff();
1374     }
1375     return ipmi::responseSuccess();
1376 }
1377 
1378 namespace boot_options
1379 {
1380 
1381 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
1382 using IpmiValue = uint8_t;
1383 constexpr auto ipmiDefault = 0;
1384 
1385 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
1386     {0x01, Source::Sources::Network},
1387     {0x02, Source::Sources::Disk},
1388     {0x05, Source::Sources::ExternalMedia},
1389     {0x0f, Source::Sources::RemovableMedia},
1390     {ipmiDefault, Source::Sources::Default}};
1391 
1392 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
1393 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT
1394     {0x03, Mode::Modes::Safe},
1395 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT
1396     {0x06, Mode::Modes::Setup},
1397     {ipmiDefault, Mode::Modes::Regular}};
1398 
1399 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
1400     {Source::Sources::Network, 0x01},
1401     {Source::Sources::Disk, 0x02},
1402     {Source::Sources::ExternalMedia, 0x05},
1403     {Source::Sources::RemovableMedia, 0x0f},
1404     {Source::Sources::Default, ipmiDefault}};
1405 
1406 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
1407 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT
1408     {Mode::Modes::Safe, 0x03},
1409 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT
1410     {Mode::Modes::Setup, 0x06},
1411     {Mode::Modes::Regular, ipmiDefault}};
1412 
1413 } // namespace boot_options
1414 
1415 /** @brief Set the property value for boot source
1416  *  @param[in] source - boot source value
1417  *  @return On failure return IPMI error.
1418  */
1419 static ipmi_ret_t setBootSource(const Source::Sources& source)
1420 {
1421     using namespace chassis::internal;
1422     using namespace chassis::internal::cache;
1423     std::variant<std::string> property = convertForMessage(source);
1424     settings::Objects& objects = getObjects();
1425     auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1426     const auto& bootSourceSetting = std::get<settings::Path>(bootSetting);
1427     auto method = dbus.new_method_call(
1428         objects.service(bootSourceSetting, bootSourceIntf).c_str(),
1429         bootSourceSetting.c_str(), ipmi::PROP_INTF, "Set");
1430     method.append(bootSourceIntf, "BootSource", property);
1431     auto reply = dbus.call(method);
1432     if (reply.is_method_error())
1433     {
1434         log<level::ERR>("Error in BootSource Set");
1435         report<InternalFailure>();
1436         return IPMI_CC_UNSPECIFIED_ERROR;
1437     }
1438     return IPMI_CC_OK;
1439 }
1440 
1441 /** @brief Set the property value for boot mode
1442  *  @param[in] mode - boot mode value
1443  *  @return On failure return IPMI error.
1444  */
1445 static ipmi_ret_t setBootMode(const Mode::Modes& mode)
1446 {
1447     using namespace chassis::internal;
1448     using namespace chassis::internal::cache;
1449     std::variant<std::string> property = convertForMessage(mode);
1450     settings::Objects& objects = getObjects();
1451     auto bootSetting = settings::boot::setting(objects, bootModeIntf);
1452     const auto& bootModeSetting = std::get<settings::Path>(bootSetting);
1453     auto method = dbus.new_method_call(
1454         objects.service(bootModeSetting, bootModeIntf).c_str(),
1455         bootModeSetting.c_str(), ipmi::PROP_INTF, "Set");
1456     method.append(bootModeIntf, "BootMode", property);
1457     auto reply = dbus.call(method);
1458     if (reply.is_method_error())
1459     {
1460         log<level::ERR>("Error in BootMode Set");
1461         report<InternalFailure>();
1462         return IPMI_CC_UNSPECIFIED_ERROR;
1463     }
1464     return IPMI_CC_OK;
1465 }
1466 
1467 ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1468                                              ipmi_request_t request,
1469                                              ipmi_response_t response,
1470                                              ipmi_data_len_t data_len,
1471                                              ipmi_context_t context)
1472 {
1473     using namespace boot_options;
1474     ipmi_ret_t rc = IPMI_CC_PARM_NOT_SUPPORTED;
1475     char* p = NULL;
1476     get_sys_boot_options_response_t* resp =
1477         (get_sys_boot_options_response_t*)response;
1478     get_sys_boot_options_t* reqptr = (get_sys_boot_options_t*)request;
1479     IpmiValue bootOption = ipmiDefault;
1480 
1481     std::memset(resp, 0, sizeof(*resp));
1482     resp->version = SET_PARM_VERSION;
1483     resp->parm = 5;
1484     resp->data[0] = SET_PARM_BOOT_FLAGS_VALID_ONE_TIME;
1485 
1486     /*
1487      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1488      * This is the only parameter used by petitboot.
1489      */
1490     if (reqptr->parameter ==
1491         static_cast<uint8_t>(BootOptionParameter::BOOT_FLAGS))
1492     {
1493 
1494         *data_len = static_cast<uint8_t>(BootOptionResponseSize::BOOT_FLAGS);
1495         using namespace chassis::internal;
1496         using namespace chassis::internal::cache;
1497 
1498         try
1499         {
1500             settings::Objects& objects = getObjects();
1501             auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1502             const auto& bootSourceSetting =
1503                 std::get<settings::Path>(bootSetting);
1504             auto oneTimeEnabled =
1505                 std::get<settings::boot::OneTimeEnabled>(bootSetting);
1506             auto method = dbus.new_method_call(
1507                 objects.service(bootSourceSetting, bootSourceIntf).c_str(),
1508                 bootSourceSetting.c_str(), ipmi::PROP_INTF, "Get");
1509             method.append(bootSourceIntf, "BootSource");
1510             auto reply = dbus.call(method);
1511             if (reply.is_method_error())
1512             {
1513                 log<level::ERR>("Error in BootSource Get");
1514                 report<InternalFailure>();
1515                 *data_len = 0;
1516                 return IPMI_CC_UNSPECIFIED_ERROR;
1517             }
1518             std::variant<std::string> result;
1519             reply.read(result);
1520             auto bootSource =
1521                 Source::convertSourcesFromString(std::get<std::string>(result));
1522 
1523             bootSetting = settings::boot::setting(objects, bootModeIntf);
1524             const auto& bootModeSetting = std::get<settings::Path>(bootSetting);
1525             method = dbus.new_method_call(
1526                 objects.service(bootModeSetting, bootModeIntf).c_str(),
1527                 bootModeSetting.c_str(), ipmi::PROP_INTF, "Get");
1528             method.append(bootModeIntf, "BootMode");
1529             reply = dbus.call(method);
1530             if (reply.is_method_error())
1531             {
1532                 log<level::ERR>("Error in BootMode Get");
1533                 report<InternalFailure>();
1534                 *data_len = 0;
1535                 return IPMI_CC_UNSPECIFIED_ERROR;
1536             }
1537             reply.read(result);
1538             auto bootMode =
1539                 Mode::convertModesFromString(std::get<std::string>(result));
1540 
1541             bootOption = sourceDbusToIpmi.at(bootSource);
1542             if ((Mode::Modes::Regular == bootMode) &&
1543                 (Source::Sources::Default == bootSource))
1544             {
1545                 bootOption = ipmiDefault;
1546             }
1547             else if (Source::Sources::Default == bootSource)
1548             {
1549                 bootOption = modeDbusToIpmi.at(bootMode);
1550             }
1551             resp->data[1] = (bootOption << 2);
1552 
1553             resp->data[0] = oneTimeEnabled
1554                                 ? SET_PARM_BOOT_FLAGS_VALID_ONE_TIME
1555                                 : SET_PARM_BOOT_FLAGS_VALID_PERMANENT;
1556 
1557             rc = IPMI_CC_OK;
1558         }
1559         catch (InternalFailure& e)
1560         {
1561             cache::objectsPtr.reset();
1562             report<InternalFailure>();
1563             *data_len = 0;
1564             return IPMI_CC_UNSPECIFIED_ERROR;
1565         }
1566     }
1567     else if (reqptr->parameter ==
1568              static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS))
1569     {
1570 
1571         *data_len =
1572             static_cast<uint8_t>(BootOptionResponseSize::OPAL_NETWORK_SETTINGS);
1573 
1574         resp->parm =
1575             static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS);
1576 
1577         int ret = getHostNetworkData(resp);
1578 
1579         if (ret < 0)
1580         {
1581 
1582             log<level::ERR>(
1583                 "getHostNetworkData failed for get_sys_boot_options.");
1584             rc = IPMI_CC_UNSPECIFIED_ERROR;
1585         }
1586         else
1587             rc = IPMI_CC_OK;
1588     }
1589 
1590     else
1591     {
1592         log<level::ERR>("Unsupported parameter",
1593                         entry("PARAM=0x%x", reqptr->parameter));
1594     }
1595 
1596     if (p)
1597         free(p);
1598 
1599     if (rc == IPMI_CC_OK)
1600     {
1601         *data_len += 2;
1602     }
1603 
1604     return rc;
1605 }
1606 
1607 ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1608                                              ipmi_request_t request,
1609                                              ipmi_response_t response,
1610                                              ipmi_data_len_t data_len,
1611                                              ipmi_context_t context)
1612 {
1613     using namespace boot_options;
1614     ipmi_ret_t rc = IPMI_CC_OK;
1615     set_sys_boot_options_t* reqptr = (set_sys_boot_options_t*)request;
1616 
1617     std::printf("IPMI SET_SYS_BOOT_OPTIONS reqptr->parameter =[%d]\n",
1618                 reqptr->parameter);
1619 
1620     // This IPMI command does not have any resposne data
1621     *data_len = 0;
1622 
1623     /*  000101
1624      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1625      * This is the only parameter used by petitboot.
1626      */
1627 
1628     if (reqptr->parameter == (uint8_t)BootOptionParameter::BOOT_FLAGS)
1629     {
1630         IpmiValue bootOption = ((reqptr->data[1] & 0x3C) >> 2);
1631         using namespace chassis::internal;
1632         using namespace chassis::internal::cache;
1633         auto oneTimeEnabled = false;
1634         constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable";
1635         constexpr auto oneTimePath =
1636             "/xyz/openbmc_project/control/host0/boot/one_time";
1637 
1638         try
1639         {
1640             bool permanent =
1641                 (reqptr->data[0] & SET_PARM_BOOT_FLAGS_PERMANENT) ==
1642                 SET_PARM_BOOT_FLAGS_PERMANENT;
1643 
1644             settings::Objects& objects = getObjects();
1645 
1646             auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1647 
1648             oneTimeEnabled =
1649                 std::get<settings::boot::OneTimeEnabled>(bootSetting);
1650 
1651             /*
1652              * Check if the current boot setting is onetime or permanent, if the
1653              * request in the command is otherwise, then set the "Enabled"
1654              * property in one_time object path to 'True' to indicate onetime
1655              * and 'False' to indicate permanent.
1656              *
1657              * Once the onetime/permanent setting is applied, then the bootMode
1658              * and bootSource is updated for the corresponding object.
1659              */
1660             if ((permanent && oneTimeEnabled) ||
1661                 (!permanent && !oneTimeEnabled))
1662             {
1663                 auto service = ipmi::getService(dbus, enabledIntf, oneTimePath);
1664 
1665                 ipmi::setDbusProperty(dbus, service, oneTimePath, enabledIntf,
1666                                       "Enabled", !permanent);
1667             }
1668 
1669             auto modeItr = modeIpmiToDbus.find(bootOption);
1670             auto sourceItr = sourceIpmiToDbus.find(bootOption);
1671             if (sourceIpmiToDbus.end() != sourceItr)
1672             {
1673                 rc = setBootSource(sourceItr->second);
1674                 if (rc != IPMI_CC_OK)
1675                 {
1676                     *data_len = 0;
1677                     return rc;
1678                 }
1679                 // If a set boot device is mapping to a boot source, then reset
1680                 // the boot mode D-Bus property to default.
1681                 // This way the ipmid code can determine which property is not
1682                 // at the default value
1683                 if (sourceItr->second != Source::Sources::Default)
1684                 {
1685                     setBootMode(Mode::Modes::Regular);
1686                 }
1687             }
1688             if (modeIpmiToDbus.end() != modeItr)
1689             {
1690                 rc = setBootMode(modeItr->second);
1691                 if (rc != IPMI_CC_OK)
1692                 {
1693                     *data_len = 0;
1694                     return rc;
1695                 }
1696                 // If a set boot device is mapping to a boot mode, then reset
1697                 // the boot source D-Bus property to default.
1698                 // This way the ipmid code can determine which property is not
1699                 // at the default value
1700                 if (modeItr->second != Mode::Modes::Regular)
1701                 {
1702                     setBootSource(Source::Sources::Default);
1703                 }
1704             }
1705             if ((modeIpmiToDbus.end() == modeItr) &&
1706                 (sourceIpmiToDbus.end() == sourceItr))
1707             {
1708                 // return error if boot option is not supported
1709                 *data_len = 0;
1710                 return IPMI_CC_INVALID_FIELD_REQUEST;
1711             }
1712         }
1713         catch (InternalFailure& e)
1714         {
1715             objectsPtr.reset();
1716             report<InternalFailure>();
1717             *data_len = 0;
1718             return IPMI_CC_UNSPECIFIED_ERROR;
1719         }
1720     }
1721     else if (reqptr->parameter ==
1722              (uint8_t)BootOptionParameter::OPAL_NETWORK_SETTINGS)
1723     {
1724 
1725         int ret = setHostNetworkData(reqptr);
1726         if (ret < 0)
1727         {
1728             log<level::ERR>(
1729                 "setHostNetworkData failed for set_sys_boot_options");
1730             rc = IPMI_CC_UNSPECIFIED_ERROR;
1731         }
1732     }
1733     else if (reqptr->parameter ==
1734              static_cast<uint8_t>(BootOptionParameter::BOOT_INFO))
1735     {
1736         // Handle parameter #4 and return command completed normally
1737         // (IPMI_CC_OK). There is no implementation in OpenBMC for this
1738         // parameter. This is added to support the ipmitool command `chassis
1739         // bootdev` which sends set on parameter #4, before setting the boot
1740         // flags.
1741         rc = IPMI_CC_OK;
1742     }
1743     else
1744     {
1745         log<level::ERR>("Unsupported parameter",
1746                         entry("PARAM=0x%x", reqptr->parameter));
1747         rc = IPMI_CC_PARM_NOT_SUPPORTED;
1748     }
1749 
1750     return rc;
1751 }
1752 
1753 /** @brief implements Get POH counter command
1754  *  @parameter
1755  *   -  none
1756  *  @returns IPMI completion code plus response data
1757  *   - minPerCount - Minutes per count
1758  *   - counterReading - counter reading
1759  */
1760 ipmi::RspType<uint8_t, // Minutes per count
1761               uint32_t // Counter reading
1762               >
1763     ipmiGetPOHCounter()
1764 {
1765     // sd_bus error
1766     try
1767     {
1768         return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount),
1769                                      getPOHCounter());
1770     }
1771     catch (std::exception& e)
1772     {
1773         log<level::ERR>(e.what());
1774         return ipmi::responseUnspecifiedError();
1775     }
1776 }
1777 
1778 ipmi::RspType<uint3_t, // policy support
1779               uint5_t  // reserved
1780               >
1781     ipmiChassisSetPowerRestorePolicy(boost::asio::yield_context yield,
1782                                      uint3_t policy, uint5_t reserved)
1783 {
1784     power_policy::DbusValue value =
1785         power_policy::RestorePolicy::Policy::AlwaysOff;
1786 
1787     if (reserved || (policy > power_policy::noChange))
1788     {
1789         phosphor::logging::log<level::ERR>(
1790             "Reserved request parameter",
1791             entry("REQ=0x%x", static_cast<int>(policy)));
1792         return ipmi::responseInvalidFieldRequest();
1793     }
1794 
1795     if (policy == power_policy::noChange)
1796     {
1797         // just return the supported policy
1798         return ipmi::responseSuccess(power_policy::allSupport, reserved);
1799     }
1800 
1801     for (auto const& it : power_policy::dbusToIpmi)
1802     {
1803         if (it.second == policy)
1804         {
1805             value = it.first;
1806             break;
1807         }
1808     }
1809 
1810     try
1811     {
1812         settings::Objects& objects = chassis::internal::cache::getObjects();
1813         const settings::Path& powerRestoreSetting =
1814             objects.map.at(chassis::internal::powerRestoreIntf).front();
1815         std::variant<std::string> property = convertForMessage(value);
1816 
1817         auto sdbusp = getSdBus();
1818         boost::system::error_code ec;
1819         sdbusp->yield_method_call<void>(
1820             yield, ec,
1821             objects
1822                 .service(powerRestoreSetting,
1823                          chassis::internal::powerRestoreIntf)
1824                 .c_str(),
1825             powerRestoreSetting, ipmi::PROP_INTF, "Set",
1826             chassis::internal::powerRestoreIntf, "PowerRestorePolicy",
1827             property);
1828         if (ec)
1829         {
1830             phosphor::logging::log<level::ERR>("Unspecified Error");
1831             return ipmi::responseUnspecifiedError();
1832         }
1833     }
1834     catch (InternalFailure& e)
1835     {
1836         chassis::internal::cache::objectsPtr.reset();
1837         report<InternalFailure>();
1838         return ipmi::responseUnspecifiedError();
1839     }
1840 
1841     return ipmi::responseSuccess(power_policy::allSupport, reserved);
1842 }
1843 
1844 void register_netfn_chassis_functions()
1845 {
1846     createIdentifyTimer();
1847 
1848     // Get Chassis Capabilities
1849     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1850                           ipmi::chassis::cmdGetChassisCapabilities,
1851                           ipmi::Privilege::User, ipmiGetChassisCap);
1852 
1853     // Set Chassis Capabilities
1854     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1855                           ipmi::chassis::cmdSetChassisCapabilities,
1856                           ipmi::Privilege::User, ipmiSetChassisCap);
1857 
1858     // <Get System Boot Options>
1859     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS, NULL,
1860                            ipmi_chassis_get_sys_boot_options,
1861                            PRIVILEGE_OPERATOR);
1862 
1863     // <Get Chassis Status>
1864     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1865                           ipmi::chassis::cmdGetChassisStatus,
1866                           ipmi::Privilege::User, ipmiGetChassisStatus);
1867 
1868     // <Chassis Control>
1869     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1870                           ipmi::chassis::cmdChassisControl,
1871                           ipmi::Privilege::Operator, ipmiChassisControl);
1872 
1873     // <Chassis Identify>
1874     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1875                           ipmi::chassis::cmdChassisIdentify,
1876                           ipmi::Privilege::Operator, ipmiChassisIdentify);
1877 
1878     // <Set System Boot Options>
1879     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_SYS_BOOT_OPTIONS, NULL,
1880                            ipmi_chassis_set_sys_boot_options,
1881                            PRIVILEGE_OPERATOR);
1882     // <Get POH Counter>
1883     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1884                           ipmi::chassis::cmdGetPohCounter,
1885                           ipmi::Privilege::User, ipmiGetPOHCounter);
1886 
1887     // <Set Power Restore Policy>
1888     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1889                           ipmi::chassis::cmdSetPowerRestorePolicy,
1890                           ipmi::Privilege::Operator,
1891                           ipmiChassisSetPowerRestorePolicy);
1892 }
1893