diff options
author | Vadim Bendebury <vbendeb@chromium.org> | 2016-04-30 12:51:05 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-05-02 17:35:45 -0700 |
commit | 2b326b446192c51755442ef3cd34df09847e3b7a (patch) | |
tree | d17abcdda4bc97cd9181167aadf916cc31c7f4b5 /chip | |
parent | b81afce806907714baa05dcd649e3f2a5d7d2a4e (diff) | |
download | chrome-ec-2b326b446192c51755442ef3cd34df09847e3b7a.tar.gz |
g: upgrade - improve verification of the first upgrade message
When an upgrade transfer starts, the target expects to receive a 12
byte transfer start message from the host, carrying an empty payload
of all zeros.
The target and host can be out of sync for whatever reason, so when
the target is expecting a transfer start message, the host can be
sending a chunk of a code block instead. In this case the target pulls
just 12 bytes off the receive queue, leaving the rest of the chunk
there, which even more complicates error recovery.
This patch makes sure that when expecting the upgrade transfer start
message the target extracts all receive data from the queue, no matter
how many bytes have been received, and then verifies that the size
matches the expected size, and that the payload is all zeros, to
confirm that the message is indeed the transfer start message.
BRANCH=none
BUG=chrome-os-partner:52586
TEST=cr50 firmware upgrade still works fine on B1
Change-Id: If5ec86d0385f97f0f361635b3903cea3a962b707
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/341618
Reviewed-by: Mary Ruthven <mruthven@chromium.org>
Diffstat (limited to 'chip')
-rw-r--r-- | chip/g/usb_upgrade.c | 41 |
1 files changed, 35 insertions, 6 deletions
diff --git a/chip/g/usb_upgrade.c b/chip/g/usb_upgrade.c index 634556cffb..182c40d7d2 100644 --- a/chip/g/usb_upgrade.c +++ b/chip/g/usb_upgrade.c @@ -92,6 +92,40 @@ static uint32_t block_size; static uint32_t block_index; /* + * Verify that the contens of the USB rx queue is a valid transfer start + * message from host, and if so - save its contents in the passed in + * update_pdu_header structure. + */ +static int valid_transfer_start(struct consumer const *consumer, size_t count, + struct update_pdu_header *pupdu) +{ + int i; + + /* + * Let's just make sure we drain the queue no matter what the contents + * are. This way they won't be in the way during next callback, even + * if these contents are not what's expected. + */ + i = count; + while (i > 0) { + QUEUE_REMOVE_UNITS(consumer->queue, pupdu, + MIN(i, sizeof(*pupdu))); + i -= sizeof(*pupdu); + } + + if (count != sizeof(struct update_pdu_header)) { + CPRINTS("FW update: wrong first block, size %d", count); + return 0; + } + + /* In the first block the payload (updu.cmd) must be all zeros. */ + for (i = 0; i < sizeof(pupdu->cmd); i++) + if (((uint8_t *)&pupdu->cmd)[i]) + return 0; + return 1; +} + +/* * When was last time a USB callback was called, in microseconds, free running * timer. */ @@ -149,13 +183,8 @@ static void upgrade_out_handler(struct consumer const *consumer, size_t count) uint32_t version; } startup_resp; - /* This better be the first block, of zero size. */ - if (count != sizeof(struct update_pdu_header)) { - CPRINTS("FW update: wrong first block size %d", - count); + if (!valid_transfer_start(consumer, count, &updu)) return; - } - QUEUE_REMOVE_UNITS(consumer->queue, &updu, count); CPRINTS("FW update: starting..."); |