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