diff options
author | Nicolas Boichat <drinkcat@google.com> | 2017-03-22 16:41:34 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2017-04-26 04:28:07 -0700 |
commit | 7f6176dc5502dc4d6b166ab2c7c90209220e82ff (patch) | |
tree | 8d9e82ff5971b11f5ed5d102fa4ce72c5faa1736 | |
parent | acb397063002fec38979c56ae6d7219dd04c1412 (diff) | |
download | chrome-ec-7f6176dc5502dc4d6b166ab2c7c90209220e82ff.tar.gz |
common/usb_update: add support for extra commands
Add support for 4 extra commands that are required to update
hammer:
- UPDATE_EXTRA_CMD_IMMEDIATE_RESET
- UPDATE_EXTRA_CMD_JUMP_TO_RW: Tells the RWSIG task to jump
to RW as soon as possible (assuming the image verifies)
- UPDATE_EXTRA_CMD_STAY_IN_RO: Tells the RWSIG task to not
jump to RW, and stay in RO, to leave enough time for
AP to update RW.
- UPDATE_EXTRA_CMD_UNLOCK_RW: Tells EC to unlock the RW
section so that it can be updated (on next reboot).
BRANCH=none
BUG=b:35587171
TEST=Test RO+RW update
cd extra/usb_updater; make
# Jump to RW
sudo ./usb_updater2 -j
sleep 0.5
# Update RO, then reboot
sudo ./usb_updater2 ../../build/hammer/ec.bin
sleep 0.5
# Update RW (first tell RO to not jump to RW)
sudo ./usb_updater2 -s
sudo ./usb_updater2 ../../build/hammer/ec.bin
TEST=Test RW update only, with RO protected
On EC console: flashwp true; reboot
cd extra/usb_updater; make
# Tell RW to unprotect RW and jump back to RO
sudo ./usb_updater2 -w
sudo ./usb_updater2 -r
sleep 0.5
# Update RW, then reboot
sudo ./usb_updater2 -s
sudo ./usb_updater2 ../../build/hammer/ec.bin
Change-Id: I5e8df7bdb4f06f2ac7b47de53dcde69c5002f578
Reviewed-on: https://chromium-review.googlesource.com/458470
Commit-Ready: Nicolas Boichat <drinkcat@chromium.org>
Tested-by: Nicolas Boichat <drinkcat@chromium.org>
Reviewed-by: Nicolas Boichat <drinkcat@chromium.org>
-rw-r--r-- | common/usb_update.c | 97 | ||||
-rw-r--r-- | include/update_fw.h | 8 |
2 files changed, 103 insertions, 2 deletions
diff --git a/common/usb_update.c b/common/usb_update.c index cf1f205763..cf5850e53d 100644 --- a/common/usb_update.c +++ b/common/usb_update.c @@ -8,7 +8,10 @@ #include "console.h" #include "consumer.h" #include "extension.h" +#include "flash.h" #include "queue_policies.h" +#include "host_command.h" +#include "rwsig.h" #include "system.h" #include "update_fw.h" #include "usb-stream.h" @@ -109,8 +112,98 @@ static int fetch_transfer_start(struct consumer const *consumer, size_t count, static int try_vendor_command(struct consumer const *consumer, size_t count) { - /* TODO(b/35587171): Vendor commands not implemented (yet). */ - return 0; + char buffer[USB_MAX_PACKET_SIZE]; + struct update_frame_header *cmd_buffer = (void *)buffer; + int rv = 0; + + /* Validate count (too short, or too long). */ + if (count < sizeof(*cmd_buffer) || count > sizeof(buffer)) + return 0; + + /* + * Let's copy off the queue the update frame header, to see if this + * is a channeled vendor command. + */ + queue_peek_units(consumer->queue, cmd_buffer, 0, sizeof(*cmd_buffer)); + if (be32toh(cmd_buffer->cmd.block_base) != UPDATE_EXTRA_CMD) + return 0; + + if (be32toh(cmd_buffer->block_size) != count) { + CPRINTS("%s: problem: block size and count mismatch (%d != %d)", + __func__, be32toh(cmd_buffer->block_size), count); + return 0; + } + + /* Get the entire command, don't remove it from the queue just yet. */ + queue_peek_units(consumer->queue, cmd_buffer, 0, count); + + /* Looks like this is a vendor command, let's verify it. */ + if (update_pdu_valid(&cmd_buffer->cmd, + count - offsetof(struct update_frame_header, cmd))) { + enum update_extra_command subcommand; + uint16_t response; + size_t response_size = sizeof(response); + + /* looks good, let's process it. */ + rv = 1; + + /* Now remove it from the queue. */ + queue_advance_head(consumer->queue, count); + + subcommand = be16toh(*((uint16_t *)(cmd_buffer + 1))); + + switch (subcommand) { + case UPDATE_EXTRA_CMD_IMMEDIATE_RESET: + CPRINTS("Rebooting!\n\n\n"); + cflush(); + system_reset(SYSTEM_RESET_MANUALLY_TRIGGERED); + /* Unreachable, unless something bad happens. */ + response = EC_RES_ERROR; + break; + case UPDATE_EXTRA_CMD_JUMP_TO_RW: +#ifdef CONFIG_RWSIG + /* + * Tell rwsig task to jump to RW. This does nothing if + * verification failed, and will only jump later on if + * verification is still in progress. + */ + rwsig_continue(); + + switch (rwsig_get_status()) { + case RWSIG_VALID: + response = EC_RES_SUCCESS; + break; + case RWSIG_INVALID: + response = EC_RES_INVALID_CHECKSUM; + break; + case RWSIG_IN_PROGRESS: + response = EC_RES_IN_PROGRESS; + break; + default: + response = EC_RES_ERROR; + } +#else + system_run_image_copy(SYSTEM_IMAGE_RW); +#endif + break; +#ifdef CONFIG_RWSIG + case UPDATE_EXTRA_CMD_STAY_IN_RO: + rwsig_abort(); + response = EC_RES_SUCCESS; + break; + case UPDATE_EXTRA_CMD_UNLOCK_RW: + flash_set_protect(EC_FLASH_PROTECT_RW_AT_BOOT, 0); + response = EC_RES_SUCCESS; + break; +#endif + default: + response = EC_RES_INVALID_COMMAND; + } + + QUEUE_ADD_UNITS(&update_to_usb, &response, response_size); + } + + return rv; } /* diff --git a/include/update_fw.h b/include/update_fw.h index 115b34d35c..e575500b19 100644 --- a/include/update_fw.h +++ b/include/update_fw.h @@ -154,6 +154,14 @@ enum first_response_pdu_header_type { /* TODO: Handle this in update_fw.c, not usb_update.c */ #define UPDATE_DONE 0xB007AB1E +#define UPDATE_EXTRA_CMD 0xB007AB1F + +enum update_extra_command { + UPDATE_EXTRA_CMD_IMMEDIATE_RESET = 0, + UPDATE_EXTRA_CMD_JUMP_TO_RW = 1, + UPDATE_EXTRA_CMD_STAY_IN_RO = 2, + UPDATE_EXTRA_CMD_UNLOCK_RW = 3, +}; void fw_update_command_handler(void *body, size_t cmd_size, |