summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hw/fsp/Makefile.inc2
-rw-r--r--hw/fsp/fsp-ipmi.c355
-rw-r--r--include/errorlog.h6
-rw-r--r--include/fsp.h6
-rw-r--r--include/psi.h4
-rw-r--r--platforms/ibm-fsp/common.c5
6 files changed, 377 insertions, 1 deletions
diff --git a/hw/fsp/Makefile.inc b/hw/fsp/Makefile.inc
index 2fe4f0dd..71690954 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-chiptod.o
+FSP_OBJS += fsp-dump.o fsp-mdst-table.o fsp-chiptod.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 00000000..e5cd6e26
--- /dev/null
+++ b/hw/fsp/fsp-ipmi.c
@@ -0,0 +1,355 @@
+/* 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_NETFN_RETURN_CODE(ipmi_msg->netfn),
+ IPMI_ERR_UNSPECIFIED);
+
+ /* 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 OPAL_SUCCESS;
+ }
+
+ 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 OPAL_SUCCESS;
+
+ 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");
+ fsp_ipmi_cmd_done(ipmi_msg->cmd,
+ IPMI_NETFN_RETURN_CODE(ipmi_msg->netfn),
+ IPMI_ERR_UNSPECIFIED);
+ return OPAL_NO_MEM;
+ }
+
+ msg->user_data = fsp_ipmi.cur_msg;
+ rc = fsp_queue_msg(msg, fsp_ipmi_req_complete);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_IPMI_REQ), "IPMI: Failed to "
+ "queue request message (%d)\n", rc);
+ fsp_freemsg(msg);
+ fsp_ipmi_cmd_done(ipmi_msg->cmd,
+ IPMI_NETFN_RETURN_CODE(ipmi_msg->netfn),
+ IPMI_ERR_UNSPECIFIED);
+ 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_NETFN_RETURN_CODE(ipmi_msg->netfn),
+ IPMI_ERR_UNSPECIFIED);
+ 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 51af88c4..af9b4412 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 3df78a17..02910840 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
@@ -793,6 +796,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 62643aa7..36240e19 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 12536bd4..59a890bb 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"
@@ -125,6 +126,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();
OpenPOWER on IntegriCloud