xref: /openbmc/phosphor-host-ipmid/chassishandler.cpp (revision 99db688569d88252cbe15d4dde6c14719d6d1537)
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 register_netfn_chassis_functions() __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         powerGood = std::get<std::string>(powerState) ==
975                     "xyz.openbmc_project.State.Chassis.PowerState.On";
976     }
977     catch (const std::exception& e)
978     {
979         try
980         {
981             // FIXME: some legacy modules use the older path; try that next
982             constexpr const char* legacyPwrCtrlObj =
983                 "/org/openbmc/control/power0";
984             constexpr const char* legacyPwrCtrlIntf =
985                 "org.openbmc.control.Power";
986             auto service =
987                 ipmi::getService(*busp, legacyPwrCtrlIntf, legacyPwrCtrlObj);
988 
989             ipmi::Value variant = ipmi::getDbusProperty(
990                 *busp, service, legacyPwrCtrlObj, legacyPwrCtrlIntf, "pgood");
991             powerGood = static_cast<bool>(std::get<int>(variant));
992         }
993         catch (const std::exception& e)
994         {
995             lg2::error("Failed to fetch pgood property: {ERROR}", "ERROR", e);
996             return std::nullopt;
997         }
998     }
999     return std::make_optional(powerGood);
1000 }
1001 
1002 /*
1003  * getACFailStatus
1004  * helper function for Get Chassis Status Command
1005  * return - bool value for ACFail (false on error)
1006  */
getACFailStatus()1007 bool getACFailStatus()
1008 {
1009     constexpr const char* powerControlObj =
1010         "/xyz/openbmc_project/Chassis/Control/Power0";
1011     constexpr const char* powerControlIntf =
1012         "xyz.openbmc_project.Chassis.Control.Power";
1013     bool acFail = false;
1014     std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
1015     try
1016     {
1017         auto service =
1018             ipmi::getService(*bus, powerControlIntf, powerControlObj);
1019 
1020         ipmi::Value variant = ipmi::getDbusProperty(
1021             *bus, service, powerControlObj, powerControlIntf, "PFail");
1022         acFail = std::get<bool>(variant);
1023     }
1024     catch (const std::exception& e)
1025     {
1026         lg2::error(
1027             "Failed to fetch PFail property ({PATH}/{INTERFAC}): {ERROR}",
1028             "PATH", powerControlObj, "INTERFACE", powerControlIntf, "ERROR", e);
1029     }
1030     return acFail;
1031 }
1032 } // namespace power_policy
1033 
getButtonEnabled(const std::string & buttonPath,const std::string & buttonIntf)1034 static std::optional<bool> getButtonEnabled(const std::string& buttonPath,
1035                                             const std::string& buttonIntf)
1036 {
1037     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
1038     bool buttonDisabled = false;
1039     try
1040     {
1041         auto service = ipmi::getService(*busp, buttonIntf, buttonPath);
1042         ipmi::Value enabled = ipmi::getDbusProperty(*busp, service, buttonPath,
1043                                                     buttonIntf, "Enabled");
1044         buttonDisabled = !std::get<bool>(enabled);
1045     }
1046     catch (const sdbusplus::exception_t& e)
1047     {
1048         lg2::error("Fail to get button Enabled property ({PATH}): {ERROR}",
1049                    "PATH", buttonPath, "ERROR", e);
1050         return std::nullopt;
1051     }
1052     return std::make_optional(buttonDisabled);
1053 }
1054 
setButtonEnabled(ipmi::Context::ptr & ctx,const std::string & buttonPath,const std::string & buttonIntf,bool enable)1055 static bool setButtonEnabled(ipmi::Context::ptr& ctx,
1056                              const std::string& buttonPath,
1057                              const std::string& buttonIntf, bool enable)
1058 {
1059     std::string service;
1060     boost::system::error_code ec;
1061     ec = ipmi::getService(ctx, buttonIntf, buttonPath, service);
1062     if (!ec)
1063     {
1064         ec = ipmi::setDbusProperty(ctx, service, buttonPath, buttonIntf,
1065                                    "Enabled", enable);
1066     }
1067     if (ec)
1068     {
1069         lg2::error(
1070             "Fail to set button Enabled property ({SERVICE}:{PATH}): {ERROR}",
1071             "SERVICE", service, "PATH", buttonPath, "ERROR", ec.message());
1072         return false;
1073     }
1074     return true;
1075 }
1076 
getChassisIntrusionStatus(ipmi::Context::ptr & ctx)1077 static std::optional<bool> getChassisIntrusionStatus(ipmi::Context::ptr& ctx)
1078 {
1079     constexpr const char* chassisIntrusionPath =
1080         "/xyz/openbmc_project/Chassis/Intrusion";
1081     constexpr const char* chassisIntrusionInf =
1082         "xyz.openbmc_project.Chassis.Intrusion";
1083 
1084     std::string service;
1085     boost::system::error_code ec = ipmi::getService(
1086         ctx, chassisIntrusionInf, chassisIntrusionPath, service);
1087     if (!ec)
1088     {
1089         std::string chassisIntrusionStr;
1090         ec = ipmi::getDbusProperty<std::string>(
1091             ctx, service, chassisIntrusionPath, chassisIntrusionInf, "Status",
1092             chassisIntrusionStr);
1093         if (!ec)
1094         {
1095             bool ret =
1096                 (chassisIntrusionStr == "HardwareIntrusion") ? true : false;
1097             return std::make_optional(ret);
1098         }
1099     }
1100     lg2::error("Fail to get Chassis Intrusion Status property "
1101                "({PATH}/{INTERFACE}): {ERROR}",
1102                "PATH", chassisIntrusionPath, "INTERFACE", chassisIntrusionInf,
1103                "ERROR", ec.message());
1104     return std::nullopt;
1105 }
1106 
1107 //----------------------------------------------------------------------
1108 // Get Chassis Status commands
1109 //----------------------------------------------------------------------
1110 ipmi::RspType<bool,    // Power is on
1111               bool,    // Power overload
1112               bool,    // Interlock
1113               bool,    // power fault
1114               bool,    // power control fault
1115               uint2_t, // power restore policy
1116               bool,    // reserved
1117 
1118               bool,    // AC failed
1119               bool,    // last power down caused by a Power overload
1120               bool,    // last power down caused by a power interlock
1121               bool,    // last power down caused by power fault
1122               bool,    // last ‘Power is on’ state was entered via IPMI command
1123               uint3_t, // reserved
1124 
1125               bool,    // Chassis intrusion active
1126               bool,    // Front Panel Lockout active
1127               bool,    // Drive Fault
1128               bool,    // Cooling/fan fault detected
1129               uint2_t, // Chassis Identify State
1130               bool,    // Chassis Identify command and state info supported
1131               bool,    // reserved
1132 
1133               bool,    // Power off button disabled
1134               bool,    // Reset button disabled
1135               bool,    // Diagnostic Interrupt button disabled
1136               bool,    // Standby (sleep) button disabled
1137               bool,    // Power off button disable allowed
1138               bool,    // Reset button disable allowed
1139               bool,    // Diagnostic Interrupt button disable allowed
1140               bool     // Standby (sleep) button disable allowed
1141               >
ipmiGetChassisStatus(ipmi::Context::ptr & ctx)1142     ipmiGetChassisStatus(ipmi::Context::ptr& ctx)
1143 {
1144     using namespace chassis::internal;
1145     std::optional<uint2_t> restorePolicy =
1146         power_policy::getPowerRestorePolicy();
1147     std::optional<bool> powerGood = power_policy::getPowerStatus();
1148     if (!restorePolicy || !powerGood)
1149     {
1150         return ipmi::responseUnspecifiedError();
1151     }
1152 
1153     //  Front Panel Button Capabilities and disable/enable status(Optional)
1154     std::optional<bool> powerButtonReading =
1155         getButtonEnabled(powerButtonPath, powerButtonIntf);
1156     // allow disable if the interface is present
1157     bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading);
1158     // default return the button is enabled (not disabled)
1159     bool powerButtonDisabled = false;
1160     if (powerButtonDisableAllow)
1161     {
1162         // return the real value of the button status, if present
1163         powerButtonDisabled = *powerButtonReading;
1164     }
1165 
1166     std::optional<bool> resetButtonReading =
1167         getButtonEnabled(resetButtonPath, resetButtonIntf);
1168     // allow disable if the interface is present
1169     bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading);
1170     // default return the button is enabled (not disabled)
1171     bool resetButtonDisabled = false;
1172     if (resetButtonDisableAllow)
1173     {
1174         // return the real value of the button status, if present
1175         resetButtonDisabled = *resetButtonReading;
1176     }
1177 
1178     bool powerDownAcFailed = power_policy::getACFailStatus();
1179 
1180     bool chassisIntrusionActive = false;
1181     std::optional<bool> chassisIntrusionStatus = getChassisIntrusionStatus(ctx);
1182     if (chassisIntrusionStatus)
1183     {
1184         chassisIntrusionActive = chassisIntrusionStatus.value();
1185     }
1186 
1187     // This response has a lot of hard-coded, unsupported fields
1188     // They are set to false or 0
1189     constexpr bool powerOverload = false;
1190     constexpr bool chassisInterlock = false;
1191     constexpr bool powerFault = false;
1192     constexpr bool powerControlFault = false;
1193     constexpr bool powerDownOverload = false;
1194     constexpr bool powerDownInterlock = false;
1195     constexpr bool powerDownPowerFault = false;
1196     constexpr bool powerStatusIPMI = false;
1197     constexpr bool frontPanelLockoutActive = false;
1198     constexpr bool driveFault = false;
1199     constexpr bool coolingFanFault = false;
1200     // chassisIdentifySupport set because this command is implemented
1201     constexpr bool chassisIdentifySupport = true;
1202     uint2_t chassisIdentifyState = types::enum_cast<uint2_t>(chassisIDState);
1203     constexpr bool diagButtonDisabled = false;
1204     constexpr bool sleepButtonDisabled = false;
1205     constexpr bool diagButtonDisableAllow = false;
1206     constexpr bool sleepButtonDisableAllow = false;
1207 
1208     return ipmi::responseSuccess(
1209         *powerGood, powerOverload, chassisInterlock, powerFault,
1210         powerControlFault, *restorePolicy,
1211         false, // reserved
1212 
1213         powerDownAcFailed, powerDownOverload, powerDownInterlock,
1214         powerDownPowerFault, powerStatusIPMI,
1215         uint3_t(0), // reserved
1216 
1217         chassisIntrusionActive, frontPanelLockoutActive, driveFault,
1218         coolingFanFault, chassisIdentifyState, chassisIdentifySupport,
1219         false, // reserved
1220 
1221         powerButtonDisabled, resetButtonDisabled, diagButtonDisabled,
1222         sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow,
1223         diagButtonDisableAllow, sleepButtonDisableAllow);
1224 }
1225 
1226 enum class IpmiRestartCause
1227 {
1228     Unknown = 0x0,
1229     RemoteCommand = 0x1,
1230     ResetButton = 0x2,
1231     PowerButton = 0x3,
1232     WatchdogTimer = 0x4,
1233     PowerPolicyAlwaysOn = 0x6,
1234     PowerPolicyPreviousState = 0x7,
1235     SoftReset = 0xa,
1236 };
1237 
1238 static IpmiRestartCause
restartCauseToIpmiRestartCause(State::Host::RestartCause cause)1239     restartCauseToIpmiRestartCause(State::Host::RestartCause cause)
1240 {
1241     switch (cause)
1242     {
1243         case State::Host::RestartCause::Unknown:
1244         {
1245             return IpmiRestartCause::Unknown;
1246         }
1247         case State::Host::RestartCause::RemoteCommand:
1248         {
1249             return IpmiRestartCause::RemoteCommand;
1250         }
1251         case State::Host::RestartCause::ResetButton:
1252         {
1253             return IpmiRestartCause::ResetButton;
1254         }
1255         case State::Host::RestartCause::PowerButton:
1256         {
1257             return IpmiRestartCause::PowerButton;
1258         }
1259         case State::Host::RestartCause::WatchdogTimer:
1260         {
1261             return IpmiRestartCause::WatchdogTimer;
1262         }
1263         case State::Host::RestartCause::PowerPolicyAlwaysOn:
1264         {
1265             return IpmiRestartCause::PowerPolicyAlwaysOn;
1266         }
1267         case State::Host::RestartCause::PowerPolicyPreviousState:
1268         {
1269             return IpmiRestartCause::PowerPolicyPreviousState;
1270         }
1271         case State::Host::RestartCause::SoftReset:
1272         {
1273             return IpmiRestartCause::SoftReset;
1274         }
1275         default:
1276         {
1277             return IpmiRestartCause::Unknown;
1278         }
1279     }
1280 }
1281 
1282 /*
1283  * getRestartCause
1284  * helper function for Get Host restart cause Command
1285  * return - optional value for RestartCause (no value on error)
1286  */
getRestartCause(ipmi::Context::ptr ctx)1287 static std::optional<uint4_t> getRestartCause(ipmi::Context::ptr ctx)
1288 {
1289     constexpr const char* restartCausePath = "/xyz/openbmc_project/state/host0";
1290     constexpr const char* restartCauseIntf = "xyz.openbmc_project.State.Host";
1291 
1292     std::string service;
1293     boost::system::error_code ec =
1294         ipmi::getService(ctx, restartCauseIntf, restartCausePath, service);
1295     if (!ec)
1296     {
1297         std::string restartCauseStr;
1298         ec = ipmi::getDbusProperty<std::string>(
1299             ctx, service, restartCausePath, restartCauseIntf, "RestartCause",
1300             restartCauseStr);
1301         if (!ec)
1302         {
1303             auto cause =
1304                 State::Host::convertRestartCauseFromString(restartCauseStr);
1305             return types::enum_cast<uint4_t>(
1306                 restartCauseToIpmiRestartCause(cause));
1307         }
1308     }
1309 
1310     lg2::error(
1311         "Failed to fetch RestartCause property ({PATH}/{INTERFACE}): {ERROR}",
1312         "ERROR", ec.message(), "PATH", restartCausePath, "INTERFACE",
1313         restartCauseIntf);
1314     return std::nullopt;
1315 }
1316 
1317 ipmi::RspType<uint4_t, // Restart Cause
1318               uint4_t, // reserved
1319               uint8_t  // channel number (not supported)
1320               >
ipmiGetSystemRestartCause(ipmi::Context::ptr ctx)1321     ipmiGetSystemRestartCause(ipmi::Context::ptr ctx)
1322 {
1323     std::optional<uint4_t> cause = getRestartCause(ctx);
1324     if (!cause)
1325     {
1326         return ipmi::responseUnspecifiedError();
1327     }
1328 
1329     constexpr uint4_t reserved = 0;
1330     auto channel = static_cast<uint8_t>(ctx->channel);
1331     return ipmi::responseSuccess(cause.value(), reserved, channel);
1332 }
1333 /** @brief Implementation of chassis control command
1334  *
1335  *  @param - chassisControl command byte
1336  *
1337  *  @return  Success or InvalidFieldRequest.
1338  */
ipmiChassisControl(ipmi::Context::ptr & ctx,uint8_t chassisControl)1339 ipmi::RspType<> ipmiChassisControl(ipmi::Context::ptr& ctx,
1340                                    uint8_t chassisControl)
1341 {
1342     int rc = 0;
1343     switch (chassisControl)
1344     {
1345         case CMD_POWER_ON:
1346             rc = initiateHostStateTransition(ctx, State::Host::Transition::On);
1347             break;
1348         case CMD_POWER_OFF:
1349             rc = initiateChassisStateTransition(
1350                 ctx, State::Chassis::Transition::Off);
1351             break;
1352         case CMD_HARD_RESET:
1353             rc = initiateHostStateTransition(
1354                 ctx, State::Host::Transition::ForceWarmReboot);
1355             break;
1356         case CMD_POWER_CYCLE:
1357             rc = initiateHostStateTransition(ctx,
1358                                              State::Host::Transition::Reboot);
1359             break;
1360         case CMD_SOFT_OFF_VIA_OVER_TEMP:
1361             rc = initiateHostStateTransition(ctx, State::Host::Transition::Off);
1362             break;
1363         case CMD_PULSE_DIAGNOSTIC_INTR:
1364             rc = doNmi(ctx);
1365             break;
1366 
1367         default:
1368         {
1369             lg2::error("Invalid Chassis Control command: {CMD}", "CMD",
1370                        lg2::hex, chassisControl);
1371             return ipmi::responseInvalidFieldRequest();
1372         }
1373     }
1374 
1375     return ((rc < 0) ? ipmi::responseUnspecifiedError()
1376                      : ipmi::responseSuccess());
1377 }
1378 
1379 /** @brief Return D-Bus connection string to enclosure identify LED object
1380  *
1381  *  @param[in, out] connection - connection to D-Bus object
1382  *  @return a IPMI return code
1383  */
getEnclosureIdentifyConnection()1384 std::string getEnclosureIdentifyConnection()
1385 {
1386     // lookup enclosure_identify group owner(s) in mapper
1387     try
1388     {
1389         return ipmi::getService(*getSdBus(), "xyz.openbmc_project.Led.Group",
1390                                 identify_led_object_name);
1391     }
1392     catch (const std::exception& e)
1393     {
1394         lg2::error("Chassis Identify: Error communicating to mapper: {ERROR}",
1395                    "ERROR", e);
1396         elog<InternalFailure>();
1397     }
1398 }
1399 
1400 /** @brief Turn On/Off enclosure identify LED
1401  *
1402  *  @param[in] flag - true to turn on LED, false to turn off
1403  *  @return a IPMI return code
1404  */
enclosureIdentifyLed(bool flag)1405 void enclosureIdentifyLed(bool flag)
1406 {
1407     using namespace chassis::internal;
1408     try
1409     {
1410         std::string connection = getEnclosureIdentifyConnection();
1411 
1412         auto msg = std::string("enclosureIdentifyLed(") +
1413                    boost::lexical_cast<std::string>(flag) + ")";
1414         lg2::debug(msg.c_str());
1415 
1416         ipmi::setDbusProperty(*getSdBus(), connection, identify_led_object_name,
1417                               "xyz.openbmc_project.Led.Group", "Asserted",
1418                               flag);
1419     }
1420     catch (const std::exception& e)
1421     {
1422         lg2::error("Chassis Identify: Error Setting State {LED_STATE}: {ERROR}",
1423                    "LED_STATE", flag, "ERROR", e);
1424         elog<InternalFailure>();
1425     }
1426 }
1427 
1428 /** @brief Callback method to turn off LED
1429  */
enclosureIdentifyLedOff()1430 void enclosureIdentifyLedOff()
1431 {
1432     try
1433     {
1434         chassisIDState = ChassisIDState::off;
1435         enclosureIdentifyLed(false);
1436     }
1437     catch (const InternalFailure& e)
1438     {
1439         report<InternalFailure>();
1440     }
1441 }
1442 
1443 /** @brief Create timer to turn on and off the enclosure LED
1444  */
createIdentifyTimer()1445 void createIdentifyTimer()
1446 {
1447     if (!identifyTimer)
1448     {
1449         identifyTimer =
1450             std::make_unique<sdbusplus::Timer>(enclosureIdentifyLedOff);
1451     }
1452 }
1453 
ipmiChassisIdentify(std::optional<uint8_t> interval,std::optional<uint8_t> force)1454 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
1455                                     std::optional<uint8_t> force)
1456 {
1457     uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT);
1458     bool forceIdentify = force.value_or(0) & 0x01;
1459 
1460     if (identifyInterval || forceIdentify)
1461     {
1462         // stop the timer if already started;
1463         // for force identify we should not turn off LED
1464         identifyTimer->stop();
1465         try
1466         {
1467             chassisIDState = ChassisIDState::temporaryOn;
1468             enclosureIdentifyLed(true);
1469         }
1470         catch (const InternalFailure& e)
1471         {
1472             report<InternalFailure>();
1473             return ipmi::responseResponseError();
1474         }
1475 
1476         if (forceIdentify)
1477         {
1478             chassisIDState = ChassisIDState::indefiniteOn;
1479             return ipmi::responseSuccess();
1480         }
1481         // start the timer
1482         auto time = std::chrono::duration_cast<std::chrono::microseconds>(
1483             std::chrono::seconds(identifyInterval));
1484         identifyTimer->start(time);
1485     }
1486     else if (!identifyInterval)
1487     {
1488         identifyTimer->stop();
1489         enclosureIdentifyLedOff();
1490     }
1491     return ipmi::responseSuccess();
1492 }
1493 
1494 namespace boot_options
1495 {
1496 
1497 using namespace sdbusplus::server::xyz::openbmc_project::control::boot;
1498 using IpmiValue = uint8_t;
1499 constexpr auto ipmiDefault = 0;
1500 
1501 std::map<IpmiValue, Type::Types> typeIpmiToDbus = {{0x00, Type::Types::Legacy},
1502                                                    {0x01, Type::Types::EFI}};
1503 
1504 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
1505     {0x01, Source::Sources::Network},
1506     {0x02, Source::Sources::Disk},
1507     {0x05, Source::Sources::ExternalMedia},
1508     {0x0f, Source::Sources::RemovableMedia},
1509     {ipmiDefault, Source::Sources::Default}};
1510 
1511 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
1512 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT
1513     {0x03, Mode::Modes::Safe},
1514 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT
1515     {0x06, Mode::Modes::Setup},
1516     {ipmiDefault, Mode::Modes::Regular}};
1517 
1518 std::map<Type::Types, IpmiValue> typeDbusToIpmi = {{Type::Types::Legacy, 0x00},
1519                                                    {Type::Types::EFI, 0x01}};
1520 
1521 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
1522     {Source::Sources::Network, 0x01},
1523     {Source::Sources::Disk, 0x02},
1524     {Source::Sources::ExternalMedia, 0x05},
1525     {Source::Sources::RemovableMedia, 0x0f},
1526     {Source::Sources::Default, ipmiDefault}};
1527 
1528 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
1529 #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT
1530     {Mode::Modes::Safe, 0x03},
1531 #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT
1532     {Mode::Modes::Setup, 0x06},
1533     {Mode::Modes::Regular, ipmiDefault}};
1534 
1535 } // namespace boot_options
1536 
1537 /** @brief Get the property value for boot source
1538  *  @param[in] ctx - context pointer
1539  *  @param[out] source - boot source value
1540  *  @return On failure return IPMI error.
1541  */
getBootSource(ipmi::Context::ptr & ctx,Source::Sources & source)1542 static ipmi::Cc getBootSource(ipmi::Context::ptr& ctx, Source::Sources& source)
1543 {
1544     using namespace chassis::internal;
1545     std::string result;
1546     std::string service;
1547     boost::system::error_code ec =
1548         getService(ctx, bootSourceIntf, bootSettingsPath, service);
1549     if (!ec)
1550     {
1551         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath,
1552                                    bootSourceIntf, "BootSource", result);
1553         if (!ec)
1554         {
1555             source = Source::convertSourcesFromString(result);
1556             return ipmi::ccSuccess;
1557         }
1558     }
1559     lg2::error("Error in BootSource Get: {ERROR}", "ERROR", ec.message());
1560     return ipmi::ccUnspecifiedError;
1561 }
1562 
1563 /** @brief Set the property value for boot source
1564  *  @param[in] ctx - context pointer
1565  *  @param[in] source - boot source value
1566  *  @return On failure return IPMI error.
1567  */
setBootSource(ipmi::Context::ptr & ctx,const Source::Sources & source)1568 static ipmi::Cc setBootSource(ipmi::Context::ptr& ctx,
1569                               const Source::Sources& source)
1570 {
1571     using namespace chassis::internal;
1572     std::string service;
1573     boost::system::error_code ec =
1574         getService(ctx, bootSourceIntf, bootSettingsPath, service);
1575     if (!ec)
1576     {
1577         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath,
1578                                    bootSourceIntf, "BootSource",
1579                                    convertForMessage(source));
1580         if (!ec)
1581         {
1582             return ipmi::ccSuccess;
1583         }
1584     }
1585     lg2::error("Error in BootSource Set: {ERROR}", "ERROR", ec.message());
1586     return ipmi::ccUnspecifiedError;
1587 }
1588 
1589 /** @brief Get the property value for boot mode
1590  *  @param[in] ctx - context pointer
1591  *  @param[out] mode - boot mode value
1592  *  @return On failure return IPMI error.
1593  */
getBootMode(ipmi::Context::ptr & ctx,Mode::Modes & mode)1594 static ipmi::Cc getBootMode(ipmi::Context::ptr& ctx, Mode::Modes& mode)
1595 {
1596     using namespace chassis::internal;
1597     std::string result;
1598     std::string service;
1599     boost::system::error_code ec =
1600         getService(ctx, bootModeIntf, bootSettingsPath, service);
1601     if (!ec)
1602     {
1603         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootModeIntf,
1604                                    "BootMode", result);
1605         if (!ec)
1606         {
1607             mode = Mode::convertModesFromString(result);
1608             return ipmi::ccSuccess;
1609         }
1610     }
1611     lg2::error("Error in BootMode Get: {ERROR}", "ERROR", ec.message());
1612     return ipmi::ccUnspecifiedError;
1613 }
1614 
1615 /** @brief Set the property value for boot mode
1616  *  @param[in] ctx - context pointer
1617  *  @param[in] mode - boot mode value
1618  *  @return On failure return IPMI error.
1619  */
setBootMode(ipmi::Context::ptr & ctx,const Mode::Modes & mode)1620 static ipmi::Cc setBootMode(ipmi::Context::ptr& ctx, const Mode::Modes& mode)
1621 {
1622     using namespace chassis::internal;
1623     std::string service;
1624     boost::system::error_code ec =
1625         getService(ctx, bootModeIntf, bootSettingsPath, service);
1626     if (!ec)
1627     {
1628         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootModeIntf,
1629                                    "BootMode", convertForMessage(mode));
1630         if (!ec)
1631         {
1632             return ipmi::ccSuccess;
1633         }
1634     }
1635     lg2::error("Error in BootMode Set: {ERROR}", "ERROR", ec.message());
1636     return ipmi::ccUnspecifiedError;
1637 }
1638 
1639 /** @brief Get the property value for boot type
1640  *  @param[in] ctx - context pointer
1641  *  @param[out] type - boot type value
1642  *  @return On failure return IPMI error.
1643  */
getBootType(ipmi::Context::ptr & ctx,Type::Types & type)1644 static ipmi::Cc getBootType(ipmi::Context::ptr& ctx, Type::Types& type)
1645 {
1646     using namespace chassis::internal;
1647     std::string result;
1648     std::string service;
1649     boost::system::error_code ec =
1650         getService(ctx, bootTypeIntf, bootSettingsPath, service);
1651 
1652     // Don't throw error if BootType interface is not present.
1653     // This interface is not relevant for some Host architectures
1654     // (for example POWER). In this case we don't won't IPMI to
1655     // return an error, but simply return bootType as EFI.
1656     type = Type::Types::EFI;
1657     if (!ec)
1658     {
1659         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootTypeIntf,
1660                                    "BootType", result);
1661         if (ec)
1662         {
1663             lg2::error("Error in BootType Get: {ERROR}", "ERROR", ec.message());
1664             return ipmi::ccUnspecifiedError;
1665         }
1666         type = Type::convertTypesFromString(result);
1667     }
1668 
1669     return ipmi::ccSuccess;
1670 }
1671 
1672 /** @brief Set the property value for boot type
1673  *  @param[in] ctx - context pointer
1674  *  @param[in] type - boot type value
1675  *  @return On failure return IPMI error.
1676  */
setBootType(ipmi::Context::ptr & ctx,const Type::Types & type)1677 static ipmi::Cc setBootType(ipmi::Context::ptr& ctx, const Type::Types& type)
1678 {
1679     using namespace chassis::internal;
1680     std::string service;
1681     boost::system::error_code ec =
1682         getService(ctx, bootTypeIntf, bootSettingsPath, service);
1683     if (!ec)
1684     {
1685         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootTypeIntf,
1686                                    "BootType", convertForMessage(type));
1687         if (ec)
1688         {
1689             lg2::error("Error in BootType Set: {ERROR}", "ERROR", ec.message());
1690             return ipmi::ccUnspecifiedError;
1691         }
1692     }
1693     // Don't throw error if BootType interface is not present.
1694     // This interface is not relevant for some Host architectures
1695     // (for example POWER). In this case we don't won't IPMI to
1696     // return an error, but want to just skip this function.
1697     return ipmi::ccSuccess;
1698 }
1699 
1700 /** @brief Get the property value for boot override enable
1701  *  @param[in] ctx - context pointer
1702  *  @param[out] enable - boot override enable
1703  *  @return On failure return IPMI error.
1704  */
getBootEnable(ipmi::Context::ptr & ctx,bool & enable)1705 static ipmi::Cc getBootEnable(ipmi::Context::ptr& ctx, bool& enable)
1706 {
1707     using namespace chassis::internal;
1708     std::string result;
1709     std::string service;
1710     boost::system::error_code ec =
1711         getService(ctx, bootEnableIntf, bootSettingsPath, service);
1712     if (!ec)
1713     {
1714         ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath,
1715                                    bootEnableIntf, "Enabled", enable);
1716         if (!ec)
1717         {
1718             return ipmi::ccSuccess;
1719         }
1720     }
1721     lg2::error("Error in Boot Override Enable Get: {ERROR}", "ERROR",
1722                ec.message());
1723     return ipmi::ccUnspecifiedError;
1724 }
1725 
1726 /** @brief Set the property value for boot override enable
1727  *  @param[in] ctx - context pointer
1728  *  @param[in] enable - boot override enable
1729  *  @return On failure return IPMI error.
1730  */
setBootEnable(ipmi::Context::ptr & ctx,const bool & enable)1731 static ipmi::Cc setBootEnable(ipmi::Context::ptr& ctx, const bool& enable)
1732 {
1733     using namespace chassis::internal;
1734     std::string service;
1735     boost::system::error_code ec =
1736         getService(ctx, bootEnableIntf, bootSettingsPath, service);
1737     if (!ec)
1738     {
1739         ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath,
1740                                    bootEnableIntf, "Enabled", enable);
1741         if (!ec)
1742         {
1743             return ipmi::ccSuccess;
1744         }
1745     }
1746     lg2::error("Error in Boot Source Override Enable Set: {ERROR}", "ERROR",
1747                ec.message());
1748     return ipmi::ccUnspecifiedError;
1749 }
1750 
1751 /** @brief Get the property value for boot override one-time
1752  *  @param[in] ctx - context pointer
1753  *  @param[out] onetime - boot override one-time
1754  *  @return On failure return IPMI error.
1755  */
getBootOneTime(ipmi::Context::ptr & ctx,bool & onetime)1756 static ipmi::Cc getBootOneTime(ipmi::Context::ptr& ctx, bool& onetime)
1757 {
1758     using namespace chassis::internal;
1759     std::string result;
1760     std::string service;
1761     boost::system::error_code ec =
1762         getService(ctx, bootOneTimeIntf, bootSettingsOneTimePath, service);
1763     if (!ec)
1764     {
1765         ec = ipmi::getDbusProperty(ctx, service, bootSettingsOneTimePath,
1766                                    bootOneTimeIntf, "Enabled", onetime);
1767         if (!ec)
1768         {
1769             return ipmi::ccSuccess;
1770         }
1771     }
1772     lg2::error("Error in Boot Override OneTime Get: {ERROR}", "ERROR",
1773                ec.message());
1774     return ipmi::ccUnspecifiedError;
1775 }
1776 
1777 /** @brief Set the property value for boot override one-time
1778  *  @param[in] ctx - context pointer
1779  *  @param[in] onetime - boot override one-time
1780  *  @return On failure return IPMI error.
1781  */
setBootOneTime(ipmi::Context::ptr & ctx,const bool & onetime)1782 static ipmi::Cc setBootOneTime(ipmi::Context::ptr& ctx, const bool& onetime)
1783 {
1784     using namespace chassis::internal;
1785     std::string service;
1786     boost::system::error_code ec =
1787         getService(ctx, bootOneTimeIntf, bootSettingsOneTimePath, service);
1788     if (!ec)
1789     {
1790         ec = ipmi::setDbusProperty(ctx, service, bootSettingsOneTimePath,
1791                                    bootOneTimeIntf, "Enabled", onetime);
1792         if (!ec)
1793         {
1794             return ipmi::ccSuccess;
1795         }
1796     }
1797     lg2::error("Error in Boot Source Override OneTime Set: {ERROR}", "ERROR",
1798                ec.message());
1799     return ipmi::ccUnspecifiedError;
1800 }
1801 
1802 static constexpr uint8_t setComplete = 0x0;
1803 static constexpr uint8_t setInProgress = 0x1;
1804 static uint8_t transferStatus = setComplete;
1805 static uint8_t bootFlagValidBitClr = 0;
1806 static uint5_t bootInitiatorAckData = 0x0;
1807 static bool cmosClear = false;
1808 
1809 /** @brief implements the Get Chassis system boot option
1810  *  @param ctx - context pointer
1811  *  @param bootOptionParameter   - boot option parameter selector
1812  *  @param reserved1    - reserved bit
1813  *  @param setSelector  - selects a particular block or set of parameters
1814  *                        under the given parameter selector
1815  *                        write as 00h if parameter doesn't use a setSelector
1816  *  @param blockSelector- selects a particular block within a set of
1817  *                        parameters write as 00h if parameter doesn't use a
1818  *                        blockSelector
1819  *
1820  *  @return IPMI completion code plus response data
1821  *  @return Payload contains below parameters:
1822  *   version             - parameter version
1823  *   bootOptionParameter - boot option parameter selector
1824  *   parmIndicator - parameter valid/invalid indicator
1825  *   data          - configuration parameter data
1826  */
ipmiChassisGetSysBootOptions(ipmi::Context::ptr ctx,uint7_t bootOptionParameter,bool reserved1,uint8_t setSelector,uint8_t blockSelector)1827 ipmi::RspType<ipmi::message::Payload> ipmiChassisGetSysBootOptions(
1828     ipmi::Context::ptr ctx, uint7_t bootOptionParameter, bool reserved1,
1829     [[maybe_unused]] uint8_t setSelector,
1830     [[maybe_unused]] uint8_t blockSelector)
1831 {
1832     ipmi::Cc rc;
1833     if (reserved1)
1834     {
1835         return ipmi::responseInvalidFieldRequest();
1836     }
1837 
1838     constexpr uint4_t version = 0x01;
1839     ipmi::message::Payload response;
1840     response.pack(version, uint4_t{});
1841     using namespace boot_options;
1842 
1843     IpmiValue bootOption = ipmiDefault;
1844 
1845     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1846         BootOptionParameter::setInProgress)
1847     {
1848         response.pack(bootOptionParameter, reserved1, transferStatus);
1849         return ipmi::responseSuccess(std::move(response));
1850     }
1851 
1852     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1853         BootOptionParameter::bootInfo)
1854     {
1855         constexpr uint8_t writeMask = 0;
1856         response.pack(bootOptionParameter, reserved1, writeMask,
1857                       bootInitiatorAckData);
1858         return ipmi::responseSuccess(std::move(response));
1859     }
1860 
1861     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1862         BootOptionParameter::bootFlagValidClr)
1863     {
1864         response.pack(bootOptionParameter, reserved1,
1865                       uint5_t{bootFlagValidBitClr}, uint3_t{});
1866         return ipmi::responseSuccess(std::move(response));
1867     }
1868 
1869     /*
1870      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1871      * This is the only parameter used by petitboot.
1872      */
1873     if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1874         BootOptionParameter::bootFlags)
1875     {
1876         using namespace chassis::internal;
1877         using namespace chassis::internal::cache;
1878 
1879         try
1880         {
1881             Source::Sources bootSource;
1882             rc = getBootSource(ctx, bootSource);
1883             if (rc != ipmi::ccSuccess)
1884             {
1885                 return ipmi::response(rc);
1886             }
1887 
1888             Type::Types bootType;
1889             rc = getBootType(ctx, bootType);
1890             if (rc != ipmi::ccSuccess)
1891             {
1892                 return ipmi::response(rc);
1893             }
1894 
1895             Mode::Modes bootMode;
1896             rc = getBootMode(ctx, bootMode);
1897             if (rc != ipmi::ccSuccess)
1898             {
1899                 return ipmi::response(rc);
1900             }
1901 
1902             bootOption = sourceDbusToIpmi.at(bootSource);
1903             if ((Mode::Modes::Regular == bootMode) &&
1904                 (Source::Sources::Default == bootSource))
1905             {
1906                 bootOption = ipmiDefault;
1907             }
1908             else if (Source::Sources::Default == bootSource)
1909             {
1910                 bootOption = modeDbusToIpmi.at(bootMode);
1911             }
1912 
1913             IpmiValue biosBootType = typeDbusToIpmi.at(bootType);
1914 
1915             bool oneTimeEnabled;
1916             rc = getBootOneTime(ctx, oneTimeEnabled);
1917             if (rc != ipmi::ccSuccess)
1918             {
1919                 return ipmi::response(rc);
1920             }
1921 
1922             uint1_t permanent = oneTimeEnabled ? 0 : 1;
1923 
1924             bool valid;
1925             rc = getBootEnable(ctx, valid);
1926             if (rc != ipmi::ccSuccess)
1927             {
1928                 return ipmi::response(rc);
1929             }
1930 
1931             uint1_t validFlag = valid ? 1 : 0;
1932 
1933             response.pack(bootOptionParameter, reserved1, uint5_t{},
1934                           uint1_t{biosBootType}, uint1_t{permanent},
1935                           uint1_t{validFlag}, uint2_t{}, uint4_t{bootOption},
1936                           uint1_t{}, cmosClear, uint8_t{}, uint8_t{},
1937                           uint8_t{});
1938             return ipmi::responseSuccess(std::move(response));
1939         }
1940         catch (const InternalFailure& e)
1941         {
1942             cache::objectsPtr.reset();
1943             report<InternalFailure>();
1944             return ipmi::responseUnspecifiedError();
1945         }
1946     }
1947     else
1948     {
1949         if ((bootOptionParameter >= oemParmStart) &&
1950             (bootOptionParameter <= oemParmEnd))
1951         {
1952             if (types::enum_cast<BootOptionParameter>(bootOptionParameter) ==
1953                 BootOptionParameter::opalNetworkSettings)
1954             {
1955                 response.pack(bootOptionParameter, reserved1);
1956                 int ret = getHostNetworkData(response);
1957                 if (ret < 0)
1958                 {
1959                     response.trailingOk = true;
1960                     lg2::error(
1961                         "getHostNetworkData failed for GetSysBootOptions.");
1962                     return ipmi::responseUnspecifiedError();
1963                 }
1964                 else
1965                 {
1966                     return ipmi::responseSuccess(std::move(response));
1967                 }
1968             }
1969             else
1970             {
1971                 lg2::error(
1972                     "ipmiChassisGetSysBootOptions: Unsupported parameter {PARAM}",
1973                     "PARAM", lg2::hex,
1974                     static_cast<uint8_t>(bootOptionParameter));
1975                 return ipmi::responseParmNotSupported();
1976             }
1977         }
1978         else
1979         {
1980             lg2::error(
1981                 "ipmiChassisGetSysBootOptions: Unsupported parameter {PARAM}",
1982                 "PARAM", lg2::hex, static_cast<uint8_t>(bootOptionParameter));
1983             return ipmi::responseParmNotSupported();
1984         }
1985     }
1986     return ipmi::responseUnspecifiedError();
1987 }
1988 
ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx,uint7_t parameterSelector,bool,ipmi::message::Payload & data)1989 ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx,
1990                                              uint7_t parameterSelector, bool,
1991                                              ipmi::message::Payload& data)
1992 {
1993     using namespace boot_options;
1994     ipmi::Cc rc;
1995 
1996     if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
1997         BootOptionParameter::setInProgress)
1998     {
1999         uint2_t setInProgressFlag;
2000         uint6_t rsvd;
2001         if (data.unpack(setInProgressFlag, rsvd) != 0 || !data.fullyUnpacked())
2002         {
2003             return ipmi::responseReqDataLenInvalid();
2004         }
2005         if (rsvd)
2006         {
2007             return ipmi::responseInvalidFieldRequest();
2008         }
2009         if ((transferStatus == setInProgress) &&
2010             (static_cast<uint8_t>(setInProgressFlag) != setComplete))
2011         {
2012             return ipmi::response(IPMI_CC_FAIL_SET_IN_PROGRESS);
2013         }
2014         transferStatus = static_cast<uint8_t>(setInProgressFlag);
2015         return ipmi::responseSuccess();
2016     }
2017 
2018     /*  000101
2019      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
2020      * This is the only parameter used by petitboot.
2021      */
2022 
2023     if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2024         BootOptionParameter::bootFlags)
2025     {
2026         uint5_t rsvd;
2027         bool validFlag;
2028         bool permanent;
2029         bool biosBootType;
2030         bool lockOutResetButton;
2031         bool screenBlank;
2032         uint4_t bootDeviceSelector;
2033         bool lockKeyboard;
2034         uint8_t data3;
2035         uint4_t biosInfo;
2036         uint4_t rsvd1;
2037         uint5_t deviceInstance;
2038         uint3_t rsvd2;
2039 
2040         if (data.unpack(rsvd, biosBootType, permanent, validFlag,
2041                         lockOutResetButton, screenBlank, bootDeviceSelector,
2042                         lockKeyboard, cmosClear, data3, biosInfo, rsvd1,
2043                         deviceInstance, rsvd2) != 0 ||
2044             !data.fullyUnpacked())
2045         {
2046             return ipmi::responseReqDataLenInvalid();
2047         }
2048         if (rsvd || rsvd1 || rsvd2)
2049         {
2050             return ipmi::responseInvalidFieldRequest();
2051         }
2052 
2053         using namespace chassis::internal;
2054         using namespace chassis::internal::cache;
2055 
2056         try
2057         {
2058             rc = setBootOneTime(ctx, !permanent);
2059             if (rc != ipmi::ccSuccess)
2060             {
2061                 return ipmi::response(rc);
2062             }
2063 
2064             rc = setBootEnable(ctx, validFlag);
2065             if (rc != ipmi::ccSuccess)
2066             {
2067                 return ipmi::response(rc);
2068             }
2069 
2070             auto modeItr =
2071                 modeIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector));
2072             auto typeItr =
2073                 typeIpmiToDbus.find(static_cast<uint8_t>(biosBootType));
2074             auto sourceItr =
2075                 sourceIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector));
2076             if (sourceIpmiToDbus.end() != sourceItr)
2077             {
2078                 rc = setBootSource(ctx, sourceItr->second);
2079                 if (rc != ipmi::ccSuccess)
2080                 {
2081                     return ipmi::response(rc);
2082                 }
2083                 // If a set boot device is mapping to a boot source, then reset
2084                 // the boot mode D-Bus property to default.
2085                 // This way the ipmid code can determine which property is not
2086                 // at the default value
2087                 if (sourceItr->second != Source::Sources::Default)
2088                 {
2089                     rc = setBootMode(ctx, Mode::Modes::Regular);
2090                     if (rc != ipmi::ccSuccess)
2091                     {
2092                         return ipmi::response(rc);
2093                     }
2094                 }
2095             }
2096 
2097             if (typeIpmiToDbus.end() != typeItr)
2098             {
2099                 rc = setBootType(ctx, typeItr->second);
2100                 if (rc != ipmi::ccSuccess)
2101                 {
2102                     return ipmi::response(rc);
2103                 }
2104             }
2105 
2106             if (modeIpmiToDbus.end() != modeItr)
2107             {
2108                 rc = setBootMode(ctx, modeItr->second);
2109                 if (rc != ipmi::ccSuccess)
2110                 {
2111                     return ipmi::response(rc);
2112                 }
2113                 // If a set boot device is mapping to a boot mode, then reset
2114                 // the boot source D-Bus property to default.
2115                 // This way the ipmid code can determine which property is not
2116                 // at the default value
2117                 if (modeItr->second != Mode::Modes::Regular)
2118                 {
2119                     rc = setBootSource(ctx, Source::Sources::Default);
2120                     if (rc != ipmi::ccSuccess)
2121                     {
2122                         return ipmi::response(rc);
2123                     }
2124                 }
2125             }
2126             if ((modeIpmiToDbus.end() == modeItr) &&
2127                 (typeIpmiToDbus.end() == typeItr) &&
2128                 (sourceIpmiToDbus.end() == sourceItr))
2129             {
2130                 // return error if boot option is not supported
2131                 lg2::error(
2132                     "ipmiChassisSetSysBootOptions: Boot option not supported");
2133                 return ipmi::responseInvalidFieldRequest();
2134             }
2135         }
2136         catch (const sdbusplus::exception_t& e)
2137         {
2138             objectsPtr.reset();
2139             report<InternalFailure>();
2140             lg2::error("ipmiChassisSetSysBootOptions: Error in setting Boot "
2141                        "flag parameters");
2142             return ipmi::responseUnspecifiedError();
2143         }
2144     }
2145     else if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2146              BootOptionParameter::bootInfo)
2147     {
2148         uint8_t writeMak;
2149         uint5_t bootInfoAck;
2150         uint3_t rsvd;
2151 
2152         if (data.unpack(writeMak, bootInfoAck, rsvd) != 0 ||
2153             !data.fullyUnpacked())
2154         {
2155             return ipmi::responseReqDataLenInvalid();
2156         }
2157         if (rsvd)
2158         {
2159             return ipmi::responseInvalidFieldRequest();
2160         }
2161         bootInitiatorAckData &= ~writeMak;
2162         bootInitiatorAckData |= (writeMak & bootInfoAck);
2163         lg2::info("ipmiChassisSetSysBootOptions: bootInfo parameter set "
2164                   "successfully");
2165         data.trailingOk = true;
2166         return ipmi::responseSuccess();
2167     }
2168     else if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2169              BootOptionParameter::bootFlagValidClr)
2170     {
2171         uint5_t bootFlagValidClr;
2172         uint3_t rsvd;
2173 
2174         if (data.unpack(bootFlagValidClr, rsvd) != 0 || !data.fullyUnpacked())
2175         {
2176             return ipmi::responseReqDataLenInvalid();
2177         }
2178         if (rsvd)
2179         {
2180             return ipmi::responseInvalidFieldRequest();
2181         }
2182         // store boot flag valid bits clear value
2183         bootFlagValidBitClr = static_cast<uint8_t>(bootFlagValidClr);
2184         lg2::info(
2185             "ipmiChassisSetSysBootOptions: bootFlagValidBits parameter set "
2186             "successfully to {VALUE}",
2187             "VALUE", lg2::hex, bootFlagValidBitClr);
2188         return ipmi::responseSuccess();
2189     }
2190     else
2191     {
2192         if ((parameterSelector >= static_cast<uint7_t>(oemParmStart)) &&
2193             (parameterSelector <= static_cast<uint7_t>(oemParmEnd)))
2194         {
2195             if (types::enum_cast<BootOptionParameter>(parameterSelector) ==
2196                 BootOptionParameter::opalNetworkSettings)
2197             {
2198                 ipmi::Cc ret = setHostNetworkData(data);
2199                 if (ret != ipmi::ccSuccess)
2200                 {
2201                     lg2::error("ipmiChassisSetSysBootOptions: Error in "
2202                                "setHostNetworkData");
2203                     data.trailingOk = true;
2204                     return ipmi::response(ret);
2205                 }
2206                 data.trailingOk = true;
2207                 return ipmi::responseSuccess();
2208             }
2209             else
2210             {
2211                 lg2::error(
2212                     "ipmiChassisSetSysBootOptions: Unsupported param: {PARAM}",
2213                     "PARAM", lg2::hex, static_cast<uint8_t>(parameterSelector));
2214                 data.trailingOk = true;
2215                 return ipmi::responseParmNotSupported();
2216             }
2217         }
2218         data.trailingOk = true;
2219         return ipmi::responseParmNotSupported();
2220     }
2221     return ipmi::responseSuccess();
2222 }
2223 
2224 /** @brief implements Get POH counter command
2225  *  @parameter
2226  *   -  none
2227  *  @returns IPMI completion code plus response data
2228  *   - minPerCount - Minutes per count
2229  *   - counterReading - counter reading
2230  */
2231 ipmi::RspType<uint8_t, // Minutes per count
2232               uint32_t // Counter reading
2233               >
ipmiGetPOHCounter()2234     ipmiGetPOHCounter()
2235 {
2236     // sd_bus error
2237     try
2238     {
2239         return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount),
2240                                      getPOHCounter());
2241     }
2242     catch (const std::exception& e)
2243     {
2244         lg2::error("getPOHCounter error: {ERROR}", "ERROR", e);
2245         return ipmi::responseUnspecifiedError();
2246     }
2247 }
2248 
2249 ipmi::RspType<uint3_t, // policy support
2250               uint5_t  // reserved
2251               >
ipmiChassisSetPowerRestorePolicy(boost::asio::yield_context yield,uint3_t policy,uint5_t reserved)2252     ipmiChassisSetPowerRestorePolicy(boost::asio::yield_context yield,
2253                                      uint3_t policy, uint5_t reserved)
2254 {
2255     power_policy::DbusValue value =
2256         power_policy::RestorePolicy::Policy::AlwaysOff;
2257 
2258     if (reserved || (policy > power_policy::noChange))
2259     {
2260         lg2::error("Reserved request parameter: {REQ}", "REQ", lg2::hex,
2261                    static_cast<int>(policy));
2262         return ipmi::responseInvalidFieldRequest();
2263     }
2264 
2265     if (policy == power_policy::noChange)
2266     {
2267         // just return the supported policy
2268         return ipmi::responseSuccess(power_policy::allSupport, reserved);
2269     }
2270 
2271     for (const auto& it : power_policy::dbusToIpmi)
2272     {
2273         if (it.second == policy)
2274         {
2275             value = it.first;
2276             break;
2277         }
2278     }
2279 
2280     try
2281     {
2282         settings::Objects& objects = chassis::internal::cache::getObjects();
2283         const settings::Path& powerRestoreSetting =
2284             objects.map.at(chassis::internal::powerRestoreIntf).front();
2285         std::variant<std::string> property = convertForMessage(value);
2286 
2287         auto sdbusp = getSdBus();
2288         boost::system::error_code ec;
2289         sdbusp->yield_method_call<void>(
2290             yield, ec,
2291             objects
2292                 .service(powerRestoreSetting,
2293                          chassis::internal::powerRestoreIntf)
2294                 .c_str(),
2295             powerRestoreSetting, ipmi::PROP_INTF, "Set",
2296             chassis::internal::powerRestoreIntf, "PowerRestorePolicy",
2297             property);
2298         if (ec)
2299         {
2300             lg2::error("Unspecified Error");
2301             return ipmi::responseUnspecifiedError();
2302         }
2303     }
2304     catch (const InternalFailure& e)
2305     {
2306         chassis::internal::cache::objectsPtr.reset();
2307         report<InternalFailure>();
2308         return ipmi::responseUnspecifiedError();
2309     }
2310 
2311     return ipmi::responseSuccess(power_policy::allSupport, reserved);
2312 }
2313 
ipmiSetFrontPanelButtonEnables(ipmi::Context::ptr ctx,bool disablePowerButton,bool disableResetButton,bool,bool,uint4_t)2314 ipmi::RspType<> ipmiSetFrontPanelButtonEnables(
2315     ipmi::Context::ptr ctx, bool disablePowerButton, bool disableResetButton,
2316     bool, bool, uint4_t)
2317 {
2318     using namespace chassis::internal;
2319 
2320     // set power button Enabled property
2321     bool success = setButtonEnabled(ctx, powerButtonPath, powerButtonIntf,
2322                                     !disablePowerButton);
2323 
2324     // set reset button Enabled property
2325     success &= setButtonEnabled(ctx, resetButtonPath, resetButtonIntf,
2326                                 !disableResetButton);
2327 
2328     if (!success)
2329     {
2330         // not all buttons were successfully set
2331         return ipmi::responseUnspecifiedError();
2332     }
2333     return ipmi::responseSuccess();
2334 }
2335 
register_netfn_chassis_functions()2336 void register_netfn_chassis_functions()
2337 {
2338     createIdentifyTimer();
2339 
2340     // Get Chassis Capabilities
2341     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2342                           ipmi::chassis::cmdGetChassisCapabilities,
2343                           ipmi::Privilege::User, ipmiGetChassisCap);
2344 
2345     // Set Front Panel Button Enables
2346     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2347                           ipmi::chassis::cmdSetFrontPanelButtonEnables,
2348                           ipmi::Privilege::Admin,
2349                           ipmiSetFrontPanelButtonEnables);
2350 
2351     // Set Chassis Capabilities
2352     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2353                           ipmi::chassis::cmdSetChassisCapabilities,
2354                           ipmi::Privilege::User, ipmiSetChassisCap);
2355 
2356     // <Get System Boot Options>
2357     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2358                           ipmi::chassis::cmdGetSystemBootOptions,
2359                           ipmi::Privilege::Operator,
2360                           ipmiChassisGetSysBootOptions);
2361 
2362     // <Get Chassis Status>
2363     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2364                           ipmi::chassis::cmdGetChassisStatus,
2365                           ipmi::Privilege::User, ipmiGetChassisStatus);
2366 
2367     // <Chassis Get System Restart Cause>
2368     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2369                           ipmi::chassis::cmdGetSystemRestartCause,
2370                           ipmi::Privilege::User, ipmiGetSystemRestartCause);
2371 
2372     // <Chassis Control>
2373     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2374                           ipmi::chassis::cmdChassisControl,
2375                           ipmi::Privilege::Operator, ipmiChassisControl);
2376 
2377     // <Chassis Identify>
2378     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2379                           ipmi::chassis::cmdChassisIdentify,
2380                           ipmi::Privilege::Operator, ipmiChassisIdentify);
2381 
2382     // <Set System Boot Options>
2383     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2384                           ipmi::chassis::cmdSetSystemBootOptions,
2385                           ipmi::Privilege::Operator,
2386                           ipmiChassisSetSysBootOptions);
2387 
2388     // <Get POH Counter>
2389     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2390                           ipmi::chassis::cmdGetPohCounter,
2391                           ipmi::Privilege::User, ipmiGetPOHCounter);
2392 
2393     // <Set Power Restore Policy>
2394     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
2395                           ipmi::chassis::cmdSetPowerRestorePolicy,
2396                           ipmi::Privilege::Operator,
2397                           ipmiChassisSetPowerRestorePolicy);
2398 }
2399