[Skiboot] [PATCH v2] fsp/ipmi: Add the in-band IPMI support for FSP systems

Neelesh Gupta neelegup at linux.vnet.ibm.com
Fri Jul 3 00:14:41 AEST 2015


FSP implements the IPMI commands support that can be accessed over
the mailbox interface from the host. The host needs to provide and
receive the message/data bytes of the IPMI command in the TCE space
in the  KCS (Keyboard Controller Style) format.

Signed-off-by: Neelesh Gupta <neelegup at linux.vnet.ibm.com>
---

v1 -> v2
* Fixed the intermittent assertion fail in the function fsp_ipmi_req_complete()
  on the conditon  'assert(fsp_ipmi_msg == fsp_ipmi.cur_msg)'

  It happened because there could be 'response message' arrived to this
  driver before the 'response' of the request and 'response message'
  handing path resets the 'cur_msg', fsp sends it in order though.. i.e.,
  'response' followed by 'response message'.
* Replaced list_del() with list_del_from() to perform extra check for
  the dequeue_msg() interface.

 hw/fsp/Makefile.inc        |    2 
 hw/fsp/fsp-ipmi.c          |  347 ++++++++++++++++++++++++++++++++++++++++++++
 include/errorlog.h         |    6 +
 include/fsp.h              |    6 +
 include/psi.h              |    4 +
 platforms/ibm-fsp/common.c |    5 +
 6 files changed, 369 insertions(+), 1 deletion(-)
 create mode 100644 hw/fsp/fsp-ipmi.c

diff --git a/hw/fsp/Makefile.inc b/hw/fsp/Makefile.inc
index d4654d5..3df832c 100644
--- a/hw/fsp/Makefile.inc
+++ b/hw/fsp/Makefile.inc
@@ -4,7 +4,7 @@ FSP_OBJS  = fsp.o fsp-console.o fsp-rtc.o fsp-nvram.o fsp-sysparam.o
 FSP_OBJS += fsp-surveillance.o fsp-codeupdate.o fsp-sensor.o
 FSP_OBJS += fsp-diag.o fsp-leds.o fsp-mem-err.o fsp-op-panel.o
 FSP_OBJS += fsp-elog-read.o fsp-elog-write.o fsp-epow.o fsp-dpo.o
-FSP_OBJS += fsp-dump.o fsp-mdst-table.o
+FSP_OBJS += fsp-dump.o fsp-mdst-table.o fsp-ipmi.o
 FSP_OBJS += fsp-attn.o
 FSP = hw/fsp/built-in.o
 $(FSP): $(FSP_OBJS:%=hw/fsp/%)
diff --git a/hw/fsp/fsp-ipmi.c b/hw/fsp/fsp-ipmi.c
new file mode 100644
index 0000000..bdfbce7
--- /dev/null
+++ b/hw/fsp/fsp-ipmi.c
@@ -0,0 +1,347 @@
+/* Copyright 2014-2015 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errorlog.h>
+#include <fsp.h>
+#include <ipmi.h>
+#include <lock.h>
+#include <opal-api.h>
+
+/*
+ * Under the hood, FSP IPMI component implements the KCS (Keyboard Controller
+ * Style) interface
+ *
+ * KCS interface request message format
+ *
+ *    BYTE 1	 BYTE 2	       BYTE 3:N
+ *  -------------------------------------
+ * | NetFn/LUN |    Cmd    |    Data     |
+ *  -------------------------------------
+ *
+ * KCS interface response message format
+ *
+ *    BYTE 1	 BYTE 2		BYTE 3	  BYTE 4:N
+ *  ------------------------------------------------
+ * | NetFn/LUN |    Cmd    |  CompCode  |   Data    |
+ *  ------------------------------------------------
+
+ */
+
+#define FSP_IPMI_REQ_MIN_LEN	2 /* NetFn + Cmd */
+#define FSP_IPMI_RESP_MIN_LEN	3 /* NetFn + Cmd + Completion code */
+
+DEFINE_LOG_ENTRY(OPAL_RC_IPMI_REQ, OPAL_PLATFORM_ERR_EVT, OPAL_IPMI,
+		 OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL,
+		 OPAL_NA, NULL);
+DEFINE_LOG_ENTRY(OPAL_RC_IPMI_RESP, OPAL_PLATFORM_ERR_EVT, OPAL_IPMI,
+		 OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL,
+		 OPAL_NA, NULL);
+
+struct fsp_ipmi_msg {
+	struct list_node	link;
+	struct ipmi_msg		ipmi_msg;
+};
+
+static struct fsp_ipmi {
+	struct list_head	msg_queue;
+	void			*ipmi_req_buf;
+	void			*ipmi_resp_buf;
+	/* There can only be one outstanding request whose reference is stored
+	 * in 'cur_msg' and the 'lock' protects against the concurrent updates
+	 * of it through request and response. The same 'lock' also protects
+	 * the list manipulation.
+	 */
+	struct fsp_ipmi_msg	*cur_msg;
+	struct lock		lock;
+} fsp_ipmi;
+
+static int fsp_ipmi_send_request(void);
+
+static void fsp_ipmi_cmd_done(uint8_t cmd, uint8_t netfn, uint8_t cc)
+{
+	struct fsp_ipmi_msg *fsp_ipmi_msg = fsp_ipmi.cur_msg;
+
+	lock(&fsp_ipmi.lock);
+	list_del(&fsp_ipmi_msg->link);
+	fsp_ipmi.cur_msg = NULL;
+	unlock(&fsp_ipmi.lock);
+
+	ipmi_cmd_done(cmd, netfn, cc, &fsp_ipmi_msg->ipmi_msg);
+}
+
+
+static void fsp_ipmi_req_complete(struct fsp_msg *msg)
+{
+	uint8_t status = (msg->resp->word1 >> 8) & 0xff;
+	uint32_t length = msg->resp->data.words[0];
+	struct fsp_ipmi_msg *fsp_ipmi_msg;
+	struct ipmi_msg *ipmi_msg;
+
+	fsp_freemsg(msg);
+
+	if (status != FSP_STATUS_SUCCESS) {
+		fsp_ipmi_msg = msg->user_data;
+		assert(fsp_ipmi_msg == fsp_ipmi.cur_msg);
+
+		ipmi_msg = &fsp_ipmi_msg->ipmi_msg;
+
+		if (length != (ipmi_msg->req_size + FSP_IPMI_REQ_MIN_LEN))
+			prlog(PR_DEBUG, "IPMI: Length mismatch in req completion "
+			      "(%d, %d)\n", ipmi_msg->req_size, length);
+
+		log_simple_error(&e_info(OPAL_RC_IPMI_REQ), "IPMI: Request "
+				 "failed with status:0x%02x\n", status);
+		/* FSP will not send the response now, so clear the current
+		 * outstanding request
+		 */
+		fsp_ipmi_cmd_done(ipmi_msg->cmd, ipmi_msg->netfn + (1 << 2),
+				  IPMI_INVALID_COMMAND_ERR);
+
+		/* Send the next request in the queue */
+		fsp_ipmi_send_request();
+	}
+}
+
+static int fsp_ipmi_send_request(void)
+{
+	uint8_t *req_buf = fsp_ipmi.ipmi_req_buf;
+	struct ipmi_msg *ipmi_msg;
+	struct fsp_msg *msg;
+	int rc;
+
+	lock(&fsp_ipmi.lock);
+	/* An outstanding request is still pending */
+	if (fsp_ipmi.cur_msg) {
+		unlock(&fsp_ipmi.lock);
+		return 0;
+	}
+
+	fsp_ipmi.cur_msg = list_top(&fsp_ipmi.msg_queue, struct fsp_ipmi_msg,
+				    link);
+	unlock(&fsp_ipmi.lock);
+
+	if (!fsp_ipmi.cur_msg)
+		return 0;
+
+	ipmi_msg = &fsp_ipmi.cur_msg->ipmi_msg;
+	prlog(PR_TRACE, "IPMI: Send request, netfn:0x%02x, cmd:0x%02x, "
+	      "req_len:%d\n", ipmi_msg->netfn, ipmi_msg->cmd, ipmi_msg->req_size);
+
+	/* KCS request message format */
+	*req_buf++ = ipmi_msg->netfn;	/* BYTE 1 */
+	*req_buf++ = ipmi_msg->cmd;	/* BYTE 2 */
+	if (ipmi_msg->req_size)
+		memcpy(req_buf, ipmi_msg->data, ipmi_msg->req_size);
+
+	msg = fsp_mkmsg(FSP_CMD_FETCH_PLAT_DATA, 5, 0, PSI_DMA_PLAT_REQ_BUF,
+			0, PSI_DMA_PLAT_RESP_BUF,
+			ipmi_msg->req_size + FSP_IPMI_REQ_MIN_LEN);
+	if (!msg) {
+		log_simple_error(&e_info(OPAL_RC_IPMI_REQ), "IPMI: Failed to "
+				 "allocate request message\n");
+		return OPAL_NO_MEM;
+	}
+
+	msg->user_data = fsp_ipmi.cur_msg;
+	rc = fsp_queue_msg(msg, fsp_ipmi_req_complete);
+	if (rc) {
+		fsp_freemsg(msg);
+		log_simple_error(&e_info(OPAL_RC_IPMI_REQ), "IPMI: Failed to "
+				 "queue request message (%d)\n", rc);
+		return OPAL_INTERNAL_ERROR;
+	}
+
+	return OPAL_SUCCESS;
+}
+
+static struct ipmi_msg *fsp_ipmi_alloc_msg(size_t req_size, size_t resp_size)
+{
+	struct fsp_ipmi_msg *fsp_ipmi_msg;
+	struct ipmi_msg *ipmi_msg;
+
+	fsp_ipmi_msg = zalloc(sizeof(*fsp_ipmi_msg) + MAX(req_size, resp_size));
+	if (!fsp_ipmi_msg)
+		return NULL;
+
+	ipmi_msg = &fsp_ipmi_msg->ipmi_msg;
+
+	ipmi_msg->req_size = req_size;
+	ipmi_msg->resp_size = resp_size;
+	ipmi_msg->data = (uint8_t *)(fsp_ipmi_msg + 1);
+
+	return ipmi_msg;
+}
+
+static void fsp_ipmi_free_msg(struct ipmi_msg *ipmi_msg)
+{
+	struct fsp_ipmi_msg *fsp_ipmi_msg = container_of(ipmi_msg,
+			struct fsp_ipmi_msg, ipmi_msg);
+
+	free(fsp_ipmi_msg);
+}
+
+static int fsp_ipmi_queue_msg(struct ipmi_msg *ipmi_msg)
+{
+	struct fsp_ipmi_msg *fsp_ipmi_msg = container_of(ipmi_msg,
+			struct fsp_ipmi_msg, ipmi_msg);
+
+	lock(&fsp_ipmi.lock);
+	list_add_tail(&fsp_ipmi.msg_queue, &fsp_ipmi_msg->link);
+	unlock(&fsp_ipmi.lock);
+
+	return fsp_ipmi_send_request();
+}
+
+static int fsp_ipmi_queue_msg_head(struct ipmi_msg *ipmi_msg)
+{
+	struct fsp_ipmi_msg *fsp_ipmi_msg = container_of(ipmi_msg,
+			struct fsp_ipmi_msg, ipmi_msg);
+
+	lock(&fsp_ipmi.lock);
+	list_add(&fsp_ipmi.msg_queue, &fsp_ipmi_msg->link);
+	unlock(&fsp_ipmi.lock);
+
+	return fsp_ipmi_send_request();
+}
+
+static int fsp_ipmi_dequeue_msg(struct ipmi_msg *ipmi_msg)
+{
+	struct fsp_ipmi_msg *fsp_ipmi_msg = container_of(ipmi_msg,
+			struct fsp_ipmi_msg, ipmi_msg);
+
+	lock(&fsp_ipmi.lock);
+	list_del_from(&fsp_ipmi.msg_queue, &fsp_ipmi_msg->link);
+	unlock(&fsp_ipmi.lock);
+
+	return 0;
+}
+
+static struct ipmi_backend fsp_ipmi_backend = {
+	.alloc_msg	= fsp_ipmi_alloc_msg,
+	.free_msg	= fsp_ipmi_free_msg,
+	.queue_msg	= fsp_ipmi_queue_msg,
+	.queue_msg_head	= fsp_ipmi_queue_msg_head,
+	.dequeue_msg	= fsp_ipmi_dequeue_msg,
+};
+
+static bool fsp_ipmi_send_response(uint32_t cmd)
+{
+	struct fsp_msg *resp;
+	int rc;
+
+	resp = fsp_mkmsg(cmd, 0);
+	if (!resp) {
+		log_simple_error(&e_info(OPAL_RC_IPMI_RESP), "IPMI: Failed to "
+				 "allocate response message\n");
+		return false;
+	}
+
+	rc = fsp_queue_msg(resp, fsp_freemsg);
+	if (rc) {
+		fsp_freemsg(resp);
+		log_simple_error(&e_info(OPAL_RC_IPMI_RESP), "IPMI: Failed to "
+				 "queue response message\n");
+		return false;
+	}
+
+	return true;
+}
+
+static bool fsp_ipmi_read_response(struct fsp_msg *msg)
+{
+	uint8_t *resp_buf = fsp_ipmi.ipmi_resp_buf;
+	uint32_t status = msg->data.words[3];
+	uint32_t length = msg->data.words[2];
+	struct ipmi_msg *ipmi_msg;
+	uint8_t netfn, cmd, cc;
+
+	assert(fsp_ipmi.cur_msg);
+	ipmi_msg = &fsp_ipmi.cur_msg->ipmi_msg;
+
+	/* Response TCE token */
+	assert(msg->data.words[1] == PSI_DMA_PLAT_RESP_BUF);
+
+	if (status != FSP_STATUS_SUCCESS) {
+		log_simple_error(&e_info(OPAL_RC_IPMI_RESP), "IPMI: Response "
+				 "with bad status:0x%02x\n", status);
+		fsp_ipmi_cmd_done(ipmi_msg->cmd, ipmi_msg->netfn + (1 << 2),
+				  IPMI_INVALID_COMMAND_ERR);
+		return fsp_ipmi_send_response(FSP_RSP_PLAT_DATA |
+					      FSP_STATUS_GENERIC_ERROR);
+	}
+
+	/* KCS response message format */
+	netfn = *resp_buf++;
+	cmd = *resp_buf++;
+	cc = *resp_buf++;
+	length -= FSP_IPMI_RESP_MIN_LEN;
+
+	prlog(PR_TRACE, "IPMI: fsp response received, netfn:0x%02x, cmd:0x%02x,"
+	      " cc:0x%02x, length:%d\n", netfn, cmd, cc, length);
+
+	if (length > ipmi_msg->resp_size) {
+		prlog(PR_DEBUG, "IPMI: Length mismatch in response (%d, %d)\n",
+		      length, ipmi_msg->resp_size);
+		length = ipmi_msg->resp_size; /* Truncate */
+		cc = IPMI_ERR_MSG_TRUNCATED;
+	}
+
+	ipmi_msg->resp_size = length;
+	if (length)
+		memcpy(ipmi_msg->data, resp_buf, length);
+
+	fsp_ipmi_cmd_done(cmd, netfn, cc);
+
+	return fsp_ipmi_send_response(FSP_RSP_PLAT_DATA);
+}
+
+static bool fsp_ipmi_response(uint32_t cmd_sub_mod, struct fsp_msg *msg)
+{
+	bool rc;
+
+	switch (cmd_sub_mod) {
+	case FSP_CMD_SEND_PLAT_DATA:
+		prlog(PR_TRACE, "FSP_CMD_SEND_PLAT_DATA command received\n");
+		rc = fsp_ipmi_read_response(msg);
+		break;
+	default:
+		return false;
+	};
+
+	/* If response sent successfully, pick the next request */
+	if (rc == true)
+		fsp_ipmi_send_request();
+
+	return rc;
+}
+
+static struct fsp_client fsp_ipmi_client = {
+	.message = fsp_ipmi_response,
+};
+
+void fsp_ipmi_init(void)
+{
+	fsp_tce_map(PSI_DMA_PLAT_REQ_BUF, fsp_ipmi.ipmi_req_buf,
+		    PSI_DMA_PLAT_REQ_BUF_SIZE);
+	fsp_tce_map(PSI_DMA_PLAT_RESP_BUF, fsp_ipmi.ipmi_resp_buf,
+		    PSI_DMA_PLAT_RESP_BUF_SIZE);
+
+	list_head_init(&fsp_ipmi.msg_queue);
+	init_lock(&fsp_ipmi.lock);
+
+	fsp_register_client(&fsp_ipmi_client, FSP_MCLASS_FETCH_SPDATA);
+	ipmi_register_backend(&fsp_ipmi_backend);
+}
diff --git a/include/errorlog.h b/include/errorlog.h
index 49c60cf..295acf6 100644
--- a/include/errorlog.h
+++ b/include/errorlog.h
@@ -192,6 +192,7 @@ struct opal_err_info {
 #define OPAL_SLW			0x534C	/* SL */
 #define OPAL_FSP			0x4650	/* FP */
 #define OPAL_I2C			0x4943	/* IC */
+#define OPAL_IPMI			0x4950  /* IP */
 
 /* SAPPHIRE SRC componenet ID*/
 #define OPAL_CU				0x1000
@@ -223,6 +224,7 @@ struct opal_err_info {
 #define OPAL_SL				0x2100
 #define OPAL_FP				0x2200
 #define OPAL_IC				0x2300
+#define OPAL_IP				0x2400
 
 enum opal_reasoncode {
 /* code update */
@@ -317,6 +319,10 @@ enum opal_reasoncode {
 	OPAL_RC_I2C_TIMEOUT	= OPAL_IC | 0x12,
 	OPAL_RC_I2C_TRANSFER	= OPAL_IC | 0x13,
 	OPAL_RC_I2C_RESET	= OPAL_IC | 0x14,
+
+/* IPMI */
+	OPAL_RC_IPMI_REQ	= OPAL_IP | 0x10,
+	OPAL_RC_IPMI_RESP	= OPAL_IP | 0x11,
 };
 
 #define DEFINE_LOG_ENTRY(reason, type, id, subsys,			\
diff --git a/include/fsp.h b/include/fsp.h
index 5689798..c6f0d8a 100644
--- a/include/fsp.h
+++ b/include/fsp.h
@@ -421,6 +421,9 @@
  */
 #define FSP_CMD_FETCH_SP_DATA	0x1d40101 /* HV->FSP: Fetch & DMA data */
 #define FSP_CMD_WRITE_SP_DATA	0x1d40201 /* HV->FSP: Fetch & DMA data */
+#define FSP_CMD_FETCH_PLAT_DATA	0x1d40500 /* HV->FSP: Platform function data */
+#define FSP_CMD_SEND_PLAT_DATA	0x0d40501 /* FSP->HV */
+#define FSP_RSP_PLAT_DATA	0x0d48500 /* HV->FSP */
 
 /* Data set IDs for SP data commands */
 #define FSP_DATASET_SP_DUMP	0x01
@@ -787,6 +790,9 @@ extern int (*fsp_flash_term_hook)(void);
 extern void fsp_init_surveillance(void);
 extern void fsp_surv_query(void);
 
+/* IPMI */
+extern void fsp_ipmi_init(void);
+
 /* Reset/Reload */
 extern void fsp_reinit_fsp(void);
 extern void fsp_trigger_reset(void);
diff --git a/include/psi.h b/include/psi.h
index 62643aa..36240e1 100644
--- a/include/psi.h
+++ b/include/psi.h
@@ -193,6 +193,10 @@
 #define PSI_DMA_MEMCONS_SZ		0x00001000
 #define PSI_DMA_LOG_BUF			0x03200000
 #define PSI_DMA_LOG_BUF_SZ		0x00100000 /* INMEM_CON_LEN */
+#define PSI_DMA_PLAT_REQ_BUF		0x03300000
+#define PSI_DMA_PLAT_REQ_BUF_SIZE	0x00001000
+#define PSI_DMA_PLAT_RESP_BUF		0x03301000
+#define PSI_DMA_PLAT_RESP_BUF_SIZE	0x00001000
 
 /* P8 only mappings */
 #define PSI_DMA_TRACE_BASE		0x04000000
diff --git a/platforms/ibm-fsp/common.c b/platforms/ibm-fsp/common.c
index d993b95..3068b2b 100644
--- a/platforms/ibm-fsp/common.c
+++ b/platforms/ibm-fsp/common.c
@@ -21,6 +21,7 @@
 #include <opal.h>
 #include <console.h>
 #include <hostservices.h>
+#include <ipmi.h>
 
 #include "ibm-fsp.h"
 
@@ -122,6 +123,10 @@ void ibm_fsp_init(void)
 	op_display(OP_LOG, OP_MOD_INIT, 0x0002);
 	fsp_init_surveillance();
 
+	/* IPMI */
+	fsp_ipmi_init();
+	ipmi_opal_init();
+
 	/* Initialize sensor access */
 	op_display(OP_LOG, OP_MOD_INIT, 0x0003);
 	fsp_init_sensor();



More information about the Skiboot mailing list