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