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