Skip to content

Commit

Permalink
prd: Implement generic HBRT - FSP interface
Browse files Browse the repository at this point in the history
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@linux.vnet.ibm.com>
Signed-off-by: Stewart Smith <stewart@linux.ibm.com>
  • Loading branch information
Vasant Hegde authored and stewartsmith committed Jun 3, 2019
1 parent fcb1d4d commit fb702bf
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 1 deletion.
77 changes: 77 additions & 0 deletions core/hostservices.c
Expand Up @@ -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;
}
6 changes: 6 additions & 0 deletions doc/opal-api/opal-messages.rst
Expand Up @@ -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.
130 changes: 129 additions & 1 deletion hw/prd.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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));
Expand Down
6 changes: 6 additions & 0 deletions include/fsp.h
Expand Up @@ -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
*/
Expand Down
1 change: 1 addition & 0 deletions include/hostservices.h
Expand Up @@ -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 */
3 changes: 3 additions & 0 deletions include/opal-api.h
Expand Up @@ -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,
};

Expand Down Expand Up @@ -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];
Expand Down
3 changes: 3 additions & 0 deletions include/prd-fw-msg.h
Expand Up @@ -44,6 +44,9 @@ struct prd_fw_msg {
__be32 size;
char data[];
} __packed errorlog;
struct {
char data;
} mbox_msg;
};
};

Expand Down
7 changes: 7 additions & 0 deletions include/psi.h
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions include/skiboot.h
Expand Up @@ -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);
Expand Down

0 comments on commit fb702bf

Please sign in to comment.