xref: /openbmc/phosphor-host-ipmid/chassishandler.cpp (revision a7d592c7415aba84268d587d9df1047e5a968d86)
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,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     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 & ~chassisCapAddrMask) != 0)
760     {
761         lg2::error("Unsupported request parameter(Bridge Addr) for REQ={REQ}",
762                    "REQ", lg2::hex, bridgeDeviceAddr);
763         return ipmi::responseInvalidFieldRequest();
764     }
765 
766     try
767     {
768         sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
769         ipmi::DbusObjectInfo chassisCapObject =
770             ipmi::getDbusObject(bus, chassisCapIntf);
771 
772         ipmi::setDbusProperty(bus, chassisCapObject.second,
773                               chassisCapObject.first, chassisCapIntf,
774                               chassisIntrusionProp, intrusion);
775 
776         ipmi::setDbusProperty(bus, chassisCapObject.second,
777                               chassisCapObject.first, chassisCapIntf,
778                               chassisFrontPanelLockoutProp, fpLockout);
779 
780         ipmi::setDbusProperty(bus, chassisCapObject.second,
781                               chassisCapObject.first, chassisCapIntf,
782                               chassisFRUDevAddrProp, fruDeviceAddr);
783 
784         ipmi::setDbusProperty(bus, chassisCapObject.second,
785                               chassisCapObject.first, chassisCapIntf,
786                               chassisSDRDevAddrProp, sdrDeviceAddr);
787 
788         ipmi::setDbusProperty(bus, chassisCapObject.second,
789                               chassisCapObject.first, chassisCapIntf,
790                               chassisSELDevAddrProp, selDeviceAddr);
791 
792         ipmi::setDbusProperty(bus, chassisCapObject.second,
793                               chassisCapObject.first, chassisCapIntf,
794                               chassisSMDevAddrProp, smDeviceAddr);
795 
796         ipmi::setDbusProperty(bus, chassisCapObject.second,
797                               chassisCapObject.first, chassisCapIntf,
798                               chassisBridgeDevAddrProp, bridgeDeviceAddr);
799     }
800     catch (const std::exception& e)
801     {
802         lg2::error("Failed to set chassis capability properties: {ERR}", "ERR",
803                    e);
804         return ipmi::responseUnspecifiedError();
805     }
806     return ipmi::responseSuccess();
807 }
808 
809 //------------------------------------------
810 // Calls into Host State Manager Dbus object
811 //------------------------------------------
initiateHostStateTransition(ipmi::Context::ptr & ctx,State::Host::Transition transition)812 int initiateHostStateTransition(ipmi::Context::ptr& ctx,
813                                 State::Host::Transition transition)
814 {
815     // OpenBMC Host State Manager dbus framework
816     constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0";
817     constexpr auto hostStateIntf = "xyz.openbmc_project.State.Host";
818 
819     // Convert to string equivalent of the passed in transition enum.
820     auto request =
821         sdbusplus::common::xyz::openbmc_project::state::convertForMessage(
822             transition);
823 
824     std::string service;
825     boost::system::error_code ec =
826         ipmi::getService(ctx, hostStateIntf, hostStatePath, service);
827 
828     if (!ec)
829     {
830         ec = ipmi::setDbusProperty(ctx, service, hostStatePath, hostStateIntf,
831                                    "RequestedHostTransition", request);
832     }
833     if (ec)
834     {
835         lg2::error(
836             "Failed to initiate transition for request {REQUEST}: {EXCEPTION}",
837             "REQUEST", request, "EXCEPTION", ec.message());
838         return -1;
839     }
840     lg2::info(
841         "Transition request {REQUEST} initiated successfully by user {USERID}",
842         "REQUEST", request, "USERID", ctx->userId);
843     return 0;
844 }
845 
846 //------------------------------------------
847 // Calls into Chassis State Manager Dbus object
848 //------------------------------------------
initiateChassisStateTransition(ipmi::Context::ptr & ctx,State::Chassis::Transition transition)849 int initiateChassisStateTransition(ipmi::Context::ptr& ctx,
850                                    State::Chassis::Transition transition)
851 {
852     // OpenBMC Chassis State Manager dbus framework
853     constexpr auto chassisStatePath = "/xyz/openbmc_project/state/chassis0";
854     constexpr auto chassisStateIntf = "xyz.openbmc_project.State.Chassis";
855 
856     std::string service;
857     boost::system::error_code ec =
858         ipmi::getService(ctx, chassisStateIntf, chassisStatePath, service);
859 
860     // Convert to string equivalent of the passed in transition enum.
861     auto request =
862         sdbusplus::common::xyz::openbmc_project::state::convertForMessage(
863             transition);
864 
865     if (!ec)
866     {
867         ec = ipmi::setDbusProperty(ctx, service, chassisStatePath,
868                                    chassisStateIntf, "RequestedPowerTransition",
869                                    request);
870     }
871     if (ec)
872     {
873         lg2::error("Failed to initiate transition {REQUEST}: {EXCEPTION}",
874                    "REQUEST", request, "EXCEPTION", ec.message());
875 
876         return -1;
877     }
878 
879     return 0;
880 }
881 
882 //------------------------------------------
883 // Trigger an NMI on the host via dbus
884 //------------------------------------------
doNmi(ipmi::Context::ptr & ctx)885 static int doNmi(ipmi::Context::ptr& ctx)
886 {
887     constexpr const char* nmiIntfName = "xyz.openbmc_project.Control.Host.NMI";
888     ipmi::DbusObjectInfo nmiObj{};
889     boost::system::error_code ec;
890 
891     ec = ipmi::getDbusObject(ctx, nmiIntfName, nmiObj);
892     if (ec)
893     {
894         lg2::error("Failed to find NMI service: {ERROR}", "ERROR",
895                    ec.message());
896         return -1;
897     }
898 
899     ec = ipmi::callDbusMethod(ctx, nmiObj.second, nmiObj.first, nmiIntfName,
900                               "NMI");
901     if (ec)
902     {
903         lg2::error("NMI call failed: {ERROR}", "ERROR", ec.message());
904         elog<InternalFailure>();
905         return -1;
906     }
907 
908     return 0;
909 }
910 
911 namespace power_policy
912 {
913 
914 using namespace sdbusplus::server::xyz::openbmc_project::control::power;
915 using IpmiValue = uint8_t;
916 using DbusValue = RestorePolicy::Policy;
917 
918 const std::map<DbusValue, IpmiValue> dbusToIpmi = {
919     {RestorePolicy::Policy::AlwaysOff, 0x00},
920     {RestorePolicy::Policy::Restore, 0x01},
921     {RestorePolicy::Policy::AlwaysOn, 0x02},
922     {RestorePolicy::Policy::None, 0x03}};
923 
924 static constexpr uint8_t noChange = 0x03;
925 static constexpr uint8_t allSupport = 0x01 | 0x02 | 0x04;
926 
927 /* helper function for Get Chassis Status Command
928  */
getPowerRestorePolicy()929 std::optional<uint2_t> getPowerRestorePolicy()
930 {
931     uint2_t restorePolicy = 0;
932     using namespace chassis::internal;
933 
934     settings::Objects& objects = cache::getObjects();
935 
936     try
937     {
938         const auto& powerRestoreSetting =
939             objects.map.at(powerRestoreIntf).front();
940         ipmi::Value result = ipmi::getDbusProperty(
941             *getSdBus(),
942             objects.service(powerRestoreSetting, powerRestoreIntf).c_str(),
943             powerRestoreSetting.c_str(), powerRestoreIntf,
944             "PowerRestorePolicy");
945         auto powerRestore = RestorePolicy::convertPolicyFromString(
946             std::get<std::string>(result));
947         restorePolicy = dbusToIpmi.at(powerRestore);
948     }
949     catch (const std::exception& e)
950     {
951         lg2::error(
952             "Failed to fetch pgood property ({PATH}/{INTERFACE}): {ERROR}",
953             "PATH", objects.map.at(powerRestoreIntf).front(), "INTERFACE",
954             powerRestoreIntf, "ERROR", e);
955         cache::objectsPtr.reset();
956         return std::nullopt;
957     }
958     return std::make_optional(restorePolicy);
959 }
960 
961 /*
962  * getPowerStatus
963  * helper function for Get Chassis Status Command
964  * return - optional value for pgood (no value on error)
965  */
getPowerStatus()966 std::optional<bool> getPowerStatus()
967 {
968     bool powerGood = false;
969     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
970     try
971     {
972         constexpr const char* chassisStatePath =
973             "/xyz/openbmc_project/state/chassis0";
974         constexpr const char* chassisStateIntf =
975             "xyz.openbmc_project.State.Chassis";
976         auto service =
977             ipmi::getService(*busp, chassisStateIntf, chassisStatePath);
978 
979         ipmi::Value powerState =
980             ipmi::getDbusProperty(*busp, service, chassisStatePath,
981                                   chassisStateIntf, "CurrentPowerState");
982         std::string powerStateStr = std::get<std::string>(powerState);
983         if (powerStateStr.ends_with(".On") ||
984             powerStateStr.ends_with(".TransitioningToOff"))
985         {
986             powerGood = true;
987         }
988     }
989     catch (const std::exception& e)
990     {
991         try
992         {
993             // FIXME: some legacy modules use the older path; try that next
994             constexpr const char* legacyPwrCtrlObj =
995                 "/org/openbmc/control/power0";
996             constexpr const char* legacyPwrCtrlIntf =
997                 "org.openbmc.control.Power";
998             auto service =
999                 ipmi::getService(*busp, legacyPwrCtrlIntf, legacyPwrCtrlObj);
1000 
1001             ipmi::Value variant = ipmi::getDbusProperty(
1002                 *busp, service, legacyPwrCtrlObj, legacyPwrCtrlIntf, "pgood");
1003             powerGood = static_cast<bool>(std::get<int>(variant));
1004         }
1005         catch (const std::exception& e)
1006         {
1007             lg2::error("Failed to fetch pgood property: {ERROR}", "ERROR", e);
1008             return std::nullopt;
1009         }
1010     }
1011     return std::make_optional(powerGood);
1012 }
1013 
1014 /*
1015  * getACFailStatus
1016  * helper function for Get Chassis Status Command
1017  * return - bool value for ACFail (false on error)
1018  */
getACFailStatus()1019 bool getACFailStatus()
1020 {
1021     constexpr const char* powerControlObj =
1022         "/xyz/openbmc_project/Chassis/Control/Power0";
1023     constexpr const char* powerControlIntf =
1024         "xyz.openbmc_project.Chassis.Control.Power";
1025     bool acFail = false;
1026     std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1027     try
1028     {
1029         auto service =
1030             ipmi::getService(*bus, powerControlIntf, powerControlObj);
1031 
1032         ipmi::Value variant = ipmi::getDbusProperty(
1033             *bus, service, powerControlObj, powerControlIntf, "PFail");
1034         acFail = std::get<bool>(variant);
1035     }
1036     catch (const std::exception& e)
1037     {
1038         lg2::error(
1039             "Failed to fetch PFail property ({PATH}/{INTERFACE}): {ERROR}",
1040             "PATH", powerControlObj, "INTERFACE", powerControlIntf, "ERROR", e);
1041     }
1042     return acFail;
1043 }
1044 } // namespace power_policy
1045 
getButtonDisabled(ipmi::Context::ptr & ctx,const std::string & buttonPath,const std::string & buttonIntf)1046 static std::optional<bool> getButtonDisabled(ipmi::Context::ptr& ctx,
1047                                              const std::string& buttonPath,
1048                                              const std::string& buttonIntf)
1049 {
1050     bool buttonDisabled = false;
1051     boost::system::error_code ec;
1052     std::string service;
1053     ec = ipmi::getService(ctx, buttonIntf, buttonPath, service);
1054     if (!ec)
1055     {
1056         bool enabled;
1057         ec = ipmi::getDbusProperty(ctx, service, buttonPath, buttonIntf,
1058                                    "Enabled", enabled);
1059         buttonDisabled = !enabled;
1060     }
1061 
1062     if (ec)
1063     {
1064         lg2::error("Fail to get button Enabled property ({PATH}): {ERROR}",
1065                    "PATH", buttonPath, "ERROR", ec.message());
1066         return std::nullopt;
1067     }
1068     return std::make_optional(buttonDisabled);
1069 }
1070 
setButtonDisabled(ipmi::Context::ptr & ctx,const std::string & buttonPath,const std::string & buttonIntf,bool disable)1071 static bool setButtonDisabled(ipmi::Context::ptr& ctx,
1072                               const std::string& buttonPath,
1073                               const std::string& buttonIntf, bool disable)
1074 {
1075     std::string service;
1076     boost::system::error_code ec;
1077     ec = ipmi::getService(ctx, buttonIntf, buttonPath, service);
1078     if (!ec)
1079     {
1080         ec = ipmi::setDbusProperty(ctx, service, buttonPath, buttonIntf,
1081                                    "Enabled", !disable);
1082     }
1083     if (ec)
1084     {
1085         lg2::error(
1086             "Fail to set button Enabled property ({SERVICE}:{PATH}): {ERROR}",
1087             "SERVICE", service, "PATH", buttonPath, "ERROR", ec.message());
1088         return false;
1089     }
1090     return true;
1091 }
1092 
getChassisIntrusionStatus(ipmi::Context::ptr & ctx)1093 static std::optional<bool> getChassisIntrusionStatus(ipmi::Context::ptr& ctx)
1094 {
1095     std::vector<std::string> interfaces = {std::string(Intrusion::interface)};
1096     ipmi::ObjectTree objs;
1097     std::string propVal;
1098     std::optional<bool> ret = std::nullopt;
1099 
1100     boost::system::error_code ec =
1101         ipmi::getSubTree(ctx, interfaces, std::string("/"), 0, objs);
1102 
1103     if (ec)
1104     {
1105         lg2::error("Fail to find Chassis Intrusion Interface on D-Bus "
1106                    "({INTERFACE}): {ERROR}",
1107                    "INTERFACE", Intrusion::interface, "ERROR", ec.message());
1108         return ret;
1109     }
1110 
1111     for (const auto& [path, map] : objs)
1112     {
1113         for (const auto& [service, intfs] : map)
1114         {
1115             ec = ipmi::getDbusProperty<std::string>(
1116                 ctx, service, path, Intrusion::interface, "Status", propVal);
1117 
1118             if (ec)
1119             {
1120                 lg2::error("Failed to get Chassis Intrusion Status property "
1121                            "({SERVICE}/{PATH}/{INTERFACE}): {ERROR}",
1122                            "SERVICE", service, "PATH", path, "INTERFACE",
1123                            Intrusion::interface, "ERROR", ec.message());
1124                 continue;
1125             }
1126 
1127             auto statusOpt =
1128                 sdbusplus::message::convert_from_string<Intrusion::Status>(
1129                     propVal);
1130             if (statusOpt)
1131             {
1132                 if (*statusOpt == Intrusion::Status::Normal)
1133                 {
1134                     ret = std::make_optional(false);
1135                 }
1136                 else
1137                 {
1138                     ret = std::make_optional(true);
1139                     return ret; // Early return on first non-Normal status
1140                 }
1141             }
1142             else
1143             {
1144                 lg2::warning(
1145                     "Invalid Intrusion::Status value received: {VALUE}",
1146                     "VALUE", propVal);
1147                 return std::nullopt;
1148             }
1149         }
1150     }
1151     return ret;
1152 }
1153 
1154 //----------------------------------------------------------------------
1155 // Get Chassis Status commands
1156 //----------------------------------------------------------------------
1157 ipmi::RspType<bool,    // Power is on
1158               bool,    // Power overload
1159               bool,    // Interlock
1160               bool,    // power fault
1161               bool,    // power control fault
1162               uint2_t, // power restore policy
1163               bool,    // reserved
1164 
1165               bool,    // AC failed
1166               bool,    // last power down caused by a Power overload
1167               bool,    // last power down caused by a power interlock
1168               bool,    // last power down caused by power fault
1169               bool,    // last ‘Power is on’ state was entered via IPMI command
1170               uint3_t, // reserved
1171 
1172               bool,    // Chassis intrusion active
1173               bool,    // Front Panel Lockout active
1174               bool,    // Drive Fault
1175               bool,    // Cooling/fan fault detected
1176               uint2_t, // Chassis Identify State
1177               bool,    // Chassis Identify command and state info supported
1178               bool,    // reserved
1179 
1180               bool,    // Power off button disabled
1181               bool,    // Reset button disabled
1182               bool,    // Diagnostic Interrupt button disabled
1183               bool,    // Standby (sleep) button disabled
1184               bool,    // Power off button disable allowed
1185               bool,    // Reset button disable allowed
1186               bool,    // Diagnostic Interrupt button disable allowed
1187               bool     // Standby (sleep) button disable allowed
1188               >
ipmiGetChassisStatus(ipmi::Context::ptr & ctx)1189     ipmiGetChassisStatus(ipmi::Context::ptr& ctx)
1190 {
1191     using namespace chassis::internal;
1192     std::optional<uint2_t> restorePolicy =
1193         power_policy::getPowerRestorePolicy();
1194     std::optional<bool> powerGood = power_policy::getPowerStatus();
1195     if (!restorePolicy || !powerGood)
1196     {
1197         return ipmi::responseUnspecifiedError();
1198     }
1199 
1200     //  Front Panel Button Capabilities and disable/enable status(Optional)
1201     std::optional<bool> powerButtonReading =
1202         getButtonDisabled(ctx, powerButtonPath, powerButtonIntf);
1203     // allow disable if the interface is present
1204     bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading);
1205     // default return the button is enabled (not disabled)
1206     bool powerButtonDisabled = false;
1207     if (powerButtonDisableAllow)
1208     {
1209         // return the real value of the button status, if present
1210         powerButtonDisabled = *powerButtonReading;
1211     }
1212 
1213     std::optional<bool> resetButtonReading =
1214         getButtonDisabled(ctx, resetButtonPath, resetButtonIntf);
1215     // allow disable if the interface is present
1216     bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading);
1217     // default return the button is enabled (not disabled)
1218     bool resetButtonDisabled = false;
1219     if (resetButtonDisableAllow)
1220     {
1221         // return the real value of the button status, if present
1222         resetButtonDisabled = *resetButtonReading;
1223     }
1224 
1225     bool powerDownAcFailed = power_policy::getACFailStatus();
1226 
1227     bool chassisIntrusionActive = false;
1228     std::optional<bool> chassisIntrusionStatus = getChassisIntrusionStatus(ctx);
1229     if (chassisIntrusionStatus)
1230     {
1231         chassisIntrusionActive = chassisIntrusionStatus.value();
1232     }
1233 
1234     // This response has a lot of hard-coded, unsupported fields
1235     // They are set to false or 0
1236     constexpr bool powerOverload = false;
1237     constexpr bool chassisInterlock = false;
1238     constexpr bool powerFault = false;
1239     constexpr bool powerControlFault = false;
1240     constexpr bool powerDownOverload = false;
1241     constexpr bool powerDownInterlock = false;
1242     constexpr bool powerDownPowerFault = false;
1243     constexpr bool powerStatusIPMI = false;
1244     constexpr bool frontPanelLockoutActive = false;
1245     constexpr bool driveFault = false;
1246     constexpr bool coolingFanFault = false;
1247     // chassisIdentifySupport set because this command is implemented
1248     constexpr bool chassisIdentifySupport = true;
1249     uint2_t chassisIdentifyState = types::enum_cast<uint2_t>(chassisIDState);
1250     constexpr bool diagButtonDisabled = false;
1251     constexpr bool sleepButtonDisabled = false;
1252     constexpr bool diagButtonDisableAllow = false;
1253     constexpr bool sleepButtonDisableAllow = false;
1254 
1255     return ipmi::responseSuccess(
1256         *powerGood, powerOverload, chassisInterlock, powerFault,
1257         powerControlFault, *restorePolicy,
1258         false, // reserved
1259 
1260         powerDownAcFailed, powerDownOverload, powerDownInterlock,
1261         powerDownPowerFault, powerStatusIPMI,
1262         uint3_t(0), // reserved
1263 
1264         chassisIntrusionActive, frontPanelLockoutActive, driveFault,
1265         coolingFanFault, chassisIdentifyState, chassisIdentifySupport,
1266         false, // reserved
1267 
1268         powerButtonDisabled, resetButtonDisabled, diagButtonDisabled,
1269         sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow,
1270         diagButtonDisableAllow, sleepButtonDisableAllow);
1271 }
1272 
1273 enum class IpmiRestartCause
1274 {
1275     Unknown = 0x0,
1276     RemoteCommand = 0x1,
1277     ResetButton = 0x2,
1278     PowerButton = 0x3,
1279     WatchdogTimer = 0x4,
1280     PowerPolicyAlwaysOn = 0x6,
1281     PowerPolicyPreviousState = 0x7,
1282     SoftReset = 0xa,
1283 };
1284 
restartCauseToIpmiRestartCause(State::Host::RestartCause cause)1285 static IpmiRestartCause restartCauseToIpmiRestartCause(
1286     State::Host::RestartCause cause)
1287 {
1288     switch (cause)
1289     {
1290         case State::Host::RestartCause::Unknown:
1291         {
1292             return IpmiRestartCause::Unknown;
1293         }
1294         case State::Host::RestartCause::RemoteCommand:
1295         {
1296             return IpmiRestartCause::RemoteCommand;
1297         }
1298         case State::Host::RestartCause::ResetButton:
1299         {
1300             return IpmiRestartCause::ResetButton;
1301         }
1302         case State::Host::RestartCause::PowerButton:
1303         {
1304             return IpmiRestartCause::PowerButton;
1305         }
1306         case State::Host::RestartCause::WatchdogTimer:
1307         {
1308             return IpmiRestartCause::WatchdogTimer;
1309         }
1310         case State::Host::RestartCause::PowerPolicyAlwaysOn:
1311         {
1312             return IpmiRestartCause::PowerPolicyAlwaysOn;
1313         }
1314         case State::Host::RestartCause::PowerPolicyPreviousState:
1315         {
1316             return IpmiRestartCause::PowerPolicyPreviousState;
1317         }
1318         case State::Host::RestartCause::SoftReset:
1319         {
1320             return IpmiRestartCause::SoftReset;
1321         }
1322         default:
1323         {
1324             return IpmiRestartCause::Unknown;
1325         }
1326     }
1327 }
1328 
1329 /*
1330  * getRestartCause
1331  * helper function for Get Host restart cause Command
1332  * return - optional value for RestartCause (no value on error)
1333  */
getRestartCause(ipmi::Context::ptr ctx)1334 static std::optional<uint4_t> getRestartCause(ipmi::Context::ptr ctx)
1335 {
1336     constexpr const char* restartCausePath = "/xyz/openbmc_project/state/host0";
1337     constexpr const char* restartCauseIntf = "xyz.openbmc_project.State.Host";
1338 
1339     std::string service;
1340     boost::system::error_code ec =
1341         ipmi::getService(ctx, restartCauseIntf, restartCausePath, service);
1342     if (!ec)
1343     {
1344         std::string restartCauseStr;
1345         ec = ipmi::getDbusProperty<std::string>(
1346             ctx, service, restartCausePath, restartCauseIntf, "RestartCause",
1347             restartCauseStr);
1348         if (!ec)
1349         {
1350             auto cause =
1351                 State::Host::convertRestartCauseFromString(restartCauseStr);
1352             return types::enum_cast<uint4_t>(
1353                 restartCauseToIpmiRestartCause(cause));
1354         }
1355     }
1356 
1357     lg2::error(
1358         "Failed to fetch RestartCause property ({PATH}/{INTERFACE}): {ERROR}",
1359         "ERROR", ec.message(), "PATH", restartCausePath, "INTERFACE",
1360         restartCauseIntf);
1361     return std::nullopt;
1362 }
1363 
1364 ipmi::RspType<uint4_t, // Restart Cause
1365               uint4_t, // reserved
1366               uint8_t  // channel number (not supported)
1367               >
ipmiGetSystemRestartCause(ipmi::Context::ptr ctx)1368     ipmiGetSystemRestartCause(ipmi::Context::ptr ctx)
1369 {
1370     std::optional<uint4_t> cause = getRestartCause(ctx);
1371     if (!cause)
1372     {
1373         return ipmi::responseUnspecifiedError();
1374     }
1375 
1376     constexpr uint4_t reserved = 0;
1377     auto channel = static_cast<uint8_t>(ctx->channel);
1378     return ipmi::responseSuccess(cause.value(), reserved, channel);
1379 }
1380 /** @brief Implementation of chassis control command
1381  *
1382  *  @param - chassisControl command byte
1383  *
1384  *  @return  Success or InvalidFieldRequest.
1385  */
ipmiChassisControl(ipmi::Context::ptr & ctx,uint8_t chassisControl)1386 ipmi::RspType<> ipmiChassisControl(ipmi::Context::ptr& ctx,
1387                                    uint8_t chassisControl)
1388 {
1389     int rc = 0;
1390     switch (chassisControl)
1391     {
1392         case cmdPowerOn:
1393             rc = initiateHostStateTransition(ctx, State::Host::Transition::On);
1394             break;
1395         case cmdPowerOff:
1396             rc = initiateChassisStateTransition(
1397                 ctx, State::Chassis::Transition::Off);
1398             break;
1399         case cmdHardReset:
1400             rc = initiateHostStateTransition(
1401                 ctx, State::Host::Transition::ForceWarmReboot);
1402             break;
1403         case cmdPowerCycle:
1404         {
1405             auto powerState = power_policy::getPowerStatus();
1406 
1407             if (powerState == std::nullopt)
1408             {
1409                 return ipmi::responseUnspecifiedError();
1410             }
1411 
1412             /*
1413              * As define in the Chapter 28.3 - Chassis Control Command of IPMI
1414              * specification: It is recommended that no action occur if system
1415              * power is off (S4/S5) when this action is selected, and that a D5
1416              * "Requiest parameter(s) not supported in this presenst state."
1417              * error completion code be returned.
1418              */
1419             if (powerState.value() == false)
1420             {
1421                 return ipmi::responseCommandNotAvailable();
1422             }
1423 
1424             rc = initiateHostStateTransition(ctx,
1425                                              State::Host::Transition::Reboot);
1426             break;
1427         }
1428         case cmdSoftOffViaOverTemp:
1429             rc = initiateHostStateTransition(ctx, State::Host::Transition::Off);
1430             break;
1431         case cmdPulseDiagnosticInterrupt:
1432             rc = doNmi(ctx);
1433             break;
1434 
1435         default:
1436         {
1437             lg2::error("Invalid Chassis Control command: {CMD}", "CMD",
1438                        lg2::hex, chassisControl);
1439             return ipmi::responseInvalidFieldRequest();
1440         }
1441     }
1442 
1443     return ((rc < 0) ? ipmi::responseUnspecifiedError()
1444                      : ipmi::responseSuccess());
1445 }
1446 
1447 /** @brief Return D-Bus connection string to enclosure identify LED object
1448  *
1449  *  @param[in, out] connection - connection to D-Bus object
1450  *  @return a IPMI return code
1451  */
getEnclosureIdentifyConnection()1452 std::string getEnclosureIdentifyConnection()
1453 {
1454     // lookup enclosure_identify group owner(s) in mapper
1455     try
1456     {
1457         return ipmi::getService(*getSdBus(), "xyz.openbmc_project.Led.Group",
1458                                 identify_led_object_name);
1459     }
1460     catch (const std::exception& e)
1461     {
1462         lg2::error("Chassis Identify: Error communicating to mapper: {ERROR}",
1463                    "ERROR", e);
1464         elog<InternalFailure>();
1465     }
1466 }
1467 
1468 /** @brief Turn On/Off enclosure identify LED
1469  *
1470  *  @param[in] flag - true to turn on LED, false to turn off
1471  *  @return a IPMI return code
1472  */
enclosureIdentifyLed(bool flag)1473 void enclosureIdentifyLed(bool flag)
1474 {
1475     using namespace chassis::internal;
1476 
1477     std::string connection = getEnclosureIdentifyConnection();
1478 
1479     lg2::debug("enclosureIdentifyLed({FLAG})", "FLAG", flag);
1480 
1481     try
1482     {
1483         ipmi::setDbusProperty(*getSdBus(), connection, identify_led_object_name,
1484                               "xyz.openbmc_project.Led.Group", "Asserted",
1485                               flag);
1486     }
1487     catch (const std::exception& e)
1488     {
1489         lg2::error("Chassis Identify: Error Setting State {LED_STATE}: {ERROR}",
1490                    "LED_STATE", flag, "ERROR", e);
1491         elog<InternalFailure>();
1492     }
1493 }
1494 
1495 /** @brief Callback method to turn off LED
1496  */
enclosureIdentifyLedOff()1497 void enclosureIdentifyLedOff()
1498 {
1499     try
1500     {
1501         chassisIDState = ChassisIDState::off;
1502         enclosureIdentifyLed(false);
1503     }
1504     catch (const InternalFailure& e)
1505     {
1506         report<InternalFailure>();
1507     }
1508 }
1509 
1510 /** @brief Create timer to turn on and off the enclosure LED
1511  */
createIdentifyTimer()1512 void createIdentifyTimer()
1513 {
1514     if (!identifyTimer)
1515     {
1516         identifyTimer =
1517             std::make_unique<sdbusplus::Timer>(enclosureIdentifyLedOff);
1518     }
1519 }
1520 
ipmiChassisIdentify(std::optional<uint8_t> interval,std::optional<uint8_t> force)1521 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
1522                                     std::optional<uint8_t> force)
1523 {
1524     uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT);
1525     bool forceIdentify = force.value_or(0) & 0x01;
1526 
1527     if (identifyInterval || forceIdentify)
1528     {
1529         // stop the timer if already started;
1530         // for force identify we should not turn off LED
1531         identifyTimer->stop();
1532         try
1533         {
1534             chassisIDState = ChassisIDState::temporaryOn;
1535             enclosureIdentifyLed(true);
1536         }
1537         catch (const InternalFailure& e)
1538         {
1539             report<InternalFailure>();
1540             return ipmi::responseResponseError();
1541         }
1542 
1543         if (forceIdentify)
1544         {
1545             chassisIDState = ChassisIDState::indefiniteOn;
1546             return ipmi::responseSuccess();
1547         }
1548         // start the timer
1549         auto time = std::chrono::duration_cast<std::chrono::microseconds>(
1550             std::chrono::seconds(identifyInterval));
1551         identifyTimer->start(time);
1552     }
1553     else if (!identifyInterval)
1554     {
1555         identifyTimer->stop();
1556         enclosureIdentifyLedOff();
1557     }
1558     return ipmi::responseSuccess();
1559 }
1560 
1561 namespace boot_options
1562 {
1563 
1564 using namespace sdbusplus::server::xyz::openbmc_project::control::boot;
1565 using IpmiValue = uint8_t;
1566 constexpr auto ipmiDefault = 0;
1567 
1568 std::map<IpmiValue, Type::Types> typeIpmiToDbus = {{0x00, Type::Types::Legacy},
1569                                                    {0x01, Type::Types::EFI}};
1570 
1571 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
1572     {0x01, Source::Sources::Network},
1573     {0x02, Source::Sources::Disk},
1574     {0x05, Source::Sources::ExternalMedia},
1575     {0x0f, Source::Sources::RemovableMedia},
1576     {ipmiDefault, Source::Sources::Default}};
1577 
1578 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
1579 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT
1580     {0x03, Mode::Modes::Safe},
1581 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT
1582     {0x06, Mode::Modes::Setup},
1583     {ipmiDefault, Mode::Modes::Regular}};
1584 
1585 std::map<Type::Types, IpmiValue> typeDbusToIpmi = {{Type::Types::Legacy, 0x00},
1586                                                    {Type::Types::EFI, 0x01}};
1587 
1588 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
1589     {Source::Sources::Network, 0x01},
1590     {Source::Sources::Disk, 0x02},
1591     {Source::Sources::ExternalMedia, 0x05},
1592     {Source::Sources::RemovableMedia, 0x0f},
1593     {Source::Sources::Default, ipmiDefault}};
1594 
1595 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
1596 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT
1597     {Mode::Modes::Safe, 0x03},
1598 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT
1599     {Mode::Modes::Setup, 0x06},
1600     {Mode::Modes::Regular, ipmiDefault}};
1601 
1602 } // namespace boot_options
1603 
1604 /** @brief Get the property value for boot source
1605  *  @param[in] ctx - context pointer
1606  *  @param[out] source - boot source value
1607  *  @return On failure return IPMI error.
1608  */
getBootSource(ipmi::Context::ptr & ctx,Source::Sources & source)1609 static ipmi::Cc getBootSource(ipmi::Context::ptr& ctx, Source::Sources& source)
1610 {
1611     using namespace chassis::internal;
1612     std::string result;
1613     std::string service;
1614     boost::system::error_code ec =
1615         getService(ctx, bootSourceIntf, bootSettingsPath, service);
1616     if (!ec)
1617     {
1618         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath,
1619                                    bootSourceIntf, "BootSource", result);
1620         if (!ec)
1621         {
1622             source = Source::convertSourcesFromString(result);
1623             return ipmi::ccSuccess;
1624         }
1625     }
1626     lg2::error("Error in BootSource Get: {ERROR}", "ERROR", ec.message());
1627     return ipmi::ccUnspecifiedError;
1628 }
1629 
1630 /** @brief Set the property value for boot source
1631  *  @param[in] ctx - context pointer
1632  *  @param[in] source - boot source value
1633  *  @return On failure return IPMI error.
1634  */
setBootSource(ipmi::Context::ptr & ctx,const Source::Sources & source)1635 static ipmi::Cc setBootSource(ipmi::Context::ptr& ctx,
1636                               const Source::Sources& source)
1637 {
1638     using namespace chassis::internal;
1639     std::string service;
1640     boost::system::error_code ec =
1641         getService(ctx, bootSourceIntf, bootSettingsPath, service);
1642     if (!ec)
1643     {
1644         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath,
1645                                    bootSourceIntf, "BootSource",
1646                                    convertForMessage(source));
1647         if (!ec)
1648         {
1649             return ipmi::ccSuccess;
1650         }
1651     }
1652     lg2::error("Error in BootSource Set: {ERROR}", "ERROR", ec.message());
1653     return ipmi::ccUnspecifiedError;
1654 }
1655 
1656 /** @brief Get the property value for boot mode
1657  *  @param[in] ctx - context pointer
1658  *  @param[out] mode - boot mode value
1659  *  @return On failure return IPMI error.
1660  */
getBootMode(ipmi::Context::ptr & ctx,Mode::Modes & mode)1661 static ipmi::Cc getBootMode(ipmi::Context::ptr& ctx, Mode::Modes& mode)
1662 {
1663     using namespace chassis::internal;
1664     std::string result;
1665     std::string service;
1666     boost::system::error_code ec =
1667         getService(ctx, bootModeIntf, bootSettingsPath, service);
1668     if (!ec)
1669     {
1670         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootModeIntf,
1671                                    "BootMode", result);
1672         if (!ec)
1673         {
1674             mode = Mode::convertModesFromString(result);
1675             return ipmi::ccSuccess;
1676         }
1677     }
1678     lg2::error("Error in BootMode Get: {ERROR}", "ERROR", ec.message());
1679     return ipmi::ccUnspecifiedError;
1680 }
1681 
1682 /** @brief Set the property value for boot mode
1683  *  @param[in] ctx - context pointer
1684  *  @param[in] mode - boot mode value
1685  *  @return On failure return IPMI error.
1686  */
setBootMode(ipmi::Context::ptr & ctx,const Mode::Modes & mode)1687 static ipmi::Cc setBootMode(ipmi::Context::ptr& ctx, const Mode::Modes& mode)
1688 {
1689     using namespace chassis::internal;
1690     std::string service;
1691     boost::system::error_code ec =
1692         getService(ctx, bootModeIntf, bootSettingsPath, service);
1693     if (!ec)
1694     {
1695         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootModeIntf,
1696                                    "BootMode", convertForMessage(mode));
1697         if (!ec)
1698         {
1699             return ipmi::ccSuccess;
1700         }
1701     }
1702     lg2::error("Error in BootMode Set: {ERROR}", "ERROR", ec.message());
1703     return ipmi::ccUnspecifiedError;
1704 }
1705 
1706 /** @brief Get the property value for boot type
1707  *  @param[in] ctx - context pointer
1708  *  @param[out] type - boot type value
1709  *  @return On failure return IPMI error.
1710  */
getBootType(ipmi::Context::ptr & ctx,Type::Types & type)1711 static ipmi::Cc getBootType(ipmi::Context::ptr& ctx, Type::Types& type)
1712 {
1713     using namespace chassis::internal;
1714     std::string result;
1715     std::string service;
1716     boost::system::error_code ec =
1717         getService(ctx, bootTypeIntf, bootSettingsPath, service);
1718 
1719     // Don't throw error if BootType interface is not present.
1720     // This interface is not relevant for some Host architectures
1721     // (for example POWER). In this case we don't won't IPMI to
1722     // return an error, but simply return bootType as EFI.
1723     type = Type::Types::EFI;
1724     if (!ec)
1725     {
1726         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootTypeIntf,
1727                                    "BootType", result);
1728         if (ec)
1729         {
1730             lg2::error("Error in BootType Get: {ERROR}", "ERROR", ec.message());
1731             return ipmi::ccUnspecifiedError;
1732         }
1733         type = Type::convertTypesFromString(result);
1734     }
1735 
1736     return ipmi::ccSuccess;
1737 }
1738 
1739 /** @brief Set the property value for boot type
1740  *  @param[in] ctx - context pointer
1741  *  @param[in] type - boot type value
1742  *  @return On failure return IPMI error.
1743  */
setBootType(ipmi::Context::ptr & ctx,const Type::Types & type)1744 static ipmi::Cc setBootType(ipmi::Context::ptr& ctx, const Type::Types& type)
1745 {
1746     using namespace chassis::internal;
1747     std::string service;
1748     boost::system::error_code ec =
1749         getService(ctx, bootTypeIntf, bootSettingsPath, service);
1750     if (!ec)
1751     {
1752         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootTypeIntf,
1753                                    "BootType", convertForMessage(type));
1754         if (ec)
1755         {
1756             lg2::error("Error in BootType Set: {ERROR}", "ERROR", ec.message());
1757             return ipmi::ccUnspecifiedError;
1758         }
1759     }
1760     // Don't throw error if BootType interface is not present.
1761     // This interface is not relevant for some Host architectures
1762     // (for example POWER). In this case we don't won't IPMI to
1763     // return an error, but want to just skip this function.
1764     return ipmi::ccSuccess;
1765 }
1766 
1767 /** @brief Get the property value for boot override enable
1768  *  @param[in] ctx - context pointer
1769  *  @param[out] enable - boot override enable
1770  *  @return On failure return IPMI error.
1771  */
getBootEnable(ipmi::Context::ptr & ctx,bool & enable)1772 static ipmi::Cc getBootEnable(ipmi::Context::ptr& ctx, bool& enable)
1773 {
1774     using namespace chassis::internal;
1775     std::string result;
1776     std::string service;
1777     boost::system::error_code ec =
1778         getService(ctx, bootEnableIntf, bootSettingsPath, service);
1779     if (!ec)
1780     {
1781         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath,
1782                                    bootEnableIntf, "Enabled", enable);
1783         if (!ec)
1784         {
1785             return ipmi::ccSuccess;
1786         }
1787     }
1788     lg2::error("Error in Boot Override Enable Get: {ERROR}", "ERROR",
1789                ec.message());
1790     return ipmi::ccUnspecifiedError;
1791 }
1792 
1793 /** @brief Set the property value for boot override enable
1794  *  @param[in] ctx - context pointer
1795  *  @param[in] enable - boot override enable
1796  *  @return On failure return IPMI error.
1797  */
setBootEnable(ipmi::Context::ptr & ctx,const bool & enable)1798 static ipmi::Cc setBootEnable(ipmi::Context::ptr& ctx, const bool& enable)
1799 {
1800     using namespace chassis::internal;
1801     std::string service;
1802     boost::system::error_code ec =
1803         getService(ctx, bootEnableIntf, bootSettingsPath, service);
1804     if (!ec)
1805     {
1806         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath,
1807                                    bootEnableIntf, "Enabled", enable);
1808         if (!ec)
1809         {
1810             return ipmi::ccSuccess;
1811         }
1812     }
1813     lg2::error("Error in Boot Source Override Enable Set: {ERROR}", "ERROR",
1814                ec.message());
1815     return ipmi::ccUnspecifiedError;
1816 }
1817 
1818 /** @brief Get the property value for boot override one-time
1819  *  @param[in] ctx - context pointer
1820  *  @param[out] onetime - boot override one-time
1821  *  @return On failure return IPMI error.
1822  */
getBootOneTime(ipmi::Context::ptr & ctx,bool & onetime)1823 static ipmi::Cc getBootOneTime(ipmi::Context::ptr& ctx, bool& onetime)
1824 {
1825     using namespace chassis::internal;
1826     std::string result;
1827     std::string service;
1828     boost::system::error_code ec =
1829         getService(ctx, bootOneTimeIntf, bootSettingsOneTimePath, service);
1830     if (!ec)
1831     {
1832         ec = ipmi::getDbusProperty(ctx, service, bootSettingsOneTimePath,
1833                                    bootOneTimeIntf, "Enabled", onetime);
1834         if (!ec)
1835         {
1836             return ipmi::ccSuccess;
1837         }
1838     }
1839     lg2::error("Error in Boot Override OneTime Get: {ERROR}", "ERROR",
1840                ec.message());
1841     return ipmi::ccUnspecifiedError;
1842 }
1843 
1844 /** @brief Set the property value for boot override one-time
1845  *  @param[in] ctx - context pointer
1846  *  @param[in] onetime - boot override one-time
1847  *  @return On failure return IPMI error.
1848  */
setBootOneTime(ipmi::Context::ptr & ctx,const bool & onetime)1849 static ipmi::Cc setBootOneTime(ipmi::Context::ptr& ctx, const bool& onetime)
1850 {
1851     using namespace chassis::internal;
1852     std::string service;
1853     boost::system::error_code ec =
1854         getService(ctx, bootOneTimeIntf, bootSettingsOneTimePath, service);
1855     if (!ec)
1856     {
1857         ec = ipmi::setDbusProperty(ctx, service, bootSettingsOneTimePath,
1858                                    bootOneTimeIntf, "Enabled", onetime);
1859         if (!ec)
1860         {
1861             return ipmi::ccSuccess;
1862         }
1863     }
1864     lg2::error("Error in Boot Source Override OneTime Set: {ERROR}", "ERROR",
1865                ec.message());
1866     return ipmi::ccUnspecifiedError;
1867 }
1868 
1869 static constexpr uint8_t setComplete = 0x0;
1870 static constexpr uint8_t setInProgress = 0x1;
1871 static uint8_t transferStatus = setComplete;
1872 static uint8_t bootFlagValidBitClr = 0;
1873 static uint5_t bootInitiatorAckData = 0x0;
1874 static bool cmosClear = false;
1875 static uint2_t biosVerbosity = 0x0;
1876 
1877 /** @brief implements the Get Chassis system boot option
1878  *  @param ctx - context pointer
1879  *  @param bootOptionParameter   - boot option parameter selector
1880  *  @param reserved1    - reserved bit
1881  *  @param setSelector  - selects a particular block or set of parameters
1882  *                        under the given parameter selector
1883  *                        write as 00h if parameter doesn't use a setSelector
1884  *  @param blockSelector- selects a particular block within a set of
1885  *                        parameters write as 00h if parameter doesn't use a
1886  *                        blockSelector
1887  *
1888  *  @return IPMI completion code plus response data
1889  *  @return Payload contains below parameters:
1890  *   version             - parameter version
1891  *   bootOptionParameter - boot option parameter selector
1892  *   parmIndicator - parameter valid/invalid indicator
1893  *   data          - configuration parameter data
1894  */
ipmiChassisGetSysBootOptions(ipmi::Context::ptr ctx,uint7_t bootOptionParameter,bool reserved1,uint8_t setSelector,uint8_t blockSelector)1895 ipmi::RspType<ipmi::message::Payload> ipmiChassisGetSysBootOptions(
1896     ipmi::Context::ptr ctx, uint7_t bootOptionParameter, bool reserved1,
1897     [[maybe_unused]] uint8_t setSelector,
1898     [[maybe_unused]] uint8_t blockSelector)
1899 {
1900     ipmi::Cc rc;
1901     if (reserved1)
1902     {
1903         return ipmi::responseInvalidFieldRequest();
1904     }
1905 
1906     constexpr uint4_t version = 0x01;
1907     ipmi::message::Payload response;
1908     response.pack(version, uint4_t{});
1909     using namespace boot_options;
1910 
1911     IpmiValue bootOption = ipmiDefault;
1912 
1913     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1914         BootOptionParameter::setInProgress)
1915     {
1916         response.pack(bootOptionParameter, reserved1, transferStatus);
1917         return ipmi::responseSuccess(std::move(response));
1918     }
1919 
1920     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1921         BootOptionParameter::bootInfo)
1922     {
1923         constexpr uint8_t writeMask = 0;
1924         response.pack(bootOptionParameter, reserved1, writeMask,
1925                       bootInitiatorAckData);
1926         return ipmi::responseSuccess(std::move(response));
1927     }
1928 
1929     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1930         BootOptionParameter::bootFlagValidClr)
1931     {
1932         response.pack(bootOptionParameter, reserved1,
1933                       uint5_t{bootFlagValidBitClr}, uint3_t{});
1934         return ipmi::responseSuccess(std::move(response));
1935     }
1936 
1937     /*
1938      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1939      * This is the only parameter used by petitboot.
1940      */
1941     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1942         BootOptionParameter::bootFlags)
1943     {
1944         using namespace chassis::internal;
1945         using namespace chassis::internal::cache;
1946 
1947         try
1948         {
1949             Source::Sources bootSource;
1950             rc = getBootSource(ctx, bootSource);
1951             if (rc != ipmi::ccSuccess)
1952             {
1953                 return ipmi::response(rc);
1954             }
1955 
1956             Type::Types bootType;
1957             rc = getBootType(ctx, bootType);
1958             if (rc != ipmi::ccSuccess)
1959             {
1960                 return ipmi::response(rc);
1961             }
1962 
1963             Mode::Modes bootMode;
1964             rc = getBootMode(ctx, bootMode);
1965             if (rc != ipmi::ccSuccess)
1966             {
1967                 return ipmi::response(rc);
1968             }
1969 
1970             bootOption = sourceDbusToIpmi.at(bootSource);
1971             if ((Mode::Modes::Regular == bootMode) &&
1972                 (Source::Sources::Default == bootSource))
1973             {
1974                 bootOption = ipmiDefault;
1975             }
1976             else if (Source::Sources::Default == bootSource)
1977             {
1978                 bootOption = modeDbusToIpmi.at(bootMode);
1979             }
1980 
1981             IpmiValue biosBootType = typeDbusToIpmi.at(bootType);
1982 
1983             bool oneTimeEnabled;
1984             rc = getBootOneTime(ctx, oneTimeEnabled);
1985             if (rc != ipmi::ccSuccess)
1986             {
1987                 return ipmi::response(rc);
1988             }
1989 
1990             uint1_t permanent = oneTimeEnabled ? 0 : 1;
1991 
1992             bool valid;
1993             rc = getBootEnable(ctx, valid);
1994             if (rc != ipmi::ccSuccess)
1995             {
1996                 return ipmi::response(rc);
1997             }
1998 
1999             uint1_t validFlag = valid ? 1 : 0;
2000 
2001             response.pack(
2002                 bootOptionParameter, reserved1, uint5_t{},
2003                 uint1_t{biosBootType}, uint1_t{permanent}, uint1_t{validFlag},
2004                 uint2_t{}, uint4_t{bootOption}, uint1_t{}, cmosClear, uint5_t{},
2005                 uint2_t{biosVerbosity}, uint1_t{}, uint8_t{}, uint8_t{});
2006             return ipmi::responseSuccess(std::move(response));
2007         }
2008         catch (const InternalFailure& e)
2009         {
2010             cache::objectsPtr.reset();
2011             report<InternalFailure>();
2012             return ipmi::responseUnspecifiedError();
2013         }
2014     }
2015     else
2016     {
2017         if ((bootOptionParameter >= oemParmStart) &&
2018             (bootOptionParameter <= oemParmEnd))
2019         {
2020             if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
2021                 BootOptionParameter::opalNetworkSettings)
2022             {
2023                 response.pack(bootOptionParameter, reserved1);
2024                 int ret = getHostNetworkData(response);
2025                 if (ret < 0)
2026                 {
2027                     response.trailingOk = true;
2028                     lg2::error(
2029                         "getHostNetworkData failed for GetSysBootOptions.");
2030                     return ipmi::responseUnspecifiedError();
2031                 }
2032                 else
2033                 {
2034                     return ipmi::responseSuccess(std::move(response));
2035                 }
2036             }
2037             else
2038             {
2039                 lg2::error(
2040                     "ipmiChassisGetSysBootOptions: Unsupported parameter {PARAM}",
2041                     "PARAM", lg2::hex,
2042                     static_cast<uint8_t>(bootOptionParameter));
2043                 return ipmi::responseParmNotSupported();
2044             }
2045         }
2046         else
2047         {
2048             lg2::error(
2049                 "ipmiChassisGetSysBootOptions: Unsupported parameter {PARAM}",
2050                 "PARAM", lg2::hex, static_cast<uint8_t>(bootOptionParameter));
2051             return ipmi::responseParmNotSupported();
2052         }
2053     }
2054     return ipmi::responseUnspecifiedError();
2055 }
2056 
ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx,uint7_t parameterSelector,bool,ipmi::message::Payload & data)2057 ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx,
2058                                              uint7_t parameterSelector, bool,
2059                                              ipmi::message::Payload& data)
2060 {
2061     using namespace boot_options;
2062     ipmi::Cc rc;
2063 
2064     if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2065         BootOptionParameter::setInProgress)
2066     {
2067         uint2_t setInProgressFlag;
2068         uint6_t rsvd;
2069         if (data.unpack(setInProgressFlag, rsvd) != 0 || !data.fullyUnpacked())
2070         {
2071             return ipmi::responseReqDataLenInvalid();
2072         }
2073         if (rsvd)
2074         {
2075             return ipmi::responseInvalidFieldRequest();
2076         }
2077         if ((transferStatus == setInProgress) &&
2078             (static_cast<uint8_t>(setInProgressFlag) != setComplete))
2079         {
2080             return ipmi::responsefailSetInProgress();
2081         }
2082         transferStatus = static_cast<uint8_t>(setInProgressFlag);
2083         return ipmi::responseSuccess();
2084     }
2085 
2086     /*  000101
2087      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
2088      * This is the only parameter used by petitboot.
2089      */
2090 
2091     if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2092         BootOptionParameter::bootFlags)
2093     {
2094         uint5_t rsvd;
2095         bool validFlag;
2096         bool permanent;
2097         bool biosBootType;
2098         bool lockOutResetButton;
2099         bool screenBlank;
2100         uint4_t bootDeviceSelector;
2101         bool lockKeyboard;
2102         uint5_t biosCtrls;
2103         bool lockOutPower;
2104         uint4_t biosInfo;
2105         uint4_t rsvd1;
2106         uint5_t deviceInstance;
2107         uint3_t rsvd2;
2108 
2109         if (data.unpack(rsvd, biosBootType, permanent, validFlag,
2110                         lockOutResetButton, screenBlank, bootDeviceSelector,
2111                         lockKeyboard, cmosClear, biosCtrls, biosVerbosity,
2112                         lockOutPower, biosInfo, rsvd1, deviceInstance, rsvd2) !=
2113                 0 ||
2114             !data.fullyUnpacked())
2115         {
2116             return ipmi::responseReqDataLenInvalid();
2117         }
2118         if (rsvd || rsvd1 || rsvd2)
2119         {
2120             return ipmi::responseInvalidFieldRequest();
2121         }
2122 
2123         using namespace chassis::internal;
2124         using namespace chassis::internal::cache;
2125 
2126         try
2127         {
2128             rc = setBootOneTime(ctx, !permanent);
2129             if (rc != ipmi::ccSuccess)
2130             {
2131                 return ipmi::response(rc);
2132             }
2133 
2134             rc = setBootEnable(ctx, validFlag);
2135             if (rc != ipmi::ccSuccess)
2136             {
2137                 return ipmi::response(rc);
2138             }
2139 
2140             auto modeItr =
2141                 modeIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector));
2142             auto typeItr =
2143                 typeIpmiToDbus.find(static_cast<uint8_t>(biosBootType));
2144             auto sourceItr =
2145                 sourceIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector));
2146             if (sourceIpmiToDbus.end() != sourceItr)
2147             {
2148                 rc = setBootSource(ctx, sourceItr->second);
2149                 if (rc != ipmi::ccSuccess)
2150                 {
2151                     return ipmi::response(rc);
2152                 }
2153                 // If a set boot device is mapping to a boot source, then reset
2154                 // the boot mode D-Bus property to default.
2155                 // This way the ipmid code can determine which property is not
2156                 // at the default value
2157                 if (sourceItr->second != Source::Sources::Default)
2158                 {
2159                     rc = setBootMode(ctx, Mode::Modes::Regular);
2160                     if (rc != ipmi::ccSuccess)
2161                     {
2162                         return ipmi::response(rc);
2163                     }
2164                 }
2165             }
2166 
2167             if (typeIpmiToDbus.end() != typeItr)
2168             {
2169                 rc = setBootType(ctx, typeItr->second);
2170                 if (rc != ipmi::ccSuccess)
2171                 {
2172                     return ipmi::response(rc);
2173                 }
2174             }
2175 
2176             if (modeIpmiToDbus.end() != modeItr)
2177             {
2178                 rc = setBootMode(ctx, modeItr->second);
2179                 if (rc != ipmi::ccSuccess)
2180                 {
2181                     return ipmi::response(rc);
2182                 }
2183                 // If a set boot device is mapping to a boot mode, then reset
2184                 // the boot source D-Bus property to default.
2185                 // This way the ipmid code can determine which property is not
2186                 // at the default value
2187                 if (modeItr->second != Mode::Modes::Regular)
2188                 {
2189                     rc = setBootSource(ctx, Source::Sources::Default);
2190                     if (rc != ipmi::ccSuccess)
2191                     {
2192                         return ipmi::response(rc);
2193                     }
2194                 }
2195             }
2196             if ((modeIpmiToDbus.end() == modeItr) &&
2197                 (typeIpmiToDbus.end() == typeItr) &&
2198                 (sourceIpmiToDbus.end() == sourceItr))
2199             {
2200                 // return error if boot option is not supported
2201                 lg2::error(
2202                     "ipmiChassisSetSysBootOptions: Boot option not supported");
2203                 return ipmi::responseInvalidFieldRequest();
2204             }
2205         }
2206         catch (const sdbusplus::exception_t& e)
2207         {
2208             objectsPtr.reset();
2209             report<InternalFailure>();
2210             lg2::error("ipmiChassisSetSysBootOptions: Error in setting Boot "
2211                        "flag parameters");
2212             return ipmi::responseUnspecifiedError();
2213         }
2214     }
2215     else if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2216              BootOptionParameter::bootInfo)
2217     {
2218         uint8_t writeMak;
2219         uint5_t bootInfoAck;
2220         uint3_t rsvd;
2221 
2222         if (data.unpack(writeMak, bootInfoAck, rsvd) != 0 ||
2223             !data.fullyUnpacked())
2224         {
2225             return ipmi::responseReqDataLenInvalid();
2226         }
2227         if (rsvd)
2228         {
2229             return ipmi::responseInvalidFieldRequest();
2230         }
2231         bootInitiatorAckData &= ~writeMak;
2232         bootInitiatorAckData |= (writeMak & bootInfoAck);
2233         lg2::info("ipmiChassisSetSysBootOptions: bootInfo parameter set "
2234                   "successfully");
2235         data.trailingOk = true;
2236         return ipmi::responseSuccess();
2237     }
2238     else if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2239              BootOptionParameter::bootFlagValidClr)
2240     {
2241         uint5_t bootFlagValidClr;
2242         uint3_t rsvd;
2243 
2244         if (data.unpack(bootFlagValidClr, rsvd) != 0 || !data.fullyUnpacked())
2245         {
2246             return ipmi::responseReqDataLenInvalid();
2247         }
2248         if (rsvd)
2249         {
2250             return ipmi::responseInvalidFieldRequest();
2251         }
2252         // store boot flag valid bits clear value
2253         bootFlagValidBitClr = static_cast<uint8_t>(bootFlagValidClr);
2254         lg2::info(
2255             "ipmiChassisSetSysBootOptions: bootFlagValidBits parameter set "
2256             "successfully to {VALUE}",
2257             "VALUE", lg2::hex, bootFlagValidBitClr);
2258         return ipmi::responseSuccess();
2259     }
2260     else
2261     {
2262         if ((parameterSelector >= static_cast<uint7_t>(oemParmStart)) &&
2263             (parameterSelector <= static_cast<uint7_t>(oemParmEnd)))
2264         {
2265             if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2266                 BootOptionParameter::opalNetworkSettings)
2267             {
2268                 ipmi::Cc ret = setHostNetworkData(data);
2269                 if (ret != ipmi::ccSuccess)
2270                 {
2271                     lg2::error("ipmiChassisSetSysBootOptions: Error in "
2272                                "setHostNetworkData");
2273                     data.trailingOk = true;
2274                     return ipmi::response(ret);
2275                 }
2276                 data.trailingOk = true;
2277                 return ipmi::responseSuccess();
2278             }
2279             else
2280             {
2281                 lg2::error(
2282                     "ipmiChassisSetSysBootOptions: Unsupported param: {PARAM}",
2283                     "PARAM", lg2::hex, static_cast<uint8_t>(parameterSelector));
2284                 data.trailingOk = true;
2285                 return ipmi::responseParmNotSupported();
2286             }
2287         }
2288         data.trailingOk = true;
2289         return ipmi::responseParmNotSupported();
2290     }
2291     return ipmi::responseSuccess();
2292 }
2293 
2294 /** @brief implements Get POH counter command
2295  *  @parameter
2296  *   -  none
2297  *  @returns IPMI completion code plus response data
2298  *   - minPerCount - Minutes per count
2299  *   - counterReading - counter reading
2300  */
2301 ipmi::RspType<uint8_t, // Minutes per count
2302               uint32_t // Counter reading
2303               >
ipmiGetPOHCounter()2304     ipmiGetPOHCounter()
2305 {
2306     // sd_bus error
2307     try
2308     {
2309         return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount),
2310                                      getPOHCounter());
2311     }
2312     catch (const std::exception& e)
2313     {
2314         lg2::error("getPOHCounter error: {ERROR}", "ERROR", e);
2315         return ipmi::responseUnspecifiedError();
2316     }
2317 }
2318 
2319 ipmi::RspType<uint3_t, // policy support
2320               uint5_t  // reserved
2321               >
ipmiChassisSetPowerRestorePolicy(ipmi::Context::ptr ctx,uint3_t policy,uint5_t reserved)2322     ipmiChassisSetPowerRestorePolicy(ipmi::Context::ptr ctx, uint3_t policy,
2323                                      uint5_t reserved)
2324 {
2325     power_policy::DbusValue value =
2326         power_policy::RestorePolicy::Policy::AlwaysOff;
2327 
2328     if (reserved || (policy > power_policy::noChange))
2329     {
2330         lg2::error("Reserved request parameter: {REQ}", "REQ", lg2::hex,
2331                    static_cast<int>(policy));
2332         return ipmi::responseInvalidFieldRequest();
2333     }
2334 
2335     if (policy == power_policy::noChange)
2336     {
2337         // just return the supported policy
2338         return ipmi::responseSuccess(power_policy::allSupport, reserved);
2339     }
2340 
2341     for (const auto& it : power_policy::dbusToIpmi)
2342     {
2343         if (it.second == policy)
2344         {
2345             value = it.first;
2346             break;
2347         }
2348     }
2349 
2350     try
2351     {
2352         settings::Objects& objects = chassis::internal::cache::getObjects();
2353         const settings::Path& powerRestoreSetting =
2354             objects.map.at(chassis::internal::powerRestoreIntf).front();
2355 
2356         boost::system::error_code ec = ipmi::setDbusProperty(
2357             ctx,
2358             objects.service(powerRestoreSetting,
2359                             chassis::internal::powerRestoreIntf),
2360             powerRestoreSetting, chassis::internal::powerRestoreIntf,
2361             "PowerRestorePolicy", convertForMessage(value));
2362         if (ec)
2363         {
2364             lg2::error("Unspecified Error");
2365             return ipmi::responseUnspecifiedError();
2366         }
2367     }
2368     catch (const InternalFailure& e)
2369     {
2370         chassis::internal::cache::objectsPtr.reset();
2371         report<InternalFailure>();
2372         return ipmi::responseUnspecifiedError();
2373     }
2374 
2375     return ipmi::responseSuccess(power_policy::allSupport, reserved);
2376 }
2377 
ipmiSetFrontPanelButtonEnables(ipmi::Context::ptr ctx,bool disablePowerButton,bool disableResetButton,bool,bool,uint4_t)2378 ipmi::RspType<> ipmiSetFrontPanelButtonEnables(
2379     ipmi::Context::ptr ctx, bool disablePowerButton, bool disableResetButton,
2380     bool, bool, uint4_t)
2381 {
2382     using namespace chassis::internal;
2383 
2384     // set power button Enabled property
2385     bool success = setButtonDisabled(ctx, powerButtonPath, powerButtonIntf,
2386                                      disablePowerButton);
2387 
2388     // set reset button Enabled property
2389     success &= setButtonDisabled(ctx, resetButtonPath, resetButtonIntf,
2390                                  disableResetButton);
2391 
2392     if (!success)
2393     {
2394         // not all buttons were successfully set
2395         return ipmi::responseUnspecifiedError();
2396     }
2397     return ipmi::responseSuccess();
2398 }
2399 
registerNetFnChassisFunctions()2400 void registerNetFnChassisFunctions()
2401 {
2402     createIdentifyTimer();
2403 
2404     // Get Chassis Capabilities
2405     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2406                           ipmi::chassis::cmdGetChassisCapabilities,
2407                           ipmi::Privilege::User, ipmiGetChassisCap);
2408 
2409     // Set Front Panel Button Enables
2410     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2411                           ipmi::chassis::cmdSetFrontPanelButtonEnables,
2412                           ipmi::Privilege::Admin,
2413                           ipmiSetFrontPanelButtonEnables);
2414 
2415     // Set Chassis Capabilities
2416     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2417                           ipmi::chassis::cmdSetChassisCapabilities,
2418                           ipmi::Privilege::User, ipmiSetChassisCap);
2419 
2420     // <Get System Boot Options>
2421     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2422                           ipmi::chassis::cmdGetSystemBootOptions,
2423                           ipmi::Privilege::Operator,
2424                           ipmiChassisGetSysBootOptions);
2425 
2426     // <Get Chassis Status>
2427     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2428                           ipmi::chassis::cmdGetChassisStatus,
2429                           ipmi::Privilege::User, ipmiGetChassisStatus);
2430 
2431     // <Chassis Get System Restart Cause>
2432     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2433                           ipmi::chassis::cmdGetSystemRestartCause,
2434                           ipmi::Privilege::User, ipmiGetSystemRestartCause);
2435 
2436     // <Chassis Control>
2437     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2438                           ipmi::chassis::cmdChassisControl,
2439                           ipmi::Privilege::Operator, ipmiChassisControl);
2440 
2441     // <Chassis Identify>
2442     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2443                           ipmi::chassis::cmdChassisIdentify,
2444                           ipmi::Privilege::Operator, ipmiChassisIdentify);
2445 
2446     // <Set System Boot Options>
2447     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2448                           ipmi::chassis::cmdSetSystemBootOptions,
2449                           ipmi::Privilege::Operator,
2450                           ipmiChassisSetSysBootOptions);
2451 
2452     // <Get POH Counter>
2453     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2454                           ipmi::chassis::cmdGetPohCounter,
2455                           ipmi::Privilege::User, ipmiGetPOHCounter);
2456 
2457     // <Set Power Restore Policy>
2458     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2459                           ipmi::chassis::cmdSetPowerRestorePolicy,
2460                           ipmi::Privilege::Operator,
2461                           ipmiChassisSetPowerRestorePolicy);
2462 }
2463