[Skiboot] [PATCH 5/7] prd: Implement generic HBRT - FSP interface
Vasant Hegde
hegdevasant at linux.vnet.ibm.com
Thu Mar 28 22:18:19 AEDT 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.
Signed-off-by: Vasant Hegde <hegdevasant at linux.vnet.ibm.com>
---
core/hostservices.c | 77 +++++++++++++++++++++++++++++++++++
hw/prd.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++-
include/fsp.h | 6 +++
include/hostservices.h | 1 +
include/prd-fw-msg.h | 12 ++++++
include/psi.h | 7 ++++
include/skiboot.h | 1 +
7 files changed, 211 insertions(+), 1 deletion(-)
diff --git a/core/hostservices.c b/core/hostservices.c
index d3a9b3cef..cb307e848 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);
+}
+
+/* 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");
+ /* FIXME: Add macro for -8193, waiting for Dan's patch */
+ prd_fw_resp_fsp_response(-8193);
+ return OPAL_SUCCESS;
+ }
+
+ /* Adjust address, size for TCE mapping */
+ addr = (u64)data & ~TCE_MASK;
+ offset = (u64)data & TCE_MASK;
+ tce_len = ALIGN_UP(dsize, 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/hw/prd.c b/hw/prd.c
index 8827e3d9d..d032d42ca 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)
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,36 @@ 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)
+{
+ int rc;
+ uint64_t fw_resp_len_old;
+ struct prd_fw_msg *fw_resp;
+
+ 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 = 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 */
+ prd_msg_fsp_req->hdr.size -= fw_resp_len_old;
+ prd_msg_fsp_req->hdr.size += prd_msg_fsp_req->fw_resp.len;
+ }
+
+ rc = opal_queue_msg_extended(OPAL_MSG_PRD, 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 +401,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 +453,76 @@ 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;
+
+ /*
+ * 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);
+
+ lock(&events_lock);
+ if (rc && prd_msg_fsp_req) {
+ free(prd_msg_fsp_req);
+ prd_msg_fsp_req = NULL;
+ }
+ unlock(&events_lock);
+ /*
+ * Callback handler from hservice_send_hbrt_msg will take
+ * care of sending response to HBRT. So just send return
+ * code to Linux.
+ */
+ return rc;
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/prd-fw-msg.h b/include/prd-fw-msg.h
index 333e594c7..f56b909a5 100644
--- a/include/prd-fw-msg.h
+++ b/include/prd-fw-msg.h
@@ -33,6 +33,17 @@ enum {
PRD_FW_MSG_TYPE_FSP_HBRT = 6,
};
+struct prd_fw_mbox_msg {
+ __be32 msgq; /* ex: MBOX::FSP_VPD_MSGQ */
+ __be32 msg_type; /* ex: VPD_MSG_TYPE:VPD_WRITE_PROC */
+ struct {
+ __be32 async:1;
+ __be32 pseudosync:1;
+ __be32 unused:30;
+ };
+ __be64 data[];
+} __packed;
+
struct prd_fw_msg {
__be64 type;
union {
@@ -44,6 +55,7 @@ struct prd_fw_msg {
__be32 size;
char data[];
} __packed errorlog;
+ struct prd_fw_mbox_msg 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