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