aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/hw/fsp/fsp-ipmi.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/hw/fsp/fsp-ipmi.c')
-rw-r--r--roms/skiboot/hw/fsp/fsp-ipmi.c400
1 files changed, 400 insertions, 0 deletions
diff --git a/roms/skiboot/hw/fsp/fsp-ipmi.c b/roms/skiboot/hw/fsp/fsp-ipmi.c
new file mode 100644
index 000000000..e368c2828
--- /dev/null
+++ b/roms/skiboot/hw/fsp/fsp-ipmi.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Conduit for IPMI messages to/from FSP
+ *
+ * Copyright 2014-2019 IBM Corp.
+ */
+
+#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);
+DEFINE_LOG_ENTRY(OPAL_RC_IPMI_RESP, OPAL_PLATFORM_ERR_EVT, OPAL_IPMI,
+ OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL,
+ OPAL_NA);
+
+DEFINE_LOG_ENTRY(OPAL_RC_IPMI_DMA_ERROR_RESP, OPAL_PLATFORM_ERR_EVT, OPAL_IPMI,
+ OPAL_PLATFORM_FIRMWARE, OPAL_INFO,
+ OPAL_NA);
+
+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);
+ if (fsp_ipmi.cur_msg == NULL) {
+ unlock(&fsp_ipmi.lock);
+ return;
+ }
+ 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 = fsp_msg_get_data_word(msg->resp, 0);
+ struct fsp_ipmi_msg *fsp_ipmi_msg = msg->user_data;
+ struct ipmi_msg *ipmi_msg;
+
+ fsp_freemsg(msg);
+
+ if (status != FSP_STATUS_SUCCESS) {
+ 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;
+
+ if (fsp_in_rr())
+ return OPAL_BUSY;
+
+ 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);
+
+ if (fsp_in_rr())
+ return OPAL_BUSY;
+
+ 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);
+
+ if (fsp_in_rr())
+ return OPAL_BUSY;
+
+ 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,
+ /* FIXME if ever use ipmi_queue_msg_sync on FSP */
+ .poll = NULL,
+};
+
+static bool fsp_ipmi_rr_notify(uint32_t cmd_sub_mod,
+ struct fsp_msg *msg __unused)
+{
+ struct ipmi_msg *ipmi_msg;
+
+ switch (cmd_sub_mod) {
+ case FSP_RESET_START:
+ return true;
+ case FSP_RELOAD_COMPLETE:
+ /*
+ * We will not get response for outstanding request. Send error
+ * message to caller and start sending new ipmi messages.
+ */
+ if (fsp_ipmi.cur_msg) {
+ ipmi_msg = &fsp_ipmi.cur_msg->ipmi_msg;
+ fsp_ipmi_cmd_done(ipmi_msg->cmd,
+ IPMI_NETFN_RETURN_CODE(ipmi_msg->netfn),
+ IPMI_ERR_UNSPECIFIED);
+ }
+ fsp_ipmi_send_request();
+ return true;
+ }
+ return false;
+}
+
+static struct fsp_client fsp_ipmi_client_rr = {
+ .message = fsp_ipmi_rr_notify,
+};
+
+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 = fsp_msg_get_data_word(msg, 3);
+ uint32_t length = fsp_msg_get_data_word(msg, 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(fsp_msg_get_data_word(msg, 1) == PSI_DMA_PLAT_RESP_BUF);
+
+ if (status != FSP_STATUS_SUCCESS) {
+ if(status == FSP_STATUS_DMA_ERROR)
+ log_simple_error(&e_info(OPAL_RC_IPMI_DMA_ERROR_RESP), "IPMI: Received "
+ "DMA ERROR response from FSP, this may be due to FSP "
+ "is in termination state:0x%02x\n", status);
+ else
+ log_simple_error(&e_info(OPAL_RC_IPMI_RESP), "IPMI: FSP response "
+ "received 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_SUCCESS);
+ }
+
+ /* 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);
+ fsp_register_client(&fsp_ipmi_client_rr, FSP_MCLASS_RR_EVENT);
+ ipmi_register_backend(&fsp_ipmi_backend);
+}