[Skiboot] [PATCH v3 08/10] prd: Implement generic HBRT - FSP interface

Vasant Hegde hegdevasant at linux.vnet.ibm.com
Sat May 11 21:42:21 AEST 2019


This patch implements generic interface to pass data from HBRT to FSP
during runtime (HBRT -> opal-prd -> kernel -> OPAL -> FSP).

HBRT sends data via firmware_request interface. We have to convert that to
MBOX format and send it to FSP. OPAL uses TCE mapped memory to send data.
FSP will reuse same memory for response. Once processing is complete FSP
sends response to OPAL. Finally OPAL calls HBRT with firmware_response
message.

Also introduces new opal_msg type (OPAL_MSG_PRD2) to pass bigger prd message
to kernel.
  - if (prd_msg > OPAL_MSG_FIXED_PARAMS_SIZE)
	use OPAL_MSG_PRD2

Signed-off-by: Vasant Hegde <hegdevasant at linux.vnet.ibm.com>
---
 core/hostservices.c            |  77 ++++++++++++++++++++++++
 doc/opal-api/opal-messages.rst |   6 ++
 hw/prd.c                       | 130 ++++++++++++++++++++++++++++++++++++++++-
 include/fsp.h                  |   6 ++
 include/hostservices.h         |   1 +
 include/opal-api.h             |   3 +
 include/prd-fw-msg.h           |   3 +
 include/psi.h                  |   7 +++
 include/skiboot.h              |   1 +
 9 files changed, 233 insertions(+), 1 deletion(-)

diff --git a/core/hostservices.c b/core/hostservices.c
index d3a9b3cef..bf1b30818 100644
--- a/core/hostservices.c
+++ b/core/hostservices.c
@@ -802,3 +802,80 @@ bool hservices_init(void)
 
 	return true;
 }		
+
+static void hservice_send_hbrt_msg_resp(struct fsp_msg *msg)
+{
+	int status = (msg->resp->word1 >> 8) & 0xff;
+
+	fsp_freemsg(msg);
+	if (status) {
+		prlog(PR_NOTICE, "HBRT: HBRT to FSP MBOX command failed "
+		      "[rc=0x%x]\n", status);
+	}
+
+	fsp_tce_unmap(PSI_DMA_HBRT_FSP_MSG, PSI_DMA_HBRT_FSP_MSG_SIZE);
+	/* Send response data to HBRT */
+	prd_fw_resp_fsp_response(status);
+}
+
+#define FSP_STATUS_RR	(-8193)
+/* Caller takes care of serializing MBOX message */
+int hservice_send_hbrt_msg(void *data, u64 dsize)
+{
+	uint32_t tce_len, offset;
+	int rc;
+	uint64_t addr;
+	struct fsp_msg *msg;
+
+	prlog(PR_NOTICE, "HBRT: HBRT - FSP message generated\n");
+
+	/* We only support FSP based system */
+	if (!fsp_present()) {
+		prlog(PR_DEBUG,
+		      "HBRT: Warning, HBRT - FSP message discarded!\n");
+		return OPAL_UNSUPPORTED;
+	}
+
+	/*
+	 * If FSP is in R/R then send specific return code to HBRT (inside
+	 * HBRT message) and return success to caller (opal_prd_msg()).
+	 */
+	if (fsp_in_rr()) {
+		prlog(PR_DEBUG,
+		      "HBRT: FSP is in R/R. Dropping HBRT - FSP message\n");
+		prd_fw_resp_fsp_response(FSP_STATUS_RR);
+		return OPAL_SUCCESS;
+	}
+
+	/* Adjust address, size for TCE mapping */
+	addr = (u64)data & ~TCE_MASK;
+	offset = (u64)data & TCE_MASK;
+	tce_len = ALIGN_UP((dsize + offset), TCE_PSIZE);
+
+	if (tce_len > PSI_DMA_HBRT_FSP_MSG_SIZE) {
+		prlog(PR_DEBUG,
+		      "HBRT: HBRT - FSP message is too big, discarded\n");
+		return OPAL_PARAMETER;
+	}
+	fsp_tce_map(PSI_DMA_HBRT_FSP_MSG, (void *)addr, tce_len);
+
+	msg = fsp_mkmsg(FSP_CMD_HBRT_TO_FSP, 3, 0,
+			(PSI_DMA_HBRT_FSP_MSG + offset), dsize);
+	if (!msg) {
+		prlog(PR_DEBUG,
+		      "HBRT: Failed to create HBRT - FSP message to FSP\n");
+		rc = OPAL_NO_MEM;
+		goto out_tce_unmap;
+	}
+
+	rc = fsp_queue_msg(msg, hservice_send_hbrt_msg_resp);
+	if (rc == 0)
+		return rc;
+
+	prlog(PR_DEBUG, "HBRT: Failed to queue HBRT message to FSP\n");
+	fsp_freemsg(msg);
+
+out_tce_unmap:
+	fsp_tce_unmap(PSI_DMA_HBRT_FSP_MSG, PSI_DMA_HBRT_FSP_MSG_SIZE);
+	return rc;
+}
diff --git a/doc/opal-api/opal-messages.rst b/doc/opal-api/opal-messages.rst
index 25acaec2c..f4ad9756a 100644
--- a/doc/opal-api/opal-messages.rst
+++ b/doc/opal-api/opal-messages.rst
@@ -219,3 +219,9 @@ these values.
 If ``opal_occ_msg.type > 2`` then host should ignore the message for now,
 new events can be defined for ``opal_occ_msg.type`` in the future versions
 of OPAL.
+
+OPAL_MSG_PRD2
+-------------
+
+This message is a OPAL-to-HBRT notification. Its same as OPAL_MSG_PRD except
+this one supports passing more than 64bytes (8*8) of data.
diff --git a/hw/prd.c b/hw/prd.c
index 87e1bae92..042d9779e 100644
--- a/hw/prd.c
+++ b/hw/prd.c
@@ -39,6 +39,7 @@ static uint64_t ipoll_status[MAX_CHIPS];
 static uint8_t _prd_msg_buf[sizeof(struct opal_prd_msg) +
 			    sizeof(struct prd_fw_msg)];
 static struct opal_prd_msg *prd_msg = (struct opal_prd_msg *)&_prd_msg_buf;
+static struct opal_prd_msg *prd_msg_fsp_req;
 static bool prd_msg_inuse, prd_active;
 static struct dt_node *prd_node;
 static bool prd_enabled = false;
@@ -112,6 +113,10 @@ static void prd_msg_consumed(void *data, int status __unused)
 		event = EVENT_OCC_RESET;
 		break;
 	case OPAL_PRD_MSG_TYPE_FIRMWARE_RESPONSE:
+		if (prd_msg_fsp_req) {
+			free(prd_msg_fsp_req);
+			prd_msg_fsp_req = NULL;
+		}
 		break;
 	case OPAL_PRD_MSG_TYPE_SBE_PASSTHROUGH:
 		proc = msg->sbe_passthrough.chip;
@@ -309,6 +314,48 @@ void prd_fsp_occ_load_start(uint32_t proc)
 	prd_event(proc, EVENT_FSP_OCC_LOAD_START);
 }
 
+void prd_fw_resp_fsp_response(int status)
+{
+	struct prd_fw_msg *fw_resp;
+	uint64_t fw_resp_len_old;
+	int rc;
+	uint16_t hdr_size;
+	enum opal_msg_type msg_type = OPAL_MSG_PRD2;
+
+	lock(&events_lock);
+
+	/* In case of failure, return code is passed via generic_resp */
+	if (status != 0) {
+		fw_resp = (struct prd_fw_msg *)prd_msg_fsp_req->fw_resp.data;
+		fw_resp->type = cpu_to_be64(PRD_FW_MSG_TYPE_RESP_GENERIC);
+		fw_resp->generic_resp.status = cpu_to_be64(status);
+
+		fw_resp_len_old = prd_msg_fsp_req->fw_resp.len;
+		prd_msg_fsp_req->fw_resp.len = cpu_to_be64(PRD_FW_MSG_BASE_SIZE +
+						 sizeof(fw_resp->generic_resp));
+
+		/* Update prd message size */
+		hdr_size = be16_to_cpu(prd_msg_fsp_req->hdr.size);
+		hdr_size -= fw_resp_len_old;
+		hdr_size += be64_to_cpu(prd_msg_fsp_req->fw_resp.len);
+		prd_msg_fsp_req->hdr.size = cpu_to_be16(hdr_size);
+	}
+
+	/*
+	 * If prd message size is <= OPAL_MSG_FIXED_PARAMS_SIZE then use
+	 * OPAL_MSG_PRD to pass data to kernel. So that it works fine on
+	 * older kernel (which does not support OPAL_MSG_PRD2).
+	 */
+	if (prd_msg_fsp_req->hdr.size < OPAL_MSG_FIXED_PARAMS_SIZE)
+		msg_type = OPAL_MSG_PRD;
+
+	rc = _opal_queue_msg(msg_type, prd_msg_fsp_req, prd_msg_consumed,
+			     prd_msg_fsp_req->hdr.size, prd_msg_fsp_req);
+	if (!rc)
+		prd_msg_inuse = true;
+	unlock(&events_lock);
+}
+
 /* incoming message handlers */
 static int prd_msg_handle_attn_ack(struct opal_prd_msg *msg)
 {
@@ -366,9 +413,10 @@ static int prd_msg_handle_fini(void)
 
 static int prd_msg_handle_firmware_req(struct opal_prd_msg *msg)
 {
-	unsigned long fw_req_len, fw_resp_len;
+	unsigned long fw_req_len, fw_resp_len, data_len;
 	struct prd_fw_msg *fw_req, *fw_resp;
 	int rc;
+	uint64_t resp_msg_size;
 
 	fw_req_len = be64_to_cpu(msg->fw_req.req_len);
 	fw_resp_len = be64_to_cpu(msg->fw_req.resp_len);
@@ -417,6 +465,86 @@ static int prd_msg_handle_firmware_req(struct opal_prd_msg *msg)
 		prd_msg->hdr.size = cpu_to_be16(sizeof(*prd_msg));
 		rc = 0;
 		break;
+	case PRD_FW_MSG_TYPE_HBRT_FSP:
+		/*
+		 * HBRT -> FSP messages are serialized. Just to be sure check
+		 * whether fsp_req message is free or not.
+		 */
+		if (prd_msg_fsp_req) {
+			prlog(PR_DEBUG, "PRD: HBRT - FSP message is busy\n");
+			rc = OPAL_BUSY;
+			break;
+		}
+
+		/*
+		 * FSP interface doesn't tell us the response data size.
+		 * Hence pass response length = request length.
+		 */
+		resp_msg_size = sizeof(msg->hdr) + sizeof(msg->token) +
+			sizeof(msg->fw_resp) + fw_req_len;
+
+		if (resp_msg_size > OPAL_PRD_MSG_SIZE_MAX) {
+			prlog(PR_DEBUG, "PRD: HBRT - FSP response size (0x%llx)"
+			      " is bigger than prd interface can handle\n",
+			      resp_msg_size);
+			rc = OPAL_INTERNAL_ERROR;
+			break;
+		}
+
+		/*
+		 * We will use fsp_queue_msg() to pass HBRT data to FSP.
+		 * We cannot directly map kernel passed data as kernel
+		 * will release the memory as soon as we return the control.
+		 * Also FSP uses same memory to pass response to HBRT. Hence
+		 * lets copy data to local memory. Then pass this memory to
+		 * FSP via TCE mapping.
+		 */
+		prd_msg_fsp_req = zalloc(resp_msg_size);
+		if (!prd_msg_fsp_req) {
+			prlog(PR_DEBUG, "PRD: Failed to allocate memory "
+			      "for HBRT - FSP message\n");
+			rc = OPAL_RESOURCE;
+			break;
+		}
+
+		/* Update message header */
+		prd_msg_fsp_req->hdr.type = OPAL_PRD_MSG_TYPE_FIRMWARE_RESPONSE;
+		prd_msg_fsp_req->hdr.size = cpu_to_be16(resp_msg_size);
+		prd_msg_fsp_req->token = 0;
+		prd_msg_fsp_req->fw_resp.len = fw_req_len;
+
+		/* copy HBRT data to local memory */
+		fw_resp = (struct prd_fw_msg *)prd_msg_fsp_req->fw_resp.data;
+		memcpy(fw_resp, fw_req, fw_req_len);
+
+		/* Update response type */
+		fw_resp->type = cpu_to_be64(PRD_FW_MSG_TYPE_HBRT_FSP);
+
+		/* Get MBOX message size */
+		data_len = fw_req_len - PRD_FW_MSG_BASE_SIZE;
+
+		/* We have to wait until FSP responds */
+		prd_msg_inuse = false;
+		/* Unlock to avoid recursive lock issue */
+		unlock(&events_lock);
+
+		/* Send message to FSP */
+		rc = hservice_send_hbrt_msg(&(fw_resp->mbox_msg), data_len);
+
+		/*
+		 * Callback handler from hservice_send_hbrt_msg will take
+		 * care of sending response to HBRT. So just send return
+		 * code to Linux.
+		 */
+		if (rc == OPAL_SUCCESS)
+			return rc;
+
+		lock(&events_lock);
+		if (prd_msg_fsp_req) {
+			free(prd_msg_fsp_req);
+			prd_msg_fsp_req = NULL;
+		}
+		break;
 	default:
 		prlog(PR_DEBUG, "PRD: Unsupported fw_request type : 0x%llx\n",
 		      be64_to_cpu(fw_req->type));
diff --git a/include/fsp.h b/include/fsp.h
index ee851eced..751886703 100644
--- a/include/fsp.h
+++ b/include/fsp.h
@@ -580,6 +580,12 @@
 #define FSP_CMD_MEM_DYN_DEALLOC	0x00e40500 /* FSP->HV: Dynamic mem dealloc */
 #define FSP_RSP_MEM_DYN_DEALLOC	0x00e48500 /* HV->FSP */
 
+/*
+ * Class F2
+ */
+#define FSP_CMD_HBRT_TO_FSP	0x1f20100 /* HV->FSP: HBRT message */
+
+
 /*
  * Functions exposed to the rest of skiboot
  */
diff --git a/include/hostservices.h b/include/hostservices.h
index 10cac63b9..8c54935d7 100644
--- a/include/hostservices.h
+++ b/include/hostservices.h
@@ -42,5 +42,6 @@ int hservice_send_error_log(uint32_t plid, uint32_t dsize, void *data);
 int hservice_wakeup(uint32_t i_core, uint32_t i_mode);
 int fsp_occ_reset_status(u64 chipid, s64 status);
 int fsp_occ_load_start_status(u64 chipid, s64 status);
+int hservice_send_hbrt_msg(void *data, u64 dsize);
 
 #endif /* __HOSTSERVICES_H */
diff --git a/include/opal-api.h b/include/opal-api.h
index e15c5b89e..11c5afbb5 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -550,6 +550,7 @@ enum opal_msg_type {
 	OPAL_MSG_DPO		= 5,
 	OPAL_MSG_PRD		= 6,
 	OPAL_MSG_OCC		= 7,
+	OPAL_MSG_PRD2		= 8,
 	OPAL_MSG_TYPE_MAX,
 };
 
@@ -1091,6 +1092,8 @@ enum opal_prd_msg_type {
 	OPAL_PRD_MSG_TYPE_FSP_OCC_LOAD_START_STATUS, /* HBRT --> OPAL */
 };
 
+#define OPAL_PRD_MSG_SIZE_MAX	(1 << 16)
+
 struct opal_prd_msg_header {
 	uint8_t		type;
 	uint8_t		pad[1];
diff --git a/include/prd-fw-msg.h b/include/prd-fw-msg.h
index 333e594c7..9333a309a 100644
--- a/include/prd-fw-msg.h
+++ b/include/prd-fw-msg.h
@@ -44,6 +44,9 @@ struct prd_fw_msg {
 			__be32	size;
 			char	data[];
 		} __packed errorlog;
+		struct {
+			char	data;
+		} mbox_msg;
 	};
 };
 
diff --git a/include/psi.h b/include/psi.h
index d51ab9465..79555ec02 100644
--- a/include/psi.h
+++ b/include/psi.h
@@ -233,6 +233,13 @@
 #define PSI_DMA_PLAT_REQ_BUF_SIZE	0x00001000
 #define PSI_DMA_PLAT_RESP_BUF		0x03301000
 #define PSI_DMA_PLAT_RESP_BUF_SIZE	0x00001000
+/*
+ * Our PRD interface can handle upto 64KB data transfer between
+ * OPAL - opal-prd. Hence adding TCE size as 68KB. If we increase
+ * OPAL - opal-prd message size, then we have to fix this.
+ */
+#define PSI_DMA_HBRT_FSP_MSG		0x03302000
+#define PSI_DMA_HBRT_FSP_MSG_SIZE	0x00011000
 
 /* P8 only mappings */
 #define PSI_DMA_TRACE_BASE		0x04000000
diff --git a/include/skiboot.h b/include/skiboot.h
index e828b1584..0a7c84a8b 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -291,6 +291,7 @@ extern void prd_init(void);
 extern void prd_register_reserved_memory(void);
 extern void prd_fsp_occ_reset(uint32_t proc);
 extern void prd_fsp_occ_load_start(u32 proc);
+extern void prd_fw_resp_fsp_response(int status);
 
 /* Flatten device-tree */
 extern void *create_dtb(const struct dt_node *root, bool exclusive);
-- 
2.14.3



More information about the Skiboot mailing list