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