diff options
author | Vadim Bendebury <vbendeb@chromium.org> | 2017-06-02 18:45:17 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2017-06-06 17:09:27 -0700 |
commit | b06942187e7025d1334bcd8d0ffa95b225c19179 (patch) | |
tree | 4159a6c21d6ab26c52aa30b5331b8ee34a88ea79 /extra | |
parent | aafff584ae4a9df771d2ef5f353265bd8cc4325d (diff) | |
download | chrome-ec-b06942187e7025d1334bcd8d0ffa95b225c19179.tar.gz |
cr50: usb_updater: add proper vendor command processing
So far vendor command processing has been a second class citizen in
the Cr50 usb_updater: return codes were mostly ignored even when using
TPM, when using USB there was no way to communicate return codes at
all.
This patch refactors the source code to use a single function to
process vendor commands over both USB and TPM, adding proper passing
of the result codes back to the caller in both cases, retrieving the
return code from the response header when using TPM and from the first
byte of the response payload when using USB.
BRANCH=cr50
BUG=b:35587387,b:35587053
TEST=verified that it is possible to update rw13, rw18 and rw20 both
over TPM and USB, which indicates that vendor commands are
properly handled.
Change-Id: I837e17b29d3b025fbca5b1ef49463cfb1729fe6c
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/525094
Reviewed-by: Aseda Aboagye <aaboagye@chromium.org>
Diffstat (limited to 'extra')
-rw-r--r-- | extra/usb_updater/usb_updater.c | 156 |
1 files changed, 117 insertions, 39 deletions
diff --git a/extra/usb_updater/usb_updater.c b/extra/usb_updater/usb_updater.c index c39673535a..ef0756794a 100644 --- a/extra/usb_updater/usb_updater.c +++ b/extra/usb_updater/usb_updater.c @@ -258,6 +258,7 @@ static int tpm_send_pkt(int fd, unsigned int digest, unsigned int addr, int response_offset = offsetof(struct upgrade_pkt, command.data); void *payload; size_t header_size; + uint32_t rv; debug("%s: sending to %#x %d bytes\n", __func__, addr, size); @@ -327,10 +328,17 @@ static int tpm_send_pkt(int fd, unsigned int digest, unsigned int addr, return -1; } - len = MIN(len, *response_size); - memcpy(response, outbuf + response_offset, len); - *response_size = len; - return 0; + if (response && response_size) { + len = MIN(len, *response_size); + memcpy(response, outbuf + response_offset, len); + *response_size = len; + } + + /* Return the actual return code from the TPM response header. */ + memcpy(&rv, &((struct upgrade_pkt *)outbuf)->ordinal, sizeof(rv)); + rv = be32toh(rv); + + return rv; } /* Release USB device and return error to the OS. */ @@ -988,7 +996,9 @@ static int ext_cmd_over_usb(struct usb_endpoint *uep, uint16_t subcommand, offsetof(struct update_frame_header, cmd.block_base)); SHA1_Final(digest, &ctx); memcpy(&ufh->cmd.block_digest, digest, sizeof(ufh->cmd.block_digest)); - xfer(uep, ufh, usb_msg_size, resp, resp_size ? *resp_size : 0); + + do_xfer(uep, ufh, usb_msg_size, resp, + resp_size ? *resp_size : 0, 1, resp_size); free(ufh); return 0; @@ -1009,25 +1019,6 @@ static void send_done(struct usb_endpoint *uep) xfer(uep, &out, sizeof(out), &out, 1); } -/* - * Corrupt the header of the inactive rw image to make sure the system can't - * rollback - */ -static void invalidate_inactive_rw(struct transfer_descriptor *td) -{ - /* Corrupt the rw image that is not running. */ - uint16_t subcommand = VENDOR_CC_INVALIDATE_INACTIVE_RW; - - if (td->ep_type == usb_xfer) { - if (protocol_version > 5) { - ext_cmd_over_usb(&td->uep, subcommand, - NULL, 0, - NULL, 0); - printf("inactive rw corrupted\n"); - } - } -} - /* Returns number of successfully transmitted image sections. */ static int transfer_image(struct transfer_descriptor *td, uint8_t *data, size_t data_len) @@ -1058,6 +1049,96 @@ static int transfer_image(struct transfer_descriptor *td, return num_txed_sections; } +static uint32_t send_vendor_command(struct transfer_descriptor *td, + uint16_t subcommand, + void *command_body, + size_t command_body_size, + void *response, + size_t *response_size) +{ + int32_t rv; + + if (td->ep_type == usb_xfer) { + /* + * When communicating over USB the response is always supposed + * to have the result code in the first byte of the response, + * to be stripped from the actual response body by this + * function. + * + * We never expect vendor command response larger than 32 bytes. + */ + uint8_t temp_response[32]; + size_t max_response_size; + + if (!response_size) { + max_response_size = 1; + } else if (*response_size < (sizeof(temp_response))) { + max_response_size = *response_size + 1; + } else { + fprintf(stderr, + "Error: Expected response too large (%zd)\n", + *response_size); + /* Should happen only when debugging. */ + exit(update_error); + } + + ext_cmd_over_usb(&td->uep, subcommand, + command_body, command_body_size, + temp_response, &max_response_size); + if (!max_response_size) { + /* + * we must be talking to an older Cr50 firmware, which + * does not return the result code in the first byte + * on success, nothing to do. + */ + if (response_size) + *response_size = 0; + rv = 0; + } else { + rv = temp_response[0]; + if (response_size) { + *response_size = max_response_size - 1; + memcpy(response, + temp_response + 1, *response_size); + } + } + } else { + + rv = tpm_send_pkt(td->tpm_fd, 0, 0, + command_body, command_body_size, + response, response_size, subcommand); + + if (rv == -1) { + fprintf(stderr, + "Error: Failed to send vendor command %d\n", + subcommand); + exit(update_error); + } + } + + return rv; /* This will be converted into uint32_t */ +} + +/* + * Corrupt the header of the inactive rw image to make sure the system can't + * rollback + */ +static void invalidate_inactive_rw(struct transfer_descriptor *td) +{ + /* Corrupt the rw image that is not running. */ + uint32_t rv; + + rv = send_vendor_command(td, VENDOR_CC_INVALIDATE_INACTIVE_RW, + NULL, 0, NULL, NULL); + if (!rv) { + printf("Inactive header invalidated\n"); + return; + } + + fprintf(stderr, "*%s: Error %#x\n", __func__, rv); + exit(update_error); +} + static struct signed_header_version ver19 = { .epoch = 0, .major = 0, @@ -1072,6 +1153,8 @@ static void generate_reset_request(struct transfer_descriptor *td) uint8_t command_body[2]; /* Max command body size. */ size_t command_body_size; uint32_t background_update_supported; + const char *reset_type; + int rv; if (protocol_version < 6) { if (td->ep_type == usb_xfer) { @@ -1113,32 +1196,27 @@ static void generate_reset_request(struct transfer_descriptor *td) response_size = 1; if (td->post_reset || td->upstart_mode) { subcommand = EXTENSION_POST_RESET; + reset_type = "posted"; } else if (background_update_supported) { subcommand = VENDOR_CC_TURN_UPDATE_ON; command_body_size = sizeof(command_body); command_body[0] = 0; command_body[1] = 100; /* Reset in 100 ms. */ + reset_type = "requested"; } else { response_size = 0; subcommand = VENDOR_CC_IMMEDIATE_RESET; + reset_type = "triggered"; } - if (td->ep_type == usb_xfer) { - ext_cmd_over_usb(&td->uep, subcommand, - command_body, command_body_size, - &response, &response_size); - } else { - /* Need to send extended command for reboot. */ - if (tpm_send_pkt(td->tpm_fd, 0, 0, - command_body, command_body_size, - &response, &response_size, subcommand) < 0) { - fprintf(stderr, "Failed to request posted reboot\n"); - exit(update_error); - } - } + rv = send_vendor_command(td, subcommand, command_body, + command_body_size, &response, &response_size); - printf("reboot %s\n", subcommand == EXTENSION_POST_RESET ? - "request posted" : "triggered"); + if (rv) { + fprintf(stderr, "*%s: Error %#x\n", __func__, rv); + exit(update_error); + } + printf("reboot %s\n", reset_type); } static int show_headers_versions(const void *image) |