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