summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/port80.c40
-rw-r--r--include/ec_commands.h32
-rw-r--r--util/ectool.c100
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},