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