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