From patchwork Thu May 25 07:05:53 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeremy Kerr X-Patchwork-Id: 766858 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3wYL1X12lqz9s3T for ; Thu, 25 May 2017 17:07:28 +1000 (AEST) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3wYL1W71r2zDqjj for ; Thu, 25 May 2017 17:07:27 +1000 (AEST) X-Original-To: skiboot@lists.ozlabs.org Delivered-To: skiboot@lists.ozlabs.org Received: from ozlabs.org (ozlabs.org [103.22.144.67]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3wYL0d53XNzDqgh for ; Thu, 25 May 2017 17:06:41 +1000 (AEST) Received: by ozlabs.org (Postfix, from userid 1023) id 3wYL0d3cK7z9sCZ; Thu, 25 May 2017 17:06:41 +1000 (AEST) From: Jeremy Kerr To: skiboot@lists.ozlabs.org Date: Thu, 25 May 2017 17:05:53 +1000 Message-Id: <1495695955-30718-11-git-send-email-jk@ozlabs.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1495695955-30718-1-git-send-email-jk@ozlabs.org> References: <1495695955-30718-1-git-send-email-jk@ozlabs.org> Subject: [Skiboot] [PATCH RFC 10/12] opal-prd: Add firmware_request & firmware_notify implementations X-BeenThere: skiboot@lists.ozlabs.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Mailing list for skiboot development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Dan Crowell MIME-Version: 1.0 Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" This change adds the implementation of firmware_request() and firmware_notify(). To do this, we need to add a message queue, so that we can properly handle out-of-order messages coming from firmware. Signed-off-by: Jeremy Kerr --- external/opal-prd/opal-prd.c | 151 ++++++++++++++++++++++++++++++++++++++++++- external/opal-prd/thunk.S | 2 +- include/opal-api.h | 16 +++++ 3 files changed, 167 insertions(+), 2 deletions(-) diff --git a/external/opal-prd/opal-prd.c b/external/opal-prd/opal-prd.c index e78a2ef..02a01a0 100644 --- a/external/opal-prd/opal-prd.c +++ b/external/opal-prd/opal-prd.c @@ -50,6 +50,8 @@ #include #include +#include + #include "opal-prd.h" #include "hostboot-interface.h" #include "module.h" @@ -65,6 +67,11 @@ struct prd_range { uint32_t instance; }; +struct prd_msgq_item { + struct list_node list; + struct opal_prd_msg msg; +}; + struct opal_prd_ctx { int fd; int socket; @@ -80,6 +87,7 @@ struct opal_prd_ctx { char *hbrt_file_name; bool use_syslog; bool expert_mode; + struct list_head msgq; struct opal_prd_msg *msg; size_t msg_alloc_len; void (*vlog)(int, const char *, va_list); @@ -121,6 +129,8 @@ static const char *hbrt_code_region_name = "ibm,hbrt-code-image"; static const int opal_prd_version = 1; static uint64_t opal_prd_ipoll = 0xf000000000000000; +static const int max_msgq_len = 16; + static const char *ipmi_devnode = "/dev/ipmi0"; static const int ipmi_timeout_ms = 5000; @@ -150,6 +160,8 @@ struct func_desc { void *toc; } hbrt_entry; +static int read_prd_msg(struct opal_prd_ctx *ctx); + static struct prd_range *find_range(const char *name, uint32_t instance) { struct prd_range *range; @@ -267,6 +279,7 @@ extern int call_mfg_htmgt_pass_thru(uint16_t i_cmdLength, uint8_t *i_cmdData, extern int call_apply_attr_override(uint8_t *i_data, size_t size); extern int call_run_command(int argc, const char **argv, char **o_outString); extern uint64_t call_get_ipoll_events(void); +extern int call_firmware_notify(uint64_t len, void *data); void hservice_puts(const char *str) { @@ -677,6 +690,96 @@ uint64_t hservice_get_interface_capabilities(uint64_t set) return 0; } +uint64_t hservice_firmware_request(uint64_t req_len, void *req, + uint64_t *resp_lenp, void *resp) +{ + struct opal_prd_msg *msg = ctx->msg; + uint64_t resp_len; + size_t size; + int rc, n; + + resp_len = be64_to_cpu(*resp_lenp); + + pr_log(LOG_DEBUG, + "HBRT: firmware request: %lu bytes req, %lu bytes resp", + req_len, resp_len); + + /* sanity check for potential overflows */ + if (req_len > 0xffff || resp_len > 0xffff) + return -1; + + size = sizeof(msg->hdr) + sizeof(msg->token) + + sizeof(msg->fw_req) + req_len; + + /* we need the entire message to fit within the 2-byte size field */ + if (size > 0xffff) + return -1; + + /* variable sized message, so we may need to expand our buffer */ + if (size > ctx->msg_alloc_len) { + ctx->msg_alloc_len = size; + ctx->msg = msg = realloc(ctx->msg, size); + } + + memset(msg, 0, size); + + /* construct request message... */ + msg->hdr.type = OPAL_PRD_MSG_TYPE_FIRMWARE_REQUEST; + msg->hdr.size = htobe16(size); + msg->fw_req.req_len = htobe64(req_len); + msg->fw_req.resp_len = htobe64(resp_len); + memcpy(msg->fw_req.data, req, req_len); + + hexdump((void *)msg, size); + + /* ... and send to firmware */ + rc = write(ctx->fd, msg, size); + if (rc != size) { + pr_log(LOG_WARNING, + "FW: Failed to send FIRMWARE_REQUEST message: %m"); + return -1; + } + + /* We have an "inner" poll loop here, as we want to ensure that the + * next entry into HBRT is the return from this function. So, only + * read from the prd fd, and queue anything that isn't a response + * to this request + */ + n = 0; + for (;;) { + struct prd_msgq_item *item; + + rc = read_prd_msg(ctx); + if (rc) + return -1; + + msg = ctx->msg; + if (msg->hdr.type == OPAL_PRD_MSG_TYPE_FIRMWARE_RESPONSE) { + size = be64toh(msg->fw_resp.len); + if (size > resp_len) + return -1; + + /* success! a valid response that fits into HBRT's + * resp buffer */ + memcpy(resp, msg->fw_resp.data, size); + *resp_lenp = htobe64(size); + return 0; + } + + /* not a response? queue up for later consumption */ + if (++n > max_msgq_len) { + pr_log(LOG_ERR, + "FW: too many messages queued (%d) while " + "waiting for FIRMWARE_RESPONSE", n); + return -1; + } + size = be16toh(msg->hdr.size); + item = malloc(sizeof(*item) + size); + memcpy(&item->msg, msg, size); + list_add_tail(&ctx->msgq, &item->list); + } +} + int hservices_init(struct opal_prd_ctx *ctx, void *code) { uint64_t *s, *d; @@ -1225,6 +1328,27 @@ static int handle_msg_occ_reset(struct opal_prd_ctx *ctx, return 0; } +static int handle_msg_firmware_notify(struct opal_prd_ctx *ctx, + struct opal_prd_msg *msg) +{ + uint64_t len; + void *buf; + + len = be64toh(msg->fw_notify.len); + buf = msg->fw_notify.data; + + pr_debug("FW: firmware notification, %ld bytes", len); + + if (!hservice_runtime->firmware_notify) { + pr_log_nocall("firmware_notify"); + return -1; + } + + call_firmware_notify(len, buf); + + return 0; +} + static int handle_prd_msg(struct opal_prd_ctx *ctx, struct opal_prd_msg *msg) { int rc = -1; @@ -1239,6 +1363,9 @@ static int handle_prd_msg(struct opal_prd_ctx *ctx, struct opal_prd_msg *msg) case OPAL_PRD_MSG_TYPE_OCC_ERROR: rc = handle_msg_occ_error(ctx, msg); break; + case OPAL_PRD_MSG_TYPE_FIRMWARE_NOTIFY: + rc = handle_msg_firmware_notify(ctx, msg); + break; default: pr_log(LOG_WARNING, "Invalid incoming message type 0x%x", msg->hdr.type); @@ -1247,6 +1374,24 @@ static int handle_prd_msg(struct opal_prd_ctx *ctx, struct opal_prd_msg *msg) return rc; } +#define list_for_each_pop(h, i, type, member) \ + for (i = list_pop((h), type, member); \ + i; \ + i = list_pop((h), type, member)) + + +static int process_msgq(struct opal_prd_ctx *ctx) +{ + struct prd_msgq_item *item; + + list_for_each_pop(&ctx->msgq, item, struct prd_msgq_item, list) { + handle_prd_msg(ctx, &item->msg); + free(item); + } + + return 0; +} + static int read_prd_msg(struct opal_prd_ctx *ctx) { struct opal_prd_msg *msg; @@ -1585,6 +1730,9 @@ static int run_attn_loop(struct opal_prd_ctx *ctx) pollfds[1].events = POLLIN | POLLERR; for (;;) { + /* run through any pending messages */ + process_msgq(ctx); + rc = poll(pollfds, 2, -1); if (rc < 0) { pr_log(LOG_ERR, "FW: event poll failed: %m"); @@ -1671,6 +1819,8 @@ static int run_prd_daemon(struct opal_prd_ctx *ctx) ctx->msg_alloc_len = sizeof(*ctx->msg); ctx->msg = malloc(ctx->msg_alloc_len); + list_head_init(&ctx->msgq); + i2c_init(); #ifdef DEBUG_I2C @@ -1700,7 +1850,6 @@ static int run_prd_daemon(struct opal_prd_ctx *ctx) goto out_close; } - if (ctx->hbrt_file_name) { rc = map_hbrt_file(ctx, ctx->hbrt_file_name); if (rc) { diff --git a/external/opal-prd/thunk.S b/external/opal-prd/thunk.S index b18e3cb..cca5890 100644 --- a/external/opal-prd/thunk.S +++ b/external/opal-prd/thunk.S @@ -197,7 +197,7 @@ hinterface: DISABLED_THUNK(hservice_map_phys_mem) DISABLED_THUNK(hservice_unmap_phys_mem) DISABLED_THUNK(hservice_hcode_scom_update) - DISABLED_THUNK(hservice_firmware_request) + CALLBACK_THUNK(hservice_firmware_request) .globl __hinterface_pad __hinterface_pad: /* Reserved space for future growth */ diff --git a/include/opal-api.h b/include/opal-api.h index 80033c6..e94747f 100644 --- a/include/opal-api.h +++ b/include/opal-api.h @@ -1029,6 +1029,9 @@ enum opal_prd_msg_type { OPAL_PRD_MSG_TYPE_OCC_ERROR, /* HBRT <-- OPAL */ OPAL_PRD_MSG_TYPE_OCC_RESET, /* HBRT <-- OPAL */ OPAL_PRD_MSG_TYPE_OCC_RESET_NOTIFY, /* HBRT --> OPAL */ + OPAL_PRD_MSG_TYPE_FIRMWARE_REQUEST, /* HBRT --> OPAL */ + OPAL_PRD_MSG_TYPE_FIRMWARE_RESPONSE, /* HBRT <-- HBRT */ + OPAL_PRD_MSG_TYPE_FIRMWARE_NOTIFY, /* HBRT <-- HBRT */ }; struct opal_prd_msg_header { @@ -1060,6 +1063,19 @@ struct opal_prd_msg { struct { __be64 chip; } occ_reset; + struct { + __be64 req_len; + __be64 resp_len; + char data[]; + } fw_req; + struct { + __be64 len; + char data[]; + } fw_resp; + struct { + __be64 len; + char data[]; + } fw_notify; }; };