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