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