diff options
author | vishwa <vishwanath@in.ibm.com> | 2015-11-20 12:43:49 -0600 |
---|---|---|
committer | vishwa <vishwanath@in.ibm.com> | 2015-11-24 11:45:50 -0600 |
commit | 3699327a1dbbfe8a8fd751266fd72dc7bf515108 (patch) | |
tree | f2ab0c129b252ac4958d0544abd1f39a47ba3494 | |
parent | 6872af6e32a93ff54544b2d32302bb236cd891a5 (diff) | |
download | phosphor-host-ipmid-3699327a1dbbfe8a8fd751266fd72dc7bf515108.tar.gz phosphor-host-ipmid-3699327a1dbbfe8a8fd751266fd72dc7bf515108.zip |
IPMI soft power off
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | apphandler.C | 67 | ||||
-rw-r--r-- | apphandler.h | 33 | ||||
-rw-r--r-- | chassishandler.C | 85 | ||||
-rw-r--r-- | chassishandler.h | 15 | ||||
-rw-r--r-- | host-services.c | 119 | ||||
-rw-r--r-- | host-services.h | 3 | ||||
-rw-r--r-- | ipmid-api.h | 2 | ||||
-rw-r--r-- | ipmid.C | 6 | ||||
-rw-r--r-- | ipmid.H | 1 |
10 files changed, 324 insertions, 12 deletions
@@ -5,8 +5,7 @@ TESTER = testit TESTADDSEL = testaddsel DAEMON = ipmid -DAEMON_OBJ = $(DAEMON).o - +DAEMON_OBJ = ipmid.o host-services.o LIB_APP_OBJ = apphandler.o \ sensorhandler.o \ @@ -28,7 +27,7 @@ LIB_APP = libapphandler.so INSTALLED_LIBS += $(LIB_APP) INSTALLED_HEADERS = ipmid-api.h -INC_FLAG += $(shell pkg-config --cflags --libs libsystemd) -I. -O2 +INC_FLAG += $(shell pkg-config --cflags --libs libsystemd) -I. -O2 LIB_FLAG += $(shell pkg-config --libs libsystemd) -rdynamic IPMID_PATH ?= -DHOST_IPMI_LIB_PATH=\"/usr/lib/host-ipmid/\" diff --git a/apphandler.C b/apphandler.C index 6467397..d3df330 100644 --- a/apphandler.C +++ b/apphandler.C @@ -10,19 +10,75 @@ extern sd_bus *bus; void register_netfn_app_functions() __attribute__((constructor)); +//--------------------------------------------------------------------- +// Called by Host on seeing a SMS_ATN bit set. Return a hardcoded +// value of 0x2 indicating we need Host read some data. +//------------------------------------------------------------------- +ipmi_ret_t ipmi_app_get_msg_flags(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_request_t request, ipmi_response_t response, + ipmi_data_len_t data_len, ipmi_context_t context) +{ + // Generic return from IPMI commands. + ipmi_ret_t rc = IPMI_CC_OK; + + printf("IPMI APP GET MSG FLAGS returning with [bit:2] set\n"); + + // From IPMI spec V2.0 for Get Message Flags Command : + // bit:[1] from LSB : 1b = Event Message Buffer Full. + // Return as 0 if Event Message Buffer is not supported, + // or when the Event Message buffer is disabled. + // TODO. For now. assume its not disabled and send "0x2" anyway: + + uint8_t set_event_msg_buffer_full = 0x2; + *data_len = sizeof(set_event_msg_buffer_full); + + // Pack the actual response + memcpy(response, &set_event_msg_buffer_full, *data_len); + + return rc; +} +//------------------------------------------------------------------- +// Called by Host post response from Get_Message_Flags +//------------------------------------------------------------------- ipmi_ret_t ipmi_app_read_event(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) { ipmi_ret_t rc = IPMI_CC_OK; - *data_len = 0; + printf("IPMI APP READ EVENT command received\n"); - printf("IPMI APP READ EVENT Ignoring for now\n"); - return rc; + // TODO : For now, this is catering only to the Soft Power Off via OEM SEL + // mechanism. If we need to make this generically used for some + // other conditions, then we can take advantage of context pointer. -} + struct oem_sel_timestamped soft_off = {0}; + *data_len = sizeof(struct oem_sel_timestamped); + + // either id[0] -or- id[1] can be filled in. We will use id[0] + soft_off.id[0] = SEL_OEM_ID_0; + soft_off.id[1] = SEL_OEM_ID_0; + soft_off.type = SEL_RECORD_TYPE_OEM; + + // Following 3 bytes are from IANA Manufactre_Id field. See below + soft_off.manuf_id[0]= 0x41; + soft_off.manuf_id[1]= 0xA7; + soft_off.manuf_id[2]= 0x00; + + // per IPMI spec NetFuntion for OEM + soft_off.netfun = 0x3A; + // Mechanism to kick start soft shutdown. + soft_off.cmd = CMD_POWER; + soft_off.data[0] = SOFT_OFF; + + // All '0xFF' since unused. + memset(&soft_off.data[1], 0xFF, 3); + + // Pack the actual response + memcpy(response, &soft_off, *data_len); + return rc; +} ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, ipmi_response_t response, @@ -359,6 +415,9 @@ void register_netfn_app_functions() ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_BMC_GLOBAL_ENABLES, NULL, ipmi_app_set_bmc_global_enables); + printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_MSG_FLAGS); + ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_MSG_FLAGS, NULL, ipmi_app_get_msg_flags); + return; } diff --git a/apphandler.h b/apphandler.h index 35a2b20..aa2a55d 100644 --- a/apphandler.h +++ b/apphandler.h @@ -1,6 +1,19 @@ #ifndef __HOST_IPMI_APP_HANDLER_H__ #define __HOST_IPMI_APP_HANDLER_H__ +#include <stdint.h> + +// These are per skiboot ipmi-sel code + +// OEM_SEL type with Timestamp +#define SEL_OEM_ID_0 0x55 +// SEL type is OEM and -not- general SEL +#define SEL_RECORD_TYPE_OEM 0xC0 +// Minor command for soft shurdown +#define SOFT_OFF 0x00 +// Major command for Any kind of power ops +#define CMD_POWER 0x04 + // IPMI commands for App net functions. enum ipmi_netfn_app_cmds { @@ -11,9 +24,27 @@ enum ipmi_netfn_app_cmds IPMI_CMD_RESET_WD = 0x22, IPMI_CMD_SET_WD = 0x24, IPMI_CMD_SET_BMC_GLOBAL_ENABLES = 0x2E, + IPMI_CMD_GET_MSG_FLAGS = 0x31, IPMI_CMD_READ_EVENT = 0x35, IPMI_CMD_GET_CAP_BIT = 0x36, - }; +// A Mechanism to tell host to shtudown hosts by sending this PEM SEL. Really +// the only used fields by skiboot are: +// id[0] / id[1] for ID_0 , ID_1 +// type : SEL_RECORD_TYPE_OEM as standard SELs are ignored by skiboot +// cmd : CMD_POWER for power functions +// data[0], specific commands. example Soft power off. power cycle, etc. +struct oem_sel_timestamped +{ + /* SEL header */ + uint8_t id[2]; + uint8_t type; + uint8_t manuf_id[3]; + uint8_t timestamp[4]; + /* OEM SEL data (6 bytes) follows */ + uint8_t netfun; + uint8_t cmd; + uint8_t data[4]; +}; #endif diff --git a/chassishandler.C b/chassishandler.C index d00a124..56b8375 100644 --- a/chassishandler.C +++ b/chassishandler.C @@ -4,6 +4,11 @@ #include <string.h> #include <stdint.h> +// OpenBMC Chassis Manager dbus framework +const char *chassis_bus_name = "org.openbmc.control.Chassis"; +const char *chassis_object_name = "/org/openbmc/control/chassis0"; +const char *chassis_intf_name = "org.openbmc.control.Chassis"; + void register_netfn_chassis_functions() __attribute__((constructor)); struct get_sys_boot_options_t { @@ -23,6 +28,82 @@ ipmi_ret_t ipmi_chassis_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd, return rc; } +//------------------------------------------------------------ +// Calls into Chassis Control Dbus object to do the power off +//------------------------------------------------------------ +int ipmi_chassis_power_off() +{ + // sd_bus error + int rc = 0; + + // SD Bus error report mechanism. + sd_bus_error bus_error = SD_BUS_ERROR_NULL; + + // Response from the call. Although there is no response for this call, + // obligated to mention this to make compiler happy. + sd_bus_message *response = NULL; + + // Gets a hook onto either a SYSTEM or SESSION bus + sd_bus *bus_type = ipmid_get_sd_bus_connection(); + + rc = sd_bus_call_method(bus_type, // On the System Bus + chassis_bus_name, // Service to contact + chassis_object_name, // Object path + chassis_intf_name, // Interface name + "powerOff", // Method to be called + &bus_error, // object to return error + &response, // Response buffer if any + NULL); // No input arguments + if(rc < 0) + { + fprintf(stderr,"ERROR initiating Power Off:[%s]\n",bus_error.message); + } + else + { + printf("Chassis Power Off initiated successfully\n"); + } + + sd_bus_error_free(&bus_error); + sd_bus_message_unref(response); + + return rc; +} + +//---------------------------------------------------------------------- +// Chassis Control commands +//---------------------------------------------------------------------- +ipmi_ret_t ipmi_chassis_control(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_request_t request, ipmi_response_t response, + ipmi_data_len_t data_len, ipmi_context_t context) +{ + // Error from power off. + int rc = 0; + + // No response for this command. + *data_len = 0; + + // Catch the actual operaton by peeking into request buffer + uint8_t chassis_ctrl_cmd = *(uint8_t *)request; + printf("Chassis Control Command: Operation:[0x%X]\n",chassis_ctrl_cmd); + + switch(chassis_ctrl_cmd) + { + case CMD_POWER_OFF: + case CMD_HARD_RESET: + { + rc = ipmi_chassis_power_off(); + break; + } + default: + { + fprintf(stderr, "Invalid Chassis Control command:[0x%X] received\n",chassis_ctrl_cmd); + rc = -1; + } + } + + return ( (rc < 0) ? IPMI_CC_INVALID : IPMI_CC_OK); +} + ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) @@ -58,5 +139,7 @@ void register_netfn_chassis_functions() printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS); ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS, NULL, ipmi_chassis_get_sys_boot_options); -} + printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_CHASSIS, IPMI_CMD_CHASSIS_CONTROL); + ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_CHASSIS_CONTROL, NULL, ipmi_chassis_control); +} diff --git a/chassishandler.h b/chassishandler.h index 99ed366..1a26411 100644 --- a/chassishandler.h +++ b/chassishandler.h @@ -1,9 +1,13 @@ #ifndef __HOST_IPMI_CHASSIS_HANDLER_H__ #define __HOST_IPMI_CHASSIS_HANDLER_H__ +#include <stdint.h> + // IPMI commands for Chassis net functions. enum ipmi_netfn_app_cmds { + // Chassis Control + IPMI_CMD_CHASSIS_CONTROL = 0x02, // Get capability bits IPMI_CMD_GET_SYS_BOOT_OPTIONS = 0x09, }; @@ -14,4 +18,15 @@ enum ipmi_chassis_return_codes IPMI_CC_PARM_NOT_SUPPORTED = 0x80, }; +// Various Chassis operations under a single command. +enum ipmi_chassis_control_cmds : uint8_t +{ + CMD_POWER_OFF = 0x00, + CMD_POWER_ON = 0x01, + CMD_POWER_CYCLE = 0x02, + CMD_HARD_RESET = 0x03, + CMD_PULSE_DIAGNOSTIC_INTR = 0x04, + CMD_SOFT_OFF_VIA_OVER_TEMP = 0x05, +}; + #endif diff --git a/host-services.c b/host-services.c new file mode 100644 index 0000000..89f0b6c --- /dev/null +++ b/host-services.c @@ -0,0 +1,119 @@ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <systemd/sd-bus.h> + +// OpenBMC Host IPMI dbus framework +const char *bus_name = "org.openbmc.HostIpmi"; +const char *object_name = "/org/openbmc/HostIpmi/1"; +const char *intf_name = "org.openbmc.HostIpmi"; + +//------------------------------------------------------------------- +// Gets called by PowerOff handler when a Soft Power off is requested +//------------------------------------------------------------------- +static int soft_power_off(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) +{ + int64_t bt_resp = -1; + int rc = 0; + + // Steps to be taken when we get this. + // 1: Send a SMS_ATN to the Host + // 2: Host receives it and sends a GetMsgFlags IPMI command + // 3: IPMID app handler will respond to that with a MSgFlag with bit:0x2 + // set indicating we have a message for Host + // 4: Host sends a GetMsgBuffer command and app handler will respond to + // that with a OEM-SEL with certain fields packed indicating to the + // host that it do a shutdown of the partitions. + // 5: Host does the partition shutdown and calls Chassis Power off command + // 6: App handler handles the command by making a call to ChassisManager + // Dbus + + // Now the job is to send the SMS_ATTN. + + // Req message contains the specifics about which method etc that we want to + // access on which bus, object + sd_bus_message *response = NULL; + + // Error return mechanism + sd_bus_error bus_error = SD_BUS_ERROR_NULL; + + // Gets a hook onto either a SYSTEM or SESSION bus + sd_bus *bus = (sd_bus *)ipmid_get_sd_bus_connection(); + + rc = sd_bus_call_method(bus, // On the System Bus + bus_name, // Service to contact + object_name, // Object path + intf_name, // Interface name + "setAttention", // Method to be called + &bus_error, // object to return error + &response, // Response buffer if any + NULL); // No input arguments + if(rc < 0) + { + fprintf(stderr,"ERROR initiating Power Off:[%s]\n",bus_error.message); + goto finish; + } + + // See if we were able to successfully raise SMS_ATN + rc = sd_bus_message_read(response, "x", &bt_resp); + if (rc < 0) + { + fprintf(stderr, "Failed to get a rc from BT for SMS_ATN: %s\n", strerror(-rc)); + goto finish; + } + +finish: + sd_bus_error_free(&bus_error); + sd_bus_message_unref(response); + + if(rc < 0) + { + return sd_bus_reply_method_return(m, "x", rc); + } + else + { + return sd_bus_reply_method_return(m, "x", bt_resp); + } +} + +//------------------------------------------- +// Function pointer of APIs exposed via Dbus +//------------------------------------------- +static const sd_bus_vtable host_services_vtable[] = +{ + SD_BUS_VTABLE_START(0), + // Takes No("") arguments -but- returns a value of type 64 bit integer("x") + SD_BUS_METHOD("SoftPowerOff", "", "x", &soft_power_off, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END, +}; + +//------------------------------------------------------ +// Called by IPMID as part of the start up +// ----------------------------------------------------- +int start_host_service(sd_bus *bus, sd_bus_slot *slot) +{ + int rc = 0; + + /* Install the object */ + rc = sd_bus_add_object_vtable(bus, + &slot, + "/org/openbmc/HostServices", /* object path */ + "org.openbmc.HostServices", /* interface name */ + host_services_vtable, + NULL); + if (rc < 0) + { + fprintf(stderr, "Failed to issue method call: %s\n", strerror(-rc)); + } + else + { + /* Take one in OpenBmc */ + rc = sd_bus_request_name(bus, "org.openbmc.HostServices", 0); + if (rc < 0) + { + fprintf(stderr, "Failed to acquire service name: %s\n", strerror(-rc)); + } + } + + return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/host-services.h b/host-services.h new file mode 100644 index 0000000..0d93480 --- /dev/null +++ b/host-services.h @@ -0,0 +1,3 @@ +#include <systemd/sd-bus.h> + +extern "C" int start_host_service(sd_bus *, sd_bus_slot *); diff --git a/ipmid-api.h b/ipmid-api.h index 5da7636..2d58961 100644 --- a/ipmid-api.h +++ b/ipmid-api.h @@ -53,7 +53,7 @@ typedef ipmi_ret_t (*ipmid_callback_t)(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t, // information of netfn, cmd, callback handler pointer and context data. // Making this a extern "C" so that plugin libraries written in C can also use // it. -extern "C" void ipmi_register_callback(ipmi_netfn_t, ipmi_cmd_t, +extern "C" void ipmi_register_callback(ipmi_netfn_t, ipmi_cmd_t, ipmi_context_t, ipmid_callback_t); // These are the command network functions, the response @@ -40,7 +40,6 @@ typedef std::pair<ipmid_callback_t, ipmi_context_t> ipmi_fn_context_t; std::map<ipmi_fn_cmd_t, ipmi_fn_context_t> g_ipmid_router_map; - #ifndef HEXDUMP_COLS #define HEXDUMP_COLS 16 #endif @@ -418,6 +417,10 @@ int main(int argc, char *argv[]) // Register all the handlers that provider implementation to IPMI commands. ipmi_register_callback_handlers(HOST_IPMI_LIB_PATH); + // Start the Host Services Dbus Objects + start_host_service(bus, slot); + + // Watch for BT messages r = sd_bus_add_match(bus, &slot, FILTER, handle_ipmi_command, NULL); if (r < 0) { fprintf(stderr, "Failed: sd_bus_add_match: %s : %s\n", strerror(-r), FILTER); @@ -427,7 +430,6 @@ int main(int argc, char *argv[]) for (;;) { /* Process requests */ - r = sd_bus_process(bus, NULL); if (r < 0) { fprintf(stderr, "Failed to process bus: %s\n", strerror(-r)); @@ -2,6 +2,7 @@ #define __HOST_IPMID_IPMI_H__ #include "ipmid-api.h" #include <stdio.h> +#include "host-services.h" // When the requester sends in a netfn and a command along with data, this // function will look for registered handlers that will handle that [netfn,cmd] |