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