diff --git a/core/hostservices.c b/core/hostservices.c index d3a9b3cefe10..bf1b30818a5f 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 c7b2e5c8c9a0..1f5d6f20b386 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 87e1bae921d0..042d9779e59e 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 ee851ecedd85..7518867032ba 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 10cac63b995e..8c54935d7ba7 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 008ce1092238..2981b46bd9bc 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 333e594c7f99..9333a309a351 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 d51ab9465e2b..79555ec02605 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 e828b1584b93..0a7c84a8b29c 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);