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