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