summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorDaisuke Nojiri <dnojiri@chromium.org>2021-03-11 13:17:35 -0800
committerCommit Bot <commit-bot@chromium.org>2021-04-05 20:50:07 +0000
commit362e02845295c56a3c1fbb4c5fd39b318a45253c (patch)
treee87d8533d0d27ce8513d407320ea9de58f353f86 /util
parenta801f6505a207ff5f0787cfb89e1810eacf4a1db (diff)
downloadchrome-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.mk1
-rw-r--r--util/ectool.c262
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;