[PATCH phosphor-host-ipmid v3] IPMI soft power off

OpenBMC Patches openbmc-patches at stwcx.xyz
Wed Nov 25 04:50:25 AEDT 2015


From: vishwa <vishwanath at in.ibm.com>

---
 Makefile         |   5 +--
 apphandler.C     |  67 +++++++++++++++++++++++++++++--
 apphandler.h     |  33 ++++++++++++++-
 chassishandler.C |  85 ++++++++++++++++++++++++++++++++++++++-
 chassishandler.h |  15 +++++++
 host-services.c  | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 host-services.h  |   3 ++
 ipmid-api.h      |   2 +-
 ipmid.C          |   6 ++-
 ipmid.H          |   1 +
 10 files changed, 324 insertions(+), 12 deletions(-)
 create mode 100644 host-services.c
 create mode 100644 host-services.h

diff --git a/Makefile b/Makefile
index 1a37c0f..1851c9f 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/ipmid.C b/ipmid.C
index 6b1eacc..440705c 100644
--- a/ipmid.C
+++ b/ipmid.C
@@ -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));
diff --git a/ipmid.H b/ipmid.H
index 73b60e6..3de2c8c 100644
--- a/ipmid.H
+++ b/ipmid.H
@@ -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]
-- 
2.6.3




More information about the openbmc mailing list