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