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