diff options
author | Daisuke Nojiri <dnojiri@chromium.org> | 2021-03-11 13:17:35 -0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-04-05 20:50:07 +0000 |
commit | 362e02845295c56a3c1fbb4c5fd39b318a45253c (patch) | |
tree | e87d8533d0d27ce8513d407320ea9de58f353f86 /util | |
parent | a801f6505a207ff5f0787cfb89e1810eacf4a1db (diff) | |
download | chrome-ec-362e02845295c56a3c1fbb4c5fd39b318a45253c.tar.gz |
PCHG: Support firmware update
This patch adds EC_CMD_PCHG_UPDATE, which allows the host to update
firmware of ctn730 via I2C.
An updater (e.g. ectool) is expected to issue EC_PCHG_UPDATE_CMD_OPEN,
multiple EC_PCHG_UPDATE_CMD_WRITEs, then EC_PCHG_UPDATE_CLOSE. Each
sub-command completion is notified to the host via EC_MKBP_EVENT_PCHG.
An updater is supposed to wait for the previous sub-command to
complete before proceeding to the next.
Example:
localhost ~ # ectool pchg 0
State: DOWNLOAD (6)
FW Version: 0x104
localhost ~ # ectool pchg 0 update 0x207000 0x105 /path/to/image.bin
Update file /path/to/image.bin (85632 bytes) is opened.
Writing firmware (port=0 ver=0x105 addr=0x207000 bsize=128):
**********************************************************************
FW update session closed (CRC32=0x7bd5c66f).
localhost ~ # ectool pchg 0 reset
Reset port 0 complete
localhost ~ # ectool pchg 0
State: ENABLED (2)
FW Version: 0x105
BUG=b:182600604, b:173235954
BRANCH=trogdor
TEST=ectool pchg 0 update 0x201200 0x105 /tmp/user_ee_X0.1_V1.5.bin
TEST=ectool pchg 0 reset
Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org>
Change-Id: I9c62f1714dd69428ab5870c443cb4eb77881a6c6
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2757099
Diffstat (limited to 'util')
-rw-r--r-- | util/build.mk | 1 | ||||
-rw-r--r-- | util/ectool.c | 262 |
2 files changed, 216 insertions, 47 deletions
diff --git a/util/build.mk b/util/build.mk index e868e32eff..9f9430a3b4 100644 --- a/util/build.mk +++ b/util/build.mk @@ -30,6 +30,7 @@ comm-objs+=comm-lpc.o comm-i2c.o misc_util.o iteflash-objs = iteflash.o usb_if.o ectool-objs=ectool.o ectool_keyscan.o ec_flash.o ec_panicinfo.o $(comm-objs) +ectool-objs+=../common/crc.o ectool_servo-objs=$(ectool-objs) comm-servo-spi.o ec_sb_firmware_update-objs=ec_sb_firmware_update.o $(comm-objs) misc_util.o ec_sb_firmware_update-objs+=powerd_lock.o diff --git a/util/ectool.c b/util/ectool.c index 091362c234..cd348aba28 100644 --- a/util/ectool.c +++ b/util/ectool.c @@ -20,6 +20,7 @@ #include "comm-host.h" #include "chipset.h" #include "compile_time_macros.h" +#include "crc.h" #include "cros_ec_dev.h" #include "ec_panicinfo.h" #include "ec_flash.h" @@ -445,6 +446,24 @@ static int read_mapped_string(uint8_t offset, char *buffer, int max_size) return ret; } +static int wait_event(long event_type, + struct ec_response_get_next_event_v1 *buffer, + size_t buffer_size, long timeout) +{ + int rv; + + rv = ec_pollevent(1 << event_type, buffer, buffer_size, timeout); + if (rv == 0) { + fprintf(stderr, "Timeout waiting for MKBP event\n"); + return -ETIMEDOUT; + } else if (rv < 0) { + perror("Error polling for MKBP event\n"); + return -EIO; + } + + return rv; +} + int cmd_adc_read(int argc, char *argv[]) { char *e; @@ -9348,65 +9367,232 @@ static void cmd_pchg_help(char *cmd) { fprintf(stderr, " Usage1: %s\n" + " Print the number of ports.\n" + "\n" " Usage2: %s <port>\n" + " Print the status of <port>.\n" "\n" - " Usage1 prints the number of ports.\n" - " Usage2 prints the status of a port.\n", - cmd, cmd); + " Usage3: %s <port> reset\n" + " Reset <port>.\n" + "\n" + " Usage4: %s <port> update <address> <version> <file>\n" + " Update firmware of <port>.\n", + cmd, cmd, cmd, cmd); +} + +static int cmd_pchg_info(const struct ec_response_pchg *res) +{ + static const char * const pchg_state_text[] = EC_PCHG_STATE_TEXT; + + printf("State: %s (%d)\n", res->state < sizeof(pchg_state_text) + ? pchg_state_text[res->state] : "UNDEF", res->state); + printf("Battery: %u%%\n", res->battery_percentage); + printf("Errors: 0x%x\n", res->error); + printf("FW Version: 0x%x\n", res->fw_version); + printf("Dropped events: %u\n", res->dropped_event_count); + return 0; } -int cmd_pchg(int argc, char *argv[]) +static int cmd_pchg_wait_event(int port, uint32_t expected) +{ + struct ec_response_get_next_event_v1 event; + const long timeout = 5000; + uint32_t *e = &event.data.host_event; + int rv; + + rv = wait_event(EC_MKBP_EVENT_PCHG, &event, sizeof(event), timeout); + if (rv < 0) + return rv; + + if (EC_MKBP_PCHG_EVENT_TO_PORT(*e) == port) { + if (*e & EC_MKBP_PCHG_UPDATE_ERROR) { + fprintf(stderr, "\nReceived update error\n"); + return -1; + } + if (*e & expected) + return 0; + } + + fprintf(stderr, "\nExpected event=0x%x but received 0x%x\n", + expected, *e); + return -1; +} + +static int cmd_pchg_update(int port, uint32_t address, uint32_t version, + const char *filename) +{ + struct ec_params_pchg_update *p = ec_outbuf; + struct ec_response_pchg_update *r = ec_inbuf; + FILE *fp; + size_t len, total; + int progress; + int rv; + + fp = fopen(filename, "rb"); + if (!fp) { + fprintf(stderr, "\nCan't open %s: %s\n", + filename, strerror(errno)); + return -1; + } + + fseek(fp, 0L, SEEK_END); + total = ftell(fp); + rewind(fp); + printf("Update file %s (%zu bytes) is opened.\n", filename, total); + + /* Open session. */ + p->port = port; + p->cmd = EC_PCHG_UPDATE_CMD_OPEN; + p->version = version; + rv = ec_command(EC_CMD_PCHG_UPDATE, 0, p, sizeof(*p), r, sizeof(*r)); + if (rv < 0) { + fprintf(stderr, "\nFailed to open update session: %d\n", rv); + fclose(fp); + return rv; + } + + if (r->block_size + sizeof(*p) > ec_max_outsize) { + fprintf(stderr, "\nBlock size (%d) is too large.\n", + r->block_size); + fclose(fp); + return -1; + } + + rv = cmd_pchg_wait_event(port, EC_MKBP_PCHG_UPDATE_OPENED); + if (rv) + return rv; + + printf("Writing firmware (port=%d ver=0x%x addr=0x%x bsize=%d):\n", + port, version, address, r->block_size); + + p->cmd = EC_PCHG_UPDATE_CMD_WRITE; + p->addr = address; + crc32_init(); + + /* Write firmware in blocks. */ + len = fread(p->data, 1, r->block_size, fp); + while (len > 0) { + int previous_progress = progress; + int i; + + crc32_hash(p->data, len); + p->size = len; + rv = ec_command(EC_CMD_PCHG_UPDATE, 0, p, + sizeof(*p) + len, NULL, 0); + if (rv < 0) { + fprintf(stderr, "\nFailed to write FW: %d\n", rv); + fclose(fp); + return rv; + } + + rv = cmd_pchg_wait_event(port, EC_MKBP_PCHG_WRITE_COMPLETE); + if (rv) + return rv; + + p->addr += len; + progress = (p->addr - address) * 100 / total; + for (i = 0; i < progress - previous_progress; i++) { + printf("*"); + fflush(stdout); + } + + len = fread(p->data, 1, r->block_size, fp); + } + + printf("\n"); + fclose(fp); + + /* Close session. */ + p->cmd = EC_PCHG_UPDATE_CMD_CLOSE; + p->crc32 = crc32_result(); + rv = ec_command(EC_CMD_PCHG_UPDATE, 0, p, sizeof(*p), NULL, 0); + + if (rv < 0) { + fprintf(stderr, "\nFailed to close update session: %d\n", rv); + return rv; + } + + rv = cmd_pchg_wait_event(port, EC_MKBP_PCHG_UPDATE_CLOSED); + if (rv) + return rv; + + printf("FW update session closed (CRC32=0x%x).\n", p->crc32); + + return 0; +} + +static int cmd_pchg(int argc, char *argv[]) { int port, port_count; + struct ec_response_pchg_count rcnt; + struct ec_params_pchg p; + struct ec_response_pchg r; + uint32_t address, version; char *e; int rv; - struct ec_response_pchg_count *rsp_count = ec_inbuf; - static const char * const pchg_state_text[] = EC_PCHG_STATE_TEXT; - rv = ec_command(EC_CMD_PCHG_COUNT, 0, NULL, 0, ec_inbuf, ec_max_insize); + rv = ec_command(EC_CMD_PCHG_COUNT, 0, NULL, 0, &rcnt, sizeof(rcnt)); if (rv < 0) { - fprintf(stderr, "Failed to get port count: %d\n", rv); + fprintf(stderr, "\nFailed to get port count: %d\n", rv); return rv; } - port_count = rsp_count->port_count; + port_count = rcnt.port_count; if (argc == 1) { - /* Usage1 */ + /* Usage.1 */ printf("%d\n", port_count); return 0; } port = strtol(argv[1], &e, 0); if ((e && *e) || port >= port_count) { - fprintf(stderr, "Bad port index\n"); + fprintf(stderr, "\nBad port index: %s\n", argv[1]); + cmd_pchg_help(argv[0]); return -1; } - if (argc < 3) { - /* Usage2 */ - struct ec_params_pchg *p = ec_outbuf; - struct ec_response_pchg *r = ec_inbuf; + p.port = port; + rv = ec_command(EC_CMD_PCHG, 1, &p, sizeof(p), &r, sizeof(r)); + if (rv < 0) { + fprintf(stderr, "\nError code: %d\n", rv); + return rv; + } - p->port = port; - rv = ec_command(EC_CMD_PCHG, 1, ec_outbuf, sizeof(*p), - ec_inbuf, ec_max_insize); + if (argc == 2) { + /* Usage.2 */ + return cmd_pchg_info(&r); + } else if (argc == 3 && !strcmp(argv[2], "reset")) { + /* Usage.3 */ + struct ec_params_pchg_update *u = ec_outbuf; + + u->cmd = EC_PCHG_UPDATE_CMD_RESET_TO_NORMAL; + rv = ec_command(EC_CMD_PCHG_UPDATE, 0, u, sizeof(*u), NULL, 0); if (rv < 0) { - fprintf(stderr, "Error code: %d\n", rv); + fprintf(stderr, "\nFailed to reset port %d: %d\n", + port, rv); + cmd_pchg_help(argv[0]); return rv; } - - printf("State: %s (%d)\n", - r->state < sizeof(pchg_state_text) ? - pchg_state_text[r->state] : "UNDEF", - r->state); - printf("Battery: %u%%\n", r->battery_percentage); - printf("Errors: 0x%x\n", r->error); - printf("FW Version: 0x%x\n", r->fw_version); - printf("Dropped events: %u\n", r->dropped_event_count); + printf("Reset port %d complete.\n", port); return 0; + } else if (argc == 6 && !strcmp(argv[2], "update")) { + /* Usage.4 */ + address = strtol(argv[3], &e, 0); + if (e && *e) { + fprintf(stderr, "\nBad address: %s.\n", argv[3]); + cmd_pchg_help(argv[0]); + return -1; + } + version = strtol(argv[4], &e, 0); + if (e && *e) { + fprintf(stderr, "\nBad version: %s.\n", argv[4]); + cmd_pchg_help(argv[0]); + return -1; + } + return cmd_pchg_update(port, address, version, argv[5]); } - fprintf(stderr, "Invalid parameter count\n\n"); + fprintf(stderr, "Invalid parameter\n\n"); cmd_pchg_help(argv[0]); return -1; @@ -10045,24 +10231,6 @@ err: return rv < 0; } -static int wait_event(long event_type, - struct ec_response_get_next_event_v1 *buffer, - size_t buffer_size, long timeout) -{ - int rv; - - rv = ec_pollevent(1 << event_type, buffer, buffer_size, timeout); - if (rv == 0) { - fprintf(stderr, "Timeout waiting for MKBP event\n"); - return -ETIMEDOUT; - } else if (rv < 0) { - perror("Error polling for MKBP event\n"); - return -EIO; - } - - return rv; -} - int cmd_wait_event(int argc, char *argv[]) { int rv, i; |