diff options
-rw-r--r-- | Makefile.toolchain | 2 | ||||
-rw-r--r-- | common/host_command.c | 2 | ||||
-rw-r--r-- | common/host_command_master.c | 13 | ||||
-rw-r--r-- | common/usb_pd_protocol.c | 83 | ||||
-rw-r--r-- | include/ec_commands.h | 20 | ||||
-rw-r--r-- | include/sha1.h | 5 | ||||
-rw-r--r-- | include/usb_pd.h | 3 | ||||
-rw-r--r-- | util/build.mk | 2 | ||||
-rw-r--r-- | util/ectool.c | 130 |
9 files changed, 250 insertions, 10 deletions
diff --git a/Makefile.toolchain b/Makefile.toolchain index 6bf27a3f78..43f390cde4 100644 --- a/Makefile.toolchain +++ b/Makefile.toolchain @@ -53,7 +53,7 @@ LIBFTDI_CFLAGS=$(shell $(PKG_CONFIG) --cflags lib${LIBFTDI_NAME}) LIBFTDI_LDLIBS=$(shell $(PKG_CONFIG) --libs lib${LIBFTDI_NAME}) BUILD_CFLAGS= $(LIBFTDI_CFLAGS) $(CPPFLAGS) -O3 $(CFLAGS_DEBUG) $(CFLAGS_WARN) -HOST_CFLAGS=$(CPPFLAGS) -O3 $(CFLAGS_DEBUG) $(CFLAGS_WARN) +HOST_CFLAGS=$(CPPFLAGS) -O3 $(CFLAGS_DEBUG) $(CFLAGS_WARN) -DHOST_TOOLS_BUILD LDFLAGS=-nostdlib -X --gc-sections BUILD_LDFLAGS=$(LIBFTDI_LDLIBS) HOST_TEST_LDFLAGS=-T core/host/host_exe.lds -lrt -pthread -rdynamic -lm\ diff --git a/common/host_command.c b/common/host_command.c index 22374059c6..6bb6176afd 100644 --- a/common/host_command.c +++ b/common/host_command.c @@ -531,7 +531,7 @@ static void host_command_debug_request(struct host_cmd_handler_args *args) enum ec_status host_command_process(struct host_cmd_handler_args *args) { const struct host_command *cmd; - enum ec_status rv; + int rv; if (hcdebug) host_command_debug_request(args); diff --git a/common/host_command_master.c b/common/host_command_master.c index a669f22111..38acd9a560 100644 --- a/common/host_command_master.c +++ b/common/host_command_master.c @@ -89,13 +89,8 @@ static int pd_host_command_internal(int command, int version, return -ret; } - ret = resp_buf[0]; resp_len = resp_buf[1]; - if (ret) - CPRINTF("[%T command 0x%02x returned error %d]\n", command, - ret); - if (resp_len > (insize + sizeof(rs))) { /* Do a dummy read to generate stop condition */ i2c_xfer(I2C_PORT_PD_MCU, CONFIG_USB_PD_I2C_SLAVE_ADDR, @@ -115,6 +110,14 @@ static int pd_host_command_internal(int command, int version, return -ret; } + /* Check for host command error code */ + ret = resp_buf[0]; + if (ret) { + CPRINTF("[%T command 0x%02x returned error %d]\n", command, + ret); + return -ret; + } + /* Read back response header and start checksum */ sum = 0; for (i = 0, d = (uint8_t *)&rs; i < sizeof(rs); i++, d++) { diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index 9e0115743c..263c9b63e0 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -962,10 +962,16 @@ packet_err: return bit; } -void pd_send_vdm(int port, uint32_t vid, int cmd, uint32_t *data, int count) +void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data, + int count) { int i; + if (count > VDO_MAX_SIZE - 1) { + CPRINTF("VDM over max size\n"); + return; + } + pd[port].vdo_data[0] = VDO(vid, cmd); pd[port].vdo_count = count + 1; for (i = 1; i < count + 1; i++) @@ -1787,4 +1793,79 @@ DECLARE_HOST_COMMAND(EC_CMD_USB_PD_CONTROL, hc_usb_pd_control, EC_VER_MASK(0)); +static int hc_remote_flash(struct host_cmd_handler_args *args) +{ + const struct ec_params_usb_pd_fw_update *p = args->params; + int port = p->port; + const uint32_t *data = &(p->size) + 1; + int i, size; + + if (p->size + sizeof(*p) > args->params_size) + return EC_RES_INVALID_PARAM; + + switch (p->cmd) { + case USB_PD_FW_REBOOT: + ccprintf("PD Update - Reboot\n"); + pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_REBOOT, NULL, 0); + + /* Delay to give time for device to reboot */ + usleep(750 * MSEC); + return EC_RES_SUCCESS; + + case USB_PD_FW_FLASH_ERASE: + ccprintf("PD Update - Erase RW flash\n"); + pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_FLASH_ERASE, NULL, 0); + + /* Wait until VDM is done */ + while (pd[port].vdm_state > 0) + task_wait_event(100*MSEC); + break; + + case USB_PD_FW_FLASH_HASH: + /* Can only write 20 bytes */ + if (p->size != 20) + return EC_RES_INVALID_PARAM; + + ccprintf("PD Update - Write RW flash hash "); + for (i = 0; i < 5; i++) + ccprintf("%08x ", *(data + i)); + ccprintf("\n"); + pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_FLASH_HASH, data, 5); + + /* Wait until VDM is done */ + while (pd[port].vdm_state > 0) + task_wait_event(100*MSEC); + break; + + case USB_PD_FW_FLASH_WRITE: + /* Data size must be a multiple of 4 */ + if (!p->size || p->size % 4) + return EC_RES_INVALID_PARAM; + + size = p->size / 4; + ccprintf("PD Update - Write RW flash\n"); + for (i = 0; i < size; i += VDO_MAX_SIZE - 1) { + pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_FLASH_WRITE, + data + i, MIN(size - i, VDO_MAX_SIZE - 1)); + + /* Wait until VDM is done */ + while (pd[port].vdm_state > 0) + task_wait_event(10*MSEC); + } + break; + + default: + return EC_RES_INVALID_PARAM; + break; + } + + if (pd[port].vdm_state < 0) + return EC_RES_ERROR; + else + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_USB_PD_FW_UPDATE, + hc_remote_flash, + EC_VER_MASK(0)); + #endif /* CONFIG_COMMON_RUNTIME */ diff --git a/include/ec_commands.h b/include/ec_commands.h index a8c9ab450b..83bca00c84 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -2528,6 +2528,26 @@ struct ec_params_usb_pd_control { uint8_t mux; } __packed; +/* Write USB-PD device FW */ +#define EC_CMD_USB_PD_FW_UPDATE 0x110 + +enum usb_pd_fw_update_cmds { + USB_PD_FW_REBOOT, + USB_PD_FW_FLASH_ERASE, + USB_PD_FW_FLASH_WRITE, + USB_PD_FW_FLASH_HASH, +}; + +struct ec_params_usb_pd_fw_update { + uint8_t cmd; + uint8_t dev_id; + uint8_t port; + uint8_t reserved; /* reserved */ + uint32_t size; /* Size to write in bytes */ + /* Followed by data to write */ +} __packed; + + /*****************************************************************************/ /* * Passthru commands diff --git a/include/sha1.h b/include/sha1.h index 152c5757d7..79ede0206a 100644 --- a/include/sha1.h +++ b/include/sha1.h @@ -9,7 +9,12 @@ #define _SHA1_H #include "common.h" +#ifdef HOST_TOOLS_BUILD +#include <string.h> +#define DIV_ROUND_UP(x, y) (((x) + ((y) - 1)) / (y)) +#else #include "util.h" +#endif #define SHA1_DIGEST_SIZE 20 #define SHA1_BLOCK_SIZE 64 diff --git a/include/usb_pd.h b/include/usb_pd.h index 3afef69d1a..83fee88cf0 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -301,7 +301,8 @@ int pd_custom_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload); * @param data Pointer to payload to send * @param data number of data objects in payload */ -void pd_send_vdm(int port, uint32_t vid, int cmd, uint32_t *data, int count); +void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data, + int count); /* Power Data Objects for the source and the sink */ extern const uint32_t pd_src_pdo[]; diff --git a/util/build.mk b/util/build.mk index 0cd5cbecc8..f0e3c6c17e 100644 --- a/util/build.mk +++ b/util/build.mk @@ -16,7 +16,7 @@ comm-objs+=comm-lpc.o else comm-objs+=comm-i2c.o endif -ectool-objs=ectool.o ectool_keyscan.o misc_util.o ec_flash.o $(comm-objs) +ectool-objs=ectool.o ectool_keyscan.o misc_util.o ec_flash.o $(comm-objs) ../common/sha1.o lbplay-objs=lbplay.o $(comm-objs) burn_my_ec-objs=ec_flash.o $(comm-objs) misc_util.o diff --git a/util/ectool.c b/util/ectool.c index 4e2f9ff1f2..8f722f974d 100644 --- a/util/ectool.c +++ b/util/ectool.c @@ -22,6 +22,7 @@ #include "lock/gec_lock.h" #include "misc_util.h" #include "panic.h" +#include "sha1.h" /* Command line options */ enum { @@ -93,6 +94,8 @@ const char help_str[] = " Erases EC flash\n" " flashinfo\n" " Prints information on the EC flash\n" + " flashpd\n" + " Flash commands over PD\n" " flashprotect [now] [enable | disable]\n" " Prints or sets EC flash protection state\n" " flashread <offset> <size> <outfile>\n" @@ -775,6 +778,132 @@ int cmd_flash_protect(int argc, char *argv[]) return 0; } +/* PD image size is 16k minus 32 bits for the RW hash */ +#define PD_RW_IMAGE_SIZE (16 * 1024 - 32) +static struct sha1_ctx ctx; +int cmd_flash_pd(int argc, char *argv[]) +{ + struct ec_params_usb_pd_fw_update *p = + (struct ec_params_usb_pd_fw_update *)ec_outbuf; + int i; + int rv, fsize, step = 96, padding_size; + char *e; + char *buf, *fw_padding; + uint32_t *data = &(p->size) + 1; + + if (argc < 4) { + fprintf(stderr, "Usage: %s <dev_id> <port> <filename>\n", + argv[0]); + return -1; + } + + p->dev_id = strtol(argv[1], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad device ID\n"); + return -1; + } + + p->port = strtol(argv[2], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad port\n"); + return -1; + } + + /* Read the input file */ + buf = read_file(argv[3], &fsize); + if (!buf) + return -1; + + /* Verify size of file */ + if (fsize > PD_RW_IMAGE_SIZE) + goto pd_flash_error; + + /* Add padding to image */ + padding_size = PD_RW_IMAGE_SIZE - fsize; + fw_padding = (char *)malloc(padding_size); + memset(fw_padding, 0xff, padding_size); + fprintf(stderr, "File size %d, Padding size %d\n", fsize, padding_size); + + /* Write expected flash hash to all 0s */ + fprintf(stderr, "Erasing expected RW hash\n"); + p->cmd = USB_PD_FW_FLASH_HASH; + p->size = 20; + for (i = 0; i < 5; i++) + *(data + i) = 0; + rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0, + p, p->size + sizeof(*p), NULL, 0); + + if (rv < 0) + goto pd_flash_error; + + /* Reboot */ + fprintf(stderr, "Rebooting\n"); + p->cmd = USB_PD_FW_REBOOT; + p->size = 0; + rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0, + p, p->size + sizeof(*p), NULL, 0); + + if (rv < 0) + goto pd_flash_error; + + /* Erase RW flash */ + fprintf(stderr, "Erasing RW flash\n"); + p->cmd = USB_PD_FW_FLASH_ERASE; + p->size = 0; + rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0, + p, p->size + sizeof(*p), NULL, 0); + + if (rv < 0) + goto pd_flash_error; + + /* Write RW flash */ + fprintf(stderr, "Writing RW flash\n"); + p->cmd = USB_PD_FW_FLASH_WRITE; + p->size = step; + + for (i = 0; i < fsize; i += step) { + p->size = MIN(fsize - i, step); + memcpy(data, buf + i, p->size); + rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0, + p, p->size + sizeof(*p), NULL, 0); + if (rv < 0) + goto pd_flash_error; + } + + /* + * TODO(crosbug.com/p/31552): Would be better to have sha1 in the RW + * binary and we won't have to calculate it here and send it down. + */ + /* Calculate sha1 of new RW flash */ + sha1_init(&ctx); + sha1_update(&ctx, buf, fsize); + sha1_update(&ctx, fw_padding, padding_size); + sha1_final(&ctx); + + /* Write expected flash hash */ + fprintf(stderr, "Setting expected RW hash\n"); + p->cmd = USB_PD_FW_FLASH_HASH; + p->size = 20; + memcpy(data, ctx.buf.b, p->size); + for (i = 0; i < 5; i++) + fprintf(stderr, "%08x ", *(data + i)); + fprintf(stderr, "\n"); + rv = ec_command(EC_CMD_USB_PD_FW_UPDATE, 0, + p, p->size + sizeof(*p), NULL, 0); + + if (rv < 0) + goto pd_flash_error; + + free(buf); + fprintf(stderr, "Complete\n"); + return 0; + +pd_flash_error: + free(buf); + fprintf(stderr, "PD flash error\n"); + return -1; +} + int cmd_serial_test(int argc, char *argv[]) { @@ -4570,6 +4699,7 @@ const struct command commands[] = { {"flashread", cmd_flash_read}, {"flashwrite", cmd_flash_write}, {"flashinfo", cmd_flash_info}, + {"flashpd", cmd_flash_pd}, {"gpioget", cmd_gpio_get}, {"gpioset", cmd_gpio_set}, {"hangdetect", cmd_hang_detect}, |