diff options
-rw-r--r-- | common/port80.c | 40 | ||||
-rw-r--r-- | include/ec_commands.h | 32 | ||||
-rw-r--r-- | util/ectool.c | 100 |
3 files changed, 170 insertions, 2 deletions
diff --git a/common/port80.c b/common/port80.c index c23dc419b3..9f6136cf5d 100644 --- a/common/port80.c +++ b/common/port80.c @@ -125,5 +125,41 @@ int port80_last_boot(struct host_cmd_handler_args *args) return EC_RES_SUCCESS; } -DECLARE_HOST_COMMAND(EC_CMD_PORT80_LAST_BOOT, - port80_last_boot, EC_VER_MASK(0)); + +int port80_command_read(struct host_cmd_handler_args *args) +{ + const struct ec_params_port80_read *p = args->params; + uint32_t offset = p->read_buffer.offset; + uint32_t entries = p->read_buffer.num_entries; + int i; + struct ec_response_port80_read *rsp = args->response; + + if (args->version == 0) + return port80_last_boot(args); + + if (p->subcmd == EC_PORT80_GET_INFO) { + rsp->get_info.writes = writes; + rsp->get_info.history_size = ARRAY_SIZE(history); + args->response_size = sizeof(rsp->get_info); + return EC_RES_SUCCESS; + } else if (p->subcmd == EC_PORT80_READ_BUFFER) { + /* do not allow bad offset or size */ + if (offset >= ARRAY_SIZE(history) || entries == 0 || + entries > args->response_max) + return EC_RES_INVALID_PARAM; + + for (i = 0; i < entries; i++) { + uint16_t e = history[(i + offset) % + ARRAY_SIZE(history)]; + rsp->data.codes[i] = e; + } + + args->response_size = entries*sizeof(uint16_t); + return EC_RES_SUCCESS; + } + + return EC_RES_INVALID_PARAM; +} +DECLARE_HOST_COMMAND(EC_CMD_PORT80_READ, + port80_command_read, + EC_VER_MASK(0) | EC_VER_MASK(1)); diff --git a/include/ec_commands.h b/include/ec_commands.h index 9b68e1f3bd..a50eb5e8b1 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -1406,8 +1406,40 @@ struct ec_response_rtc { /*****************************************************************************/ /* Port80 log access */ +/* Maximum entries that can be read/written in a single command */ +#define EC_PORT80_SIZE_MAX 32 + /* Get last port80 code from previous boot */ #define EC_CMD_PORT80_LAST_BOOT 0x48 +#define EC_CMD_PORT80_READ 0x48 + +enum ec_port80_subcmd { + EC_PORT80_GET_INFO = 0, + EC_PORT80_READ_BUFFER, +}; + +struct ec_params_port80_read { + uint16_t subcmd; + union { + struct { + uint32_t offset; + uint32_t num_entries; + } read_buffer; + }; +} __packed; + +struct ec_response_port80_read { + union { + struct { + uint32_t writes; + uint32_t history_size; + uint32_t last_boot; + } get_info; + struct { + uint16_t codes[EC_PORT80_SIZE_MAX]; + } data; + }; +} __packed; struct ec_response_port80_last_boot { uint16_t code; diff --git a/util/ectool.c b/util/ectool.c index b9da55fada..1fd2b895dc 100644 --- a/util/ectool.c +++ b/util/ectool.c @@ -116,6 +116,8 @@ const char help_str[] = " Whether or not the AP should pause in S5 on shutdown\n" " port80flood\n" " Rapidly write bytes to port 80\n" + " port80read\n" + " Print history of port 80 write\n" " powerinfo\n" " Prints power-related information\n" " protoinfo\n" @@ -4042,6 +4044,103 @@ static int cmd_hang_detect(int argc, char *argv[]) return -1; } +enum port_80_event { + PORT_80_EVENT_RESUME = 0x1001, /* S3->S0 transition */ + PORT_80_EVENT_RESET = 0x1002, /* RESET transition */ +}; + +int cmd_port80_read(int argc, char *argv[]) +{ + struct ec_params_port80_read p; + int cmdver = 1, rv; + int i, head, tail; + uint16_t *history; + uint32_t writes, history_size; + struct ec_response_port80_read rsp; + int printed = 0; + + if (!ec_cmd_version_supported(EC_CMD_PORT80_READ, cmdver)) { + /* fall back to last boot */ + struct ec_response_port80_last_boot r; + rv = ec_command(EC_CMD_PORT80_LAST_BOOT, 0, + NULL, 0, &r, sizeof(r)); + fprintf(stderr, "Last boot %2x\n", r.code); + printf("done.\n"); + return 0; + } + + + /* read writes and history_size */ + p.subcmd = EC_PORT80_GET_INFO; + rv = ec_command(EC_CMD_PORT80_READ, cmdver, + &p, sizeof(p), &rsp, sizeof(rsp)); + if (rv < 0) { + fprintf(stderr, "Read error at writes\n"); + return rv; + } + writes = rsp.get_info.writes; + history_size = rsp.get_info.history_size; + + history = malloc(history_size*sizeof(uint16_t)); + if (!history) { + fprintf(stderr, "Unable to allocate buffer.\n"); + return -1; + } + /* As the history buffer is quite large, we read data in chunks, with + size in bytes of EC_PORT80_SIZE_MAX in each chunk. + Incrementing offset until all history buffer has been read. To + simplify the design, chose HISTORY_LEN is always multiple of + EC_PORT80_SIZE_MAX. + + offset: entry offset from the beginning of history buffer. + num_entries: number of entries requested. + */ + p.subcmd = EC_PORT80_READ_BUFFER; + for (i = 0; i < history_size; i += EC_PORT80_SIZE_MAX) { + p.read_buffer.offset = i; + p.read_buffer.num_entries = EC_PORT80_SIZE_MAX; + rv = ec_command(EC_CMD_PORT80_READ, cmdver, + &p, sizeof(p), &rsp, sizeof(rsp)); + if (rv < 0) { + fprintf(stderr, "Read error at offset %d\n", i); + free(history); + return rv; + } + memcpy((void *)(history + i), rsp.data.codes, + EC_PORT80_SIZE_MAX*sizeof(uint16_t)); + } + + head = writes; + if (head > history_size) + tail = head - history_size; + else + tail = 0; + + fprintf(stderr, "Port 80 writes"); + for (i = tail; i < head; i++) { + int e = history[i % history_size]; + switch (e) { + case PORT_80_EVENT_RESUME: + fprintf(stderr, "\n(S3->S0)"); + printed = 0; + break; + case PORT_80_EVENT_RESET: + fprintf(stderr, "\n(RESET)"); + printed = 0; + break; + default: + if (!(printed++ % 20)) + fprintf(stderr, "\n "); + fprintf(stderr, " %02x", e); + } + } + fprintf(stderr, " <--new\n"); + + free(history); + printf("done.\n"); + return 0; +} + struct command { const char *name; int (*handler)(int argc, char *argv[]); @@ -4094,6 +4193,7 @@ const struct command commands[] = { {"motionsense", cmd_motionsense}, {"panicinfo", cmd_panic_info}, {"pause_in_s5", cmd_s5}, + {"port80read", cmd_port80_read}, {"powerinfo", cmd_power_info}, {"protoinfo", cmd_proto_info}, {"pstoreinfo", cmd_pstore_info}, |