aboutsummaryrefslogtreecommitdiff
path: root/external
diff options
context:
space:
mode:
authorJeremy Kerr <jk@ozlabs.org>2017-05-26 12:54:27 +1000
committerStewart Smith <stewart@linux.vnet.ibm.com>2017-06-06 17:59:45 +1000
commitf15a0910edff4700797d85487cedbce6ae8768dd (patch)
tree8adab3e3b2caa29ba2a11e67f6d2182619516698 /external
parent252356c13e05defa6d1724d8747256af761146e9 (diff)
downloadskiboot-f15a0910edff4700797d85487cedbce6ae8768dd.zip
skiboot-f15a0910edff4700797d85487cedbce6ae8768dd.tar.gz
skiboot-f15a0910edff4700797d85487cedbce6ae8768dd.tar.bz2
opal-prd: Add support for variable-sized messages
With the introductuion of the opaque firmware channel, we want to support variable-sized messages. Rather than expecting to read an entire 'struct opal_prd_msg' in one read() call, we can split this over mutiple reads, potentially expanding our message buffer. Signed-off-by: Jeremy Kerr <jk@ozlabs.org> Reviewed-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com> Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'external')
-rw-r--r--external/opal-prd/opal-prd.c78
1 files changed, 66 insertions, 12 deletions
diff --git a/external/opal-prd/opal-prd.c b/external/opal-prd/opal-prd.c
index 02ce185..68d9cbc 100644
--- a/external/opal-prd/opal-prd.c
+++ b/external/opal-prd/opal-prd.c
@@ -80,6 +80,8 @@ struct opal_prd_ctx {
char *hbrt_file_name;
bool use_syslog;
bool expert_mode;
+ struct opal_prd_msg *msg;
+ size_t msg_alloc_len;
void (*vlog)(int, const char *, va_list);
};
@@ -1239,41 +1241,82 @@ static int handle_msg_occ_reset(struct opal_prd_ctx *ctx,
static int handle_prd_msg(struct opal_prd_ctx *ctx)
{
- struct opal_prd_msg msg;
+ struct opal_prd_msg *msg;
int size;
int rc;
- rc = read(ctx->fd, &msg, sizeof(msg));
+ msg = ctx->msg;
+
+ rc = read(ctx->fd, msg, ctx->msg_alloc_len);
if (rc < 0 && errno == EAGAIN)
return -1;
- if (rc != sizeof(msg)) {
- pr_log(LOG_WARNING, "FW: Error reading events from OPAL: %m");
+ /* we need at least enough for the message header... */
+ if (rc < 0) {
+ pr_log(LOG_WARNING, "FW: error reading from firmware: %m");
+ return -1;
+ }
+
+ if (rc < sizeof(msg->hdr)) {
+ pr_log(LOG_WARNING, "FW: short message read from firmware");
return -1;
}
- size = htobe16(msg.hdr.size);
- if (size < sizeof(msg)) {
+ /* ... and for the reported message size to be sane */
+ size = htobe16(msg->hdr.size);
+ if (size < sizeof(msg->hdr)) {
pr_log(LOG_ERR, "FW: Mismatched message size "
"between opal-prd and firmware "
"(%d from FW, %zd expected)",
- size, sizeof(msg));
+ size, sizeof(msg->hdr));
return -1;
}
- switch (msg.hdr.type) {
+ /* expand our message buffer if necessary... */
+ if (size > ctx->msg_alloc_len) {
+ msg = realloc(ctx->msg, size);
+ if (!msg) {
+ pr_log(LOG_ERR,
+ "FW: Can't expand PRD message buffer: %m");
+ return -1;
+ }
+ ctx->msg = msg;
+ ctx->msg_alloc_len = size;
+ }
+
+ /* ... and complete the read */
+ if (size > rc) {
+ size_t pos;
+
+ for (pos = rc; pos < size;) {
+ rc = read(ctx->fd, msg + pos, size - pos);
+
+ if (rc < 0 && errno == EAGAIN)
+ continue;
+
+ if (rc <= 0) {
+ pr_log(LOG_WARNING,
+ "FW: error reading from firmware: %m");
+ return -1;
+ }
+
+ pos += rc;
+ }
+ }
+
+ switch (msg->hdr.type) {
case OPAL_PRD_MSG_TYPE_ATTN:
- rc = handle_msg_attn(ctx, &msg);
+ rc = handle_msg_attn(ctx, msg);
break;
case OPAL_PRD_MSG_TYPE_OCC_RESET:
- rc = handle_msg_occ_reset(ctx, &msg);
+ rc = handle_msg_occ_reset(ctx, msg);
break;
case OPAL_PRD_MSG_TYPE_OCC_ERROR:
- rc = handle_msg_occ_error(ctx, &msg);
+ rc = handle_msg_occ_error(ctx, msg);
break;
default:
pr_log(LOG_WARNING, "Invalid incoming message type 0x%x",
- msg.hdr.type);
+ msg->hdr.type);
return -1;
}
@@ -1641,6 +1684,15 @@ static int run_prd_daemon(struct opal_prd_ctx *ctx)
ctx->fd = -1;
ctx->socket = -1;
+ /* set up our message buffer */
+ ctx->msg_alloc_len = sizeof(*ctx->msg);
+ ctx->msg = malloc(ctx->msg_alloc_len);
+ if (!ctx->msg) {
+ pr_log(LOG_ERR, "FW: Can't allocate PRD message buffer: %m");
+ return -1;
+ }
+
+
i2c_init();
#ifdef DEBUG_I2C
@@ -1728,6 +1780,8 @@ out_close:
close(ctx->fd);
if (ctx->socket != -1)
close(ctx->socket);
+ if (ctx->msg)
+ free(ctx->msg);
return rc;
}