aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/hostservices.c77
-rw-r--r--doc/opal-api/opal-messages.rst6
-rw-r--r--hw/prd.c130
-rw-r--r--include/fsp.h6
-rw-r--r--include/hostservices.h1
-rw-r--r--include/opal-api.h3
-rw-r--r--include/prd-fw-msg.h3
-rw-r--r--include/psi.h7
-rw-r--r--include/skiboot.h1
9 files changed, 233 insertions, 1 deletions
diff --git a/core/hostservices.c b/core/hostservices.c
index d3a9b3c..bf1b308 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 c7b2e5c..1f5d6f2 100644
--- a/doc/opal-api/opal-messages.rst
+++ b/doc/opal-api/opal-messages.rst
@@ -227,3 +227,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 87e1bae..042d977 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 ee851ec..7518867 100644
--- a/include/fsp.h
+++ b/include/fsp.h
@@ -581,6 +581,12 @@
#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 10cac63..8c54935 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 008ce10..2981b46 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -553,6 +553,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,
};
@@ -1094,6 +1095,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 333e594..9333a30 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 d51ab94..79555ec 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 e828b15..0a7c84a 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);