1 #include "config.h"
2 
3 #include "chassishandler.hpp"
4 
5 #include "settings.hpp"
6 
7 #include <arpa/inet.h>
8 #include <endian.h>
9 #include <limits.h>
10 #include <mapper.h>
11 #include <netinet/in.h>
12 
13 #include <array>
14 #include <chrono>
15 #include <cstring>
16 #include <filesystem>
17 #include <fstream>
18 #include <future>
19 #include <ipmid/api.hpp>
20 #include <ipmid/types.hpp>
21 #include <ipmid/utils.hpp>
22 #include <map>
23 #include <phosphor-logging/elog-errors.hpp>
24 #include <phosphor-logging/log.hpp>
25 #include <sdbusplus/bus.hpp>
26 #include <sdbusplus/message/types.hpp>
27 #include <sdbusplus/server/object.hpp>
28 #include <sdbusplus/timer.hpp>
29 #include <sstream>
30 #include <string>
31 #include <xyz/openbmc_project/Common/error.hpp>
32 #include <xyz/openbmc_project/Control/Boot/Mode/server.hpp>
33 #include <xyz/openbmc_project/Control/Boot/Source/server.hpp>
34 #include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp>
35 #include <xyz/openbmc_project/State/Host/server.hpp>
36 #include <xyz/openbmc_project/State/PowerOnHours/server.hpp>
37 
38 // Defines
39 #define SET_PARM_VERSION 0x01
40 #define SET_PARM_BOOT_FLAGS_PERMANENT 0x40
41 #define SET_PARM_BOOT_FLAGS_VALID_ONE_TIME 0x80
42 #define SET_PARM_BOOT_FLAGS_VALID_PERMANENT 0xC0
43 
44 std::unique_ptr<phosphor::Timer> identifyTimer
45     __attribute__((init_priority(101)));
46 
47 constexpr size_t SIZE_MAC = 18;
48 constexpr size_t SIZE_BOOT_OPTION = (uint8_t)
49     BootOptionResponseSize::OPAL_NETWORK_SETTINGS; // Maximum size of the boot
50                                                    // option parametrs
51 constexpr size_t SIZE_PREFIX = 7;
52 constexpr size_t MAX_PREFIX_VALUE = 32;
53 constexpr size_t SIZE_COOKIE = 4;
54 constexpr size_t SIZE_VERSION = 2;
55 constexpr size_t DEFAULT_IDENTIFY_TIME_OUT = 15;
56 
57 // PetiBoot-Specific
58 static constexpr uint8_t net_conf_initial_bytes[] = {0x80, 0x21, 0x70, 0x62,
59                                                      0x21, 0x00, 0x01, 0x06};
60 
61 static constexpr size_t COOKIE_OFFSET = 1;
62 static constexpr size_t VERSION_OFFSET = 5;
63 static constexpr size_t ADDR_SIZE_OFFSET = 8;
64 static constexpr size_t MAC_OFFSET = 9;
65 static constexpr size_t ADDRTYPE_OFFSET = 16;
66 static constexpr size_t IPADDR_OFFSET = 17;
67 
68 static constexpr size_t encIdentifyObjectsSize = 1;
69 static constexpr size_t chassisIdentifyReqLength = 2;
70 static constexpr size_t identifyIntervalPos = 0;
71 static constexpr size_t forceIdentifyPos = 1;
72 
73 void register_netfn_chassis_functions() __attribute__((constructor));
74 
75 // Host settings in dbus
76 // Service name should be referenced by connection name got via object mapper
77 const char* settings_object_name = "/org/openbmc/settings/host0";
78 const char* settings_intf_name = "org.freedesktop.DBus.Properties";
79 const char* identify_led_object_name =
80     "/xyz/openbmc_project/led/groups/enclosure_identify";
81 
82 constexpr auto SETTINGS_ROOT = "/";
83 constexpr auto SETTINGS_MATCH = "host0";
84 
85 constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP";
86 constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress";
87 
88 static constexpr auto chassisStateRoot = "/xyz/openbmc_project/state";
89 static constexpr auto chassisPOHStateIntf =
90     "xyz.openbmc_project.State.PowerOnHours";
91 static constexpr auto pOHCounterProperty = "POHCounter";
92 static constexpr auto match = "chassis0";
93 const static constexpr char chassisCapIntf[] =
94     "xyz.openbmc_project.Control.ChassisCapabilities";
95 const static constexpr char chassisCapFlagsProp[] = "CapabilitiesFlags";
96 const static constexpr char chassisFRUDevAddrProp[] = "FRUDeviceAddress";
97 const static constexpr char chassisSDRDevAddrProp[] = "SDRDeviceAddress";
98 const static constexpr char chassisSELDevAddrProp[] = "SELDeviceAddress";
99 const static constexpr char chassisSMDevAddrProp[] = "SMDeviceAddress";
100 const static constexpr char chassisBridgeDevAddrProp[] = "BridgeDeviceAddress";
101 static constexpr uint8_t chassisCapFlagMask = 0x0f;
102 static constexpr uint8_t chassisCapAddrMask = 0xfe;
103 
104 typedef struct
105 {
106     uint8_t cap_flags;
107     uint8_t fru_info_dev_addr;
108     uint8_t sdr_dev_addr;
109     uint8_t sel_dev_addr;
110     uint8_t system_management_dev_addr;
111     uint8_t bridge_dev_addr;
112 } __attribute__((packed)) ipmi_chassis_cap_t;
113 
114 typedef struct
115 {
116     uint8_t cur_power_state;
117     uint8_t last_power_event;
118     uint8_t misc_power_state;
119     uint8_t front_panel_button_cap_status;
120 } __attribute__((packed)) ipmi_get_chassis_status_t;
121 
122 /**
123  * @struct Get POH counter command response data
124  */
125 struct GetPOHCountResponse
126 {
127     uint8_t minPerCount;       ///< Minutes per count
128     uint8_t counterReading[4]; ///< Counter reading
129 } __attribute__((packed));
130 
131 // Phosphor Host State manager
132 namespace State = sdbusplus::xyz::openbmc_project::State::server;
133 
134 namespace fs = std::filesystem;
135 
136 using namespace phosphor::logging;
137 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
138 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
139 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 msg = std::string("enclosureIdentifyLed(") +
1156                boost::lexical_cast<std::string>(flag) + ")";
1157     log<level::DEBUG>(msg.c_str());
1158     auto led =
1159         dbus.new_method_call(connection.c_str(), identify_led_object_name,
1160                              "org.freedesktop.DBus.Properties", "Set");
1161     led.append("xyz.openbmc_project.Led.Group", "Asserted",
1162                sdbusplus::message::variant<bool>(flag));
1163     auto ledReply = dbus.call(led);
1164     if (ledReply.is_method_error())
1165     {
1166         log<level::ERR>("Chassis Identify: Error Setting State On/Off\n",
1167                         entry("LED_STATE=%d", flag));
1168         elog<InternalFailure>();
1169     }
1170 }
1171 
1172 /** @brief Callback method to turn off LED
1173  */
1174 void enclosureIdentifyLedOff()
1175 {
1176     try
1177     {
1178         enclosureIdentifyLed(false);
1179     }
1180     catch (const InternalFailure& e)
1181     {
1182         report<InternalFailure>();
1183     }
1184 }
1185 
1186 /** @brief Create timer to turn on and off the enclosure LED
1187  */
1188 void createIdentifyTimer()
1189 {
1190     if (!identifyTimer)
1191     {
1192         identifyTimer =
1193             std::make_unique<phosphor::Timer>(enclosureIdentifyLedOff);
1194     }
1195 }
1196 
1197 ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval,
1198                                     std::optional<uint8_t> force)
1199 {
1200     uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT);
1201     bool forceIdentify = force.value_or(0) & 0x01;
1202 
1203     if (identifyInterval || forceIdentify)
1204     {
1205         // stop the timer if already started;
1206         // for force identify we should not turn off LED
1207         identifyTimer->stop();
1208         try
1209         {
1210             enclosureIdentifyLed(true);
1211         }
1212         catch (const InternalFailure& e)
1213         {
1214             report<InternalFailure>();
1215             return ipmi::responseResponseError();
1216         }
1217 
1218         if (forceIdentify)
1219         {
1220             return ipmi::responseSuccess();
1221         }
1222         // start the timer
1223         auto time = std::chrono::duration_cast<std::chrono::microseconds>(
1224             std::chrono::seconds(identifyInterval));
1225         identifyTimer->start(time);
1226     }
1227     else if (!identifyInterval)
1228     {
1229         identifyTimer->stop();
1230         enclosureIdentifyLedOff();
1231     }
1232     return ipmi::responseSuccess();
1233 }
1234 
1235 namespace boot_options
1236 {
1237 
1238 using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server;
1239 using IpmiValue = uint8_t;
1240 constexpr auto ipmiDefault = 0;
1241 
1242 std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = {
1243     {0x01, Source::Sources::Network},
1244     {0x02, Source::Sources::Disk},
1245     {0x05, Source::Sources::ExternalMedia},
1246     {ipmiDefault, Source::Sources::Default}};
1247 
1248 std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = {
1249     {0x03, Mode::Modes::Safe},
1250     {0x06, Mode::Modes::Setup},
1251     {ipmiDefault, Mode::Modes::Regular}};
1252 
1253 std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = {
1254     {Source::Sources::Network, 0x01},
1255     {Source::Sources::Disk, 0x02},
1256     {Source::Sources::ExternalMedia, 0x05},
1257     {Source::Sources::Default, ipmiDefault}};
1258 
1259 std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = {
1260     {Mode::Modes::Safe, 0x03},
1261     {Mode::Modes::Setup, 0x06},
1262     {Mode::Modes::Regular, ipmiDefault}};
1263 
1264 } // namespace boot_options
1265 
1266 /** @brief Set the property value for boot source
1267  *  @param[in] source - boot source value
1268  *  @return On failure return IPMI error.
1269  */
1270 static ipmi_ret_t setBootSource(const Source::Sources& source)
1271 {
1272     using namespace chassis::internal;
1273     using namespace chassis::internal::cache;
1274     sdbusplus::message::variant<std::string> property =
1275         convertForMessage(source);
1276     auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1277     const auto& bootSourceSetting = std::get<settings::Path>(bootSetting);
1278     auto method = dbus.new_method_call(
1279         objects.service(bootSourceSetting, bootSourceIntf).c_str(),
1280         bootSourceSetting.c_str(), ipmi::PROP_INTF, "Set");
1281     method.append(bootSourceIntf, "BootSource", property);
1282     auto reply = dbus.call(method);
1283     if (reply.is_method_error())
1284     {
1285         log<level::ERR>("Error in BootSource Set");
1286         report<InternalFailure>();
1287         return IPMI_CC_UNSPECIFIED_ERROR;
1288     }
1289     return IPMI_CC_OK;
1290 }
1291 
1292 /** @brief Set the property value for boot mode
1293  *  @param[in] mode - boot mode value
1294  *  @return On failure return IPMI error.
1295  */
1296 static ipmi_ret_t setBootMode(const Mode::Modes& mode)
1297 {
1298     using namespace chassis::internal;
1299     using namespace chassis::internal::cache;
1300     sdbusplus::message::variant<std::string> property = convertForMessage(mode);
1301     auto bootSetting = settings::boot::setting(objects, bootModeIntf);
1302     const auto& bootModeSetting = std::get<settings::Path>(bootSetting);
1303     auto method = dbus.new_method_call(
1304         objects.service(bootModeSetting, bootModeIntf).c_str(),
1305         bootModeSetting.c_str(), ipmi::PROP_INTF, "Set");
1306     method.append(bootModeIntf, "BootMode", property);
1307     auto reply = dbus.call(method);
1308     if (reply.is_method_error())
1309     {
1310         log<level::ERR>("Error in BootMode Set");
1311         report<InternalFailure>();
1312         return IPMI_CC_UNSPECIFIED_ERROR;
1313     }
1314     return IPMI_CC_OK;
1315 }
1316 
1317 ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1318                                              ipmi_request_t request,
1319                                              ipmi_response_t response,
1320                                              ipmi_data_len_t data_len,
1321                                              ipmi_context_t context)
1322 {
1323     using namespace boot_options;
1324     ipmi_ret_t rc = IPMI_CC_PARM_NOT_SUPPORTED;
1325     char* p = NULL;
1326     get_sys_boot_options_response_t* resp =
1327         (get_sys_boot_options_response_t*)response;
1328     get_sys_boot_options_t* reqptr = (get_sys_boot_options_t*)request;
1329     IpmiValue bootOption = ipmiDefault;
1330 
1331     std::memset(resp, 0, sizeof(*resp));
1332     resp->version = SET_PARM_VERSION;
1333     resp->parm = 5;
1334     resp->data[0] = SET_PARM_BOOT_FLAGS_VALID_ONE_TIME;
1335 
1336     /*
1337      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1338      * This is the only parameter used by petitboot.
1339      */
1340     if (reqptr->parameter ==
1341         static_cast<uint8_t>(BootOptionParameter::BOOT_FLAGS))
1342     {
1343 
1344         *data_len = static_cast<uint8_t>(BootOptionResponseSize::BOOT_FLAGS);
1345         using namespace chassis::internal;
1346         using namespace chassis::internal::cache;
1347 
1348         try
1349         {
1350             auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1351             const auto& bootSourceSetting =
1352                 std::get<settings::Path>(bootSetting);
1353             auto oneTimeEnabled =
1354                 std::get<settings::boot::OneTimeEnabled>(bootSetting);
1355             auto method = dbus.new_method_call(
1356                 objects.service(bootSourceSetting, bootSourceIntf).c_str(),
1357                 bootSourceSetting.c_str(), ipmi::PROP_INTF, "Get");
1358             method.append(bootSourceIntf, "BootSource");
1359             auto reply = dbus.call(method);
1360             if (reply.is_method_error())
1361             {
1362                 log<level::ERR>("Error in BootSource Get");
1363                 report<InternalFailure>();
1364                 *data_len = 0;
1365                 return IPMI_CC_UNSPECIFIED_ERROR;
1366             }
1367             sdbusplus::message::variant<std::string> result;
1368             reply.read(result);
1369             auto bootSource = Source::convertSourcesFromString(
1370                 variant_ns::get<std::string>(result));
1371 
1372             bootSetting = settings::boot::setting(objects, bootModeIntf);
1373             const auto& bootModeSetting = std::get<settings::Path>(bootSetting);
1374             method = dbus.new_method_call(
1375                 objects.service(bootModeSetting, bootModeIntf).c_str(),
1376                 bootModeSetting.c_str(), ipmi::PROP_INTF, "Get");
1377             method.append(bootModeIntf, "BootMode");
1378             reply = dbus.call(method);
1379             if (reply.is_method_error())
1380             {
1381                 log<level::ERR>("Error in BootMode Get");
1382                 report<InternalFailure>();
1383                 *data_len = 0;
1384                 return IPMI_CC_UNSPECIFIED_ERROR;
1385             }
1386             reply.read(result);
1387             auto bootMode = Mode::convertModesFromString(
1388                 variant_ns::get<std::string>(result));
1389 
1390             bootOption = sourceDbusToIpmi.at(bootSource);
1391             if ((Mode::Modes::Regular == bootMode) &&
1392                 (Source::Sources::Default == bootSource))
1393             {
1394                 bootOption = ipmiDefault;
1395             }
1396             else if (Source::Sources::Default == bootSource)
1397             {
1398                 bootOption = modeDbusToIpmi.at(bootMode);
1399             }
1400             resp->data[1] = (bootOption << 2);
1401 
1402             resp->data[0] = oneTimeEnabled
1403                                 ? SET_PARM_BOOT_FLAGS_VALID_ONE_TIME
1404                                 : SET_PARM_BOOT_FLAGS_VALID_PERMANENT;
1405 
1406             rc = IPMI_CC_OK;
1407         }
1408         catch (InternalFailure& e)
1409         {
1410             report<InternalFailure>();
1411             *data_len = 0;
1412             return IPMI_CC_UNSPECIFIED_ERROR;
1413         }
1414     }
1415     else if (reqptr->parameter ==
1416              static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS))
1417     {
1418 
1419         *data_len =
1420             static_cast<uint8_t>(BootOptionResponseSize::OPAL_NETWORK_SETTINGS);
1421 
1422         resp->parm =
1423             static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS);
1424 
1425         int ret = getHostNetworkData(resp);
1426 
1427         if (ret < 0)
1428         {
1429 
1430             log<level::ERR>(
1431                 "getHostNetworkData failed for get_sys_boot_options.");
1432             rc = IPMI_CC_UNSPECIFIED_ERROR;
1433         }
1434         else
1435             rc = IPMI_CC_OK;
1436     }
1437 
1438     else
1439     {
1440         log<level::ERR>("Unsupported parameter",
1441                         entry("PARAM=0x%x", reqptr->parameter));
1442     }
1443 
1444     if (p)
1445         free(p);
1446 
1447     if (rc == IPMI_CC_OK)
1448     {
1449         *data_len += 2;
1450     }
1451 
1452     return rc;
1453 }
1454 
1455 ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1456                                              ipmi_request_t request,
1457                                              ipmi_response_t response,
1458                                              ipmi_data_len_t data_len,
1459                                              ipmi_context_t context)
1460 {
1461     using namespace boot_options;
1462     ipmi_ret_t rc = IPMI_CC_OK;
1463     set_sys_boot_options_t* reqptr = (set_sys_boot_options_t*)request;
1464 
1465     std::printf("IPMI SET_SYS_BOOT_OPTIONS reqptr->parameter =[%d]\n",
1466                 reqptr->parameter);
1467 
1468     // This IPMI command does not have any resposne data
1469     *data_len = 0;
1470 
1471     /*  000101
1472      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1473      * This is the only parameter used by petitboot.
1474      */
1475 
1476     if (reqptr->parameter == (uint8_t)BootOptionParameter::BOOT_FLAGS)
1477     {
1478         IpmiValue bootOption = ((reqptr->data[1] & 0x3C) >> 2);
1479         using namespace chassis::internal;
1480         using namespace chassis::internal::cache;
1481         auto oneTimeEnabled = false;
1482         constexpr auto enabledIntf = "xyz.openbmc_project.Object.Enable";
1483         constexpr auto oneTimePath =
1484             "/xyz/openbmc_project/control/host0/boot/one_time";
1485 
1486         try
1487         {
1488             bool permanent =
1489                 (reqptr->data[0] & SET_PARM_BOOT_FLAGS_PERMANENT) ==
1490                 SET_PARM_BOOT_FLAGS_PERMANENT;
1491 
1492             auto bootSetting = settings::boot::setting(objects, bootSourceIntf);
1493 
1494             oneTimeEnabled =
1495                 std::get<settings::boot::OneTimeEnabled>(bootSetting);
1496 
1497             /*
1498              * Check if the current boot setting is onetime or permanent, if the
1499              * request in the command is otherwise, then set the "Enabled"
1500              * property in one_time object path to 'True' to indicate onetime
1501              * and 'False' to indicate permanent.
1502              *
1503              * Once the onetime/permanent setting is applied, then the bootMode
1504              * and bootSource is updated for the corresponding object.
1505              */
1506             if ((permanent && oneTimeEnabled) ||
1507                 (!permanent && !oneTimeEnabled))
1508             {
1509                 auto service = ipmi::getService(dbus, enabledIntf, oneTimePath);
1510 
1511                 ipmi::setDbusProperty(dbus, service, oneTimePath, enabledIntf,
1512                                       "Enabled", !permanent);
1513             }
1514 
1515             auto modeItr = modeIpmiToDbus.find(bootOption);
1516             auto sourceItr = sourceIpmiToDbus.find(bootOption);
1517             if (sourceIpmiToDbus.end() != sourceItr)
1518             {
1519                 rc = setBootSource(sourceItr->second);
1520                 if (rc != IPMI_CC_OK)
1521                 {
1522                     *data_len = 0;
1523                     return rc;
1524                 }
1525                 // If a set boot device is mapping to a boot source, then reset
1526                 // the boot mode D-Bus property to default.
1527                 // This way the ipmid code can determine which property is not
1528                 // at the default value
1529                 if (sourceItr->second != Source::Sources::Default)
1530                 {
1531                     setBootMode(Mode::Modes::Regular);
1532                 }
1533             }
1534             if (modeIpmiToDbus.end() != modeItr)
1535             {
1536                 rc = setBootMode(modeItr->second);
1537                 if (rc != IPMI_CC_OK)
1538                 {
1539                     *data_len = 0;
1540                     return rc;
1541                 }
1542                 // If a set boot device is mapping to a boot mode, then reset
1543                 // the boot source D-Bus property to default.
1544                 // This way the ipmid code can determine which property is not
1545                 // at the default value
1546                 if (modeItr->second != Mode::Modes::Regular)
1547                 {
1548                     setBootSource(Source::Sources::Default);
1549                 }
1550             }
1551         }
1552         catch (InternalFailure& e)
1553         {
1554             report<InternalFailure>();
1555             *data_len = 0;
1556             return IPMI_CC_UNSPECIFIED_ERROR;
1557         }
1558     }
1559     else if (reqptr->parameter ==
1560              (uint8_t)BootOptionParameter::OPAL_NETWORK_SETTINGS)
1561     {
1562 
1563         int ret = setHostNetworkData(reqptr);
1564         if (ret < 0)
1565         {
1566             log<level::ERR>(
1567                 "setHostNetworkData failed for set_sys_boot_options");
1568             rc = IPMI_CC_UNSPECIFIED_ERROR;
1569         }
1570     }
1571     else if (reqptr->parameter ==
1572              static_cast<uint8_t>(BootOptionParameter::BOOT_INFO))
1573     {
1574         // Handle parameter #4 and return command completed normally
1575         // (IPMI_CC_OK). There is no implementation in OpenBMC for this
1576         // parameter. This is added to support the ipmitool command `chassis
1577         // bootdev` which sends set on parameter #4, before setting the boot
1578         // flags.
1579         rc = IPMI_CC_OK;
1580     }
1581     else
1582     {
1583         log<level::ERR>("Unsupported parameter",
1584                         entry("PARAM=0x%x", reqptr->parameter));
1585         rc = IPMI_CC_PARM_NOT_SUPPORTED;
1586     }
1587 
1588     return rc;
1589 }
1590 
1591 ipmi_ret_t ipmiGetPOHCounter(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1592                              ipmi_request_t request, ipmi_response_t response,
1593                              ipmi_data_len_t data_len, ipmi_context_t context)
1594 {
1595     // sd_bus error
1596     ipmi_ret_t rc = IPMI_CC_OK;
1597 
1598     auto resptr = reinterpret_cast<GetPOHCountResponse*>(response);
1599 
1600     try
1601     {
1602         auto pohCounter = getPOHCounter();
1603         resptr->counterReading[0] = pohCounter;
1604         resptr->counterReading[1] = pohCounter >> 8;
1605         resptr->counterReading[2] = pohCounter >> 16;
1606         resptr->counterReading[3] = pohCounter >> 24;
1607     }
1608     catch (std::exception& e)
1609     {
1610         log<level::ERR>(e.what());
1611         return IPMI_CC_UNSPECIFIED_ERROR;
1612     }
1613 
1614     resptr->minPerCount = poh::minutesPerCount;
1615     *data_len = sizeof(GetPOHCountResponse);
1616 
1617     return rc;
1618 }
1619 
1620 ipmi_ret_t ipmi_chassis_set_power_restore_policy(
1621     ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
1622     ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
1623 {
1624     auto* reqptr = reinterpret_cast<uint8_t*>(request);
1625     auto* resptr = reinterpret_cast<uint8_t*>(response);
1626     uint8_t reqPolicy = 0;
1627 
1628     power_policy::DbusValue value =
1629         power_policy::RestorePolicy::Policy::AlwaysOff;
1630 
1631     if (*data_len != power_policy::setPolicyReqLen)
1632     {
1633         phosphor::logging::log<level::ERR>("Unsupported request length",
1634                                            entry("LEN=0x%x", *data_len));
1635         *data_len = 0;
1636         return IPMI_CC_REQ_DATA_LEN_INVALID;
1637     }
1638 
1639     if (*reqptr > power_policy::noChange)
1640     {
1641         phosphor::logging::log<level::ERR>("Reserved request parameter",
1642                                            entry("REQ=0x%x", *reqptr));
1643         *data_len = 0;
1644         return IPMI_CC_PARM_OUT_OF_RANGE;
1645     }
1646 
1647     reqPolicy = *reqptr & power_policy::policyBitMask;
1648     if (reqPolicy == power_policy::noChange)
1649     {
1650         // just return the supported policy
1651         *resptr = power_policy::allSupport;
1652         *data_len = power_policy::setPolicyReqLen;
1653         return IPMI_CC_OK;
1654     }
1655 
1656     for (auto const& it : power_policy::dbusToIpmi)
1657     {
1658         if (it.second == reqPolicy)
1659         {
1660             value = it.first;
1661             break;
1662         }
1663     }
1664 
1665     try
1666     {
1667         const settings::Path& powerRestoreSetting =
1668             chassis::internal::cache::objects.map
1669                 .at(chassis::internal::powerRestoreIntf)
1670                 .front();
1671         sdbusplus::message::variant<std::string> property =
1672             convertForMessage(value);
1673 
1674         auto method = chassis::internal::dbus.new_method_call(
1675             chassis::internal::cache::objects
1676                 .service(powerRestoreSetting,
1677                          chassis::internal::powerRestoreIntf)
1678                 .c_str(),
1679             powerRestoreSetting.c_str(), ipmi::PROP_INTF, "Set");
1680 
1681         method.append(chassis::internal::powerRestoreIntf, "PowerRestorePolicy",
1682                       property);
1683         auto reply = chassis::internal::dbus.call(method);
1684         if (reply.is_method_error())
1685         {
1686             phosphor::logging::log<level::ERR>("Unspecified Error");
1687             *data_len = 0;
1688             return IPMI_CC_UNSPECIFIED_ERROR;
1689         }
1690     }
1691     catch (InternalFailure& e)
1692     {
1693         report<InternalFailure>();
1694         *data_len = 0;
1695         return IPMI_CC_UNSPECIFIED_ERROR;
1696     }
1697 
1698     *resptr = power_policy::allSupport;
1699     *data_len = power_policy::setPolicyReqLen;
1700     return IPMI_CC_OK;
1701 }
1702 
1703 void register_netfn_chassis_functions()
1704 {
1705     createIdentifyTimer();
1706 
1707     // <Wildcard Command>
1708     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_WILDCARD, NULL,
1709                            ipmi_chassis_wildcard, PRIVILEGE_USER);
1710 
1711     // Get Chassis Capabilities
1712     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_CHASSIS_CAP, NULL,
1713                            ipmi_get_chassis_cap, PRIVILEGE_USER);
1714 
1715     // Set Chassis Capabilities
1716     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_CHASSIS_CAP, NULL,
1717                            ipmi_set_chassis_cap, PRIVILEGE_USER);
1718 
1719     // <Get System Boot Options>
1720     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS, NULL,
1721                            ipmi_chassis_get_sys_boot_options,
1722                            PRIVILEGE_OPERATOR);
1723 
1724     // <Get Chassis Status>
1725     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_CHASSIS_STATUS, NULL,
1726                            ipmi_get_chassis_status, PRIVILEGE_USER);
1727 
1728     // <Chassis Control>
1729     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_CHASSIS_CONTROL, NULL,
1730                            ipmi_chassis_control, PRIVILEGE_OPERATOR);
1731 
1732     // <Chassis Identify>
1733     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis,
1734                           ipmi::chassis::cmdChassisIdentify,
1735                           ipmi::Privilege::Operator, ipmiChassisIdentify);
1736 
1737     // <Set System Boot Options>
1738     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_SYS_BOOT_OPTIONS, NULL,
1739                            ipmi_chassis_set_sys_boot_options,
1740                            PRIVILEGE_OPERATOR);
1741     // <Get POH Counter>
1742     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_POH_COUNTER, NULL,
1743                            ipmiGetPOHCounter, PRIVILEGE_USER);
1744 
1745     // <Set Power Restore Policy>
1746     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_RESTORE_POLICY, NULL,
1747                            ipmi_chassis_set_power_restore_policy,
1748                            PRIVILEGE_OPERATOR);
1749 }
1750