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