1 #include "config.h"
2 
3 #include "chassishandler.hpp"
4 
5 #include "settings.hpp"
6 
7 #include <arpa/inet.h>
8 #include <endian.h>
9 #include <limits.h>
10 #include <mapper.h>
11 #include <netinet/in.h>
12 
13 #include <array>
14 #include <chrono>
15 #include <cstring>
16 #include <filesystem>
17 #include <fstream>
18 #include <future>
19 #include <ipmid/api.hpp>
20 #include <ipmid/types.hpp>
21 #include <ipmid/utils.hpp>
22 #include <map>
23 #include <phosphor-logging/elog-errors.hpp>
24 #include <phosphor-logging/log.hpp>
25 #include <sdbusplus/bus.hpp>
26 #include <sdbusplus/message/types.hpp>
27 #include <sdbusplus/server/object.hpp>
28 #include <sdbusplus/timer.hpp>
29 #include <sstream>
30 #include <string>
31 #include <xyz/openbmc_project/Common/error.hpp>
32 #include <xyz/openbmc_project/Control/Boot/Mode/server.hpp>
33 #include <xyz/openbmc_project/Control/Boot/Source/server.hpp>
34 #include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp>
35 #include <xyz/openbmc_project/State/Host/server.hpp>
36 #include <xyz/openbmc_project/State/PowerOnHours/server.hpp>
37 
38 // Defines
39 #define SET_PARM_VERSION 0x01
40 #define SET_PARM_BOOT_FLAGS_PERMANENT 0x40
41 #define SET_PARM_BOOT_FLAGS_VALID_ONE_TIME 0x80
42 #define SET_PARM_BOOT_FLAGS_VALID_PERMANENT 0xC0
43 
44 std::unique_ptr<phosphor::Timer> identifyTimer
45     __attribute__((init_priority(101)));
46 
47 constexpr size_t SIZE_MAC = 18;
48 constexpr size_t SIZE_BOOT_OPTION = (uint8_t)
49     BootOptionResponseSize::OPAL_NETWORK_SETTINGS; // Maximum size of the boot
50                                                    // option parametrs
51 constexpr size_t SIZE_PREFIX = 7;
52 constexpr size_t MAX_PREFIX_VALUE = 32;
53 constexpr size_t SIZE_COOKIE = 4;
54 constexpr size_t SIZE_VERSION = 2;
55 constexpr size_t DEFAULT_IDENTIFY_TIME_OUT = 15;
56 
57 // PetiBoot-Specific
58 static constexpr uint8_t net_conf_initial_bytes[] = {0x80, 0x21, 0x70, 0x62,
59                                                      0x21, 0x00, 0x01, 0x06};
60 
61 static constexpr size_t COOKIE_OFFSET = 1;
62 static constexpr size_t VERSION_OFFSET = 5;
63 static constexpr size_t ADDR_SIZE_OFFSET = 8;
64 static constexpr size_t MAC_OFFSET = 9;
65 static constexpr size_t ADDRTYPE_OFFSET = 16;
66 static constexpr size_t IPADDR_OFFSET = 17;
67 
68 static constexpr size_t encIdentifyObjectsSize = 1;
69 static constexpr size_t chassisIdentifyReqLength = 2;
70 static constexpr size_t identifyIntervalPos = 0;
71 static constexpr size_t forceIdentifyPos = 1;
72 
73 void register_netfn_chassis_functions() __attribute__((constructor));
74 
75 // Host settings in dbus
76 // Service name should be referenced by connection name got via object mapper
77 const char* settings_object_name = "/org/openbmc/settings/host0";
78 const char* settings_intf_name = "org.freedesktop.DBus.Properties";
79 const char* identify_led_object_name =
80     "/xyz/openbmc_project/led/groups/enclosure_identify";
81 
82 constexpr auto SETTINGS_ROOT = "/";
83 constexpr auto SETTINGS_MATCH = "host0";
84 
85 constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP";
86 constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress";
87 
88 static constexpr auto chassisStateRoot = "/xyz/openbmc_project/state";
89 static constexpr auto chassisPOHStateIntf =
90     "xyz.openbmc_project.State.PowerOnHours";
91 static constexpr auto pOHCounterProperty = "POHCounter";
92 static constexpr auto match = "chassis0";
93 const static constexpr char chassisCapIntf[] =
94     "xyz.openbmc_project.Control.ChassisCapabilities";
95 const static constexpr char chassisCapFlagsProp[] = "CapabilitiesFlags";
96 const static constexpr char chassisFRUDevAddrProp[] = "FRUDeviceAddress";
97 const static constexpr char chassisSDRDevAddrProp[] = "SDRDeviceAddress";
98 const static constexpr char chassisSELDevAddrProp[] = "SELDeviceAddress";
99 const static constexpr char chassisSMDevAddrProp[] = "SMDeviceAddress";
100 const static constexpr char chassisBridgeDevAddrProp[] = "BridgeDeviceAddress";
101 static constexpr uint8_t chassisCapFlagMask = 0x0f;
102 static constexpr uint8_t chassisCapAddrMask = 0xfe;
103 
104 typedef struct
105 {
106     uint8_t cap_flags;
107     uint8_t fru_info_dev_addr;
108     uint8_t sdr_dev_addr;
109     uint8_t sel_dev_addr;
110     uint8_t system_management_dev_addr;
111     uint8_t bridge_dev_addr;
112 } __attribute__((packed)) ipmi_chassis_cap_t;
113 
114 typedef struct
115 {
116     uint8_t cur_power_state;
117     uint8_t last_power_event;
118     uint8_t misc_power_state;
119     uint8_t front_panel_button_cap_status;
120 } __attribute__((packed)) ipmi_get_chassis_status_t;
121 
122 // Phosphor Host State manager
123 namespace State = sdbusplus::xyz::openbmc_project::State::server;
124 
125 namespace fs = std::filesystem;
126 
127 using namespace phosphor::logging;
128 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
129 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
130 
131 namespace chassis
132 {
133 namespace internal
134 {
135 
136 constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode";
137 constexpr auto bootSourceIntf = "xyz.openbmc_project.Control.Boot.Source";
138 constexpr auto powerRestoreIntf =
139     "xyz.openbmc_project.Control.Power.RestorePolicy";
140 sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection());
141 
142 namespace cache
143 {
144 
145 settings::Objects objects(dbus,
146                           {bootModeIntf, bootSourceIntf, powerRestoreIntf});
147 
148 } // namespace cache
149 } // namespace internal
150 } // namespace chassis
151 
152 namespace poh
153 {
154 
155 constexpr auto minutesPerCount = 60;
156 
157 } // namespace poh
158 
159 struct get_sys_boot_options_t
160 {
161     uint8_t parameter;
162     uint8_t set;
163     uint8_t block;
164 } __attribute__((packed));
165 
166 struct get_sys_boot_options_response_t
167 {
168     uint8_t version;
169     uint8_t parm;
170     uint8_t data[SIZE_BOOT_OPTION];
171 } __attribute__((packed));
172 
173 struct set_sys_boot_options_t
174 {
175     uint8_t parameter;
176     uint8_t data[SIZE_BOOT_OPTION];
177 } __attribute__((packed));
178 
179 int getHostNetworkData(get_sys_boot_options_response_t* respptr)
180 {
181     ipmi::PropertyMap properties;
182     int rc = 0;
183     uint8_t addrSize = ipmi::network::IPV4_ADDRESS_SIZE_BYTE;
184 
185     try
186     {
187         // TODO There may be cases where an interface is implemented by multiple
188         // objects,to handle such cases we are interested on that object
189         //  which are on interested busname.
190         //  Currenlty mapper doesn't give the readable busname(gives busid)
191         //  so we can't match with bus name so giving some object specific info
192         //  as SETTINGS_MATCH.
193         //  Later SETTINGS_MATCH will be replaced with busname.
194 
195         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
196 
197         auto ipObjectInfo = ipmi::getDbusObject(bus, IP_INTERFACE,
198                                                 SETTINGS_ROOT, SETTINGS_MATCH);
199 
200         auto macObjectInfo = ipmi::getDbusObject(bus, MAC_INTERFACE,
201                                                  SETTINGS_ROOT, SETTINGS_MATCH);
202 
203         properties = ipmi::getAllDbusProperties(
204             bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE);
205         auto variant = ipmi::getDbusProperty(bus, macObjectInfo.second,
206                                              macObjectInfo.first, MAC_INTERFACE,
207                                              "MACAddress");
208 
209         auto ipAddress = std::get<std::string>(properties["Address"]);
210 
211         auto gateway = std::get<std::string>(properties["Gateway"]);
212 
213         auto prefix = std::get<uint8_t>(properties["PrefixLength"]);
214 
215         uint8_t isStatic =
216             (std::get<std::string>(properties["Origin"]) ==
217              "xyz.openbmc_project.Network.IP.AddressOrigin.Static")
218                 ? 1
219                 : 0;
220 
221         auto MACAddress = std::get<std::string>(variant);
222 
223         // it is expected here that we should get the valid data
224         // but we may also get the default values.
225         // Validation of the data is done by settings.
226         //
227         // if mac address is default mac address then
228         // don't send blank override.
229         if ((MACAddress == ipmi::network::DEFAULT_MAC_ADDRESS))
230         {
231             std::memset(respptr->data, 0, SIZE_BOOT_OPTION);
232             rc = -1;
233             return rc;
234         }
235         // if addr is static then ipaddress,gateway,prefix
236         // should not be default one,don't send blank override.
237         if (isStatic)
238         {
239             if ((ipAddress == ipmi::network::DEFAULT_ADDRESS) ||
240                 (gateway == ipmi::network::DEFAULT_ADDRESS) || (!prefix))
241             {
242                 std::memset(respptr->data, 0, SIZE_BOOT_OPTION);
243                 rc = -1;
244                 return rc;
245             }
246         }
247 
248         sscanf(
249             MACAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT,
250             (respptr->data + MAC_OFFSET), (respptr->data + MAC_OFFSET + 1),
251             (respptr->data + MAC_OFFSET + 2), (respptr->data + MAC_OFFSET + 3),
252             (respptr->data + MAC_OFFSET + 4), (respptr->data + MAC_OFFSET + 5));
253 
254         respptr->data[MAC_OFFSET + 6] = 0x00;
255 
256         std::memcpy(respptr->data + ADDRTYPE_OFFSET, &isStatic,
257                     sizeof(isStatic));
258 
259         uint8_t addressFamily = (std::get<std::string>(properties["Type"]) ==
260                                  "xyz.openbmc_project.Network.IP.Protocol.IPv4")
261                                     ? AF_INET
262                                     : AF_INET6;
263 
264         addrSize = (addressFamily == AF_INET)
265                        ? ipmi::network::IPV4_ADDRESS_SIZE_BYTE
266                        : ipmi::network::IPV6_ADDRESS_SIZE_BYTE;
267 
268         // ipaddress and gateway would be in IPv4 format
269         inet_pton(addressFamily, ipAddress.c_str(),
270                   (respptr->data + IPADDR_OFFSET));
271 
272         uint8_t prefixOffset = IPADDR_OFFSET + addrSize;
273 
274         std::memcpy(respptr->data + prefixOffset, &prefix, sizeof(prefix));
275 
276         uint8_t gatewayOffset = prefixOffset + sizeof(decltype(prefix));
277 
278         inet_pton(addressFamily, gateway.c_str(),
279                   (respptr->data + gatewayOffset));
280     }
281     catch (InternalFailure& e)
282     {
283         commit<InternalFailure>();
284         std::memset(respptr->data, 0, SIZE_BOOT_OPTION);
285         rc = -1;
286         return rc;
287     }
288 
289     // PetiBoot-Specific
290     // If success then copy the first 9 bytes to the data
291     std::memcpy(respptr->data, net_conf_initial_bytes,
292                 sizeof(net_conf_initial_bytes));
293 
294     std::memcpy(respptr->data + ADDR_SIZE_OFFSET, &addrSize, sizeof(addrSize));
295 
296 #ifdef _IPMI_DEBUG_
297     std::printf("\n===Printing the IPMI Formatted Data========\n");
298 
299     for (uint8_t pos = 0; pos < index; pos++)
300     {
301         std::printf("%02x ", respptr->data[pos]);
302     }
303 #endif
304 
305     return rc;
306 }
307 
308 /** @brief convert IPv4 and IPv6 addresses from binary to text form.
309  *  @param[in] family - IPv4/Ipv6
310  *  @param[in] data - req data pointer.
311  *  @param[in] offset - offset in the data.
312  *  @param[in] addrSize - size of the data which needs to be read from offset.
313  *  @returns address in text form.
314  */
315 
316 std::string getAddrStr(uint8_t family, uint8_t* data, uint8_t offset,
317                        uint8_t addrSize)
318 {
319     char ipAddr[INET6_ADDRSTRLEN] = {};
320 
321     switch (family)
322     {
323         case AF_INET:
324         {
325             struct sockaddr_in addr4
326             {
327             };
328             std::memcpy(&addr4.sin_addr.s_addr, &data[offset], addrSize);
329 
330             inet_ntop(AF_INET, &addr4.sin_addr, ipAddr, INET_ADDRSTRLEN);
331 
332             break;
333         }
334         case AF_INET6:
335         {
336             struct sockaddr_in6 addr6
337             {
338             };
339             std::memcpy(&addr6.sin6_addr.s6_addr, &data[offset], addrSize);
340 
341             inet_ntop(AF_INET6, &addr6.sin6_addr, ipAddr, INET6_ADDRSTRLEN);
342 
343             break;
344         }
345         default:
346         {
347             return {};
348         }
349     }
350 
351     return ipAddr;
352 }
353 
354 int setHostNetworkData(set_sys_boot_options_t* reqptr)
355 {
356     using namespace std::string_literals;
357     std::string host_network_config;
358     char mac[]{"00:00:00:00:00:00"};
359     std::string ipAddress, gateway;
360     char addrOrigin{0};
361     uint8_t addrSize{0};
362     std::string addressOrigin =
363         "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP";
364     std::string addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
365     uint8_t prefix{0};
366     uint32_t zeroCookie = 0;
367     uint8_t family = AF_INET;
368 
369     // cookie starts from second byte
370     // version starts from sixth byte
371 
372     try
373     {
374         do
375         {
376             // cookie ==  0x21 0x70 0x62 0x21
377             if (memcmp(&(reqptr->data[COOKIE_OFFSET]),
378                        (net_conf_initial_bytes + COOKIE_OFFSET),
379                        SIZE_COOKIE) != 0)
380             {
381                 // cookie == 0
382                 if (memcmp(&(reqptr->data[COOKIE_OFFSET]), &zeroCookie,
383                            SIZE_COOKIE) == 0)
384                 {
385                     // need to zero out the network settings.
386                     break;
387                 }
388 
389                 log<level::ERR>("Invalid Cookie");
390                 elog<InternalFailure>();
391             }
392 
393             // vesion == 0x00 0x01
394             if (memcmp(&(reqptr->data[VERSION_OFFSET]),
395                        (net_conf_initial_bytes + VERSION_OFFSET),
396                        SIZE_VERSION) != 0)
397             {
398 
399                 log<level::ERR>("Invalid Version");
400                 elog<InternalFailure>();
401             }
402 
403             std::snprintf(
404                 mac, SIZE_MAC, ipmi::network::MAC_ADDRESS_FORMAT,
405                 reqptr->data[MAC_OFFSET], reqptr->data[MAC_OFFSET + 1],
406                 reqptr->data[MAC_OFFSET + 2], reqptr->data[MAC_OFFSET + 3],
407                 reqptr->data[MAC_OFFSET + 4], reqptr->data[MAC_OFFSET + 5]);
408 
409             std::memcpy(&addrOrigin, &(reqptr->data[ADDRTYPE_OFFSET]),
410                         sizeof(decltype(addrOrigin)));
411 
412             if (addrOrigin)
413             {
414                 addressOrigin =
415                     "xyz.openbmc_project.Network.IP.AddressOrigin.Static";
416             }
417 
418             // Get the address size
419             std::memcpy(&addrSize, &reqptr->data[ADDR_SIZE_OFFSET],
420                         sizeof(addrSize));
421 
422             uint8_t prefixOffset = IPADDR_OFFSET + addrSize;
423 
424             std::memcpy(&prefix, &(reqptr->data[prefixOffset]),
425                         sizeof(decltype(prefix)));
426 
427             uint8_t gatewayOffset = prefixOffset + sizeof(decltype(prefix));
428 
429             if (addrSize != ipmi::network::IPV4_ADDRESS_SIZE_BYTE)
430             {
431                 addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv6";
432                 family = AF_INET6;
433             }
434 
435             ipAddress =
436                 getAddrStr(family, reqptr->data, IPADDR_OFFSET, addrSize);
437 
438             gateway = getAddrStr(family, reqptr->data, gatewayOffset, addrSize);
439 
440         } while (0);
441 
442         // Cookie == 0 or it is a valid cookie
443         host_network_config += "ipaddress="s + ipAddress + ",prefix="s +
444                                std::to_string(prefix) + ",gateway="s + gateway +
445                                ",mac="s + mac + ",addressOrigin="s +
446                                addressOrigin;
447 
448         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
449 
450         auto ipObjectInfo = ipmi::getDbusObject(bus, IP_INTERFACE,
451                                                 SETTINGS_ROOT, SETTINGS_MATCH);
452         auto macObjectInfo = ipmi::getDbusObject(bus, MAC_INTERFACE,
453                                                  SETTINGS_ROOT, SETTINGS_MATCH);
454         // set the dbus property
455         ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first,
456                               IP_INTERFACE, "Address", std::string(ipAddress));
457         ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first,
458                               IP_INTERFACE, "PrefixLength", prefix);
459         ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first,
460                               IP_INTERFACE, "Origin", addressOrigin);
461         ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first,
462                               IP_INTERFACE, "Gateway", std::string(gateway));
463         ipmi::setDbusProperty(
464             bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE, "Type",
465             std::string("xyz.openbmc_project.Network.IP.Protocol.IPv4"));
466         ipmi::setDbusProperty(bus, macObjectInfo.second, macObjectInfo.first,
467                               MAC_INTERFACE, "MACAddress", std::string(mac));
468 
469         log<level::DEBUG>(
470             "Network configuration changed",
471             entry("NETWORKCONFIG=%s", host_network_config.c_str()));
472     }
473     catch (InternalFailure& e)
474     {
475         commit<InternalFailure>();
476         return -1;
477     }
478 
479     return 0;
480 }
481 
482 uint32_t getPOHCounter()
483 {
484     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
485 
486     auto chassisStateObj =
487         ipmi::getDbusObject(bus, chassisPOHStateIntf, chassisStateRoot, match);
488 
489     auto service =
490         ipmi::getService(bus, chassisPOHStateIntf, chassisStateObj.first);
491 
492     auto propValue =
493         ipmi::getDbusProperty(bus, service, chassisStateObj.first,
494                               chassisPOHStateIntf, pOHCounterProperty);
495 
496     return std::get<uint32_t>(propValue);
497 }
498 
499 ipmi_ret_t ipmi_chassis_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
500                                  ipmi_request_t request,
501                                  ipmi_response_t response,
502                                  ipmi_data_len_t data_len,
503                                  ipmi_context_t context)
504 {
505     // Status code.
506     ipmi_ret_t rc = IPMI_CC_INVALID;
507     *data_len = 0;
508     return rc;
509 }
510 
511 ipmi_ret_t ipmi_get_chassis_cap(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
512                                 ipmi_request_t request,
513                                 ipmi_response_t response,
514                                 ipmi_data_len_t data_len,
515                                 ipmi_context_t context)
516 {
517     // sd_bus error
518     ipmi_ret_t rc = IPMI_CC_OK;
519 
520     ipmi_chassis_cap_t chassis_cap{};
521 
522     if (*data_len != 0)
523     {
524         return IPMI_CC_REQ_DATA_LEN_INVALID;
525     }
526 
527     *data_len = sizeof(ipmi_chassis_cap_t);
528 
529     try
530     {
531         sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
532 
533         ipmi::DbusObjectInfo chassisCapObject =
534             ipmi::getDbusObject(bus, chassisCapIntf);
535 
536         // capabilities flags
537         // [7..4] - reserved
538         // [3] – 1b = provides power interlock  (IPM 1.5)
539         // [2] – 1b = provides Diagnostic Interrupt (FP NMI)
540         // [1] – 1b = provides “Front Panel Lockout” (indicates that the chassis
541         // has capabilities
542         //            to lock out external power control and reset button or
543         //            front panel interfaces and/or detect tampering with those
544         //            interfaces).
545         // [0] -1b = Chassis provides intrusion (physical security) sensor.
546         // set to default value 0x0.
547         ipmi::Value variant = ipmi::getDbusProperty(
548             bus, chassisCapObject.second, chassisCapObject.first,
549             chassisCapIntf, chassisCapFlagsProp);
550         chassis_cap.cap_flags = std::get<uint8_t>(variant);
551 
552         variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
553                                         chassisCapObject.first, chassisCapIntf,
554                                         chassisFRUDevAddrProp);
555         // Chassis FRU info Device Address.
556         chassis_cap.fru_info_dev_addr = std::get<uint8_t>(variant);
557 
558         variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
559                                         chassisCapObject.first, chassisCapIntf,
560                                         chassisSDRDevAddrProp);
561         // Chassis SDR Device Address.
562         chassis_cap.sdr_dev_addr = std::get<uint8_t>(variant);
563 
564         variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
565                                         chassisCapObject.first, chassisCapIntf,
566                                         chassisSELDevAddrProp);
567         // Chassis SEL Device Address.
568         chassis_cap.sel_dev_addr = std::get<uint8_t>(variant);
569 
570         variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
571                                         chassisCapObject.first, chassisCapIntf,
572                                         chassisSMDevAddrProp);
573         // Chassis System Management Device Address.
574         chassis_cap.system_management_dev_addr = std::get<uint8_t>(variant);
575 
576         variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
577                                         chassisCapObject.first, chassisCapIntf,
578                                         chassisBridgeDevAddrProp);
579         // Chassis Bridge Device Address.
580         chassis_cap.bridge_dev_addr = std::get<uint8_t>(variant);
581         uint8_t* respP = reinterpret_cast<uint8_t*>(response);
582         uint8_t* chassisP = reinterpret_cast<uint8_t*>(&chassis_cap);
583         std::copy(chassisP, chassisP + *data_len, respP);
584     }
585     catch (std::exception& e)
586     {
587         log<level::ERR>(e.what());
588         rc = IPMI_CC_UNSPECIFIED_ERROR;
589         *data_len = 0;
590         return rc;
591     }
592 
593     return rc;
594 }
595 
596 ipmi_ret_t ipmi_set_chassis_cap(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
597                                 ipmi_request_t request,
598                                 ipmi_response_t response,
599                                 ipmi_data_len_t data_len,
600                                 ipmi_context_t context)
601 {
602     ipmi_ret_t rc = IPMI_CC_OK;
603 
604     if (*data_len != sizeof(ipmi_chassis_cap_t))
605     {
606         log<level::ERR>("Unsupported request length",
607                         entry("LEN=0x%x", *data_len));
608         *data_len = 0;
609         return IPMI_CC_REQ_DATA_LEN_INVALID;
610     }
611 
612     ipmi_chassis_cap_t* chassisCap = static_cast<ipmi_chassis_cap_t*>(request);
613 
614     *data_len = 0;
615 
616     // check input data
617     if (0 != (chassisCap->cap_flags & ~chassisCapFlagMask))
618     {
619         log<level::ERR>("Unsupported request parameter(CAP Flags)",
620                         entry("REQ=0x%x", chassisCap->cap_flags));
621         return IPMI_CC_INVALID_FIELD_REQUEST;
622     }
623 
624     if (0 != (chassisCap->fru_info_dev_addr & ~chassisCapAddrMask))
625     {
626         log<level::ERR>("Unsupported request parameter(FRU Addr)",
627                         entry("REQ=0x%x", chassisCap->fru_info_dev_addr));
628         return IPMI_CC_INVALID_FIELD_REQUEST;
629     }
630 
631     if (0 != (chassisCap->sdr_dev_addr & ~chassisCapAddrMask))
632     {
633         log<level::ERR>("Unsupported request parameter(SDR Addr)",
634                         entry("REQ=0x%x", chassisCap->sdr_dev_addr));
635         return IPMI_CC_INVALID_FIELD_REQUEST;
636     }
637 
638     if (0 != (chassisCap->sel_dev_addr & ~chassisCapAddrMask))
639     {
640         log<level::ERR>("Unsupported request parameter(SEL Addr)",
641                         entry("REQ=0x%x", chassisCap->sel_dev_addr));
642         return IPMI_CC_INVALID_FIELD_REQUEST;
643     }
644 
645     if (0 != (chassisCap->system_management_dev_addr & ~chassisCapAddrMask))
646     {
647         log<level::ERR>(
648             "Unsupported request parameter(SM Addr)",
649             entry("REQ=0x%x", chassisCap->system_management_dev_addr));
650         return IPMI_CC_INVALID_FIELD_REQUEST;
651     }
652 
653     if (0 != (chassisCap->bridge_dev_addr & ~chassisCapAddrMask))
654     {
655         log<level::ERR>("Unsupported request parameter(Bridge Addr)",
656                         entry("REQ=0x%x", chassisCap->bridge_dev_addr));
657         return IPMI_CC_INVALID_FIELD_REQUEST;
658     }
659 
660     try
661     {
662         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
663         ipmi::DbusObjectInfo chassisCapObject =
664             ipmi::getDbusObject(bus, chassisCapIntf);
665 
666         ipmi::setDbusProperty(bus, chassisCapObject.second,
667                               chassisCapObject.first, chassisCapIntf,
668                               chassisCapFlagsProp, chassisCap->cap_flags);
669 
670         ipmi::setDbusProperty(bus, chassisCapObject.second,
671                               chassisCapObject.first, chassisCapIntf,
672                               chassisFRUDevAddrProp,
673                               chassisCap->fru_info_dev_addr);
674 
675         ipmi::setDbusProperty(bus, chassisCapObject.second,
676                               chassisCapObject.first, chassisCapIntf,
677                               chassisSDRDevAddrProp, chassisCap->sdr_dev_addr);
678 
679         ipmi::setDbusProperty(bus, chassisCapObject.second,
680                               chassisCapObject.first, chassisCapIntf,
681                               chassisSELDevAddrProp, chassisCap->sel_dev_addr);
682 
683         ipmi::setDbusProperty(bus, chassisCapObject.second,
684                               chassisCapObject.first, chassisCapIntf,
685                               chassisSMDevAddrProp,
686                               chassisCap->system_management_dev_addr);
687 
688         ipmi::setDbusProperty(bus, chassisCapObject.second,
689                               chassisCapObject.first, chassisCapIntf,
690                               chassisBridgeDevAddrProp,
691                               chassisCap->bridge_dev_addr);
692     }
693     catch (std::exception& e)
694     {
695         log<level::ERR>(e.what());
696         rc = IPMI_CC_UNSPECIFIED_ERROR;
697         return rc;
698     }
699 
700     return rc;
701 }
702 
703 //------------------------------------------
704 // Calls into Host State Manager Dbus object
705 //------------------------------------------
706 int initiate_state_transition(State::Host::Transition transition)
707 {
708     // OpenBMC Host State Manager dbus framework
709     constexpr auto HOST_STATE_MANAGER_ROOT = "/xyz/openbmc_project/state/host0";
710     constexpr auto HOST_STATE_MANAGER_IFACE = "xyz.openbmc_project.State.Host";
711     constexpr auto DBUS_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
712     constexpr auto PROPERTY = "RequestedHostTransition";
713 
714     // sd_bus error
715     int rc = 0;
716     char* busname = NULL;
717 
718     // SD Bus error report mechanism.
719     sd_bus_error bus_error = SD_BUS_ERROR_NULL;
720 
721     // Gets a hook onto either a SYSTEM or SESSION bus
722     sd_bus* bus_type = ipmid_get_sd_bus_connection();
723     rc = mapper_get_service(bus_type, HOST_STATE_MANAGER_ROOT, &busname);
724     if (rc < 0)
725     {
726         log<level::ERR>(
727             "Failed to get bus name",
728             entry("ERRNO=0x%X, OBJPATH=%s", -rc, HOST_STATE_MANAGER_ROOT));
729         return rc;
730     }
731 
732     // Convert to string equivalent of the passed in transition enum.
733     auto request = State::convertForMessage(transition);
734 
735     rc = sd_bus_call_method(bus_type,                // On the system bus
736                             busname,                 // Service to contact
737                             HOST_STATE_MANAGER_ROOT, // Object path
738                             DBUS_PROPERTY_IFACE,     // Interface name
739                             "Set",                   // Method to be called
740                             &bus_error,              // object to return error
741                             nullptr,                 // Response buffer if any
742                             "ssv",                   // Takes 3 arguments
743                             HOST_STATE_MANAGER_IFACE, PROPERTY, "s",
744                             request.c_str());
745     if (rc < 0)
746     {
747         log<level::ERR>("Failed to initiate transition",
748                         entry("ERRNO=0x%X, REQUEST=%s", -rc, request.c_str()));
749     }
750     else
751     {
752         log<level::INFO>("Transition request initiated successfully");
753     }
754 
755     sd_bus_error_free(&bus_error);
756     free(busname);
757 
758     return rc;
759 }
760 
761 namespace power_policy
762 {
763 
764 using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
765 using IpmiValue = uint8_t;
766 using DbusValue = RestorePolicy::Policy;
767 
768 std::map<DbusValue, IpmiValue> dbusToIpmi = {
769     {RestorePolicy::Policy::AlwaysOff, 0x00},
770     {RestorePolicy::Policy::Restore, 0x01},
771     {RestorePolicy::Policy::AlwaysOn, 0x02}};
772 
773 static constexpr uint8_t noChange = 0x03;
774 static constexpr uint8_t allSupport = 0x01 | 0x02 | 0x04;
775 static constexpr uint8_t policyBitMask = 0x07;
776 static constexpr uint8_t setPolicyReqLen = 1;
777 } // namespace power_policy
778 
779 //----------------------------------------------------------------------
780 // Get Chassis Status commands
781 //----------------------------------------------------------------------
782 ipmi_ret_t ipmi_get_chassis_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
783                                    ipmi_request_t request,
784                                    ipmi_response_t response,
785                                    ipmi_data_len_t data_len,
786                                    ipmi_context_t context)
787 {
788     const char* objname = "/org/openbmc/control/power0";
789     const char* intf = "org.openbmc.control.Power";
790 
791     sd_bus* bus = NULL;
792     sd_bus_message* reply = NULL;
793     int r = 0;
794     int pgood = 0;
795     char* busname = NULL;
796     ipmi_ret_t rc = IPMI_CC_OK;
797     ipmi_get_chassis_status_t chassis_status{};
798 
799     uint8_t s = 0;
800 
801     using namespace chassis::internal;
802     using namespace chassis::internal::cache;
803     using namespace power_policy;
804 
805     const auto& powerRestoreSetting = objects.map.at(powerRestoreIntf).front();
806     auto method = dbus.new_method_call(
807         objects.service(powerRestoreSetting, powerRestoreIntf).c_str(),
808         powerRestoreSetting.c_str(), ipmi::PROP_INTF, "Get");
809     method.append(powerRestoreIntf, "PowerRestorePolicy");
810     auto resp = dbus.call(method);
811     if (resp.is_method_error())
812     {
813         log<level::ERR>("Error in PowerRestorePolicy Get");
814         report<InternalFailure>();
815         *data_len = 0;
816         return IPMI_CC_UNSPECIFIED_ERROR;
817     }
818     sdbusplus::message::variant<std::string> result;
819     resp.read(result);
820     auto powerRestore =
821         RestorePolicy::convertPolicyFromString(std::get<std::string>(result));
822 
823     *data_len = 4;
824 
825     bus = ipmid_get_sd_bus_connection();
826 
827     r = mapper_get_service(bus, objname, &busname);
828     if (r < 0)
829     {
830         log<level::ERR>("Failed to get bus name", entry("ERRNO=0x%X", -r));
831         rc = IPMI_CC_UNSPECIFIED_ERROR;
832         goto finish;
833     }
834 
835     r = sd_bus_get_property(bus, busname, objname, intf, "pgood", NULL, &reply,
836                             "i");
837     if (r < 0)
838     {
839         log<level::ERR>("Failed to call sd_bus_get_property",
840                         entry("PROPERTY=%s", "pgood"), entry("ERRNO=0x%X", -r),
841                         entry("BUS=%s", busname), entry("PATH=%s", objname),
842                         entry("INTERFACE=%s", intf));
843         rc = IPMI_CC_UNSPECIFIED_ERROR;
844         goto finish;
845     }
846 
847     r = sd_bus_message_read(reply, "i", &pgood);
848     if (r < 0)
849     {
850         log<level::ERR>("Failed to read sensor:", entry("ERRNO=0x%X", -r));
851         rc = IPMI_CC_UNSPECIFIED_ERROR;
852         goto finish;
853     }
854 
855     s = dbusToIpmi.at(powerRestore);
856 
857     // Current Power State
858     // [7] reserved
859     // [6..5] power restore policy
860     //          00b = chassis stays powered off after AC/mains returns
861     //          01b = after AC returns, power is restored to the state that was
862     //          in effect when AC/mains was lost.
863     //          10b = chassis always powers up after AC/mains returns
864     //          11b = unknow
865     //        Set to 00b, by observing the hardware behavior.
866     //        Do we need to define a dbus property to identify the restore
867     //        policy?
868 
869     // [4] power control fault
870     //       1b = controller attempted to turn system power on or off, but
871     //       system did not enter desired state.
872     //       Set to 0b, since We don't support it..
873 
874     // [3] power fault
875     //       1b = fault detected in main power subsystem.
876     //       set to 0b. for we don't support it.
877 
878     // [2] 1b = interlock (chassis is presently shut down because a chassis
879     //       panel interlock switch is active). (IPMI 1.5)
880     //       set to 0b,  for we don't support it.
881 
882     // [1] power overload
883     //      1b = system shutdown because of power overload condition.
884     //       set to 0b,  for we don't support it.
885 
886     // [0] power is on
887     //       1b = system power is on
888     //       0b = system power is off(soft-off S4/S5, or mechanical off)
889 
890     chassis_status.cur_power_state = ((s & 0x3) << 5) | (pgood & 0x1);
891 
892     // Last Power Event
893     // [7..5] – reserved
894     // [4] – 1b = last ‘Power is on’ state was entered via IPMI command
895     // [3] – 1b = last power down caused by power fault
896     // [2] – 1b = last power down caused by a power interlock being activated
897     // [1] – 1b = last power down caused by a Power overload
898     // [0] – 1b = AC failed
899     // set to 0x0,  for we don't support these fields.
900 
901     chassis_status.last_power_event = 0;
902 
903     // Misc. Chassis State
904     // [7] – reserved
905     // [6] – 1b = Chassis Identify command and state info supported (Optional)
906     //       0b = Chassis Identify command support unspecified via this command.
907     //       (The Get Command Support command , if implemented, would still
908     //       indicate support for the Chassis Identify command)
909     // [5..4] – Chassis Identify State. Mandatory when bit[6] =1b, reserved
910     // (return
911     //          as 00b) otherwise. Returns the present chassis identify state.
912     //           Refer to the Chassis Identify command for more info.
913     //         00b = chassis identify state = Off
914     //         01b = chassis identify state = Temporary(timed) On
915     //         10b = chassis identify state = Indefinite On
916     //         11b = reserved
917     // [3] – 1b = Cooling/fan fault detected
918     // [2] – 1b = Drive Fault
919     // [1] – 1b = Front Panel Lockout active (power off and reset via chassis
920     //       push-buttons disabled.)
921     // [0] – 1b = Chassis Intrusion active
922     //  set to 0,  for we don't support them.
923     chassis_status.misc_power_state = 0;
924 
925     //  Front Panel Button Capabilities and disable/enable status(Optional)
926     //  set to 0,  for we don't support them.
927     chassis_status.front_panel_button_cap_status = 0;
928 
929     // Pack the actual response
930     std::memcpy(response, &chassis_status, *data_len);
931 
932 finish:
933     free(busname);
934     reply = sd_bus_message_unref(reply);
935 
936     return rc;
937 }
938 
939 //-------------------------------------------------------------
940 // Send a command to SoftPowerOff application to stop any timer
941 //-------------------------------------------------------------
942 int stop_soft_off_timer()
943 {
944     constexpr auto iface = "org.freedesktop.DBus.Properties";
945     constexpr auto soft_off_iface = "xyz.openbmc_project.Ipmi.Internal."
946                                     "SoftPowerOff";
947 
948     constexpr auto property = "ResponseReceived";
949     constexpr auto value = "xyz.openbmc_project.Ipmi.Internal."
950                            "SoftPowerOff.HostResponse.HostShutdown";
951 
952     // Get the system bus where most system services are provided.
953     auto bus = ipmid_get_sd_bus_connection();
954 
955     // Get the service name
956     // TODO openbmc/openbmc#1661 - Mapper refactor
957     //
958     // See openbmc/openbmc#1743 for some details but high level summary is that
959     // for now the code will directly call the soft off interface due to a
960     // race condition with mapper usage
961     //
962     // char *busname = nullptr;
963     // auto r = mapper_get_service(bus, SOFTOFF_OBJPATH, &busname);
964     // if (r < 0)
965     //{
966     //    fprintf(stderr, "Failed to get %s bus name: %s\n",
967     //            SOFTOFF_OBJPATH, -r);
968     //    return r;
969     //}
970 
971     // No error object or reply expected.
972     int rc = sd_bus_call_method(bus, SOFTOFF_BUSNAME, SOFTOFF_OBJPATH, iface,
973                                 "Set", nullptr, nullptr, "ssv", soft_off_iface,
974                                 property, "s", value);
975     if (rc < 0)
976     {
977         log<level::ERR>("Failed to set property in SoftPowerOff object",
978                         entry("ERRNO=0x%X", -rc));
979     }
980 
981     // TODO openbmc/openbmc#1661 - Mapper refactor
982     // free(busname);
983     return rc;
984 }
985 
986 //----------------------------------------------------------------------
987 // Create file to indicate there is no need for softoff notification to host
988 //----------------------------------------------------------------------
989 void indicate_no_softoff_needed()
990 {
991     fs::path path{HOST_INBAND_REQUEST_DIR};
992     if (!fs::is_directory(path))
993     {
994         fs::create_directory(path);
995     }
996 
997     // Add the host instance (default 0 for now) to the file name
998     std::string file{HOST_INBAND_REQUEST_FILE};
999     auto size = std::snprintf(nullptr, 0, file.c_str(), 0);
1000     size++; // null
1001     std::unique_ptr<char[]> buf(new char[size]);
1002     std::snprintf(buf.get(), size, file.c_str(), 0);
1003 
1004     // Append file name to directory and create it
1005     path /= buf.get();
1006     std::ofstream(path.c_str());
1007 }
1008 
1009 /** @brief Implementation of chassis control command
1010  *
1011  *  @param - chassisControl command byte
1012  *
1013  *  @return  Success or InvalidFieldRequest.
1014  */
1015 ipmi::RspType<> ipmiChassisControl(uint8_t chassisControl)
1016 {
1017     int rc = 0;
1018     switch (chassisControl)
1019     {
1020         case CMD_POWER_ON:
1021             rc = initiate_state_transition(State::Host::Transition::On);
1022             break;
1023         case CMD_POWER_OFF:
1024             // This path would be hit in 2 conditions.
1025             // 1: When user asks for power off using ipmi chassis command 0x04
1026             // 2: Host asking for power off post shutting down.
1027 
1028             // If it's a host requested power off, then need to nudge Softoff
1029             // application that it needs to stop the watchdog timer if running.
1030             // If it is a user requested power off, then this is not really
1031             // needed. But then we need to differentiate between user and host
1032             // calling this same command
1033 
1034             // For now, we are going ahead with trying to nudge the soft off and
1035             // interpret the failure to do so as a non softoff case
1036             rc = stop_soft_off_timer();
1037 
1038             // Only request the Off transition if the soft power off
1039             // application is not running
1040             if (rc < 0)
1041             {
1042                 // First create a file to indicate to the soft off application
1043                 // that it should not run. Not doing this will result in State
1044                 // manager doing a default soft power off when asked for power
1045                 // off.
1046                 indicate_no_softoff_needed();
1047 
1048                 // Now request the shutdown
1049                 rc = initiate_state_transition(State::Host::Transition::Off);
1050             }
1051             else
1052             {
1053                 log<level::INFO>("Soft off is running, so let shutdown target "
1054                                  "stop the host");
1055             }
1056             break;
1057 
1058         case CMD_HARD_RESET:
1059         case CMD_POWER_CYCLE:
1060             // SPEC has a section that says certain implementations can trigger
1061             // PowerOn if power is Off when a command to power cycle is
1062             // requested
1063 
1064             // First create a file to indicate to the soft off application
1065             // that it should not run since this is a direct user initiated
1066             // power reboot request (i.e. a reboot request that is not
1067             // originating via a soft power off SMS request)
1068             indicate_no_softoff_needed();
1069 
1070             rc = initiate_state_transition(State::Host::Transition::Reboot);
1071             break;
1072 
1073         case CMD_SOFT_OFF_VIA_OVER_TEMP:
1074             // Request Host State Manager to do a soft power off
1075             rc = initiate_state_transition(State::Host::Transition::Off);
1076             break;
1077 
1078         default:
1079         {
1080             log<level::ERR>("Invalid Chassis Control command",
1081                             entry("CMD=0x%X", chassisControl));
1082             return ipmi::responseInvalidFieldRequest();
1083         }
1084     }
1085 
1086     return ((rc < 0) ? ipmi::responseUnspecifiedError()
1087                      : ipmi::responseSuccess());
1088 }
1089 
1090 /** @brief Return D-Bus connection string to enclosure identify LED object
1091  *
1092  *  @param[in, out] connection - connection to D-Bus object
1093  *  @return a IPMI return code
1094  */
1095 std::string getEnclosureIdentifyConnection()
1096 {
1097     // lookup enclosure_identify group owner(s) in mapper
1098     auto mapperCall = chassis::internal::dbus.new_method_call(
1099         ipmi::MAPPER_BUS_NAME, ipmi::MAPPER_OBJ, ipmi::MAPPER_INTF,
1100         "GetObject");
1101 
1102     mapperCall.append(identify_led_object_name);
1103     static const std::vector<std::string> interfaces = {
1104         "xyz.openbmc_project.Led.Group"};
1105     mapperCall.append(interfaces);
1106     auto mapperReply = chassis::internal::dbus.call(mapperCall);
1107     if (mapperReply.is_method_error())
1108     {
1109         log<level::ERR>("Chassis Identify: Error communicating to mapper.");
1110         elog<InternalFailure>();
1111     }
1112     std::vector<std::pair<std::string, std::vector<std::string>>> mapperResp;
1113     mapperReply.read(mapperResp);
1114 
1115     if (mapperResp.size() != encIdentifyObjectsSize)
1116     {
1117         log<level::ERR>(
1118             "Invalid number of enclosure identify objects.",
1119             entry("ENC_IDENTITY_OBJECTS_SIZE=%d", mapperResp.size()));
1120         elog<InternalFailure>();
1121     }
1122     auto pair = mapperResp[encIdentifyObjectsSize - 1];
1123     return pair.first;
1124 }
1125 
1126 /** @brief Turn On/Off enclosure identify LED
1127  *
1128  *  @param[in] flag - true to turn on LED, false to turn off
1129  *  @return a IPMI return code
1130  */
1131 void enclosureIdentifyLed(bool flag)
1132 {
1133     using namespace chassis::internal;
1134     std::string connection = std::move(getEnclosureIdentifyConnection());
1135     auto msg = std::string("enclosureIdentifyLed(") +
1136                boost::lexical_cast<std::string>(flag) + ")";
1137     log<level::DEBUG>(msg.c_str());
1138     auto led =
1139         dbus.new_method_call(connection.c_str(), identify_led_object_name,
1140                              "org.freedesktop.DBus.Properties", "Set");
1141     led.append("xyz.openbmc_project.Led.Group", "Asserted",
1142                sdbusplus::message::variant<bool>(flag));
1143     auto ledReply = dbus.call(led);
1144     if (ledReply.is_method_error())
1145     {
1146         log<level::ERR>("Chassis Identify: Error Setting State On/Off\n",
1147                         entry("LED_STATE=%d", flag));
1148         elog<InternalFailure>();
1149     }
1150 }
1151 
1152 /** @brief Callback method to turn off LED
1153  */
1154 void enclosureIdentifyLedOff()
1155 {
1156     try
1157     {
1158         enclosureIdentifyLed(false);
1159     }
1160     catch (const InternalFailure& e)
1161     {
1162         report<InternalFailure>();
1163     }
1164 }
1165 
1166 /** @brief Create timer to turn on and off the enclosure LED
1167  */
1168 void createIdentifyTimer()
1169 {
1170     if (!identifyTimer)
1171     {
1172         identifyTimer =
1173             std::make_unique<phosphor::Timer>(enclosureIdentifyLedOff);
1174     }
1175 }
1176 
1177 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
1178                                     std::optional<uint8_t> force)
1179 {
1180     uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT);
1181     bool forceIdentify = force.value_or(0) & 0x01;
1182 
1183     if (identifyInterval || forceIdentify)
1184     {
1185         // stop the timer if already started;
1186         // for force identify we should not turn off LED
1187         identifyTimer->stop();
1188         try
1189         {
1190             enclosureIdentifyLed(true);
1191         }
1192         catch (const InternalFailure& e)
1193         {
1194             report<InternalFailure>();
1195             return ipmi::responseResponseError();
1196         }
1197 
1198         if (forceIdentify)
1199         {
1200             return ipmi::responseSuccess();
1201         }
1202         // start the timer
1203         auto time = std::chrono::duration_cast<std::chrono::microseconds>(
1204             std::chrono::seconds(identifyInterval));
1205         identifyTimer->start(time);
1206     }
1207     else if (!identifyInterval)
1208     {
1209         identifyTimer->stop();
1210         enclosureIdentifyLedOff();
1211     }
1212     return ipmi::responseSuccess();
1213 }
1214 
1215 namespace boot_options
1216 {
1217 
1218 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
1219 using IpmiValue = uint8_t;
1220 constexpr auto ipmiDefault = 0;
1221 
1222 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
1223     {0x01, Source::Sources::Network},
1224     {0x02, Source::Sources::Disk},
1225     {0x05, Source::Sources::ExternalMedia},
1226     {ipmiDefault, Source::Sources::Default}};
1227 
1228 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
1229     {0x03, Mode::Modes::Safe},
1230     {0x06, Mode::Modes::Setup},
1231     {ipmiDefault, Mode::Modes::Regular}};
1232 
1233 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
1234     {Source::Sources::Network, 0x01},
1235     {Source::Sources::Disk, 0x02},
1236     {Source::Sources::ExternalMedia, 0x05},
1237     {Source::Sources::Default, ipmiDefault}};
1238 
1239 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
1240     {Mode::Modes::Safe, 0x03},
1241     {Mode::Modes::Setup, 0x06},
1242     {Mode::Modes::Regular, ipmiDefault}};
1243 
1244 } // namespace boot_options
1245 
1246 /** @brief Set the property value for boot source
1247  *  @param[in] source - boot source value
1248  *  @return On failure return IPMI error.
1249  */
1250 static ipmi_ret_t setBootSource(const Source::Sources& source)
1251 {
1252     using namespace chassis::internal;
1253     using namespace chassis::internal::cache;
1254     sdbusplus::message::variant<std::string> property =
1255         convertForMessage(source);
1256     auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1257     const auto& bootSourceSetting = std::get<settings::Path>(bootSetting);
1258     auto method = dbus.new_method_call(
1259         objects.service(bootSourceSetting, bootSourceIntf).c_str(),
1260         bootSourceSetting.c_str(), ipmi::PROP_INTF, "Set");
1261     method.append(bootSourceIntf, "BootSource", property);
1262     auto reply = dbus.call(method);
1263     if (reply.is_method_error())
1264     {
1265         log<level::ERR>("Error in BootSource Set");
1266         report<InternalFailure>();
1267         return IPMI_CC_UNSPECIFIED_ERROR;
1268     }
1269     return IPMI_CC_OK;
1270 }
1271 
1272 /** @brief Set the property value for boot mode
1273  *  @param[in] mode - boot mode value
1274  *  @return On failure return IPMI error.
1275  */
1276 static ipmi_ret_t setBootMode(const Mode::Modes& mode)
1277 {
1278     using namespace chassis::internal;
1279     using namespace chassis::internal::cache;
1280     sdbusplus::message::variant<std::string> property = convertForMessage(mode);
1281     auto bootSetting = settings::boot::setting(objects, bootModeIntf);
1282     const auto& bootModeSetting = std::get<settings::Path>(bootSetting);
1283     auto method = dbus.new_method_call(
1284         objects.service(bootModeSetting, bootModeIntf).c_str(),
1285         bootModeSetting.c_str(), ipmi::PROP_INTF, "Set");
1286     method.append(bootModeIntf, "BootMode", property);
1287     auto reply = dbus.call(method);
1288     if (reply.is_method_error())
1289     {
1290         log<level::ERR>("Error in BootMode Set");
1291         report<InternalFailure>();
1292         return IPMI_CC_UNSPECIFIED_ERROR;
1293     }
1294     return IPMI_CC_OK;
1295 }
1296 
1297 ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1298                                              ipmi_request_t request,
1299                                              ipmi_response_t response,
1300                                              ipmi_data_len_t data_len,
1301                                              ipmi_context_t context)
1302 {
1303     using namespace boot_options;
1304     ipmi_ret_t rc = IPMI_CC_PARM_NOT_SUPPORTED;
1305     char* p = NULL;
1306     get_sys_boot_options_response_t* resp =
1307         (get_sys_boot_options_response_t*)response;
1308     get_sys_boot_options_t* reqptr = (get_sys_boot_options_t*)request;
1309     IpmiValue bootOption = ipmiDefault;
1310 
1311     std::memset(resp, 0, sizeof(*resp));
1312     resp->version = SET_PARM_VERSION;
1313     resp->parm = 5;
1314     resp->data[0] = SET_PARM_BOOT_FLAGS_VALID_ONE_TIME;
1315 
1316     /*
1317      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1318      * This is the only parameter used by petitboot.
1319      */
1320     if (reqptr->parameter ==
1321         static_cast<uint8_t>(BootOptionParameter::BOOT_FLAGS))
1322     {
1323 
1324         *data_len = static_cast<uint8_t>(BootOptionResponseSize::BOOT_FLAGS);
1325         using namespace chassis::internal;
1326         using namespace chassis::internal::cache;
1327 
1328         try
1329         {
1330             auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1331             const auto& bootSourceSetting =
1332                 std::get<settings::Path>(bootSetting);
1333             auto oneTimeEnabled =
1334                 std::get<settings::boot::OneTimeEnabled>(bootSetting);
1335             auto method = dbus.new_method_call(
1336                 objects.service(bootSourceSetting, bootSourceIntf).c_str(),
1337                 bootSourceSetting.c_str(), ipmi::PROP_INTF, "Get");
1338             method.append(bootSourceIntf, "BootSource");
1339             auto reply = dbus.call(method);
1340             if (reply.is_method_error())
1341             {
1342                 log<level::ERR>("Error in BootSource Get");
1343                 report<InternalFailure>();
1344                 *data_len = 0;
1345                 return IPMI_CC_UNSPECIFIED_ERROR;
1346             }
1347             sdbusplus::message::variant<std::string> result;
1348             reply.read(result);
1349             auto bootSource =
1350                 Source::convertSourcesFromString(std::get<std::string>(result));
1351 
1352             bootSetting = settings::boot::setting(objects, bootModeIntf);
1353             const auto& bootModeSetting = std::get<settings::Path>(bootSetting);
1354             method = dbus.new_method_call(
1355                 objects.service(bootModeSetting, bootModeIntf).c_str(),
1356                 bootModeSetting.c_str(), ipmi::PROP_INTF, "Get");
1357             method.append(bootModeIntf, "BootMode");
1358             reply = dbus.call(method);
1359             if (reply.is_method_error())
1360             {
1361                 log<level::ERR>("Error in BootMode Get");
1362                 report<InternalFailure>();
1363                 *data_len = 0;
1364                 return IPMI_CC_UNSPECIFIED_ERROR;
1365             }
1366             reply.read(result);
1367             auto bootMode =
1368                 Mode::convertModesFromString(std::get<std::string>(result));
1369 
1370             bootOption = sourceDbusToIpmi.at(bootSource);
1371             if ((Mode::Modes::Regular == bootMode) &&
1372                 (Source::Sources::Default == bootSource))
1373             {
1374                 bootOption = ipmiDefault;
1375             }
1376             else if (Source::Sources::Default == bootSource)
1377             {
1378                 bootOption = modeDbusToIpmi.at(bootMode);
1379             }
1380             resp->data[1] = (bootOption << 2);
1381 
1382             resp->data[0] = oneTimeEnabled
1383                                 ? SET_PARM_BOOT_FLAGS_VALID_ONE_TIME
1384                                 : SET_PARM_BOOT_FLAGS_VALID_PERMANENT;
1385 
1386             rc = IPMI_CC_OK;
1387         }
1388         catch (InternalFailure& e)
1389         {
1390             report<InternalFailure>();
1391             *data_len = 0;
1392             return IPMI_CC_UNSPECIFIED_ERROR;
1393         }
1394     }
1395     else if (reqptr->parameter ==
1396              static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS))
1397     {
1398 
1399         *data_len =
1400             static_cast<uint8_t>(BootOptionResponseSize::OPAL_NETWORK_SETTINGS);
1401 
1402         resp->parm =
1403             static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS);
1404 
1405         int ret = getHostNetworkData(resp);
1406 
1407         if (ret < 0)
1408         {
1409 
1410             log<level::ERR>(
1411                 "getHostNetworkData failed for get_sys_boot_options.");
1412             rc = IPMI_CC_UNSPECIFIED_ERROR;
1413         }
1414         else
1415             rc = IPMI_CC_OK;
1416     }
1417 
1418     else
1419     {
1420         log<level::ERR>("Unsupported parameter",
1421                         entry("PARAM=0x%x", reqptr->parameter));
1422     }
1423 
1424     if (p)
1425         free(p);
1426 
1427     if (rc == IPMI_CC_OK)
1428     {
1429         *data_len += 2;
1430     }
1431 
1432     return rc;
1433 }
1434 
1435 ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1436                                              ipmi_request_t request,
1437                                              ipmi_response_t response,
1438                                              ipmi_data_len_t data_len,
1439                                              ipmi_context_t context)
1440 {
1441     using namespace boot_options;
1442     ipmi_ret_t rc = IPMI_CC_OK;
1443     set_sys_boot_options_t* reqptr = (set_sys_boot_options_t*)request;
1444 
1445     std::printf("IPMI SET_SYS_BOOT_OPTIONS reqptr->parameter =[%d]\n",
1446                 reqptr->parameter);
1447 
1448     // This IPMI command does not have any resposne data
1449     *data_len = 0;
1450 
1451     /*  000101
1452      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1453      * This is the only parameter used by petitboot.
1454      */
1455 
1456     if (reqptr->parameter == (uint8_t)BootOptionParameter::BOOT_FLAGS)
1457     {
1458         IpmiValue bootOption = ((reqptr->data[1] & 0x3C) >> 2);
1459         using namespace chassis::internal;
1460         using namespace chassis::internal::cache;
1461         auto oneTimeEnabled = false;
1462         constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable";
1463         constexpr auto oneTimePath =
1464             "/xyz/openbmc_project/control/host0/boot/one_time";
1465 
1466         try
1467         {
1468             bool permanent =
1469                 (reqptr->data[0] & SET_PARM_BOOT_FLAGS_PERMANENT) ==
1470                 SET_PARM_BOOT_FLAGS_PERMANENT;
1471 
1472             auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1473 
1474             oneTimeEnabled =
1475                 std::get<settings::boot::OneTimeEnabled>(bootSetting);
1476 
1477             /*
1478              * Check if the current boot setting is onetime or permanent, if the
1479              * request in the command is otherwise, then set the "Enabled"
1480              * property in one_time object path to 'True' to indicate onetime
1481              * and 'False' to indicate permanent.
1482              *
1483              * Once the onetime/permanent setting is applied, then the bootMode
1484              * and bootSource is updated for the corresponding object.
1485              */
1486             if ((permanent && oneTimeEnabled) ||
1487                 (!permanent && !oneTimeEnabled))
1488             {
1489                 auto service = ipmi::getService(dbus, enabledIntf, oneTimePath);
1490 
1491                 ipmi::setDbusProperty(dbus, service, oneTimePath, enabledIntf,
1492                                       "Enabled", !permanent);
1493             }
1494 
1495             auto modeItr = modeIpmiToDbus.find(bootOption);
1496             auto sourceItr = sourceIpmiToDbus.find(bootOption);
1497             if (sourceIpmiToDbus.end() != sourceItr)
1498             {
1499                 rc = setBootSource(sourceItr->second);
1500                 if (rc != IPMI_CC_OK)
1501                 {
1502                     *data_len = 0;
1503                     return rc;
1504                 }
1505                 // If a set boot device is mapping to a boot source, then reset
1506                 // the boot mode D-Bus property to default.
1507                 // This way the ipmid code can determine which property is not
1508                 // at the default value
1509                 if (sourceItr->second != Source::Sources::Default)
1510                 {
1511                     setBootMode(Mode::Modes::Regular);
1512                 }
1513             }
1514             if (modeIpmiToDbus.end() != modeItr)
1515             {
1516                 rc = setBootMode(modeItr->second);
1517                 if (rc != IPMI_CC_OK)
1518                 {
1519                     *data_len = 0;
1520                     return rc;
1521                 }
1522                 // If a set boot device is mapping to a boot mode, then reset
1523                 // the boot source D-Bus property to default.
1524                 // This way the ipmid code can determine which property is not
1525                 // at the default value
1526                 if (modeItr->second != Mode::Modes::Regular)
1527                 {
1528                     setBootSource(Source::Sources::Default);
1529                 }
1530             }
1531         }
1532         catch (InternalFailure& e)
1533         {
1534             report<InternalFailure>();
1535             *data_len = 0;
1536             return IPMI_CC_UNSPECIFIED_ERROR;
1537         }
1538     }
1539     else if (reqptr->parameter ==
1540              (uint8_t)BootOptionParameter::OPAL_NETWORK_SETTINGS)
1541     {
1542 
1543         int ret = setHostNetworkData(reqptr);
1544         if (ret < 0)
1545         {
1546             log<level::ERR>(
1547                 "setHostNetworkData failed for set_sys_boot_options");
1548             rc = IPMI_CC_UNSPECIFIED_ERROR;
1549         }
1550     }
1551     else if (reqptr->parameter ==
1552              static_cast<uint8_t>(BootOptionParameter::BOOT_INFO))
1553     {
1554         // Handle parameter #4 and return command completed normally
1555         // (IPMI_CC_OK). There is no implementation in OpenBMC for this
1556         // parameter. This is added to support the ipmitool command `chassis
1557         // bootdev` which sends set on parameter #4, before setting the boot
1558         // flags.
1559         rc = IPMI_CC_OK;
1560     }
1561     else
1562     {
1563         log<level::ERR>("Unsupported parameter",
1564                         entry("PARAM=0x%x", reqptr->parameter));
1565         rc = IPMI_CC_PARM_NOT_SUPPORTED;
1566     }
1567 
1568     return rc;
1569 }
1570 
1571 /** @brief implements Get POH counter command
1572  *  @parameter
1573  *   -  none
1574  *  @returns IPMI completion code plus response data
1575  *   - minPerCount - Minutes per count
1576  *   - counterReading - counter reading
1577  */
1578 ipmi::RspType<uint8_t, // Minutes per count
1579               uint32_t // Counter reading
1580               >
1581     ipmiGetPOHCounter()
1582 {
1583     // sd_bus error
1584     try
1585     {
1586         return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount),
1587                                      getPOHCounter());
1588     }
1589     catch (std::exception& e)
1590     {
1591         log<level::ERR>(e.what());
1592         return ipmi::responseUnspecifiedError();
1593     }
1594 }
1595 
1596 ipmi_ret_t ipmi_chassis_set_power_restore_policy(
1597     ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1598     ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1599 {
1600     auto* reqptr = reinterpret_cast<uint8_t*>(request);
1601     auto* resptr = reinterpret_cast<uint8_t*>(response);
1602     uint8_t reqPolicy = 0;
1603 
1604     power_policy::DbusValue value =
1605         power_policy::RestorePolicy::Policy::AlwaysOff;
1606 
1607     if (*data_len != power_policy::setPolicyReqLen)
1608     {
1609         phosphor::logging::log<level::ERR>("Unsupported request length",
1610                                            entry("LEN=0x%x", *data_len));
1611         *data_len = 0;
1612         return IPMI_CC_REQ_DATA_LEN_INVALID;
1613     }
1614 
1615     if (*reqptr > power_policy::noChange)
1616     {
1617         phosphor::logging::log<level::ERR>("Reserved request parameter",
1618                                            entry("REQ=0x%x", *reqptr));
1619         *data_len = 0;
1620         return IPMI_CC_PARM_OUT_OF_RANGE;
1621     }
1622 
1623     reqPolicy = *reqptr & power_policy::policyBitMask;
1624     if (reqPolicy == power_policy::noChange)
1625     {
1626         // just return the supported policy
1627         *resptr = power_policy::allSupport;
1628         *data_len = power_policy::setPolicyReqLen;
1629         return IPMI_CC_OK;
1630     }
1631 
1632     for (auto const& it : power_policy::dbusToIpmi)
1633     {
1634         if (it.second == reqPolicy)
1635         {
1636             value = it.first;
1637             break;
1638         }
1639     }
1640 
1641     try
1642     {
1643         const settings::Path& powerRestoreSetting =
1644             chassis::internal::cache::objects.map
1645                 .at(chassis::internal::powerRestoreIntf)
1646                 .front();
1647         sdbusplus::message::variant<std::string> property =
1648             convertForMessage(value);
1649 
1650         auto method = chassis::internal::dbus.new_method_call(
1651             chassis::internal::cache::objects
1652                 .service(powerRestoreSetting,
1653                          chassis::internal::powerRestoreIntf)
1654                 .c_str(),
1655             powerRestoreSetting.c_str(), ipmi::PROP_INTF, "Set");
1656 
1657         method.append(chassis::internal::powerRestoreIntf, "PowerRestorePolicy",
1658                       property);
1659         auto reply = chassis::internal::dbus.call(method);
1660         if (reply.is_method_error())
1661         {
1662             phosphor::logging::log<level::ERR>("Unspecified Error");
1663             *data_len = 0;
1664             return IPMI_CC_UNSPECIFIED_ERROR;
1665         }
1666     }
1667     catch (InternalFailure& e)
1668     {
1669         report<InternalFailure>();
1670         *data_len = 0;
1671         return IPMI_CC_UNSPECIFIED_ERROR;
1672     }
1673 
1674     *resptr = power_policy::allSupport;
1675     *data_len = power_policy::setPolicyReqLen;
1676     return IPMI_CC_OK;
1677 }
1678 
1679 void register_netfn_chassis_functions()
1680 {
1681     createIdentifyTimer();
1682 
1683     // <Wildcard Command>
1684     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_WILDCARD, NULL,
1685                            ipmi_chassis_wildcard, PRIVILEGE_USER);
1686 
1687     // Get Chassis Capabilities
1688     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_CHASSIS_CAP, NULL,
1689                            ipmi_get_chassis_cap, PRIVILEGE_USER);
1690 
1691     // Set Chassis Capabilities
1692     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_CHASSIS_CAP, NULL,
1693                            ipmi_set_chassis_cap, PRIVILEGE_USER);
1694 
1695     // <Get System Boot Options>
1696     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS, NULL,
1697                            ipmi_chassis_get_sys_boot_options,
1698                            PRIVILEGE_OPERATOR);
1699 
1700     // <Get Chassis Status>
1701     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_CHASSIS_STATUS, NULL,
1702                            ipmi_get_chassis_status, PRIVILEGE_USER);
1703 
1704     // <Chassis Control>
1705     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1706                           ipmi::chassis::cmdChassisControl,
1707                           ipmi::Privilege::Operator, ipmiChassisControl);
1708 
1709     // <Chassis Identify>
1710     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1711                           ipmi::chassis::cmdChassisIdentify,
1712                           ipmi::Privilege::Operator, ipmiChassisIdentify);
1713 
1714     // <Set System Boot Options>
1715     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_SYS_BOOT_OPTIONS, NULL,
1716                            ipmi_chassis_set_sys_boot_options,
1717                            PRIVILEGE_OPERATOR);
1718     // <Get POH Counter>
1719     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1720                           ipmi::chassis::cmdGetPohCounter,
1721                           ipmi::Privilege::User, ipmiGetPOHCounter);
1722 
1723     // <Set Power Restore Policy>
1724     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_RESTORE_POLICY, NULL,
1725                            ipmi_chassis_set_power_restore_policy,
1726                            PRIVILEGE_OPERATOR);
1727 }
1728