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