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