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