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 size_t encIdentifyObjectsSize = 1;
70 static constexpr size_t chassisIdentifyReqLength = 2;
71 static constexpr size_t identifyIntervalPos = 0;
72 static constexpr size_t forceIdentifyPos = 1;
73 
74 void register_netfn_chassis_functions() __attribute__((constructor));
75 
76 // Host settings in dbus
77 // Service name should be referenced by connection name got via object mapper
78 const char* settings_object_name = "/org/openbmc/settings/host0";
79 const char* settings_intf_name = "org.freedesktop.DBus.Properties";
80 const char* identify_led_object_name =
81     "/xyz/openbmc_project/led/groups/enclosure_identify";
82 
83 constexpr auto SETTINGS_ROOT = "/";
84 constexpr auto SETTINGS_MATCH = "host0";
85 
86 constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP";
87 constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress";
88 
89 static constexpr auto chassisStateRoot = "/xyz/openbmc_project/state";
90 static constexpr auto chassisPOHStateIntf =
91     "xyz.openbmc_project.State.PowerOnHours";
92 static constexpr auto pOHCounterProperty = "POHCounter";
93 static constexpr auto match = "chassis0";
94 const static constexpr char chassisCapIntf[] =
95     "xyz.openbmc_project.Control.ChassisCapabilities";
96 const static constexpr char chassisCapFlagsProp[] = "CapabilitiesFlags";
97 const static constexpr char chassisFRUDevAddrProp[] = "FRUDeviceAddress";
98 const static constexpr char chassisSDRDevAddrProp[] = "SDRDeviceAddress";
99 const static constexpr char chassisSELDevAddrProp[] = "SELDeviceAddress";
100 const static constexpr char chassisSMDevAddrProp[] = "SMDeviceAddress";
101 const static constexpr char chassisBridgeDevAddrProp[] = "BridgeDeviceAddress";
102 static constexpr uint8_t chassisCapFlagMask = 0x0f;
103 static constexpr uint8_t chassisCapAddrMask = 0xfe;
104 static constexpr const char* powerButtonIntf =
105     "xyz.openbmc_project.Chassis.Buttons.Power";
106 static constexpr const char* powerButtonPath =
107     "/xyz/openbmc_project/Chassis/Buttons/Power0";
108 static constexpr const char* resetButtonIntf =
109     "xyz.openbmc_project.Chassis.Buttons.Reset";
110 static constexpr const char* resetButtonPath =
111     "/xyz/openbmc_project/Chassis/Buttons/Reset0";
112 
113 typedef struct
114 {
115     uint8_t cap_flags;
116     uint8_t fru_info_dev_addr;
117     uint8_t sdr_dev_addr;
118     uint8_t sel_dev_addr;
119     uint8_t system_management_dev_addr;
120     uint8_t bridge_dev_addr;
121 } __attribute__((packed)) ipmi_chassis_cap_t;
122 
123 typedef struct
124 {
125     uint8_t cur_power_state;
126     uint8_t last_power_event;
127     uint8_t misc_power_state;
128     uint8_t front_panel_button_cap_status;
129 } __attribute__((packed)) ipmi_get_chassis_status_t;
130 
131 // Phosphor Host State manager
132 namespace State = sdbusplus::xyz::openbmc_project::State::server;
133 
134 namespace fs = std::filesystem;
135 
136 using namespace phosphor::logging;
137 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
138 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
139 
140 namespace chassis
141 {
142 namespace internal
143 {
144 
145 constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
146 constexpr auto bootSourceIntf = "xyz.openbmc_project.Control.Boot.Source";
147 constexpr auto powerRestoreIntf =
148     "xyz.openbmc_project.Control.Power.RestorePolicy";
149 sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection());
150 
151 namespace cache
152 {
153 
154 settings::Objects objects(dbus,
155                           {bootModeIntf, bootSourceIntf, powerRestoreIntf});
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 ipmi_ret_t ipmi_chassis_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
509                                  ipmi_request_t request,
510                                  ipmi_response_t response,
511                                  ipmi_data_len_t data_len,
512                                  ipmi_context_t context)
513 {
514     // Status code.
515     ipmi_ret_t rc = IPMI_CC_INVALID;
516     *data_len = 0;
517     return rc;
518 }
519 
520 ipmi_ret_t ipmi_get_chassis_cap(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
521                                 ipmi_request_t request,
522                                 ipmi_response_t response,
523                                 ipmi_data_len_t data_len,
524                                 ipmi_context_t context)
525 {
526     // sd_bus error
527     ipmi_ret_t rc = IPMI_CC_OK;
528 
529     ipmi_chassis_cap_t chassis_cap{};
530 
531     if (*data_len != 0)
532     {
533         return IPMI_CC_REQ_DATA_LEN_INVALID;
534     }
535 
536     *data_len = sizeof(ipmi_chassis_cap_t);
537 
538     try
539     {
540         sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
541 
542         ipmi::DbusObjectInfo chassisCapObject =
543             ipmi::getDbusObject(bus, chassisCapIntf);
544 
545         // capabilities flags
546         // [7..4] - reserved
547         // [3] – 1b = provides power interlock  (IPM 1.5)
548         // [2] – 1b = provides Diagnostic Interrupt (FP NMI)
549         // [1] – 1b = provides “Front Panel Lockout” (indicates that the chassis
550         // has capabilities
551         //            to lock out external power control and reset button or
552         //            front panel interfaces and/or detect tampering with those
553         //            interfaces).
554         // [0] -1b = Chassis provides intrusion (physical security) sensor.
555         // set to default value 0x0.
556         ipmi::Value variant = ipmi::getDbusProperty(
557             bus, chassisCapObject.second, chassisCapObject.first,
558             chassisCapIntf, chassisCapFlagsProp);
559         chassis_cap.cap_flags = std::get<uint8_t>(variant);
560 
561         variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
562                                         chassisCapObject.first, chassisCapIntf,
563                                         chassisFRUDevAddrProp);
564         // Chassis FRU info Device Address.
565         chassis_cap.fru_info_dev_addr = std::get<uint8_t>(variant);
566 
567         variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
568                                         chassisCapObject.first, chassisCapIntf,
569                                         chassisSDRDevAddrProp);
570         // Chassis SDR Device Address.
571         chassis_cap.sdr_dev_addr = std::get<uint8_t>(variant);
572 
573         variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
574                                         chassisCapObject.first, chassisCapIntf,
575                                         chassisSELDevAddrProp);
576         // Chassis SEL Device Address.
577         chassis_cap.sel_dev_addr = std::get<uint8_t>(variant);
578 
579         variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
580                                         chassisCapObject.first, chassisCapIntf,
581                                         chassisSMDevAddrProp);
582         // Chassis System Management Device Address.
583         chassis_cap.system_management_dev_addr = std::get<uint8_t>(variant);
584 
585         variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
586                                         chassisCapObject.first, chassisCapIntf,
587                                         chassisBridgeDevAddrProp);
588         // Chassis Bridge Device Address.
589         chassis_cap.bridge_dev_addr = std::get<uint8_t>(variant);
590         uint8_t* respP = reinterpret_cast<uint8_t*>(response);
591         uint8_t* chassisP = reinterpret_cast<uint8_t*>(&chassis_cap);
592         std::copy(chassisP, chassisP + *data_len, respP);
593     }
594     catch (std::exception& e)
595     {
596         log<level::ERR>(e.what());
597         rc = IPMI_CC_UNSPECIFIED_ERROR;
598         *data_len = 0;
599         return rc;
600     }
601 
602     return rc;
603 }
604 
605 /** @brief implements set chassis capalibities command
606  *  @param intrusion        - chassis intrusion
607  *  @param fpLockout        - frontpannel lockout
608  *  @param reserved1        - skip one bit
609  *  @param fruDeviceAddr    - chassis FRU info Device Address
610  *  @param sdrDeviceAddr    - chassis SDR device address
611  *  @param selDeviceAddr    - chassis SEL device address
612  *  @param smDeviceAddr     - chassis system management device address
613  *  @param bridgeDeviceAddr - chassis bridge device address
614  *
615  *  @returns IPMI completion code
616  */
617 ipmi::RspType<> ipmiSetChassisCap(bool intrusion, bool fpLockout,
618                                   uint6_t reserved1,
619 
620                                   uint8_t fruDeviceAddr,
621 
622                                   uint8_t sdrDeviceAddr,
623 
624                                   uint8_t selDeviceAddr,
625 
626                                   uint8_t smDeviceAddr,
627 
628                                   uint8_t bridgeDeviceAddr)
629 {
630 
631     // check input data
632     if (reserved1 != 0)
633     {
634         log<level::ERR>("Unsupported request parameter");
635         return ipmi::responseInvalidFieldRequest();
636     }
637 
638     if ((fruDeviceAddr & ~chassisCapAddrMask) != 0)
639     {
640         log<level::ERR>("Unsupported request parameter(FRU Addr)",
641                         entry("REQ=0x%x", fruDeviceAddr));
642         return ipmi::responseInvalidFieldRequest();
643     }
644     if ((sdrDeviceAddr & ~chassisCapAddrMask) != 0)
645     {
646         log<level::ERR>("Unsupported request parameter(SDR Addr)",
647                         entry("REQ=0x%x", sdrDeviceAddr));
648         return ipmi::responseInvalidFieldRequest();
649     }
650 
651     if ((selDeviceAddr & ~chassisCapAddrMask) != 0)
652     {
653         log<level::ERR>("Unsupported request parameter(SEL Addr)",
654                         entry("REQ=0x%x", selDeviceAddr));
655         return ipmi::responseInvalidFieldRequest();
656     }
657 
658     if ((smDeviceAddr & ~chassisCapAddrMask) != 0)
659     {
660         log<level::ERR>("Unsupported request parameter(SM Addr)",
661                         entry("REQ=0x%x", smDeviceAddr));
662         return ipmi::responseInvalidFieldRequest();
663     }
664 
665     if ((bridgeDeviceAddr & ~chassisCapAddrMask) != 0)
666     {
667         log<level::ERR>("Unsupported request parameter(Bridge Addr)",
668                         entry("REQ=0x%x", bridgeDeviceAddr));
669         return ipmi::responseInvalidFieldRequest();
670     }
671 
672     uint8_t capFlags = (static_cast<uint8_t>(intrusion)) |
673                        ((static_cast<uint8_t>(fpLockout)) << 1);
674     try
675     {
676         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
677         ipmi::DbusObjectInfo chassisCapObject =
678             ipmi::getDbusObject(bus, chassisCapIntf);
679 
680         ipmi::setDbusProperty(bus, chassisCapObject.second,
681                               chassisCapObject.first, chassisCapIntf,
682                               chassisCapFlagsProp, capFlags);
683 
684         ipmi::setDbusProperty(bus, chassisCapObject.second,
685                               chassisCapObject.first, chassisCapIntf,
686                               chassisFRUDevAddrProp, fruDeviceAddr);
687 
688         ipmi::setDbusProperty(bus, chassisCapObject.second,
689                               chassisCapObject.first, chassisCapIntf,
690                               chassisSDRDevAddrProp, sdrDeviceAddr);
691 
692         ipmi::setDbusProperty(bus, chassisCapObject.second,
693                               chassisCapObject.first, chassisCapIntf,
694                               chassisSELDevAddrProp, selDeviceAddr);
695 
696         ipmi::setDbusProperty(bus, chassisCapObject.second,
697                               chassisCapObject.first, chassisCapIntf,
698                               chassisSMDevAddrProp, smDeviceAddr);
699 
700         ipmi::setDbusProperty(bus, chassisCapObject.second,
701                               chassisCapObject.first, chassisCapIntf,
702                               chassisBridgeDevAddrProp, bridgeDeviceAddr);
703     }
704     catch (std::exception& e)
705     {
706         log<level::ERR>(e.what());
707         return ipmi::responseUnspecifiedError();
708     }
709     return ipmi::responseSuccess();
710 }
711 
712 //------------------------------------------
713 // Calls into Host State Manager Dbus object
714 //------------------------------------------
715 int initiate_state_transition(State::Host::Transition transition)
716 {
717     // OpenBMC Host State Manager dbus framework
718     constexpr auto HOST_STATE_MANAGER_ROOT = "/xyz/openbmc_project/state/host0";
719     constexpr auto HOST_STATE_MANAGER_IFACE = "xyz.openbmc_project.State.Host";
720     constexpr auto DBUS_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
721     constexpr auto PROPERTY = "RequestedHostTransition";
722 
723     // sd_bus error
724     int rc = 0;
725     char* busname = NULL;
726 
727     // SD Bus error report mechanism.
728     sd_bus_error bus_error = SD_BUS_ERROR_NULL;
729 
730     // Gets a hook onto either a SYSTEM or SESSION bus
731     sd_bus* bus_type = ipmid_get_sd_bus_connection();
732     rc = mapper_get_service(bus_type, HOST_STATE_MANAGER_ROOT, &busname);
733     if (rc < 0)
734     {
735         log<level::ERR>(
736             "Failed to get bus name",
737             entry("ERRNO=0x%X, OBJPATH=%s", -rc, HOST_STATE_MANAGER_ROOT));
738         return rc;
739     }
740 
741     // Convert to string equivalent of the passed in transition enum.
742     auto request = State::convertForMessage(transition);
743 
744     rc = sd_bus_call_method(bus_type,                // On the system bus
745                             busname,                 // Service to contact
746                             HOST_STATE_MANAGER_ROOT, // Object path
747                             DBUS_PROPERTY_IFACE,     // Interface name
748                             "Set",                   // Method to be called
749                             &bus_error,              // object to return error
750                             nullptr,                 // Response buffer if any
751                             "ssv",                   // Takes 3 arguments
752                             HOST_STATE_MANAGER_IFACE, PROPERTY, "s",
753                             request.c_str());
754     if (rc < 0)
755     {
756         log<level::ERR>("Failed to initiate transition",
757                         entry("ERRNO=0x%X, REQUEST=%s", -rc, request.c_str()));
758     }
759     else
760     {
761         log<level::INFO>("Transition request initiated successfully");
762     }
763 
764     sd_bus_error_free(&bus_error);
765     free(busname);
766 
767     return rc;
768 }
769 
770 namespace power_policy
771 {
772 
773 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
774 using IpmiValue = uint8_t;
775 using DbusValue = RestorePolicy::Policy;
776 
777 const std::map<DbusValue, IpmiValue> dbusToIpmi = {
778     {RestorePolicy::Policy::AlwaysOff, 0x00},
779     {RestorePolicy::Policy::Restore, 0x01},
780     {RestorePolicy::Policy::AlwaysOn, 0x02}};
781 
782 static constexpr uint8_t noChange = 0x03;
783 static constexpr uint8_t allSupport = 0x01 | 0x02 | 0x04;
784 
785 /* helper function for Get Chassis Status Command
786  */
787 std::optional<uint2_t> getPowerRestorePolicy()
788 {
789     uint2_t restorePolicy = 0;
790     using namespace chassis::internal;
791 
792     try
793     {
794         const auto& powerRestoreSetting =
795             cache::objects.map.at(powerRestoreIntf).front();
796         ipmi::Value result = ipmi::getDbusProperty(
797             *getSdBus(),
798             cache::objects.service(powerRestoreSetting, powerRestoreIntf)
799                 .c_str(),
800             powerRestoreSetting.c_str(), powerRestoreIntf,
801             "PowerRestorePolicy");
802         auto powerRestore = RestorePolicy::convertPolicyFromString(
803             std::get<std::string>(result));
804         restorePolicy = dbusToIpmi.at(powerRestore);
805     }
806     catch (const std::exception& e)
807     {
808         log<level::ERR>(
809             "Failed to fetch pgood property", entry("ERROR=%s", e.what()),
810             entry("PATH=%s",
811                   cache::objects.map.at(powerRestoreIntf).front().c_str()),
812             entry("INTERFACE=%s", powerRestoreIntf));
813         return std::nullopt;
814     }
815     return std::make_optional(restorePolicy);
816 }
817 
818 /*
819  * getPowerStatus
820  * helper function for Get Chassis Status Command
821  * return - optional value for pgood (no value on error)
822  */
823 std::optional<bool> getPowerStatus()
824 {
825     bool powerGood = false;
826     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
827     try
828     {
829         constexpr const char* chassisStatePath =
830             "/xyz/openbmc_project/state/chassis0";
831         constexpr const char* chassisStateIntf =
832             "xyz.openbmc_project.State.Chassis";
833         auto service =
834             ipmi::getService(*busp, chassisStateIntf, chassisStatePath);
835 
836         ipmi::Value powerState =
837             ipmi::getDbusProperty(*busp, service, chassisStatePath,
838                                   chassisStateIntf, "CurrentPowerState");
839         powerGood = std::get<std::string>(powerState) ==
840                     "xyz.openbmc_project.State.Chassis.PowerState.On";
841     }
842     catch (const std::exception& e)
843     {
844         try
845         {
846             // FIXME: some legacy modules use the older path; try that next
847             constexpr const char* legacyPwrCtrlObj =
848                 "/org/openbmc/control/power0";
849             constexpr const char* legacyPwrCtrlIntf =
850                 "org.openbmc.control.Power";
851             auto service =
852                 ipmi::getService(*busp, legacyPwrCtrlIntf, legacyPwrCtrlObj);
853 
854             ipmi::Value variant = ipmi::getDbusProperty(
855                 *busp, service, legacyPwrCtrlObj, legacyPwrCtrlIntf, "pgood");
856             powerGood = static_cast<bool>(std::get<int>(variant));
857         }
858         catch (const std::exception& e)
859         {
860             log<level::ERR>("Failed to fetch pgood property",
861                             entry("ERROR=%s", e.what()));
862             return std::nullopt;
863         }
864     }
865     return std::make_optional(powerGood);
866 }
867 
868 /*
869  * getACFailStatus
870  * helper function for Get Chassis Status Command
871  * return - bool value for ACFail (false on error)
872  */
873 bool getACFailStatus()
874 {
875     constexpr const char* powerControlObj =
876         "/xyz/openbmc_project/Chassis/Control/Power0";
877     constexpr const char* powerControlIntf =
878         "xyz.openbmc_project.Chassis.Control.Power";
879     bool acFail = false;
880     std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
881     try
882     {
883         auto service =
884             ipmi::getService(*bus, powerControlIntf, powerControlObj);
885 
886         ipmi::Value variant = ipmi::getDbusProperty(
887             *bus, service, powerControlObj, powerControlIntf, "PFail");
888         acFail = std::get<bool>(variant);
889     }
890     catch (const std::exception& e)
891     {
892         log<level::ERR>("Failed to fetch PFail property",
893                         entry("ERROR=%s", e.what()),
894                         entry("PATH=%s", powerControlObj),
895                         entry("INTERFACE=%s", powerControlIntf));
896     }
897     return acFail;
898 }
899 } // namespace power_policy
900 
901 static std::optional<bool> getButtonEnabled(const std::string& buttonPath,
902                                             const std::string& buttonIntf)
903 {
904     std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
905     bool buttonDisabled = false;
906     try
907     {
908         auto service = ipmi::getService(*busp, buttonIntf, buttonPath);
909         ipmi::Value enabled = ipmi::getDbusProperty(*busp, service, buttonPath,
910                                                     buttonIntf, "Enabled");
911         buttonDisabled = !std::get<bool>(enabled);
912     }
913     catch (sdbusplus::exception::SdBusError& e)
914     {
915         log<level::ERR>("Fail to get button Enabled property",
916                         entry("PATH=%s", buttonPath.c_str()),
917                         entry("ERROR=%s", e.what()));
918         return std::nullopt;
919     }
920     return std::make_optional(buttonDisabled);
921 }
922 
923 //----------------------------------------------------------------------
924 // Get Chassis Status commands
925 //----------------------------------------------------------------------
926 ipmi::RspType<bool,    // Power is on
927               bool,    // Power overload
928               bool,    // Interlock
929               bool,    // power fault
930               bool,    // power control fault
931               uint2_t, // power restore policy
932               bool,    // reserved
933 
934               bool, // AC failed
935               bool, // last power down caused by a Power overload
936               bool, // last power down caused by a power interlock
937               bool, // last power down caused by power fault
938               bool, // last ‘Power is on’ state was entered via IPMI command
939               uint3_t, // reserved
940 
941               bool,    // Chassis intrusion active
942               bool,    // Front Panel Lockout active
943               bool,    // Drive Fault
944               bool,    // Cooling/fan fault detected
945               uint2_t, // Chassis Identify State
946               bool,    // Chassis Identify command and state info supported
947               bool,    // reserved
948 
949               bool, // Power off button disabled
950               bool, // Reset button disabled
951               bool, // Diagnostic Interrupt button disabled
952               bool, // Standby (sleep) button disabled
953               bool, // Power off button disable allowed
954               bool, // Reset button disable allowed
955               bool, // Diagnostic Interrupt button disable allowed
956               bool  // Standby (sleep) button disable allowed
957               >
958     ipmiGetChassisStatus()
959 {
960     using namespace chassis::internal;
961     std::optional<uint2_t> restorePolicy =
962         power_policy::getPowerRestorePolicy();
963     std::optional<bool> powerGood = power_policy::getPowerStatus();
964     if (!restorePolicy || !powerGood)
965     {
966         return ipmi::responseUnspecifiedError();
967     }
968 
969     //  Front Panel Button Capabilities and disable/enable status(Optional)
970     std::optional<bool> powerButtonReading =
971         getButtonEnabled(powerButtonPath, powerButtonIntf);
972     // allow disable if the interface is present
973     bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading);
974     // default return the button is enabled (not disabled)
975     bool powerButtonDisabled = false;
976     if (powerButtonDisableAllow)
977     {
978         // return the real value of the button status, if present
979         powerButtonDisabled = *powerButtonReading;
980     }
981 
982     std::optional<bool> resetButtonReading =
983         getButtonEnabled(resetButtonPath, resetButtonIntf);
984     // allow disable if the interface is present
985     bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading);
986     // default return the button is enabled (not disabled)
987     bool resetButtonDisabled = false;
988     if (resetButtonDisableAllow)
989     {
990         // return the real value of the button status, if present
991         resetButtonDisabled = *resetButtonReading;
992     }
993 
994     bool powerDownAcFailed = power_policy::getACFailStatus();
995 
996     // This response has a lot of hard-coded, unsupported fields
997     // They are set to false or 0
998     constexpr bool powerOverload = false;
999     constexpr bool chassisInterlock = false;
1000     constexpr bool powerFault = false;
1001     constexpr bool powerControlFault = false;
1002     constexpr bool powerDownOverload = false;
1003     constexpr bool powerDownInterlock = false;
1004     constexpr bool powerDownPowerFault = false;
1005     constexpr bool powerStatusIPMI = false;
1006     constexpr bool chassisIntrusionActive = false;
1007     constexpr bool frontPanelLockoutActive = false;
1008     constexpr bool driveFault = false;
1009     constexpr bool coolingFanFault = false;
1010     // chassisIdentifySupport set because this command is implemented
1011     constexpr bool chassisIdentifySupport = true;
1012     uint2_t chassisIdentifyState = static_cast<uint2_t>(chassisIDState);
1013     constexpr bool diagButtonDisabled = false;
1014     constexpr bool sleepButtonDisabled = false;
1015     constexpr bool diagButtonDisableAllow = false;
1016     constexpr bool sleepButtonDisableAllow = false;
1017 
1018     return ipmi::responseSuccess(
1019         *powerGood, powerOverload, chassisInterlock, powerFault,
1020         powerControlFault, *restorePolicy,
1021         false, // reserved
1022 
1023         powerDownAcFailed, powerDownOverload, powerDownInterlock,
1024         powerDownPowerFault, powerStatusIPMI,
1025         uint3_t(0), // reserved
1026 
1027         chassisIntrusionActive, frontPanelLockoutActive, driveFault,
1028         coolingFanFault, chassisIdentifyState, chassisIdentifySupport,
1029         false, // reserved
1030 
1031         powerButtonDisabled, resetButtonDisabled, diagButtonDisabled,
1032         sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow,
1033         diagButtonDisableAllow, sleepButtonDisableAllow);
1034 }
1035 
1036 //-------------------------------------------------------------
1037 // Send a command to SoftPowerOff application to stop any timer
1038 //-------------------------------------------------------------
1039 int stop_soft_off_timer()
1040 {
1041     constexpr auto iface = "org.freedesktop.DBus.Properties";
1042     constexpr auto soft_off_iface = "xyz.openbmc_project.Ipmi.Internal."
1043                                     "SoftPowerOff";
1044 
1045     constexpr auto property = "ResponseReceived";
1046     constexpr auto value = "xyz.openbmc_project.Ipmi.Internal."
1047                            "SoftPowerOff.HostResponse.HostShutdown";
1048 
1049     // Get the system bus where most system services are provided.
1050     auto bus = ipmid_get_sd_bus_connection();
1051 
1052     // Get the service name
1053     // TODO openbmc/openbmc#1661 - Mapper refactor
1054     //
1055     // See openbmc/openbmc#1743 for some details but high level summary is that
1056     // for now the code will directly call the soft off interface due to a
1057     // race condition with mapper usage
1058     //
1059     // char *busname = nullptr;
1060     // auto r = mapper_get_service(bus, SOFTOFF_OBJPATH, &busname);
1061     // if (r < 0)
1062     //{
1063     //    fprintf(stderr, "Failed to get %s bus name: %s\n",
1064     //            SOFTOFF_OBJPATH, -r);
1065     //    return r;
1066     //}
1067 
1068     // No error object or reply expected.
1069     int rc = sd_bus_call_method(bus, SOFTOFF_BUSNAME, SOFTOFF_OBJPATH, iface,
1070                                 "Set", nullptr, nullptr, "ssv", soft_off_iface,
1071                                 property, "s", value);
1072     if (rc < 0)
1073     {
1074         log<level::ERR>("Failed to set property in SoftPowerOff object",
1075                         entry("ERRNO=0x%X", -rc));
1076     }
1077 
1078     // TODO openbmc/openbmc#1661 - Mapper refactor
1079     // free(busname);
1080     return rc;
1081 }
1082 
1083 //----------------------------------------------------------------------
1084 // Create file to indicate there is no need for softoff notification to host
1085 //----------------------------------------------------------------------
1086 void indicate_no_softoff_needed()
1087 {
1088     fs::path path{HOST_INBAND_REQUEST_DIR};
1089     if (!fs::is_directory(path))
1090     {
1091         fs::create_directory(path);
1092     }
1093 
1094     // Add the host instance (default 0 for now) to the file name
1095     std::string file{HOST_INBAND_REQUEST_FILE};
1096     auto size = std::snprintf(nullptr, 0, file.c_str(), 0);
1097     size++; // null
1098     std::unique_ptr<char[]> buf(new char[size]);
1099     std::snprintf(buf.get(), size, file.c_str(), 0);
1100 
1101     // Append file name to directory and create it
1102     path /= buf.get();
1103     std::ofstream(path.c_str());
1104 }
1105 
1106 /** @brief Implementation of chassis control command
1107  *
1108  *  @param - chassisControl command byte
1109  *
1110  *  @return  Success or InvalidFieldRequest.
1111  */
1112 ipmi::RspType<> ipmiChassisControl(uint8_t chassisControl)
1113 {
1114     int rc = 0;
1115     switch (chassisControl)
1116     {
1117         case CMD_POWER_ON:
1118             rc = initiate_state_transition(State::Host::Transition::On);
1119             break;
1120         case CMD_POWER_OFF:
1121             // This path would be hit in 2 conditions.
1122             // 1: When user asks for power off using ipmi chassis command 0x04
1123             // 2: Host asking for power off post shutting down.
1124 
1125             // If it's a host requested power off, then need to nudge Softoff
1126             // application that it needs to stop the watchdog timer if running.
1127             // If it is a user requested power off, then this is not really
1128             // needed. But then we need to differentiate between user and host
1129             // calling this same command
1130 
1131             // For now, we are going ahead with trying to nudge the soft off and
1132             // interpret the failure to do so as a non softoff case
1133             rc = stop_soft_off_timer();
1134 
1135             // Only request the Off transition if the soft power off
1136             // application is not running
1137             if (rc < 0)
1138             {
1139                 // First create a file to indicate to the soft off application
1140                 // that it should not run. Not doing this will result in State
1141                 // manager doing a default soft power off when asked for power
1142                 // off.
1143                 indicate_no_softoff_needed();
1144 
1145                 // Now request the shutdown
1146                 rc = initiate_state_transition(State::Host::Transition::Off);
1147             }
1148             else
1149             {
1150                 log<level::INFO>("Soft off is running, so let shutdown target "
1151                                  "stop the host");
1152             }
1153             break;
1154 
1155         case CMD_HARD_RESET:
1156         case CMD_POWER_CYCLE:
1157             // SPEC has a section that says certain implementations can trigger
1158             // PowerOn if power is Off when a command to power cycle is
1159             // requested
1160 
1161             // First create a file to indicate to the soft off application
1162             // that it should not run since this is a direct user initiated
1163             // power reboot request (i.e. a reboot request that is not
1164             // originating via a soft power off SMS request)
1165             indicate_no_softoff_needed();
1166 
1167             rc = initiate_state_transition(State::Host::Transition::Reboot);
1168             break;
1169 
1170         case CMD_SOFT_OFF_VIA_OVER_TEMP:
1171             // Request Host State Manager to do a soft power off
1172             rc = initiate_state_transition(State::Host::Transition::Off);
1173             break;
1174 
1175         default:
1176         {
1177             log<level::ERR>("Invalid Chassis Control command",
1178                             entry("CMD=0x%X", chassisControl));
1179             return ipmi::responseInvalidFieldRequest();
1180         }
1181     }
1182 
1183     return ((rc < 0) ? ipmi::responseUnspecifiedError()
1184                      : ipmi::responseSuccess());
1185 }
1186 
1187 /** @brief Return D-Bus connection string to enclosure identify LED object
1188  *
1189  *  @param[in, out] connection - connection to D-Bus object
1190  *  @return a IPMI return code
1191  */
1192 std::string getEnclosureIdentifyConnection()
1193 {
1194     // lookup enclosure_identify group owner(s) in mapper
1195     auto mapperCall = chassis::internal::dbus.new_method_call(
1196         ipmi::MAPPER_BUS_NAME, ipmi::MAPPER_OBJ, ipmi::MAPPER_INTF,
1197         "GetObject");
1198 
1199     mapperCall.append(identify_led_object_name);
1200     static const std::vector<std::string> interfaces = {
1201         "xyz.openbmc_project.Led.Group"};
1202     mapperCall.append(interfaces);
1203     auto mapperReply = chassis::internal::dbus.call(mapperCall);
1204     if (mapperReply.is_method_error())
1205     {
1206         log<level::ERR>("Chassis Identify: Error communicating to mapper.");
1207         elog<InternalFailure>();
1208     }
1209     std::vector<std::pair<std::string, std::vector<std::string>>> mapperResp;
1210     mapperReply.read(mapperResp);
1211 
1212     if (mapperResp.size() != encIdentifyObjectsSize)
1213     {
1214         log<level::ERR>(
1215             "Invalid number of enclosure identify objects.",
1216             entry("ENC_IDENTITY_OBJECTS_SIZE=%d", mapperResp.size()));
1217         elog<InternalFailure>();
1218     }
1219     auto pair = mapperResp[encIdentifyObjectsSize - 1];
1220     return pair.first;
1221 }
1222 
1223 /** @brief Turn On/Off enclosure identify LED
1224  *
1225  *  @param[in] flag - true to turn on LED, false to turn off
1226  *  @return a IPMI return code
1227  */
1228 void enclosureIdentifyLed(bool flag)
1229 {
1230     using namespace chassis::internal;
1231     std::string connection = std::move(getEnclosureIdentifyConnection());
1232     auto msg = std::string("enclosureIdentifyLed(") +
1233                boost::lexical_cast<std::string>(flag) + ")";
1234     log<level::DEBUG>(msg.c_str());
1235     auto led =
1236         dbus.new_method_call(connection.c_str(), identify_led_object_name,
1237                              "org.freedesktop.DBus.Properties", "Set");
1238     led.append("xyz.openbmc_project.Led.Group", "Asserted",
1239                std::variant<bool>(flag));
1240     auto ledReply = dbus.call(led);
1241     if (ledReply.is_method_error())
1242     {
1243         log<level::ERR>("Chassis Identify: Error Setting State On/Off\n",
1244                         entry("LED_STATE=%d", flag));
1245         elog<InternalFailure>();
1246     }
1247 }
1248 
1249 /** @brief Callback method to turn off LED
1250  */
1251 void enclosureIdentifyLedOff()
1252 {
1253     try
1254     {
1255         chassisIDState = ChassisIDState::off;
1256         enclosureIdentifyLed(false);
1257     }
1258     catch (const InternalFailure& e)
1259     {
1260         report<InternalFailure>();
1261     }
1262 }
1263 
1264 /** @brief Create timer to turn on and off the enclosure LED
1265  */
1266 void createIdentifyTimer()
1267 {
1268     if (!identifyTimer)
1269     {
1270         identifyTimer =
1271             std::make_unique<phosphor::Timer>(enclosureIdentifyLedOff);
1272     }
1273 }
1274 
1275 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
1276                                     std::optional<uint8_t> force)
1277 {
1278     uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT);
1279     bool forceIdentify = force.value_or(0) & 0x01;
1280 
1281     if (identifyInterval || forceIdentify)
1282     {
1283         // stop the timer if already started;
1284         // for force identify we should not turn off LED
1285         identifyTimer->stop();
1286         try
1287         {
1288             chassisIDState = ChassisIDState::temporaryOn;
1289             enclosureIdentifyLed(true);
1290         }
1291         catch (const InternalFailure& e)
1292         {
1293             report<InternalFailure>();
1294             return ipmi::responseResponseError();
1295         }
1296 
1297         if (forceIdentify)
1298         {
1299             chassisIDState = ChassisIDState::indefiniteOn;
1300             return ipmi::responseSuccess();
1301         }
1302         // start the timer
1303         auto time = std::chrono::duration_cast<std::chrono::microseconds>(
1304             std::chrono::seconds(identifyInterval));
1305         identifyTimer->start(time);
1306     }
1307     else if (!identifyInterval)
1308     {
1309         identifyTimer->stop();
1310         enclosureIdentifyLedOff();
1311     }
1312     return ipmi::responseSuccess();
1313 }
1314 
1315 namespace boot_options
1316 {
1317 
1318 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
1319 using IpmiValue = uint8_t;
1320 constexpr auto ipmiDefault = 0;
1321 
1322 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
1323     {0x01, Source::Sources::Network},
1324     {0x02, Source::Sources::Disk},
1325     {0x05, Source::Sources::ExternalMedia},
1326     {0x0f, Source::Sources::RemovableMedia},
1327     {ipmiDefault, Source::Sources::Default}};
1328 
1329 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
1330     {0x03, Mode::Modes::Safe},
1331     {0x06, Mode::Modes::Setup},
1332     {ipmiDefault, Mode::Modes::Regular}};
1333 
1334 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
1335     {Source::Sources::Network, 0x01},
1336     {Source::Sources::Disk, 0x02},
1337     {Source::Sources::ExternalMedia, 0x05},
1338     {Source::Sources::RemovableMedia, 0x0f},
1339     {Source::Sources::Default, ipmiDefault}};
1340 
1341 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
1342     {Mode::Modes::Safe, 0x03},
1343     {Mode::Modes::Setup, 0x06},
1344     {Mode::Modes::Regular, ipmiDefault}};
1345 
1346 } // namespace boot_options
1347 
1348 /** @brief Set the property value for boot source
1349  *  @param[in] source - boot source value
1350  *  @return On failure return IPMI error.
1351  */
1352 static ipmi_ret_t setBootSource(const Source::Sources& source)
1353 {
1354     using namespace chassis::internal;
1355     using namespace chassis::internal::cache;
1356     std::variant<std::string> property = convertForMessage(source);
1357     auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1358     const auto& bootSourceSetting = std::get<settings::Path>(bootSetting);
1359     auto method = dbus.new_method_call(
1360         objects.service(bootSourceSetting, bootSourceIntf).c_str(),
1361         bootSourceSetting.c_str(), ipmi::PROP_INTF, "Set");
1362     method.append(bootSourceIntf, "BootSource", property);
1363     auto reply = dbus.call(method);
1364     if (reply.is_method_error())
1365     {
1366         log<level::ERR>("Error in BootSource Set");
1367         report<InternalFailure>();
1368         return IPMI_CC_UNSPECIFIED_ERROR;
1369     }
1370     return IPMI_CC_OK;
1371 }
1372 
1373 /** @brief Set the property value for boot mode
1374  *  @param[in] mode - boot mode value
1375  *  @return On failure return IPMI error.
1376  */
1377 static ipmi_ret_t setBootMode(const Mode::Modes& mode)
1378 {
1379     using namespace chassis::internal;
1380     using namespace chassis::internal::cache;
1381     std::variant<std::string> property = convertForMessage(mode);
1382     auto bootSetting = settings::boot::setting(objects, bootModeIntf);
1383     const auto& bootModeSetting = std::get<settings::Path>(bootSetting);
1384     auto method = dbus.new_method_call(
1385         objects.service(bootModeSetting, bootModeIntf).c_str(),
1386         bootModeSetting.c_str(), ipmi::PROP_INTF, "Set");
1387     method.append(bootModeIntf, "BootMode", property);
1388     auto reply = dbus.call(method);
1389     if (reply.is_method_error())
1390     {
1391         log<level::ERR>("Error in BootMode Set");
1392         report<InternalFailure>();
1393         return IPMI_CC_UNSPECIFIED_ERROR;
1394     }
1395     return IPMI_CC_OK;
1396 }
1397 
1398 ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1399                                              ipmi_request_t request,
1400                                              ipmi_response_t response,
1401                                              ipmi_data_len_t data_len,
1402                                              ipmi_context_t context)
1403 {
1404     using namespace boot_options;
1405     ipmi_ret_t rc = IPMI_CC_PARM_NOT_SUPPORTED;
1406     char* p = NULL;
1407     get_sys_boot_options_response_t* resp =
1408         (get_sys_boot_options_response_t*)response;
1409     get_sys_boot_options_t* reqptr = (get_sys_boot_options_t*)request;
1410     IpmiValue bootOption = ipmiDefault;
1411 
1412     std::memset(resp, 0, sizeof(*resp));
1413     resp->version = SET_PARM_VERSION;
1414     resp->parm = 5;
1415     resp->data[0] = SET_PARM_BOOT_FLAGS_VALID_ONE_TIME;
1416 
1417     /*
1418      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1419      * This is the only parameter used by petitboot.
1420      */
1421     if (reqptr->parameter ==
1422         static_cast<uint8_t>(BootOptionParameter::BOOT_FLAGS))
1423     {
1424 
1425         *data_len = static_cast<uint8_t>(BootOptionResponseSize::BOOT_FLAGS);
1426         using namespace chassis::internal;
1427         using namespace chassis::internal::cache;
1428 
1429         try
1430         {
1431             auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1432             const auto& bootSourceSetting =
1433                 std::get<settings::Path>(bootSetting);
1434             auto oneTimeEnabled =
1435                 std::get<settings::boot::OneTimeEnabled>(bootSetting);
1436             auto method = dbus.new_method_call(
1437                 objects.service(bootSourceSetting, bootSourceIntf).c_str(),
1438                 bootSourceSetting.c_str(), ipmi::PROP_INTF, "Get");
1439             method.append(bootSourceIntf, "BootSource");
1440             auto reply = dbus.call(method);
1441             if (reply.is_method_error())
1442             {
1443                 log<level::ERR>("Error in BootSource Get");
1444                 report<InternalFailure>();
1445                 *data_len = 0;
1446                 return IPMI_CC_UNSPECIFIED_ERROR;
1447             }
1448             std::variant<std::string> result;
1449             reply.read(result);
1450             auto bootSource =
1451                 Source::convertSourcesFromString(std::get<std::string>(result));
1452 
1453             bootSetting = settings::boot::setting(objects, bootModeIntf);
1454             const auto& bootModeSetting = std::get<settings::Path>(bootSetting);
1455             method = dbus.new_method_call(
1456                 objects.service(bootModeSetting, bootModeIntf).c_str(),
1457                 bootModeSetting.c_str(), ipmi::PROP_INTF, "Get");
1458             method.append(bootModeIntf, "BootMode");
1459             reply = dbus.call(method);
1460             if (reply.is_method_error())
1461             {
1462                 log<level::ERR>("Error in BootMode Get");
1463                 report<InternalFailure>();
1464                 *data_len = 0;
1465                 return IPMI_CC_UNSPECIFIED_ERROR;
1466             }
1467             reply.read(result);
1468             auto bootMode =
1469                 Mode::convertModesFromString(std::get<std::string>(result));
1470 
1471             bootOption = sourceDbusToIpmi.at(bootSource);
1472             if ((Mode::Modes::Regular == bootMode) &&
1473                 (Source::Sources::Default == bootSource))
1474             {
1475                 bootOption = ipmiDefault;
1476             }
1477             else if (Source::Sources::Default == bootSource)
1478             {
1479                 bootOption = modeDbusToIpmi.at(bootMode);
1480             }
1481             resp->data[1] = (bootOption << 2);
1482 
1483             resp->data[0] = oneTimeEnabled
1484                                 ? SET_PARM_BOOT_FLAGS_VALID_ONE_TIME
1485                                 : SET_PARM_BOOT_FLAGS_VALID_PERMANENT;
1486 
1487             rc = IPMI_CC_OK;
1488         }
1489         catch (InternalFailure& e)
1490         {
1491             report<InternalFailure>();
1492             *data_len = 0;
1493             return IPMI_CC_UNSPECIFIED_ERROR;
1494         }
1495     }
1496     else if (reqptr->parameter ==
1497              static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS))
1498     {
1499 
1500         *data_len =
1501             static_cast<uint8_t>(BootOptionResponseSize::OPAL_NETWORK_SETTINGS);
1502 
1503         resp->parm =
1504             static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS);
1505 
1506         int ret = getHostNetworkData(resp);
1507 
1508         if (ret < 0)
1509         {
1510 
1511             log<level::ERR>(
1512                 "getHostNetworkData failed for get_sys_boot_options.");
1513             rc = IPMI_CC_UNSPECIFIED_ERROR;
1514         }
1515         else
1516             rc = IPMI_CC_OK;
1517     }
1518 
1519     else
1520     {
1521         log<level::ERR>("Unsupported parameter",
1522                         entry("PARAM=0x%x", reqptr->parameter));
1523     }
1524 
1525     if (p)
1526         free(p);
1527 
1528     if (rc == IPMI_CC_OK)
1529     {
1530         *data_len += 2;
1531     }
1532 
1533     return rc;
1534 }
1535 
1536 ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1537                                              ipmi_request_t request,
1538                                              ipmi_response_t response,
1539                                              ipmi_data_len_t data_len,
1540                                              ipmi_context_t context)
1541 {
1542     using namespace boot_options;
1543     ipmi_ret_t rc = IPMI_CC_OK;
1544     set_sys_boot_options_t* reqptr = (set_sys_boot_options_t*)request;
1545 
1546     std::printf("IPMI SET_SYS_BOOT_OPTIONS reqptr->parameter =[%d]\n",
1547                 reqptr->parameter);
1548 
1549     // This IPMI command does not have any resposne data
1550     *data_len = 0;
1551 
1552     /*  000101
1553      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1554      * This is the only parameter used by petitboot.
1555      */
1556 
1557     if (reqptr->parameter == (uint8_t)BootOptionParameter::BOOT_FLAGS)
1558     {
1559         IpmiValue bootOption = ((reqptr->data[1] & 0x3C) >> 2);
1560         using namespace chassis::internal;
1561         using namespace chassis::internal::cache;
1562         auto oneTimeEnabled = false;
1563         constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable";
1564         constexpr auto oneTimePath =
1565             "/xyz/openbmc_project/control/host0/boot/one_time";
1566 
1567         try
1568         {
1569             bool permanent =
1570                 (reqptr->data[0] & SET_PARM_BOOT_FLAGS_PERMANENT) ==
1571                 SET_PARM_BOOT_FLAGS_PERMANENT;
1572 
1573             auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1574 
1575             oneTimeEnabled =
1576                 std::get<settings::boot::OneTimeEnabled>(bootSetting);
1577 
1578             /*
1579              * Check if the current boot setting is onetime or permanent, if the
1580              * request in the command is otherwise, then set the "Enabled"
1581              * property in one_time object path to 'True' to indicate onetime
1582              * and 'False' to indicate permanent.
1583              *
1584              * Once the onetime/permanent setting is applied, then the bootMode
1585              * and bootSource is updated for the corresponding object.
1586              */
1587             if ((permanent && oneTimeEnabled) ||
1588                 (!permanent && !oneTimeEnabled))
1589             {
1590                 auto service = ipmi::getService(dbus, enabledIntf, oneTimePath);
1591 
1592                 ipmi::setDbusProperty(dbus, service, oneTimePath, enabledIntf,
1593                                       "Enabled", !permanent);
1594             }
1595 
1596             auto modeItr = modeIpmiToDbus.find(bootOption);
1597             auto sourceItr = sourceIpmiToDbus.find(bootOption);
1598             if (sourceIpmiToDbus.end() != sourceItr)
1599             {
1600                 rc = setBootSource(sourceItr->second);
1601                 if (rc != IPMI_CC_OK)
1602                 {
1603                     *data_len = 0;
1604                     return rc;
1605                 }
1606                 // If a set boot device is mapping to a boot source, then reset
1607                 // the boot mode D-Bus property to default.
1608                 // This way the ipmid code can determine which property is not
1609                 // at the default value
1610                 if (sourceItr->second != Source::Sources::Default)
1611                 {
1612                     setBootMode(Mode::Modes::Regular);
1613                 }
1614             }
1615             if (modeIpmiToDbus.end() != modeItr)
1616             {
1617                 rc = setBootMode(modeItr->second);
1618                 if (rc != IPMI_CC_OK)
1619                 {
1620                     *data_len = 0;
1621                     return rc;
1622                 }
1623                 // If a set boot device is mapping to a boot mode, then reset
1624                 // the boot source D-Bus property to default.
1625                 // This way the ipmid code can determine which property is not
1626                 // at the default value
1627                 if (modeItr->second != Mode::Modes::Regular)
1628                 {
1629                     setBootSource(Source::Sources::Default);
1630                 }
1631             }
1632             if ((modeIpmiToDbus.end() == modeItr) &&
1633                 (sourceIpmiToDbus.end() == sourceItr))
1634             {
1635                 // return error if boot option is not supported
1636                 *data_len = 0;
1637                 return IPMI_CC_INVALID_FIELD_REQUEST;
1638             }
1639         }
1640         catch (InternalFailure& e)
1641         {
1642             report<InternalFailure>();
1643             *data_len = 0;
1644             return IPMI_CC_UNSPECIFIED_ERROR;
1645         }
1646     }
1647     else if (reqptr->parameter ==
1648              (uint8_t)BootOptionParameter::OPAL_NETWORK_SETTINGS)
1649     {
1650 
1651         int ret = setHostNetworkData(reqptr);
1652         if (ret < 0)
1653         {
1654             log<level::ERR>(
1655                 "setHostNetworkData failed for set_sys_boot_options");
1656             rc = IPMI_CC_UNSPECIFIED_ERROR;
1657         }
1658     }
1659     else if (reqptr->parameter ==
1660              static_cast<uint8_t>(BootOptionParameter::BOOT_INFO))
1661     {
1662         // Handle parameter #4 and return command completed normally
1663         // (IPMI_CC_OK). There is no implementation in OpenBMC for this
1664         // parameter. This is added to support the ipmitool command `chassis
1665         // bootdev` which sends set on parameter #4, before setting the boot
1666         // flags.
1667         rc = IPMI_CC_OK;
1668     }
1669     else
1670     {
1671         log<level::ERR>("Unsupported parameter",
1672                         entry("PARAM=0x%x", reqptr->parameter));
1673         rc = IPMI_CC_PARM_NOT_SUPPORTED;
1674     }
1675 
1676     return rc;
1677 }
1678 
1679 /** @brief implements Get POH counter command
1680  *  @parameter
1681  *   -  none
1682  *  @returns IPMI completion code plus response data
1683  *   - minPerCount - Minutes per count
1684  *   - counterReading - counter reading
1685  */
1686 ipmi::RspType<uint8_t, // Minutes per count
1687               uint32_t // Counter reading
1688               >
1689     ipmiGetPOHCounter()
1690 {
1691     // sd_bus error
1692     try
1693     {
1694         return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount),
1695                                      getPOHCounter());
1696     }
1697     catch (std::exception& e)
1698     {
1699         log<level::ERR>(e.what());
1700         return ipmi::responseUnspecifiedError();
1701     }
1702 }
1703 
1704 ipmi::RspType<uint3_t, // policy support
1705               uint5_t  // reserved
1706               >
1707     ipmiChassisSetPowerRestorePolicy(boost::asio::yield_context yield,
1708                                      uint3_t policy, uint5_t reserved)
1709 {
1710     power_policy::DbusValue value =
1711         power_policy::RestorePolicy::Policy::AlwaysOff;
1712 
1713     if (reserved || (policy > power_policy::noChange))
1714     {
1715         phosphor::logging::log<level::ERR>(
1716             "Reserved request parameter",
1717             entry("REQ=0x%x", static_cast<int>(policy)));
1718         return ipmi::responseInvalidFieldRequest();
1719     }
1720 
1721     if (policy == power_policy::noChange)
1722     {
1723         // just return the supported policy
1724         return ipmi::responseSuccess(power_policy::allSupport, reserved);
1725     }
1726 
1727     for (auto const& it : power_policy::dbusToIpmi)
1728     {
1729         if (it.second == policy)
1730         {
1731             value = it.first;
1732             break;
1733         }
1734     }
1735 
1736     try
1737     {
1738         const settings::Path& powerRestoreSetting =
1739             chassis::internal::cache::objects.map
1740                 .at(chassis::internal::powerRestoreIntf)
1741                 .front();
1742         std::variant<std::string> property = convertForMessage(value);
1743 
1744         auto sdbusp = getSdBus();
1745         boost::system::error_code ec;
1746         sdbusp->yield_method_call<void>(
1747             yield, ec,
1748             chassis::internal::cache::objects
1749                 .service(powerRestoreSetting,
1750                          chassis::internal::powerRestoreIntf)
1751                 .c_str(),
1752             powerRestoreSetting, ipmi::PROP_INTF, "Set",
1753             chassis::internal::powerRestoreIntf, "PowerRestorePolicy",
1754             property);
1755         if (ec)
1756         {
1757             phosphor::logging::log<level::ERR>("Unspecified Error");
1758             return ipmi::responseUnspecifiedError();
1759         }
1760     }
1761     catch (InternalFailure& e)
1762     {
1763         report<InternalFailure>();
1764         return ipmi::responseUnspecifiedError();
1765     }
1766 
1767     return ipmi::responseSuccess(power_policy::allSupport, reserved);
1768 }
1769 
1770 void register_netfn_chassis_functions()
1771 {
1772     createIdentifyTimer();
1773 
1774     // <Wildcard Command>
1775     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_WILDCARD, NULL,
1776                            ipmi_chassis_wildcard, PRIVILEGE_USER);
1777 
1778     // Get Chassis Capabilities
1779     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_CHASSIS_CAP, NULL,
1780                            ipmi_get_chassis_cap, PRIVILEGE_USER);
1781 
1782     // Set Chassis Capabilities
1783     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1784                           ipmi::chassis::cmdSetChassisCapabilities,
1785                           ipmi::Privilege::User, ipmiSetChassisCap);
1786 
1787     // <Get System Boot Options>
1788     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS, NULL,
1789                            ipmi_chassis_get_sys_boot_options,
1790                            PRIVILEGE_OPERATOR);
1791 
1792     // <Get Chassis Status>
1793     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1794                           ipmi::chassis::cmdGetChassisStatus,
1795                           ipmi::Privilege::User, ipmiGetChassisStatus);
1796 
1797     // <Chassis Control>
1798     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1799                           ipmi::chassis::cmdChassisControl,
1800                           ipmi::Privilege::Operator, ipmiChassisControl);
1801 
1802     // <Chassis Identify>
1803     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1804                           ipmi::chassis::cmdChassisIdentify,
1805                           ipmi::Privilege::Operator, ipmiChassisIdentify);
1806 
1807     // <Set System Boot Options>
1808     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_SYS_BOOT_OPTIONS, NULL,
1809                            ipmi_chassis_set_sys_boot_options,
1810                            PRIVILEGE_OPERATOR);
1811     // <Get POH Counter>
1812     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1813                           ipmi::chassis::cmdGetPohCounter,
1814                           ipmi::Privilege::User, ipmiGetPOHCounter);
1815 
1816     // <Set Power Restore Policy>
1817     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1818                           ipmi::chassis::cmdSetPowerRestorePolicy,
1819                           ipmi::Privilege::Operator,
1820                           ipmiChassisSetPowerRestorePolicy);
1821 }
1822