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 /**
123  * @struct Get POH counter command response data
124  */
125 struct GetPOHCountResponse
126 {
127     uint8_t minPerCount;       ///< Minutes per count
128     uint8_t counterReading[4]; ///< Counter reading
129 } __attribute__((packed));
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 ipmi_ret_t ipmi_set_chassis_cap(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
606                                 ipmi_request_t request,
607                                 ipmi_response_t response,
608                                 ipmi_data_len_t data_len,
609                                 ipmi_context_t context)
610 {
611     ipmi_ret_t rc = IPMI_CC_OK;
612 
613     if (*data_len != sizeof(ipmi_chassis_cap_t))
614     {
615         log<level::ERR>("Unsupported request length",
616                         entry("LEN=0x%x", *data_len));
617         *data_len = 0;
618         return IPMI_CC_REQ_DATA_LEN_INVALID;
619     }
620 
621     ipmi_chassis_cap_t* chassisCap = static_cast<ipmi_chassis_cap_t*>(request);
622 
623     *data_len = 0;
624 
625     // check input data
626     if (0 != (chassisCap->cap_flags & ~chassisCapFlagMask))
627     {
628         log<level::ERR>("Unsupported request parameter(CAP Flags)",
629                         entry("REQ=0x%x", chassisCap->cap_flags));
630         return IPMI_CC_INVALID_FIELD_REQUEST;
631     }
632 
633     if (0 != (chassisCap->fru_info_dev_addr & ~chassisCapAddrMask))
634     {
635         log<level::ERR>("Unsupported request parameter(FRU Addr)",
636                         entry("REQ=0x%x", chassisCap->fru_info_dev_addr));
637         return IPMI_CC_INVALID_FIELD_REQUEST;
638     }
639 
640     if (0 != (chassisCap->sdr_dev_addr & ~chassisCapAddrMask))
641     {
642         log<level::ERR>("Unsupported request parameter(SDR Addr)",
643                         entry("REQ=0x%x", chassisCap->sdr_dev_addr));
644         return IPMI_CC_INVALID_FIELD_REQUEST;
645     }
646 
647     if (0 != (chassisCap->sel_dev_addr & ~chassisCapAddrMask))
648     {
649         log<level::ERR>("Unsupported request parameter(SEL Addr)",
650                         entry("REQ=0x%x", chassisCap->sel_dev_addr));
651         return IPMI_CC_INVALID_FIELD_REQUEST;
652     }
653 
654     if (0 != (chassisCap->system_management_dev_addr & ~chassisCapAddrMask))
655     {
656         log<level::ERR>(
657             "Unsupported request parameter(SM Addr)",
658             entry("REQ=0x%x", chassisCap->system_management_dev_addr));
659         return IPMI_CC_INVALID_FIELD_REQUEST;
660     }
661 
662     if (0 != (chassisCap->bridge_dev_addr & ~chassisCapAddrMask))
663     {
664         log<level::ERR>("Unsupported request parameter(Bridge Addr)",
665                         entry("REQ=0x%x", chassisCap->bridge_dev_addr));
666         return IPMI_CC_INVALID_FIELD_REQUEST;
667     }
668 
669     try
670     {
671         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
672         ipmi::DbusObjectInfo chassisCapObject =
673             ipmi::getDbusObject(bus, chassisCapIntf);
674 
675         ipmi::setDbusProperty(bus, chassisCapObject.second,
676                               chassisCapObject.first, chassisCapIntf,
677                               chassisCapFlagsProp, chassisCap->cap_flags);
678 
679         ipmi::setDbusProperty(bus, chassisCapObject.second,
680                               chassisCapObject.first, chassisCapIntf,
681                               chassisFRUDevAddrProp,
682                               chassisCap->fru_info_dev_addr);
683 
684         ipmi::setDbusProperty(bus, chassisCapObject.second,
685                               chassisCapObject.first, chassisCapIntf,
686                               chassisSDRDevAddrProp, chassisCap->sdr_dev_addr);
687 
688         ipmi::setDbusProperty(bus, chassisCapObject.second,
689                               chassisCapObject.first, chassisCapIntf,
690                               chassisSELDevAddrProp, chassisCap->sel_dev_addr);
691 
692         ipmi::setDbusProperty(bus, chassisCapObject.second,
693                               chassisCapObject.first, chassisCapIntf,
694                               chassisSMDevAddrProp,
695                               chassisCap->system_management_dev_addr);
696 
697         ipmi::setDbusProperty(bus, chassisCapObject.second,
698                               chassisCapObject.first, chassisCapIntf,
699                               chassisBridgeDevAddrProp,
700                               chassisCap->bridge_dev_addr);
701     }
702     catch (std::exception& e)
703     {
704         log<level::ERR>(e.what());
705         rc = IPMI_CC_UNSPECIFIED_ERROR;
706         return rc;
707     }
708 
709     return rc;
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 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 static constexpr uint8_t policyBitMask = 0x07;
785 static constexpr uint8_t setPolicyReqLen = 1;
786 } // namespace power_policy
787 
788 //----------------------------------------------------------------------
789 // Get Chassis Status commands
790 //----------------------------------------------------------------------
791 ipmi_ret_t ipmi_get_chassis_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
792                                    ipmi_request_t request,
793                                    ipmi_response_t response,
794                                    ipmi_data_len_t data_len,
795                                    ipmi_context_t context)
796 {
797     const char* objname = "/org/openbmc/control/power0";
798     const char* intf = "org.openbmc.control.Power";
799 
800     sd_bus* bus = NULL;
801     sd_bus_message* reply = NULL;
802     int r = 0;
803     int pgood = 0;
804     char* busname = NULL;
805     ipmi_ret_t rc = IPMI_CC_OK;
806     ipmi_get_chassis_status_t chassis_status{};
807 
808     uint8_t s = 0;
809 
810     using namespace chassis::internal;
811     using namespace chassis::internal::cache;
812     using namespace power_policy;
813 
814     const auto& powerRestoreSetting = objects.map.at(powerRestoreIntf).front();
815     auto method = dbus.new_method_call(
816         objects.service(powerRestoreSetting, powerRestoreIntf).c_str(),
817         powerRestoreSetting.c_str(), ipmi::PROP_INTF, "Get");
818     method.append(powerRestoreIntf, "PowerRestorePolicy");
819     auto resp = dbus.call(method);
820     if (resp.is_method_error())
821     {
822         log<level::ERR>("Error in PowerRestorePolicy Get");
823         report<InternalFailure>();
824         *data_len = 0;
825         return IPMI_CC_UNSPECIFIED_ERROR;
826     }
827     sdbusplus::message::variant<std::string> result;
828     resp.read(result);
829     auto powerRestore =
830         RestorePolicy::convertPolicyFromString(std::get<std::string>(result));
831 
832     *data_len = 4;
833 
834     bus = ipmid_get_sd_bus_connection();
835 
836     r = mapper_get_service(bus, objname, &busname);
837     if (r < 0)
838     {
839         log<level::ERR>("Failed to get bus name", entry("ERRNO=0x%X", -r));
840         rc = IPMI_CC_UNSPECIFIED_ERROR;
841         goto finish;
842     }
843 
844     r = sd_bus_get_property(bus, busname, objname, intf, "pgood", NULL, &reply,
845                             "i");
846     if (r < 0)
847     {
848         log<level::ERR>("Failed to call sd_bus_get_property",
849                         entry("PROPERTY=%s", "pgood"), entry("ERRNO=0x%X", -r),
850                         entry("BUS=%s", busname), entry("PATH=%s", objname),
851                         entry("INTERFACE=%s", intf));
852         rc = IPMI_CC_UNSPECIFIED_ERROR;
853         goto finish;
854     }
855 
856     r = sd_bus_message_read(reply, "i", &pgood);
857     if (r < 0)
858     {
859         log<level::ERR>("Failed to read sensor:", entry("ERRNO=0x%X", -r));
860         rc = IPMI_CC_UNSPECIFIED_ERROR;
861         goto finish;
862     }
863 
864     s = dbusToIpmi.at(powerRestore);
865 
866     // Current Power State
867     // [7] reserved
868     // [6..5] power restore policy
869     //          00b = chassis stays powered off after AC/mains returns
870     //          01b = after AC returns, power is restored to the state that was
871     //          in effect when AC/mains was lost.
872     //          10b = chassis always powers up after AC/mains returns
873     //          11b = unknow
874     //        Set to 00b, by observing the hardware behavior.
875     //        Do we need to define a dbus property to identify the restore
876     //        policy?
877 
878     // [4] power control fault
879     //       1b = controller attempted to turn system power on or off, but
880     //       system did not enter desired state.
881     //       Set to 0b, since We don't support it..
882 
883     // [3] power fault
884     //       1b = fault detected in main power subsystem.
885     //       set to 0b. for we don't support it.
886 
887     // [2] 1b = interlock (chassis is presently shut down because a chassis
888     //       panel interlock switch is active). (IPMI 1.5)
889     //       set to 0b,  for we don't support it.
890 
891     // [1] power overload
892     //      1b = system shutdown because of power overload condition.
893     //       set to 0b,  for we don't support it.
894 
895     // [0] power is on
896     //       1b = system power is on
897     //       0b = system power is off(soft-off S4/S5, or mechanical off)
898 
899     chassis_status.cur_power_state = ((s & 0x3) << 5) | (pgood & 0x1);
900 
901     // Last Power Event
902     // [7..5] – reserved
903     // [4] – 1b = last ‘Power is on’ state was entered via IPMI command
904     // [3] – 1b = last power down caused by power fault
905     // [2] – 1b = last power down caused by a power interlock being activated
906     // [1] – 1b = last power down caused by a Power overload
907     // [0] – 1b = AC failed
908     // set to 0x0,  for we don't support these fields.
909 
910     chassis_status.last_power_event = 0;
911 
912     // Misc. Chassis State
913     // [7] – reserved
914     // [6] – 1b = Chassis Identify command and state info supported (Optional)
915     //       0b = Chassis Identify command support unspecified via this command.
916     //       (The Get Command Support command , if implemented, would still
917     //       indicate support for the Chassis Identify command)
918     // [5..4] – Chassis Identify State. Mandatory when bit[6] =1b, reserved
919     // (return
920     //          as 00b) otherwise. Returns the present chassis identify state.
921     //           Refer to the Chassis Identify command for more info.
922     //         00b = chassis identify state = Off
923     //         01b = chassis identify state = Temporary(timed) On
924     //         10b = chassis identify state = Indefinite On
925     //         11b = reserved
926     // [3] – 1b = Cooling/fan fault detected
927     // [2] – 1b = Drive Fault
928     // [1] – 1b = Front Panel Lockout active (power off and reset via chassis
929     //       push-buttons disabled.)
930     // [0] – 1b = Chassis Intrusion active
931     //  set to 0,  for we don't support them.
932     chassis_status.misc_power_state = 0;
933 
934     //  Front Panel Button Capabilities and disable/enable status(Optional)
935     //  set to 0,  for we don't support them.
936     chassis_status.front_panel_button_cap_status = 0;
937 
938     // Pack the actual response
939     std::memcpy(response, &chassis_status, *data_len);
940 
941 finish:
942     free(busname);
943     reply = sd_bus_message_unref(reply);
944 
945     return rc;
946 }
947 
948 //-------------------------------------------------------------
949 // Send a command to SoftPowerOff application to stop any timer
950 //-------------------------------------------------------------
951 int stop_soft_off_timer()
952 {
953     constexpr auto iface = "org.freedesktop.DBus.Properties";
954     constexpr auto soft_off_iface = "xyz.openbmc_project.Ipmi.Internal."
955                                     "SoftPowerOff";
956 
957     constexpr auto property = "ResponseReceived";
958     constexpr auto value = "xyz.openbmc_project.Ipmi.Internal."
959                            "SoftPowerOff.HostResponse.HostShutdown";
960 
961     // Get the system bus where most system services are provided.
962     auto bus = ipmid_get_sd_bus_connection();
963 
964     // Get the service name
965     // TODO openbmc/openbmc#1661 - Mapper refactor
966     //
967     // See openbmc/openbmc#1743 for some details but high level summary is that
968     // for now the code will directly call the soft off interface due to a
969     // race condition with mapper usage
970     //
971     // char *busname = nullptr;
972     // auto r = mapper_get_service(bus, SOFTOFF_OBJPATH, &busname);
973     // if (r < 0)
974     //{
975     //    fprintf(stderr, "Failed to get %s bus name: %s\n",
976     //            SOFTOFF_OBJPATH, -r);
977     //    return r;
978     //}
979 
980     // No error object or reply expected.
981     int rc = sd_bus_call_method(bus, SOFTOFF_BUSNAME, SOFTOFF_OBJPATH, iface,
982                                 "Set", nullptr, nullptr, "ssv", soft_off_iface,
983                                 property, "s", value);
984     if (rc < 0)
985     {
986         log<level::ERR>("Failed to set property in SoftPowerOff object",
987                         entry("ERRNO=0x%X", -rc));
988     }
989 
990     // TODO openbmc/openbmc#1661 - Mapper refactor
991     // free(busname);
992     return rc;
993 }
994 
995 //----------------------------------------------------------------------
996 // Create file to indicate there is no need for softoff notification to host
997 //----------------------------------------------------------------------
998 void indicate_no_softoff_needed()
999 {
1000     fs::path path{HOST_INBAND_REQUEST_DIR};
1001     if (!fs::is_directory(path))
1002     {
1003         fs::create_directory(path);
1004     }
1005 
1006     // Add the host instance (default 0 for now) to the file name
1007     std::string file{HOST_INBAND_REQUEST_FILE};
1008     auto size = std::snprintf(nullptr, 0, file.c_str(), 0);
1009     size++; // null
1010     std::unique_ptr<char[]> buf(new char[size]);
1011     std::snprintf(buf.get(), size, file.c_str(), 0);
1012 
1013     // Append file name to directory and create it
1014     path /= buf.get();
1015     std::ofstream(path.c_str());
1016 }
1017 
1018 //----------------------------------------------------------------------
1019 // Chassis Control commands
1020 //----------------------------------------------------------------------
1021 ipmi_ret_t ipmi_chassis_control(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1022                                 ipmi_request_t request,
1023                                 ipmi_response_t response,
1024                                 ipmi_data_len_t data_len,
1025                                 ipmi_context_t context)
1026 {
1027     // Error from power off.
1028     int rc = 0;
1029 
1030     // No response for this command.
1031     *data_len = 0;
1032 
1033     // Catch the actual operaton by peeking into request buffer
1034     uint8_t chassis_ctrl_cmd = *(uint8_t*)request;
1035 
1036     switch (chassis_ctrl_cmd)
1037     {
1038         case CMD_POWER_ON:
1039             rc = initiate_state_transition(State::Host::Transition::On);
1040             break;
1041         case CMD_POWER_OFF:
1042             // This path would be hit in 2 conditions.
1043             // 1: When user asks for power off using ipmi chassis command 0x04
1044             // 2: Host asking for power off post shutting down.
1045 
1046             // If it's a host requested power off, then need to nudge Softoff
1047             // application that it needs to stop the watchdog timer if running.
1048             // If it is a user requested power off, then this is not really
1049             // needed. But then we need to differentiate between user and host
1050             // calling this same command
1051 
1052             // For now, we are going ahead with trying to nudge the soft off and
1053             // interpret the failure to do so as a non softoff case
1054             rc = stop_soft_off_timer();
1055 
1056             // Only request the Off transition if the soft power off
1057             // application is not running
1058             if (rc < 0)
1059             {
1060                 // First create a file to indicate to the soft off application
1061                 // that it should not run. Not doing this will result in State
1062                 // manager doing a default soft power off when asked for power
1063                 // off.
1064                 indicate_no_softoff_needed();
1065 
1066                 // Now request the shutdown
1067                 rc = initiate_state_transition(State::Host::Transition::Off);
1068             }
1069             else
1070             {
1071                 log<level::INFO>("Soft off is running, so let shutdown target "
1072                                  "stop the host");
1073             }
1074             break;
1075 
1076         case CMD_HARD_RESET:
1077         case CMD_POWER_CYCLE:
1078             // SPEC has a section that says certain implementations can trigger
1079             // PowerOn if power is Off when a command to power cycle is
1080             // requested
1081 
1082             // First create a file to indicate to the soft off application
1083             // that it should not run since this is a direct user initiated
1084             // power reboot request (i.e. a reboot request that is not
1085             // originating via a soft power off SMS request)
1086             indicate_no_softoff_needed();
1087 
1088             rc = initiate_state_transition(State::Host::Transition::Reboot);
1089             break;
1090 
1091         case CMD_SOFT_OFF_VIA_OVER_TEMP:
1092             // Request Host State Manager to do a soft power off
1093             rc = initiate_state_transition(State::Host::Transition::Off);
1094             break;
1095 
1096         default:
1097         {
1098             log<level::ERR>("Invalid Chassis Control command",
1099                             entry("CMD=0x%X", chassis_ctrl_cmd));
1100             rc = -1;
1101         }
1102     }
1103 
1104     return ((rc < 0) ? IPMI_CC_INVALID : IPMI_CC_OK);
1105 }
1106 
1107 /** @brief Return D-Bus connection string to enclosure identify LED object
1108  *
1109  *  @param[in, out] connection - connection to D-Bus object
1110  *  @return a IPMI return code
1111  */
1112 std::string getEnclosureIdentifyConnection()
1113 {
1114     // lookup enclosure_identify group owner(s) in mapper
1115     auto mapperCall = chassis::internal::dbus.new_method_call(
1116         ipmi::MAPPER_BUS_NAME, ipmi::MAPPER_OBJ, ipmi::MAPPER_INTF,
1117         "GetObject");
1118 
1119     mapperCall.append(identify_led_object_name);
1120     static const std::vector<std::string> interfaces = {
1121         "xyz.openbmc_project.Led.Group"};
1122     mapperCall.append(interfaces);
1123     auto mapperReply = chassis::internal::dbus.call(mapperCall);
1124     if (mapperReply.is_method_error())
1125     {
1126         log<level::ERR>("Chassis Identify: Error communicating to mapper.");
1127         elog<InternalFailure>();
1128     }
1129     std::vector<std::pair<std::string, std::vector<std::string>>> mapperResp;
1130     mapperReply.read(mapperResp);
1131 
1132     if (mapperResp.size() != encIdentifyObjectsSize)
1133     {
1134         log<level::ERR>(
1135             "Invalid number of enclosure identify objects.",
1136             entry("ENC_IDENTITY_OBJECTS_SIZE=%d", mapperResp.size()));
1137         elog<InternalFailure>();
1138     }
1139     auto pair = mapperResp[encIdentifyObjectsSize - 1];
1140     return pair.first;
1141 }
1142 
1143 /** @brief Turn On/Off enclosure identify LED
1144  *
1145  *  @param[in] flag - true to turn on LED, false to turn off
1146  *  @return a IPMI return code
1147  */
1148 void enclosureIdentifyLed(bool flag)
1149 {
1150     using namespace chassis::internal;
1151     std::string connection = std::move(getEnclosureIdentifyConnection());
1152     auto msg = std::string("enclosureIdentifyLed(") +
1153                boost::lexical_cast<std::string>(flag) + ")";
1154     log<level::DEBUG>(msg.c_str());
1155     auto led =
1156         dbus.new_method_call(connection.c_str(), identify_led_object_name,
1157                              "org.freedesktop.DBus.Properties", "Set");
1158     led.append("xyz.openbmc_project.Led.Group", "Asserted",
1159                sdbusplus::message::variant<bool>(flag));
1160     auto ledReply = dbus.call(led);
1161     if (ledReply.is_method_error())
1162     {
1163         log<level::ERR>("Chassis Identify: Error Setting State On/Off\n",
1164                         entry("LED_STATE=%d", flag));
1165         elog<InternalFailure>();
1166     }
1167 }
1168 
1169 /** @brief Callback method to turn off LED
1170  */
1171 void enclosureIdentifyLedOff()
1172 {
1173     try
1174     {
1175         enclosureIdentifyLed(false);
1176     }
1177     catch (const InternalFailure& e)
1178     {
1179         report<InternalFailure>();
1180     }
1181 }
1182 
1183 /** @brief Create timer to turn on and off the enclosure LED
1184  */
1185 void createIdentifyTimer()
1186 {
1187     if (!identifyTimer)
1188     {
1189         identifyTimer =
1190             std::make_unique<phosphor::Timer>(enclosureIdentifyLedOff);
1191     }
1192 }
1193 
1194 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
1195                                     std::optional<uint8_t> force)
1196 {
1197     uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT);
1198     bool forceIdentify = force.value_or(0) & 0x01;
1199 
1200     if (identifyInterval || forceIdentify)
1201     {
1202         // stop the timer if already started;
1203         // for force identify we should not turn off LED
1204         identifyTimer->stop();
1205         try
1206         {
1207             enclosureIdentifyLed(true);
1208         }
1209         catch (const InternalFailure& e)
1210         {
1211             report<InternalFailure>();
1212             return ipmi::responseResponseError();
1213         }
1214 
1215         if (forceIdentify)
1216         {
1217             return ipmi::responseSuccess();
1218         }
1219         // start the timer
1220         auto time = std::chrono::duration_cast<std::chrono::microseconds>(
1221             std::chrono::seconds(identifyInterval));
1222         identifyTimer->start(time);
1223     }
1224     else if (!identifyInterval)
1225     {
1226         identifyTimer->stop();
1227         enclosureIdentifyLedOff();
1228     }
1229     return ipmi::responseSuccess();
1230 }
1231 
1232 namespace boot_options
1233 {
1234 
1235 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
1236 using IpmiValue = uint8_t;
1237 constexpr auto ipmiDefault = 0;
1238 
1239 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
1240     {0x01, Source::Sources::Network},
1241     {0x02, Source::Sources::Disk},
1242     {0x05, Source::Sources::ExternalMedia},
1243     {ipmiDefault, Source::Sources::Default}};
1244 
1245 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
1246     {0x03, Mode::Modes::Safe},
1247     {0x06, Mode::Modes::Setup},
1248     {ipmiDefault, Mode::Modes::Regular}};
1249 
1250 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
1251     {Source::Sources::Network, 0x01},
1252     {Source::Sources::Disk, 0x02},
1253     {Source::Sources::ExternalMedia, 0x05},
1254     {Source::Sources::Default, ipmiDefault}};
1255 
1256 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
1257     {Mode::Modes::Safe, 0x03},
1258     {Mode::Modes::Setup, 0x06},
1259     {Mode::Modes::Regular, ipmiDefault}};
1260 
1261 } // namespace boot_options
1262 
1263 /** @brief Set the property value for boot source
1264  *  @param[in] source - boot source value
1265  *  @return On failure return IPMI error.
1266  */
1267 static ipmi_ret_t setBootSource(const Source::Sources& source)
1268 {
1269     using namespace chassis::internal;
1270     using namespace chassis::internal::cache;
1271     sdbusplus::message::variant<std::string> property =
1272         convertForMessage(source);
1273     auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1274     const auto& bootSourceSetting = std::get<settings::Path>(bootSetting);
1275     auto method = dbus.new_method_call(
1276         objects.service(bootSourceSetting, bootSourceIntf).c_str(),
1277         bootSourceSetting.c_str(), ipmi::PROP_INTF, "Set");
1278     method.append(bootSourceIntf, "BootSource", property);
1279     auto reply = dbus.call(method);
1280     if (reply.is_method_error())
1281     {
1282         log<level::ERR>("Error in BootSource Set");
1283         report<InternalFailure>();
1284         return IPMI_CC_UNSPECIFIED_ERROR;
1285     }
1286     return IPMI_CC_OK;
1287 }
1288 
1289 /** @brief Set the property value for boot mode
1290  *  @param[in] mode - boot mode value
1291  *  @return On failure return IPMI error.
1292  */
1293 static ipmi_ret_t setBootMode(const Mode::Modes& mode)
1294 {
1295     using namespace chassis::internal;
1296     using namespace chassis::internal::cache;
1297     sdbusplus::message::variant<std::string> property = convertForMessage(mode);
1298     auto bootSetting = settings::boot::setting(objects, bootModeIntf);
1299     const auto& bootModeSetting = std::get<settings::Path>(bootSetting);
1300     auto method = dbus.new_method_call(
1301         objects.service(bootModeSetting, bootModeIntf).c_str(),
1302         bootModeSetting.c_str(), ipmi::PROP_INTF, "Set");
1303     method.append(bootModeIntf, "BootMode", property);
1304     auto reply = dbus.call(method);
1305     if (reply.is_method_error())
1306     {
1307         log<level::ERR>("Error in BootMode Set");
1308         report<InternalFailure>();
1309         return IPMI_CC_UNSPECIFIED_ERROR;
1310     }
1311     return IPMI_CC_OK;
1312 }
1313 
1314 ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1315                                              ipmi_request_t request,
1316                                              ipmi_response_t response,
1317                                              ipmi_data_len_t data_len,
1318                                              ipmi_context_t context)
1319 {
1320     using namespace boot_options;
1321     ipmi_ret_t rc = IPMI_CC_PARM_NOT_SUPPORTED;
1322     char* p = NULL;
1323     get_sys_boot_options_response_t* resp =
1324         (get_sys_boot_options_response_t*)response;
1325     get_sys_boot_options_t* reqptr = (get_sys_boot_options_t*)request;
1326     IpmiValue bootOption = ipmiDefault;
1327 
1328     std::memset(resp, 0, sizeof(*resp));
1329     resp->version = SET_PARM_VERSION;
1330     resp->parm = 5;
1331     resp->data[0] = SET_PARM_BOOT_FLAGS_VALID_ONE_TIME;
1332 
1333     /*
1334      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1335      * This is the only parameter used by petitboot.
1336      */
1337     if (reqptr->parameter ==
1338         static_cast<uint8_t>(BootOptionParameter::BOOT_FLAGS))
1339     {
1340 
1341         *data_len = static_cast<uint8_t>(BootOptionResponseSize::BOOT_FLAGS);
1342         using namespace chassis::internal;
1343         using namespace chassis::internal::cache;
1344 
1345         try
1346         {
1347             auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1348             const auto& bootSourceSetting =
1349                 std::get<settings::Path>(bootSetting);
1350             auto oneTimeEnabled =
1351                 std::get<settings::boot::OneTimeEnabled>(bootSetting);
1352             auto method = dbus.new_method_call(
1353                 objects.service(bootSourceSetting, bootSourceIntf).c_str(),
1354                 bootSourceSetting.c_str(), ipmi::PROP_INTF, "Get");
1355             method.append(bootSourceIntf, "BootSource");
1356             auto reply = dbus.call(method);
1357             if (reply.is_method_error())
1358             {
1359                 log<level::ERR>("Error in BootSource Get");
1360                 report<InternalFailure>();
1361                 *data_len = 0;
1362                 return IPMI_CC_UNSPECIFIED_ERROR;
1363             }
1364             sdbusplus::message::variant<std::string> result;
1365             reply.read(result);
1366             auto bootSource =
1367                 Source::convertSourcesFromString(std::get<std::string>(result));
1368 
1369             bootSetting = settings::boot::setting(objects, bootModeIntf);
1370             const auto& bootModeSetting = std::get<settings::Path>(bootSetting);
1371             method = dbus.new_method_call(
1372                 objects.service(bootModeSetting, bootModeIntf).c_str(),
1373                 bootModeSetting.c_str(), ipmi::PROP_INTF, "Get");
1374             method.append(bootModeIntf, "BootMode");
1375             reply = dbus.call(method);
1376             if (reply.is_method_error())
1377             {
1378                 log<level::ERR>("Error in BootMode Get");
1379                 report<InternalFailure>();
1380                 *data_len = 0;
1381                 return IPMI_CC_UNSPECIFIED_ERROR;
1382             }
1383             reply.read(result);
1384             auto bootMode =
1385                 Mode::convertModesFromString(std::get<std::string>(result));
1386 
1387             bootOption = sourceDbusToIpmi.at(bootSource);
1388             if ((Mode::Modes::Regular == bootMode) &&
1389                 (Source::Sources::Default == bootSource))
1390             {
1391                 bootOption = ipmiDefault;
1392             }
1393             else if (Source::Sources::Default == bootSource)
1394             {
1395                 bootOption = modeDbusToIpmi.at(bootMode);
1396             }
1397             resp->data[1] = (bootOption << 2);
1398 
1399             resp->data[0] = oneTimeEnabled
1400                                 ? SET_PARM_BOOT_FLAGS_VALID_ONE_TIME
1401                                 : SET_PARM_BOOT_FLAGS_VALID_PERMANENT;
1402 
1403             rc = IPMI_CC_OK;
1404         }
1405         catch (InternalFailure& e)
1406         {
1407             report<InternalFailure>();
1408             *data_len = 0;
1409             return IPMI_CC_UNSPECIFIED_ERROR;
1410         }
1411     }
1412     else if (reqptr->parameter ==
1413              static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS))
1414     {
1415 
1416         *data_len =
1417             static_cast<uint8_t>(BootOptionResponseSize::OPAL_NETWORK_SETTINGS);
1418 
1419         resp->parm =
1420             static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS);
1421 
1422         int ret = getHostNetworkData(resp);
1423 
1424         if (ret < 0)
1425         {
1426 
1427             log<level::ERR>(
1428                 "getHostNetworkData failed for get_sys_boot_options.");
1429             rc = IPMI_CC_UNSPECIFIED_ERROR;
1430         }
1431         else
1432             rc = IPMI_CC_OK;
1433     }
1434 
1435     else
1436     {
1437         log<level::ERR>("Unsupported parameter",
1438                         entry("PARAM=0x%x", reqptr->parameter));
1439     }
1440 
1441     if (p)
1442         free(p);
1443 
1444     if (rc == IPMI_CC_OK)
1445     {
1446         *data_len += 2;
1447     }
1448 
1449     return rc;
1450 }
1451 
1452 ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1453                                              ipmi_request_t request,
1454                                              ipmi_response_t response,
1455                                              ipmi_data_len_t data_len,
1456                                              ipmi_context_t context)
1457 {
1458     using namespace boot_options;
1459     ipmi_ret_t rc = IPMI_CC_OK;
1460     set_sys_boot_options_t* reqptr = (set_sys_boot_options_t*)request;
1461 
1462     std::printf("IPMI SET_SYS_BOOT_OPTIONS reqptr->parameter =[%d]\n",
1463                 reqptr->parameter);
1464 
1465     // This IPMI command does not have any resposne data
1466     *data_len = 0;
1467 
1468     /*  000101
1469      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1470      * This is the only parameter used by petitboot.
1471      */
1472 
1473     if (reqptr->parameter == (uint8_t)BootOptionParameter::BOOT_FLAGS)
1474     {
1475         IpmiValue bootOption = ((reqptr->data[1] & 0x3C) >> 2);
1476         using namespace chassis::internal;
1477         using namespace chassis::internal::cache;
1478         auto oneTimeEnabled = false;
1479         constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable";
1480         constexpr auto oneTimePath =
1481             "/xyz/openbmc_project/control/host0/boot/one_time";
1482 
1483         try
1484         {
1485             bool permanent =
1486                 (reqptr->data[0] & SET_PARM_BOOT_FLAGS_PERMANENT) ==
1487                 SET_PARM_BOOT_FLAGS_PERMANENT;
1488 
1489             auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1490 
1491             oneTimeEnabled =
1492                 std::get<settings::boot::OneTimeEnabled>(bootSetting);
1493 
1494             /*
1495              * Check if the current boot setting is onetime or permanent, if the
1496              * request in the command is otherwise, then set the "Enabled"
1497              * property in one_time object path to 'True' to indicate onetime
1498              * and 'False' to indicate permanent.
1499              *
1500              * Once the onetime/permanent setting is applied, then the bootMode
1501              * and bootSource is updated for the corresponding object.
1502              */
1503             if ((permanent && oneTimeEnabled) ||
1504                 (!permanent && !oneTimeEnabled))
1505             {
1506                 auto service = ipmi::getService(dbus, enabledIntf, oneTimePath);
1507 
1508                 ipmi::setDbusProperty(dbus, service, oneTimePath, enabledIntf,
1509                                       "Enabled", !permanent);
1510             }
1511 
1512             auto modeItr = modeIpmiToDbus.find(bootOption);
1513             auto sourceItr = sourceIpmiToDbus.find(bootOption);
1514             if (sourceIpmiToDbus.end() != sourceItr)
1515             {
1516                 rc = setBootSource(sourceItr->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 source, then reset
1523                 // the boot mode D-Bus property to default.
1524                 // This way the ipmid code can determine which property is not
1525                 // at the default value
1526                 if (sourceItr->second != Source::Sources::Default)
1527                 {
1528                     setBootMode(Mode::Modes::Regular);
1529                 }
1530             }
1531             if (modeIpmiToDbus.end() != modeItr)
1532             {
1533                 rc = setBootMode(modeItr->second);
1534                 if (rc != IPMI_CC_OK)
1535                 {
1536                     *data_len = 0;
1537                     return rc;
1538                 }
1539                 // If a set boot device is mapping to a boot mode, then reset
1540                 // the boot source D-Bus property to default.
1541                 // This way the ipmid code can determine which property is not
1542                 // at the default value
1543                 if (modeItr->second != Mode::Modes::Regular)
1544                 {
1545                     setBootSource(Source::Sources::Default);
1546                 }
1547             }
1548         }
1549         catch (InternalFailure& e)
1550         {
1551             report<InternalFailure>();
1552             *data_len = 0;
1553             return IPMI_CC_UNSPECIFIED_ERROR;
1554         }
1555     }
1556     else if (reqptr->parameter ==
1557              (uint8_t)BootOptionParameter::OPAL_NETWORK_SETTINGS)
1558     {
1559 
1560         int ret = setHostNetworkData(reqptr);
1561         if (ret < 0)
1562         {
1563             log<level::ERR>(
1564                 "setHostNetworkData failed for set_sys_boot_options");
1565             rc = IPMI_CC_UNSPECIFIED_ERROR;
1566         }
1567     }
1568     else if (reqptr->parameter ==
1569              static_cast<uint8_t>(BootOptionParameter::BOOT_INFO))
1570     {
1571         // Handle parameter #4 and return command completed normally
1572         // (IPMI_CC_OK). There is no implementation in OpenBMC for this
1573         // parameter. This is added to support the ipmitool command `chassis
1574         // bootdev` which sends set on parameter #4, before setting the boot
1575         // flags.
1576         rc = IPMI_CC_OK;
1577     }
1578     else
1579     {
1580         log<level::ERR>("Unsupported parameter",
1581                         entry("PARAM=0x%x", reqptr->parameter));
1582         rc = IPMI_CC_PARM_NOT_SUPPORTED;
1583     }
1584 
1585     return rc;
1586 }
1587 
1588 ipmi_ret_t ipmiGetPOHCounter(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1589                              ipmi_request_t request, ipmi_response_t response,
1590                              ipmi_data_len_t data_len, ipmi_context_t context)
1591 {
1592     // sd_bus error
1593     ipmi_ret_t rc = IPMI_CC_OK;
1594 
1595     auto resptr = reinterpret_cast<GetPOHCountResponse*>(response);
1596 
1597     try
1598     {
1599         auto pohCounter = getPOHCounter();
1600         resptr->counterReading[0] = pohCounter;
1601         resptr->counterReading[1] = pohCounter >> 8;
1602         resptr->counterReading[2] = pohCounter >> 16;
1603         resptr->counterReading[3] = pohCounter >> 24;
1604     }
1605     catch (std::exception& e)
1606     {
1607         log<level::ERR>(e.what());
1608         return IPMI_CC_UNSPECIFIED_ERROR;
1609     }
1610 
1611     resptr->minPerCount = poh::minutesPerCount;
1612     *data_len = sizeof(GetPOHCountResponse);
1613 
1614     return rc;
1615 }
1616 
1617 ipmi_ret_t ipmi_chassis_set_power_restore_policy(
1618     ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1619     ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1620 {
1621     auto* reqptr = reinterpret_cast<uint8_t*>(request);
1622     auto* resptr = reinterpret_cast<uint8_t*>(response);
1623     uint8_t reqPolicy = 0;
1624 
1625     power_policy::DbusValue value =
1626         power_policy::RestorePolicy::Policy::AlwaysOff;
1627 
1628     if (*data_len != power_policy::setPolicyReqLen)
1629     {
1630         phosphor::logging::log<level::ERR>("Unsupported request length",
1631                                            entry("LEN=0x%x", *data_len));
1632         *data_len = 0;
1633         return IPMI_CC_REQ_DATA_LEN_INVALID;
1634     }
1635 
1636     if (*reqptr > power_policy::noChange)
1637     {
1638         phosphor::logging::log<level::ERR>("Reserved request parameter",
1639                                            entry("REQ=0x%x", *reqptr));
1640         *data_len = 0;
1641         return IPMI_CC_PARM_OUT_OF_RANGE;
1642     }
1643 
1644     reqPolicy = *reqptr & power_policy::policyBitMask;
1645     if (reqPolicy == power_policy::noChange)
1646     {
1647         // just return the supported policy
1648         *resptr = power_policy::allSupport;
1649         *data_len = power_policy::setPolicyReqLen;
1650         return IPMI_CC_OK;
1651     }
1652 
1653     for (auto const& it : power_policy::dbusToIpmi)
1654     {
1655         if (it.second == reqPolicy)
1656         {
1657             value = it.first;
1658             break;
1659         }
1660     }
1661 
1662     try
1663     {
1664         const settings::Path& powerRestoreSetting =
1665             chassis::internal::cache::objects.map
1666                 .at(chassis::internal::powerRestoreIntf)
1667                 .front();
1668         sdbusplus::message::variant<std::string> property =
1669             convertForMessage(value);
1670 
1671         auto method = chassis::internal::dbus.new_method_call(
1672             chassis::internal::cache::objects
1673                 .service(powerRestoreSetting,
1674                          chassis::internal::powerRestoreIntf)
1675                 .c_str(),
1676             powerRestoreSetting.c_str(), ipmi::PROP_INTF, "Set");
1677 
1678         method.append(chassis::internal::powerRestoreIntf, "PowerRestorePolicy",
1679                       property);
1680         auto reply = chassis::internal::dbus.call(method);
1681         if (reply.is_method_error())
1682         {
1683             phosphor::logging::log<level::ERR>("Unspecified Error");
1684             *data_len = 0;
1685             return IPMI_CC_UNSPECIFIED_ERROR;
1686         }
1687     }
1688     catch (InternalFailure& e)
1689     {
1690         report<InternalFailure>();
1691         *data_len = 0;
1692         return IPMI_CC_UNSPECIFIED_ERROR;
1693     }
1694 
1695     *resptr = power_policy::allSupport;
1696     *data_len = power_policy::setPolicyReqLen;
1697     return IPMI_CC_OK;
1698 }
1699 
1700 void register_netfn_chassis_functions()
1701 {
1702     createIdentifyTimer();
1703 
1704     // <Wildcard Command>
1705     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_WILDCARD, NULL,
1706                            ipmi_chassis_wildcard, PRIVILEGE_USER);
1707 
1708     // Get Chassis Capabilities
1709     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_CHASSIS_CAP, NULL,
1710                            ipmi_get_chassis_cap, PRIVILEGE_USER);
1711 
1712     // Set Chassis Capabilities
1713     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_CHASSIS_CAP, NULL,
1714                            ipmi_set_chassis_cap, PRIVILEGE_USER);
1715 
1716     // <Get System Boot Options>
1717     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS, NULL,
1718                            ipmi_chassis_get_sys_boot_options,
1719                            PRIVILEGE_OPERATOR);
1720 
1721     // <Get Chassis Status>
1722     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_CHASSIS_STATUS, NULL,
1723                            ipmi_get_chassis_status, PRIVILEGE_USER);
1724 
1725     // <Chassis Control>
1726     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_CHASSIS_CONTROL, NULL,
1727                            ipmi_chassis_control, PRIVILEGE_OPERATOR);
1728 
1729     // <Chassis Identify>
1730     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1731                           ipmi::chassis::cmdChassisIdentify,
1732                           ipmi::Privilege::Operator, ipmiChassisIdentify);
1733 
1734     // <Set System Boot Options>
1735     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_SYS_BOOT_OPTIONS, NULL,
1736                            ipmi_chassis_set_sys_boot_options,
1737                            PRIVILEGE_OPERATOR);
1738     // <Get POH Counter>
1739     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_POH_COUNTER, NULL,
1740                            ipmiGetPOHCounter, PRIVILEGE_USER);
1741 
1742     // <Set Power Restore Policy>
1743     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_RESTORE_POLICY, NULL,
1744                            ipmi_chassis_set_power_restore_policy,
1745                            PRIVILEGE_OPERATOR);
1746 }
1747