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