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