1 #include "chassishandler.h"
2 #include "host-ipmid/ipmid-api.h"
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <stdint.h>
6 #include <mapper.h>
7 #include <arpa/inet.h>
8 #include <netinet/in.h>
9 #include <limits.h>
10 #include <string.h>
11 #include <endian.h>
12 #include <sstream>
13 #include <array>
14 #include <fstream>
15 #include <experimental/filesystem>
16 #include <phosphor-logging/log.hpp>
17 #include <xyz/openbmc_project/State/Host/server.hpp>
18 #include "config.h"
19 
20 //Defines
21 #define SET_PARM_VERSION                     0x01
22 #define SET_PARM_BOOT_FLAGS_PERMANENT        0x40 //boot flags data1 7th bit on
23 #define SET_PARM_BOOT_FLAGS_VALID_ONE_TIME   0x80 //boot flags data1 8th bit on
24 #define SET_PARM_BOOT_FLAGS_VALID_PERMANENT  0xC0 //boot flags data1 7 & 8 bit on
25 
26 constexpr size_t SIZE_MAC  = 18;
27 constexpr size_t SIZE_BOOT_OPTION = (uint8_t)BootOptionResponseSize::
28         OPAL_NETWORK_SETTINGS;//Maximum size of the boot option parametrs
29 constexpr size_t SIZE_PREFIX = 7;
30 constexpr size_t MAX_PREFIX_VALUE = 32;
31 constexpr size_t SIZE_COOKIE = 4;
32 constexpr size_t SIZE_VERSION = 2;
33 constexpr auto   MAC_ADDRESS_FORMAT = "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx";
34 constexpr auto   IP_ADDRESS_FORMAT = "%d.%d.%d.%d";
35 constexpr auto   PREFIX_FORMAT = "%hhd";
36 constexpr auto   ADDR_TYPE_FORMAT = "%hhx";
37 //PetiBoot-Specific
38 static constexpr uint8_t net_conf_initial_bytes[] = {0x80,0x21, 0x70 ,0x62 ,0x21,
39         0x00 ,0x01 ,0x06 ,0x04};
40 
41 static constexpr size_t COOKIE_OFFSET = 1;
42 static constexpr size_t VERSION_OFFSET = 5;
43 static constexpr size_t MAC_OFFSET = 9;
44 static constexpr size_t ADDRTYPE_OFFSET = 16;
45 static constexpr size_t IPADDR_OFFSET = 17;
46 static constexpr size_t PREFIX_OFFSET = 21;
47 static constexpr size_t GATEWAY_OFFSET = 22;
48 
49 using namespace phosphor::logging;
50 
51 
52 void register_netfn_chassis_functions() __attribute__((constructor));
53 
54 // Host settings in dbus
55 // Service name should be referenced by connection name got via object mapper
56 const char *settings_object_name  =  "/org/openbmc/settings/host0";
57 const char *settings_intf_name    =  "org.freedesktop.DBus.Properties";
58 const char *host_intf_name        =  "org.openbmc.settings.Host";
59 
60 typedef struct
61 {
62     uint8_t cap_flags;
63     uint8_t fru_info_dev_addr;
64     uint8_t sdr_dev_addr;
65     uint8_t sel_dev_addr;
66     uint8_t system_management_dev_addr;
67     uint8_t bridge_dev_addr;
68 }__attribute__((packed)) ipmi_chassis_cap_t;
69 
70 typedef struct
71 {
72     uint8_t cur_power_state;
73     uint8_t last_power_event;
74     uint8_t misc_power_state;
75     uint8_t front_panel_button_cap_status;
76 }__attribute__((packed)) ipmi_get_chassis_status_t;
77 
78 // Phosphor Host State manager
79 namespace State = sdbusplus::xyz::openbmc_project::State::server;
80 
81 namespace fs = std::experimental::filesystem;
82 
83 int dbus_get_property(const char *name, char **buf)
84 {
85     sd_bus_error error = SD_BUS_ERROR_NULL;
86     sd_bus_message *m = NULL;
87     sd_bus *bus = NULL;
88     char *temp_buf = NULL;
89     char *connection = NULL;
90     int r;
91 
92     // Get the system bus where most system services are provided.
93     bus = ipmid_get_sd_bus_connection();
94 
95     r = mapper_get_service(bus, settings_object_name, &connection);
96     if (r < 0) {
97         fprintf(stderr, "Failed to get %s connection: %s\n",
98                 settings_object_name, strerror(-r));
99         goto finish;
100     }
101 
102     /*
103      * Bus, service, object path, interface and method are provided to call
104      * the method.
105      * Signatures and input arguments are provided by the arguments at the
106      * end.
107      */
108     r = sd_bus_call_method(bus,
109                            connection,                                 /* service to contact */
110                            settings_object_name,                       /* object path */
111                            settings_intf_name,                         /* interface name */
112                            "Get",                                      /* method name */
113                            &error,                                     /* object to return error in */
114                            &m,                                         /* return message on success */
115                            "ss",                                       /* input signature */
116                            host_intf_name,                             /* first argument */
117                            name);                                      /* second argument */
118 
119     if (r < 0) {
120         fprintf(stderr, "Failed to issue method call: %s\n", error.message);
121         goto finish;
122     }
123 
124     /*
125      * The output should be parsed exactly the same as the output formatting
126      * specified.
127      */
128     r = sd_bus_message_read(m, "v", "s", &temp_buf);
129     if (r < 0) {
130         fprintf(stderr, "Failed to parse response message: %s\n", strerror(-r));
131         goto finish;
132     }
133 
134     *buf = strdup(temp_buf);
135     /*    *buf = (char*) malloc(strlen(temp_buf));
136     if (*buf) {
137         strcpy(*buf, temp_buf);
138     }
139      */
140     printf("IPMID boot option property get: {%s}.\n", (char *) temp_buf);
141 
142 finish:
143     sd_bus_error_free(&error);
144     sd_bus_message_unref(m);
145     free(connection);
146 
147     return r;
148 }
149 
150 int dbus_set_property(const char * name, const char *value)
151 {
152     sd_bus_error error = SD_BUS_ERROR_NULL;
153     sd_bus_message *m = NULL;
154     sd_bus *bus = NULL;
155     char *connection = NULL;
156     int r;
157 
158     // Get the system bus where most system services are provided.
159     bus = ipmid_get_sd_bus_connection();
160 
161     r = mapper_get_service(bus, settings_object_name, &connection);
162     if (r < 0) {
163         fprintf(stderr, "Failed to get %s connection: %s\n",
164                 settings_object_name, strerror(-r));
165         goto finish;
166     }
167 
168     /*
169      * Bus, service, object path, interface and method are provided to call
170      * the method.
171      * Signatures and input arguments are provided by the arguments at the
172      * end.
173      */
174     r = sd_bus_call_method(bus,
175                            connection,                                 /* service to contact */
176                            settings_object_name,                       /* object path */
177                            settings_intf_name,                         /* interface name */
178                            "Set",                                      /* method name */
179                            &error,                                     /* object to return error in */
180                            &m,                                         /* return message on success */
181                            "ssv",                                      /* input signature */
182                            host_intf_name,                             /* first argument */
183                            name,                                       /* second argument */
184                            "s",                                        /* third argument */
185                            value);                                     /* fourth argument */
186 
187     if (r < 0) {
188         fprintf(stderr, "Failed to issue method call: %s\n", error.message);
189         goto finish;
190     }
191 
192     printf("IPMID boot option property set: {%s}.\n", value);
193 
194     finish:
195     sd_bus_error_free(&error);
196     sd_bus_message_unref(m);
197     free(connection);
198 
199     return r;
200 }
201 
202 struct get_sys_boot_options_t {
203     uint8_t parameter;
204     uint8_t set;
205     uint8_t block;
206 }  __attribute__ ((packed));
207 
208 struct get_sys_boot_options_response_t {
209     uint8_t version;
210     uint8_t parm;
211     uint8_t data[SIZE_BOOT_OPTION];
212 }  __attribute__ ((packed));
213 
214 struct set_sys_boot_options_t {
215     uint8_t parameter;
216     uint8_t data[SIZE_BOOT_OPTION];
217 }  __attribute__ ((packed));
218 
219 struct host_network_config_t {
220     std::string ipaddress;
221     std::string prefix;
222     std::string gateway;
223     std::string macaddress;
224     std::string addrType;
225 
226     host_network_config_t()=default;
227 };
228 
229 void fillNetworkConfig( host_network_config_t & host_config ,
230                         const std::string& conf_str ) {
231 
232     constexpr auto COMMA_DELIMITER = ",";
233     constexpr auto EQUAL_DELIMITER = "=";
234     size_t  commaDelimtrPos = 0;
235     size_t  equalDelimtrPos = 0,commaDelimtrPrevPos = 0;
236     std::string value;
237     while ( commaDelimtrPos < conf_str.length() ) {
238 
239         commaDelimtrPos = conf_str.find(COMMA_DELIMITER,commaDelimtrPos);
240         //This condition is to extract the last
241         //Substring as we will not be having the delimeter
242         //at end. std::string::npos is -1
243 
244         if ( commaDelimtrPos == std::string::npos ) {
245             commaDelimtrPos = conf_str.length();
246         }
247 
248         equalDelimtrPos = conf_str.find (EQUAL_DELIMITER,commaDelimtrPrevPos);
249 
250         //foo,ipaddress=1234
251         if ( equalDelimtrPos == std::string::npos ) {
252 
253             commaDelimtrPos++;
254             commaDelimtrPrevPos= commaDelimtrPos;
255             continue;
256         }
257 
258         value = conf_str.substr((equalDelimtrPos+1),
259                                 commaDelimtrPos-(equalDelimtrPos+1));
260 
261 #ifdef _IPMI_DEBUG_
262         printf ("Name=[%s],Value=[%s],commaDelimtrPos=[%d],\
263                 commaDelimtrPrevPos=[%d],equalDelimtrPos=[%d]\n",
264                 name.c_str(),value.c_str(),commaDelimtrPos,
265                 commaDelimtrPrevPos,equalDelimtrPos);
266 #endif
267 
268         if ( 0 == conf_str.compare(commaDelimtrPrevPos,
269                                    equalDelimtrPos-commaDelimtrPrevPos,
270                                    "ipaddress" )) {
271             host_config.ipaddress = std::move(value);
272         }
273         else if ( 0 == conf_str.compare(commaDelimtrPrevPos,
274                                         equalDelimtrPos-commaDelimtrPrevPos,
275                                         "prefix" )) {
276             host_config.prefix = std::move(value);
277         }
278         else if ( 0 == conf_str.compare(commaDelimtrPrevPos,
279                                         equalDelimtrPos-commaDelimtrPrevPos,
280                                         "gateway" )) {
281             host_config.gateway = std::move(value);
282         }
283         else if ( 0 == conf_str.compare(commaDelimtrPrevPos,
284                                         equalDelimtrPos-commaDelimtrPrevPos,
285                                         "mac" )) {
286             host_config.macaddress = std::move(value);
287         }
288         else if ( 0 == conf_str.compare(commaDelimtrPrevPos,
289                                         equalDelimtrPos-commaDelimtrPrevPos,
290                                         "addr_type" )) {
291             host_config.addrType = std::move(value);
292         }
293 
294         commaDelimtrPos++;
295         commaDelimtrPrevPos= commaDelimtrPos;
296     }
297 }
298 
299 int  getHostNetworkData(get_sys_boot_options_response_t* respptr)
300 {
301 
302     char *prop = nullptr;
303     int rc = dbus_get_property("network_config",&prop);
304 
305     if ( rc < 0 ) {
306         fprintf(stderr, "Dbus get property(boot_flags) failed\
307                 for get_sys_boot_options.\n");
308         return rc;
309     }
310 
311     std::string conf_str(prop);
312 
313     if ( prop ) {
314 
315         free(prop);
316         prop = nullptr;
317     }
318 
319     /* network_config property Value would be in the form of
320      * ipaddress=1.1.1.1,prefix=16,gateway=2.2.2.2,mac=11:22:33:44:55:66,dhcp=0
321      */
322 
323     /* Parsing the string and fill the hostconfig structure with the
324      * values */
325 
326     printf ("Configuration String[%s]\n ",conf_str.c_str());
327 
328     host_network_config_t host_config;
329 
330     // Fill the host_config from the configuration string
331     fillNetworkConfig(host_config,conf_str);
332 
333     //Assigning the index as intialByteLength as it is fixed and prefilled.
334     printf ("host_config.macaddress.c_str()=[%s]\n",host_config.macaddress.c_str());
335     do{
336 
337         rc = sscanf(host_config.macaddress.c_str(),MAC_ADDRESS_FORMAT,
338                     (respptr->data+MAC_OFFSET), (respptr->data+MAC_OFFSET+1),
339                     (respptr->data+MAC_OFFSET+2),(respptr->data+MAC_OFFSET+3),
340                     (respptr->data+MAC_OFFSET+4), (respptr->data+MAC_OFFSET+5));
341 
342 
343         if ( rc < 6 ){
344             fprintf(stderr, "sscanf Failed in extracting mac address.\n");
345             rc = -1;
346             break;
347         }
348 
349         //Conevrt the dhcp,ipaddress,mask and gateway as hex number
350         respptr->data[MAC_OFFSET+6]=0x00;
351 
352         rc = sscanf(host_config.addrType.c_str(),ADDR_TYPE_FORMAT,
353                     (respptr->data+ADDRTYPE_OFFSET));
354 
355         if ( rc <= 0 ) {
356             fprintf(stderr, "sscanf Failed in extracting address type.\n");
357             rc = -1;
358             break;
359         }
360 
361         //ipaddress and gateway would be in IPv4 format
362         rc = inet_pton(AF_INET,host_config.ipaddress.c_str(),
363                        (respptr->data+IPADDR_OFFSET));
364 
365         if ( rc <= 0 ) {
366             fprintf(stderr, "inet_pton failed during ipaddress coneversion\n");
367             rc = -1;
368             break;
369         }
370 
371         rc = sscanf(host_config.prefix.c_str(),PREFIX_FORMAT,
372                     (respptr->data+PREFIX_OFFSET));
373 
374         if ( rc <= 0 ) {
375             fprintf(stderr, "sscanf failed during prefix extraction.\n");
376             rc = -1;
377             break;
378         }
379 
380         rc = inet_pton(AF_INET,host_config.gateway.c_str(),
381                        (respptr->data+GATEWAY_OFFSET));
382 
383         if ( rc <= 0 ) {
384             fprintf(stderr, "inet_pton failed during gateway conversion.\n");
385             rc = -1;
386             break;
387         }
388 
389     }while (0);
390 
391     if ( rc ) {
392 
393         //PetiBoot-Specific
394         //If sucess then copy the first 9 bytes to the data
395         //else set the respptr to 0
396 
397         memcpy(respptr->data,net_conf_initial_bytes,
398                sizeof(net_conf_initial_bytes));
399 
400 #ifdef _IPMI_DEBUG_
401         printf ("\n===Printing the IPMI Formatted Data========\n");
402 
403         for ( uint8_t pos = 0; pos<index; pos++ )
404             printf("%02x ", respptr->data[pos]);
405 #endif
406 
407     }else {
408 
409         memset(respptr->data,0,SIZE_BOOT_OPTION);
410     }
411 
412     return rc;
413 }
414 
415 int setHostNetworkData(set_sys_boot_options_t * reqptr)
416 {
417     std::string host_network_config;
418     char mac[SIZE_MAC] = {0};
419     char ipAddress[INET_ADDRSTRLEN] = {0};
420     char gateway[INET_ADDRSTRLEN] = {0};
421     char dhcp[SIZE_PREFIX] = {0};
422     char prefix[SIZE_PREFIX] = {0};
423     int rc = 0;
424     uint32_t zeroCookie=0;
425 
426     //cookie starts from second byte
427     // version starts from sixth byte
428 
429     do {
430 
431         // cookie ==  0x21 0x70 0x62 0x21
432         if ( memcmp(&(reqptr->data[COOKIE_OFFSET]),
433                     (net_conf_initial_bytes+COOKIE_OFFSET),
434                     SIZE_COOKIE) != 0 ) {
435             //cookie == 0
436             if (  memcmp(&(reqptr->data[COOKIE_OFFSET]),
437                          &zeroCookie,
438                          SIZE_COOKIE) == 0 ) {
439                 rc = 0;
440                 break;
441             }
442             //Invalid cookie
443             fprintf(stderr, "Invalid Cookie\n");
444             rc = -1;
445             break;
446         }
447         // vesion == 0x00 0x01
448         if ( memcmp(&(reqptr->data[VERSION_OFFSET]),
449                     (net_conf_initial_bytes+VERSION_OFFSET),
450                     SIZE_VERSION) != 0 ) {
451 
452             fprintf(stderr, "Invalid Version\n");
453             rc = -1;
454             break;
455         }
456 
457         snprintf(mac, SIZE_MAC, MAC_ADDRESS_FORMAT,
458                  reqptr->data[MAC_OFFSET],
459                  reqptr->data[MAC_OFFSET+1],
460                  reqptr->data[MAC_OFFSET+2],
461                  reqptr->data[MAC_OFFSET+3],
462                  reqptr->data[MAC_OFFSET+4],
463                  reqptr->data[MAC_OFFSET+5]);
464 
465         snprintf(dhcp,SIZE_PREFIX, ADDR_TYPE_FORMAT, reqptr->data[ADDRTYPE_OFFSET]);
466         //Validating the address  type which could be
467         //either static or dynamic
468         if( *(reqptr->data+ADDRTYPE_OFFSET) > 1 ) {
469 
470             fprintf(stderr, "Invalid Address Type\n");
471             rc = -1;
472             break;
473 
474         }
475 
476         snprintf(ipAddress, INET_ADDRSTRLEN, IP_ADDRESS_FORMAT,
477                  reqptr->data[IPADDR_OFFSET], reqptr->data[IPADDR_OFFSET+1],
478                  reqptr->data[IPADDR_OFFSET+2], reqptr->data[IPADDR_OFFSET+3]);
479 
480         //validating prefix
481         if ( *(reqptr->data+PREFIX_OFFSET) > (uint8_t)MAX_PREFIX_VALUE ) {
482 
483             fprintf(stderr, "Invalid Prefix\n");
484             rc = -1;
485             break;
486         }
487 
488         snprintf(prefix,SIZE_PREFIX,PREFIX_FORMAT, reqptr->data[PREFIX_OFFSET]);
489 
490         snprintf(gateway, INET_ADDRSTRLEN,IP_ADDRESS_FORMAT,
491                  reqptr->data[GATEWAY_OFFSET],
492                  reqptr->data[GATEWAY_OFFSET+1],
493                  reqptr->data[GATEWAY_OFFSET+2],
494                  reqptr->data[GATEWAY_OFFSET+3]);
495 
496 
497     }while(0);
498 
499     if( !rc )
500     {
501         //Cookie == 0 or it is a valid cookie
502         host_network_config += "ipaddress="+std::string(ipAddress)+",prefix="+
503                 std::string(prefix)+",gateway="+std::string(gateway)+
504                 ",mac="+std::string(mac)+",addr_type="+std::string(dhcp);
505 
506         printf ("Network configuration changed: %s\n",host_network_config.c_str());
507 
508         rc = dbus_set_property("network_config",host_network_config.c_str());
509 
510         if ( rc < 0 ) {
511             fprintf(stderr, "Dbus set property(network_config)\
512                     failed for set_sys_boot_options.\n");
513             rc = -1;
514         }
515 
516     }
517     return rc;
518 }
519 
520 ipmi_ret_t ipmi_chassis_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
521                                  ipmi_request_t request,
522                                  ipmi_response_t response,
523                                  ipmi_data_len_t data_len,
524                                  ipmi_context_t context)
525 {
526     printf("Handling CHASSIS WILDCARD Netfn:[0x%X], Cmd:[0x%X]\n",netfn, cmd);
527     // Status code.
528     ipmi_ret_t rc = IPMI_CC_INVALID;
529     *data_len = 0;
530     return rc;
531 }
532 
533 ipmi_ret_t ipmi_get_chassis_cap(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
534                                 ipmi_request_t request, ipmi_response_t response,
535                                 ipmi_data_len_t data_len, ipmi_context_t context)
536 {
537     // sd_bus error
538     ipmi_ret_t rc = IPMI_CC_OK;
539 
540     ipmi_chassis_cap_t chassis_cap{};
541 
542     *data_len = sizeof(ipmi_chassis_cap_t);
543 
544     // TODO: need future work. Get those flag from MRW.
545 
546     // capabilities flags
547     // [7..4] - reserved
548     // [3] – 1b = provides power interlock  (IPM 1.5)
549     // [2] – 1b = provides Diagnostic Interrupt (FP NMI)
550     // [1] – 1b = provides “Front Panel Lockout” (indicates that the chassis has capabilities
551     //            to lock out external power control and reset button or front panel interfaces
552     //            and/or detect tampering with those interfaces).
553     // [0] -1b = Chassis provides intrusion (physical security) sensor.
554     // set to default value 0x0.
555     chassis_cap.cap_flags = 0x0;
556 
557     // Since we do not have a separate SDR Device/SEL Device/ FRU repository.
558     // The 20h was given as those 5 device addresses.
559     // Chassis FRU info Device Address
560     chassis_cap.fru_info_dev_addr = 0x20;
561 
562     // Chassis SDR Device Address
563     chassis_cap.sdr_dev_addr = 0x20;
564 
565     // Chassis SEL Device Address
566     chassis_cap.sel_dev_addr = 0x20;
567 
568     // Chassis System Management Device Address
569     chassis_cap.system_management_dev_addr = 0x20;
570 
571     // Chassis Bridge Device Address.
572     chassis_cap.bridge_dev_addr = 0x20;
573 
574     memcpy(response, &chassis_cap, *data_len);
575 
576     return rc;
577 }
578 
579 //------------------------------------------
580 // Calls into Host State Manager Dbus object
581 //------------------------------------------
582 int initiate_state_transition(State::Host::Transition transition)
583 {
584     // OpenBMC Host State Manager dbus framework
585     constexpr auto HOST_STATE_MANAGER_ROOT  = "/xyz/openbmc_project/state/host0";
586     constexpr auto HOST_STATE_MANAGER_IFACE = "xyz.openbmc_project.State.Host";
587     constexpr auto DBUS_PROPERTY_IFACE      = "org.freedesktop.DBus.Properties";
588     constexpr auto PROPERTY                 = "RequestedHostTransition";
589 
590     // sd_bus error
591     int rc = 0;
592     char  *busname = NULL;
593 
594     // SD Bus error report mechanism.
595     sd_bus_error bus_error = SD_BUS_ERROR_NULL;
596 
597     // Gets a hook onto either a SYSTEM or SESSION bus
598     sd_bus *bus_type = ipmid_get_sd_bus_connection();
599     rc = mapper_get_service(bus_type, HOST_STATE_MANAGER_ROOT, &busname);
600     if (rc < 0)
601     {
602         log<level::ERR>("Failed to get bus name",
603                         entry("ERROR=%s, OBJPATH=%s",
604                               strerror(-rc), HOST_STATE_MANAGER_ROOT));
605         return rc;
606     }
607 
608     // Convert to string equivalent of the passed in transition enum.
609     auto request = State::convertForMessage(transition);
610 
611     rc = sd_bus_call_method(bus_type,                // On the system bus
612                             busname,                 // Service to contact
613                             HOST_STATE_MANAGER_ROOT, // Object path
614                             DBUS_PROPERTY_IFACE,     // Interface name
615                             "Set",                   // Method to be called
616                             &bus_error,              // object to return error
617                             nullptr,                 // Response buffer if any
618                             "ssv",                   // Takes 3 arguments
619                             HOST_STATE_MANAGER_IFACE,
620                             PROPERTY,
621                             "s", request.c_str());
622     if(rc < 0)
623     {
624         log<level::ERR>("Failed to initiate transition",
625                         entry("ERROR=%s, REQUEST=%s",
626                               bus_error.message, request.c_str()));
627     }
628     else
629     {
630         log<level::INFO>("Transition request initiated successfully");
631     }
632 
633     sd_bus_error_free(&bus_error);
634     free(busname);
635 
636     return rc;
637 }
638 
639 struct hostPowerPolicyTypeMap_t
640 {
641     uint8_t policyNum;
642     char    policyName[19];
643 };
644 
645 hostPowerPolicyTypeMap_t g_hostPowerPolicyTypeMap_t[] = {
646 
647         {0x00, "LEAVE_OFF"},
648         {0x01, "RESTORE_LAST_STATE"},
649         {0x02, "ALWAYS_POWER_ON"},
650         {0x03, "UNKNOWN"}
651 };
652 
653 uint8_t get_host_power_policy(char *p)
654 {
655 
656     hostPowerPolicyTypeMap_t *s = g_hostPowerPolicyTypeMap_t;
657 
658     while (s->policyNum != 0x03) {
659         if (!strcmp(s->policyName,p))
660             break;
661         s++;
662     }
663 
664     return s->policyNum;
665 }
666 
667 //----------------------------------------------------------------------
668 // Get Chassis Status commands
669 //----------------------------------------------------------------------
670 ipmi_ret_t ipmi_get_chassis_status(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
671                                    ipmi_request_t request,
672                                    ipmi_response_t response,
673                                    ipmi_data_len_t data_len,
674                                    ipmi_context_t context)
675 {
676     const char  *objname = "/org/openbmc/control/power0";
677     const char  *intf = "org.openbmc.control.Power";
678 
679     sd_bus *bus = NULL;
680     sd_bus_message *reply = NULL;
681     int r = 0;
682     int pgood = 0;
683     char *busname = NULL;
684     ipmi_ret_t rc = IPMI_CC_OK;
685     ipmi_get_chassis_status_t chassis_status{};
686 
687     char *p = NULL;
688     uint8_t s = 0;
689 
690     // Get the system bus where most system services are provided.
691     bus = ipmid_get_sd_bus_connection();
692 
693     *data_len = 4;
694 
695     r = mapper_get_service(bus, objname, &busname);
696     if (r < 0) {
697         fprintf(stderr, "Failed to get bus name, return value: %s.\n", strerror(-r));
698         rc = IPMI_CC_UNSPECIFIED_ERROR;
699         goto finish;
700     }
701 
702     r = sd_bus_get_property(bus, busname, objname, intf, "pgood", NULL, &reply, "i");
703     if (r < 0) {
704         fprintf(stderr, "Failed to call sd_bus_get_property:%d,  %s\n", r, strerror(-r));
705         fprintf(stderr, "Bus: %s, Path: %s, Interface: %s\n",
706                 busname, objname, intf);
707         rc = IPMI_CC_UNSPECIFIED_ERROR;
708         goto finish;
709     }
710 
711     r = sd_bus_message_read(reply, "i", &pgood);
712     if (r < 0) {
713         fprintf(stderr, "Failed to read sensor: %s\n", strerror(-r));
714         rc = IPMI_CC_UNSPECIFIED_ERROR;
715         goto finish;
716     }
717 
718     printf("pgood is 0x%02x\n", pgood);
719 
720     // Get Power Policy
721     r = dbus_get_property("power_policy",&p);
722 
723     if (r < 0) {
724         fprintf(stderr, "Dbus get property(power_policy) failed for get_sys_boot_options.\n");
725         rc = IPMI_CC_UNSPECIFIED_ERROR;
726     } else {
727         s = get_host_power_policy(p);
728     }
729 
730     if (p)
731     {
732         free(p);
733         p = NULL;
734     }
735 
736     // Current Power State
737     // [7] reserved
738     // [6..5] power restore policy
739     //          00b = chassis stays powered off after AC/mains returns
740     //          01b = after AC returns, power is restored to the state that was
741     //          in effect when AC/mains was lost.
742     //          10b = chassis always powers up after AC/mains returns
743     //          11b = unknow
744     //        Set to 00b, by observing the hardware behavior.
745     //        Do we need to define a dbus property to identify the restore policy?
746 
747     // [4] power control fault
748     //       1b = controller attempted to turn system power on or off, but
749     //       system did not enter desired state.
750     //       Set to 0b, since We don't support it..
751 
752     // [3] power fault
753     //       1b = fault detected in main power subsystem.
754     //       set to 0b. for we don't support it.
755 
756     // [2] 1b = interlock (chassis is presently shut down because a chassis
757     //       panel interlock switch is active). (IPMI 1.5)
758     //       set to 0b,  for we don't support it.
759 
760     // [1] power overload
761     //      1b = system shutdown because of power overload condition.
762     //       set to 0b,  for we don't support it.
763 
764     // [0] power is on
765     //       1b = system power is on
766     //       0b = system power is off(soft-off S4/S5, or mechanical off)
767 
768     chassis_status.cur_power_state = ((s & 0x3)<<5) | (pgood & 0x1);
769 
770     // Last Power Event
771     // [7..5] – reserved
772     // [4] – 1b = last ‘Power is on’ state was entered via IPMI command
773     // [3] – 1b = last power down caused by power fault
774     // [2] – 1b = last power down caused by a power interlock being activated
775     // [1] – 1b = last power down caused by a Power overload
776     // [0] – 1b = AC failed
777     // set to 0x0,  for we don't support these fields.
778 
779     chassis_status.last_power_event = 0;
780 
781     // Misc. Chassis State
782     // [7] – reserved
783     // [6] – 1b = Chassis Identify command and state info supported (Optional)
784     //       0b = Chassis Identify command support unspecified via this command.
785     //       (The Get Command Support command , if implemented, would still
786     //       indicate support for the Chassis Identify command)
787     // [5..4] – Chassis Identify State. Mandatory when bit[6] =1b, reserved (return
788     //          as 00b) otherwise. Returns the present chassis identify state.
789     //           Refer to the Chassis Identify command for more info.
790     //         00b = chassis identify state = Off
791     //         01b = chassis identify state = Temporary(timed) On
792     //         10b = chassis identify state = Indefinite On
793     //         11b = reserved
794     // [3] – 1b = Cooling/fan fault detected
795     // [2] – 1b = Drive Fault
796     // [1] – 1b = Front Panel Lockout active (power off and reset via chassis
797     //       push-buttons disabled.)
798     // [0] – 1b = Chassis Intrusion active
799     //  set to 0,  for we don't support them.
800     chassis_status.misc_power_state = 0;
801 
802     //  Front Panel Button Capabilities and disable/enable status(Optional)
803     //  set to 0,  for we don't support them.
804     chassis_status.front_panel_button_cap_status = 0;
805 
806     // Pack the actual response
807     memcpy(response, &chassis_status, *data_len);
808 
809 finish:
810     free(busname);
811     reply = sd_bus_message_unref(reply);
812 
813     return rc;
814 }
815 
816 //-------------------------------------------------------------
817 // Send a command to SoftPowerOff application to stop any timer
818 //-------------------------------------------------------------
819 int stop_soft_off_timer()
820 {
821     constexpr auto iface            = "org.freedesktop.DBus.Properties";
822     constexpr auto soft_off_iface   = "xyz.openbmc_project.Ipmi.Internal."
823             "SoftPowerOff";
824 
825     constexpr auto property         = "ResponseReceived";
826     constexpr auto value            = "xyz.openbmc_project.Ipmi.Internal."
827             "SoftPowerOff.HostResponse.HostShutdown";
828 
829     // Get the system bus where most system services are provided.
830     auto bus = ipmid_get_sd_bus_connection();
831 
832     // Get the service name
833     // TODO openbmc/openbmc#1661 - Mapper refactor
834     //
835     // See openbmc/openbmc#1743 for some details but high level summary is that
836     // for now the code will directly call the soft off interface due to a
837     // race condition with mapper usage
838     //
839     //char *busname = nullptr;
840     //auto r = mapper_get_service(bus, SOFTOFF_OBJPATH, &busname);
841     //if (r < 0)
842     //{
843     //    fprintf(stderr, "Failed to get %s bus name: %s\n",
844     //            SOFTOFF_OBJPATH, strerror(-r));
845     //    return r;
846     //}
847 
848     // No error object or reply expected.
849     int rc = sd_bus_call_method(bus, SOFTOFF_BUSNAME, SOFTOFF_OBJPATH, iface,
850                                 "Set", nullptr, nullptr, "ssv",
851                                 soft_off_iface, property, "s", value);
852     if (rc < 0)
853     {
854         fprintf(stderr, "Failed to set property in SoftPowerOff object: %s\n",
855                 strerror(-rc));
856     }
857 
858     //TODO openbmc/openbmc#1661 - Mapper refactor
859     //free(busname);
860     return rc;
861 }
862 
863 //----------------------------------------------------------------------
864 // Create file to indicate there is no need for softoff notification to host
865 //----------------------------------------------------------------------
866 void indicate_no_softoff_needed()
867 {
868     fs::path path{HOST_INBAND_REQUEST_DIR};
869     if (!fs::is_directory(path))
870     {
871         fs::create_directory(path);
872     }
873 
874     // Add the host instance (default 0 for now) to the file name
875     std::string file{HOST_INBAND_REQUEST_FILE};
876     auto size = std::snprintf(nullptr,0,file.c_str(),0);
877     size++; // null
878     std::unique_ptr<char[]> buf(new char[size]);
879     std::snprintf(buf.get(),size,file.c_str(),0);
880 
881     // Append file name to directory and create it
882     path /= buf.get();
883     std::ofstream(path.c_str());
884 }
885 
886 //----------------------------------------------------------------------
887 // Chassis Control commands
888 //----------------------------------------------------------------------
889 ipmi_ret_t ipmi_chassis_control(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
890                                 ipmi_request_t request,
891                                 ipmi_response_t response,
892                                 ipmi_data_len_t data_len,
893                                 ipmi_context_t context)
894 {
895     // Error from power off.
896     int rc = 0;
897 
898     // No response for this command.
899     *data_len = 0;
900 
901     // Catch the actual operaton by peeking into request buffer
902     uint8_t chassis_ctrl_cmd = *(uint8_t *)request;
903     printf("Chassis Control Command: Operation:[0x%X]\n",chassis_ctrl_cmd);
904 
905     switch(chassis_ctrl_cmd)
906     {
907         case CMD_POWER_ON:
908             rc = initiate_state_transition(State::Host::Transition::On);
909             break;
910         case CMD_POWER_OFF:
911             // Need to Nudge SoftPowerOff application that it needs to stop the
912             // watchdog timer if running.
913             rc = stop_soft_off_timer();
914             // Only request the Off transition if the soft power off
915             // application is not running
916             if (rc < 0)
917             {
918                 log<level::INFO>("Did not find soft off service so request "
919                                  "Host:Transition:Off");
920 
921                 // First create a file to indicate to the soft off application
922                 // that it should not run since this is a direct user initiated
923                 // power off request (i.e. a power off request that is not
924                 // originating via a soft power off SMS request)
925                 indicate_no_softoff_needed();
926 
927                 // Now request the shutdown
928                 rc = initiate_state_transition(State::Host::Transition::Off);
929             }
930             else
931             {
932                 log<level::INFO>("Soft off is running, so let that stop "
933                                  "the host");
934             }
935 
936             break;
937 
938         case CMD_HARD_RESET:
939         case CMD_POWER_CYCLE:
940             // SPEC has a section that says certain implementations can trigger
941             // PowerOn if power is Off when a command to power cycle is
942             // requested
943 
944             // First create a file to indicate to the soft off application
945             // that it should not run since this is a direct user initiated
946             // power reboot request (i.e. a reboot request that is not
947             // originating via a soft power off SMS request)
948             indicate_no_softoff_needed();
949 
950             rc = initiate_state_transition(State::Host::Transition::Reboot);
951             break;
952         default:
953         {
954             fprintf(stderr, "Invalid Chassis Control command:[0x%X] received\n",chassis_ctrl_cmd);
955             rc = -1;
956         }
957     }
958 
959     return ( (rc < 0) ? IPMI_CC_INVALID : IPMI_CC_OK);
960 }
961 
962 struct bootOptionTypeMap_t {
963     uint8_t ipmibootflag;
964     char    dbusname[8];
965 };
966 
967 #define INVALID_STRING "Invalid"
968 // dbus supports this list of boot devices.
969 bootOptionTypeMap_t g_bootOptionTypeMap_t[] = {
970 
971         {0x01, "Network"},
972         {0x02, "Disk"},
973         {0x03, "Safe"},
974         {0x05, "CDROM"},
975         {0x06, "Setup"},
976         {0x00, "Default"},
977         {0xFF, INVALID_STRING}
978 };
979 
980 uint8_t get_ipmi_boot_option(char *p) {
981 
982     bootOptionTypeMap_t *s = g_bootOptionTypeMap_t;
983 
984     while (s->ipmibootflag != 0xFF) {
985         if (!strcmp(s->dbusname,p))
986             break;
987         s++;
988     }
989 
990     if (!s->ipmibootflag)
991         printf("Failed to find Sensor Type %s\n", p);
992 
993     return s->ipmibootflag;
994 }
995 
996 char* get_boot_option_by_ipmi(uint8_t p) {
997 
998     bootOptionTypeMap_t *s = g_bootOptionTypeMap_t;
999 
1000     while (s->ipmibootflag != 0xFF) {
1001 
1002         if (s->ipmibootflag == p)
1003             break;
1004 
1005         s++;
1006     }
1007 
1008 
1009     if (!s->ipmibootflag)
1010         printf("Failed to find Sensor Type 0x%x\n", p);
1011 
1012     return s->dbusname;
1013 }
1014 
1015 ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1016                                              ipmi_request_t request,
1017                                              ipmi_response_t response,
1018                                              ipmi_data_len_t data_len,
1019                                              ipmi_context_t context)
1020 {
1021     ipmi_ret_t rc = IPMI_CC_PARM_NOT_SUPPORTED;
1022     char *p = NULL;
1023     get_sys_boot_options_response_t *resp = (get_sys_boot_options_response_t *) response;
1024     get_sys_boot_options_t *reqptr = (get_sys_boot_options_t*) request;
1025     uint8_t s;
1026 
1027     printf("IPMI GET_SYS_BOOT_OPTIONS\n");
1028 
1029     memset(resp,0,sizeof(*resp));
1030     resp->version   = SET_PARM_VERSION;
1031     resp->parm      = 5;
1032     resp->data[0]   = SET_PARM_BOOT_FLAGS_VALID_ONE_TIME;
1033 
1034 
1035     /*
1036      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1037      * This is the only parameter used by petitboot.
1038      */
1039     if ( reqptr->parameter == static_cast<uint8_t>
1040     ( BootOptionParameter::BOOT_FLAGS )) {
1041 
1042         *data_len = static_cast<uint8_t>(BootOptionResponseSize::BOOT_FLAGS);
1043         /* Get the boot device */
1044         int r = dbus_get_property("boot_flags",&p);
1045 
1046         if (r < 0) {
1047             fprintf(stderr, "Dbus get property(boot_flags) failed for get_sys_boot_options.\n");
1048             rc = IPMI_CC_UNSPECIFIED_ERROR;
1049 
1050         } else {
1051 
1052             s = get_ipmi_boot_option(p);
1053             resp->data[1] = (s << 2);
1054             rc = IPMI_CC_OK;
1055 
1056         }
1057 
1058         if (p)
1059         {
1060             free(p);
1061             p = NULL;
1062         }
1063 
1064         /* Get the boot policy */
1065         r = dbus_get_property("boot_policy",&p);
1066 
1067         if (r < 0) {
1068             fprintf(stderr, "Dbus get property(boot_policy) failed for get_sys_boot_options.\n");
1069             rc = IPMI_CC_UNSPECIFIED_ERROR;
1070 
1071         } else {
1072 
1073             printf("BootPolicy is[%s]", p);
1074             resp->data[0] = (strncmp(p,"ONETIME",strlen("ONETIME"))==0) ?
1075                     SET_PARM_BOOT_FLAGS_VALID_ONE_TIME:
1076                     SET_PARM_BOOT_FLAGS_VALID_PERMANENT;
1077             rc = IPMI_CC_OK;
1078 
1079         }
1080 
1081 
1082     } else if ( reqptr->parameter == static_cast<uint8_t>
1083     ( BootOptionParameter::OPAL_NETWORK_SETTINGS )) {
1084 
1085         *data_len = static_cast<uint8_t>(BootOptionResponseSize::OPAL_NETWORK_SETTINGS);
1086 
1087         resp->parm = static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS);
1088 
1089         int ret = getHostNetworkData(resp);
1090 
1091         if (ret < 0) {
1092 
1093             fprintf(stderr, "getHostNetworkData failed for get_sys_boot_options.\n");
1094             rc = IPMI_CC_UNSPECIFIED_ERROR;
1095 
1096         }else
1097             rc = IPMI_CC_OK;
1098     }
1099 
1100     else {
1101         fprintf(stderr, "Unsupported parameter 0x%x\n", reqptr->parameter);
1102     }
1103 
1104     if (p)
1105         free(p);
1106 
1107     if (rc == IPMI_CC_OK)
1108     {
1109         *data_len += 2;
1110     }
1111 
1112     return rc;
1113 }
1114 
1115 
1116 
1117 ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1118                                              ipmi_request_t request,
1119                                              ipmi_response_t response,
1120                                              ipmi_data_len_t data_len,
1121                                              ipmi_context_t context)
1122 {
1123     ipmi_ret_t rc = IPMI_CC_OK;
1124     char *s;
1125     set_sys_boot_options_t *reqptr = (set_sys_boot_options_t *) request;
1126 
1127     printf("IPMI SET_SYS_BOOT_OPTIONS reqptr->parameter =[%d]\n",reqptr->parameter);
1128 
1129     // This IPMI command does not have any resposne data
1130     *data_len = 0;
1131 
1132     /*  000101
1133      * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
1134      * This is the only parameter used by petitboot.
1135      */
1136 
1137     if (reqptr->parameter == (uint8_t)BootOptionParameter::BOOT_FLAGS) {
1138 
1139         s = get_boot_option_by_ipmi(((reqptr->data[1] & 0x3C) >> 2));
1140 
1141         printf("%d: %s\n", __LINE__, s);
1142         if (!strcmp(s,INVALID_STRING)) {
1143 
1144             rc = IPMI_CC_PARM_NOT_SUPPORTED;
1145 
1146         } else {
1147 
1148             int r = dbus_set_property("boot_flags",s);
1149 
1150             if (r < 0) {
1151                 fprintf(stderr, "Dbus set property(boot_flags) failed for set_sys_boot_options.\n");
1152                 rc = IPMI_CC_UNSPECIFIED_ERROR;
1153             }
1154         }
1155 
1156         /* setting the boot policy */
1157         s = (char *)(((reqptr->data[0] & SET_PARM_BOOT_FLAGS_PERMANENT) ==
1158                 SET_PARM_BOOT_FLAGS_PERMANENT) ?"PERMANENT":"ONETIME");
1159 
1160         printf ( "\nBoot Policy is %s",s);
1161         int r = dbus_set_property("boot_policy",s);
1162 
1163         if (r < 0) {
1164             fprintf(stderr, "Dbus set property(boot_policy) failed for set_sys_boot_options.\n");
1165             rc = IPMI_CC_UNSPECIFIED_ERROR;
1166         }
1167 
1168     } else if (reqptr->parameter ==
1169             (uint8_t)BootOptionParameter::OPAL_NETWORK_SETTINGS) {
1170 
1171         int ret = setHostNetworkData(reqptr);
1172         if (ret < 0) {
1173             fprintf(stderr, "setHostNetworkData failed for set_sys_boot_options.\n");
1174             rc = IPMI_CC_UNSPECIFIED_ERROR;
1175         }
1176     }
1177     else {
1178         fprintf(stderr, "Unsupported parameter 0x%x\n", reqptr->parameter);
1179         rc = IPMI_CC_PARM_NOT_SUPPORTED;
1180     }
1181 
1182     return rc;
1183 }
1184 
1185 void register_netfn_chassis_functions()
1186 {
1187     // <Wildcard Command>
1188     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_CHASSIS, IPMI_CMD_WILDCARD);
1189     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_WILDCARD, NULL, ipmi_chassis_wildcard,
1190                            PRIVILEGE_USER);
1191 
1192     // Get Chassis Capabilities
1193     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_CHASSIS, IPMI_CMD_GET_CHASSIS_CAP);
1194     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_CHASSIS_CAP, NULL, ipmi_get_chassis_cap,
1195                            PRIVILEGE_USER);
1196 
1197     // <Get System Boot Options>
1198     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS);
1199     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS, NULL,
1200                            ipmi_chassis_get_sys_boot_options, PRIVILEGE_OPERATOR);
1201 
1202     // <Get Chassis Status>
1203     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_CHASSIS, IPMI_CMD_CHASSIS_STATUS);
1204     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_CHASSIS_STATUS, NULL, ipmi_get_chassis_status,
1205                            PRIVILEGE_USER);
1206 
1207     // <Chassis Control>
1208     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_CHASSIS, IPMI_CMD_CHASSIS_CONTROL);
1209     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_CHASSIS_CONTROL, NULL, ipmi_chassis_control,
1210                            PRIVILEGE_OPERATOR);
1211 
1212     // <Set System Boot Options>
1213     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_CHASSIS, IPMI_CMD_SET_SYS_BOOT_OPTIONS);
1214     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_SYS_BOOT_OPTIONS, NULL,
1215                            ipmi_chassis_set_sys_boot_options, PRIVILEGE_OPERATOR);
1216 }
1217