From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001
From: Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com>
Date: Tue, 10 Oct 2023 14:33:42 +0000
Subject: Add submodule dependency files

Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
---
 roms/skiboot/hw/fsp/fsp-ipmi.c | 400 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 400 insertions(+)
 create mode 100644 roms/skiboot/hw/fsp/fsp-ipmi.c

(limited to 'roms/skiboot/hw/fsp/fsp-ipmi.c')

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);
+}
-- 
cgit