summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWenkai Du <wenkai.du@intel.com>2014-05-02 09:03:28 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-05-07 03:41:27 +0000
commit372317af34b2def08bd22e7be1552402dd6e456f (patch)
treea60718cb08790d07549e02529607815d9ef2b3ec
parent4ba7a1502dabc4728d8e68572642dafafbd54692 (diff)
downloadchrome-ec-372317af34b2def08bd22e7be1552402dd6e456f.tar.gz
CHROMIUM: ec: Add Port 80 history access from ectools
While debugging reboot issue, it was difficult to get POST code from failing boards. Currently POST code is only accessible from EC console. Not all boards are fitted with servo board. This patch adds Port 80 history access from ectool. Reuse command code 0x48, EC_CMD_PORT80_LAST_BOOT with version 1. Signed-off-by: Wenkai Du <wenkai.du@intel.com> BUG=chrome-os-partner:28514 BRANCH=rambi TEST=manually test on rambi to confirm port 80 history match EC console Change-Id: If204d8fb457d8d8d18055f8282a406a35c03305e Reviewed-on: https://chromium-review.googlesource.com/198012 Reviewed-by: Randall Spangler <rspangler@chromium.org> Reviewed-by: Wenkai Du <wenkai.du@intel.com> Commit-Queue: Wenkai Du <wenkai.du@intel.com> Tested-by: Wenkai Du <wenkai.du@intel.com>
-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},