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