diff options
author | Simon Glass <sjg@chromium.org> | 2016-07-07 22:00:30 -0600 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-07-15 16:51:40 -0700 |
commit | 9249a3569c35df1ffbecf6ef67fbcc4976861d8f (patch) | |
tree | 9b65c2fcacb8ed13f9e37af4e0b2853464630035 /extra | |
parent | 13518652d27ce739d9af71d441e41765c9851941 (diff) | |
download | chrome-ec-9249a3569c35df1ffbecf6ef67fbcc4976861d8f.tar.gz |
cr50: Modify USB updater to allow updates over /dev/tmp0 too
A new option is being added to the set of command line options, '-s',
once it is specified, the utility tries to update the CR50 using
/dev/tpm0.
BRANCH=none
BUG=chrome-os-partner:54992
TEST=emerge-gru ec-utils, copy /build/kevin/usr/sbin/usb_updater and
~/trunk/src/platform/ec/build/cr50/ec.bin to the DUT, try running
the update command on the DUT:
$ <path to>/usb_updater -s <path to>/ec.bin
observe in the end of the log:
vvvvvvvvvvvvvvvvvvvvvvvvvvvvv
-------
update complete
bye
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
restart the cr50 and verify that it is running the new image.
Change-Id: Idb755328f911f3065f01df420488c7a1b4a32500
Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/358967
Reviewed-by: Andrey Pronin <apronin@chromium.org>
Diffstat (limited to 'extra')
-rw-r--r-- | extra/usb_updater/usb_updater.c | 197 |
1 files changed, 174 insertions, 23 deletions
diff --git a/extra/usb_updater/usb_updater.c b/extra/usb_updater/usb_updater.c index 1b071b46e6..cfd232e758 100644 --- a/extra/usb_updater/usb_updater.c +++ b/extra/usb_updater/usb_updater.c @@ -4,7 +4,9 @@ * found in the LICENSE file. */ +#include <asm/byteorder.h> #include <endian.h> +#include <fcntl.h> #include <getopt.h> #include <libusb.h> #include <openssl/sha.h> @@ -25,6 +27,13 @@ #include "upgrade_fw.h" #include "config_chip.h" #include "board.h" +#include "compile_time_macros.h" + +#ifdef DEBUG +#define debug printf +#else +#define debug(fmt, args...) +#endif /* Look for Cr50 FW update interface */ #define VID USB_VID_GOOGLE @@ -32,23 +41,114 @@ #define SUBCLASS USB_SUBCLASS_GOOGLE_CR50 #define PROTOCOL USB_PROTOCOL_GOOGLE_CR50_NON_HC_FW_UPDATE + +/* + * Need to create an entire TPM PDU when upgrading over /dev/tpm0 and need to + * have space to prepare the entire PDU. + */ +struct upgrade_pkt { + __be16 tag; + __be32 length; + __be32 ordinal; + __be16 subcmd; + __be32 digest; + __be32 address; + char data[0]; +} __packed; + +#define SIGNED_TRANSFER_SIZE 1024 +#define MAX_BUF_SIZE (SIGNED_TRANSFER_SIZE + sizeof(struct upgrade_pkt)) +#define EXT_CMD 0xbaccd00a +#define FW_UPGRADE 4 + struct usb_endpoint { struct libusb_device_handle *devh; uint8_t ep_num; int chunk_len; }; -/* Globals */ +struct transfer_endpoint { + enum transfer_type { + usb_xfer = 0, + spi_xfer = 1 + } ep_type; + union { + struct usb_endpoint uep; + int tpm_fd; + }; +}; + static uint32_t protocol_version; static char *progname; -static char *short_opts = ":d:h"; +static char *short_opts = ":d:hs"; static const struct option long_opts[] = { /* name hasarg *flag val */ {"device", 1, NULL, 'd'}, {"help", 0, NULL, 'h'}, - {NULL, 0, NULL, 0}, + {"spi", 0, NULL, 's'}, + {NULL, 0, NULL, 0}, }; +/* Prepare and transfer a block to /dev/tpm0, get a reply. */ +static int tpm_send_pkt(int fd, unsigned int digest, unsigned int addr, + const void *data, int size, + void *response, int *response_size) +{ + /* Used by transfer to /dev/tpm0 */ + static uint8_t outbuf[MAX_BUF_SIZE]; + + struct upgrade_pkt *out = (struct upgrade_pkt *)outbuf; + /* Use the same structure, it will not be filled completely. */ + struct upgrade_pkt reply; + int len, done; + int response_offset = offsetof(struct upgrade_pkt, digest); + + debug("%s: sending to %#x %d bytes\n", __func__, addr, size); + + len = size + sizeof(struct upgrade_pkt); + + out->tag = __cpu_to_be16(0x8001); + out->length = __cpu_to_be32(len); + out->ordinal = __cpu_to_be32(EXT_CMD); + out->subcmd = __cpu_to_be16(FW_UPGRADE); + out->digest = digest; + out->address = __cpu_to_be32(addr); + memcpy(out->data, data, size); +#ifdef DEBUG + { + int i; + + debug("Writing %d bytes to TPM at %x\n", len, addr); + for (i = 0; i < 20; i++) + debug("%2.2x ", outbuf[i]); + debug("\n"); + } +#endif + done = write(fd, out, len); + if (done < 0) { + perror("Could not write to TPM"); + return -1; + } else if (done != len) { + fprintf(stderr, "Error: Wrote %x bytes, expected to write %x\n", + done, len); + return -1; + } + + len = read(fd, &reply, sizeof(reply)); + if ((len < response_offset) || + (len > ((int)(response_offset + sizeof(uint32_t))))) { + fprintf(stderr, "Problems reading from TPM, got %d bytes\n", + len); + return -1; + } + + debug("Read %x bytes from TPM\n", len); + len = len - response_offset; + memcpy(response, &reply.digest, len); + *response_size = len; + return 0; +} + /* Release USB device and return error to the OS. */ static void shut_down(struct usb_endpoint *uep) { @@ -68,6 +168,7 @@ static void usage(int errs) "\n" " -d,--device VID:PID USB device (default %04x:%04x)\n" " -h,--help Show this message\n" + " -s,--spi Use /dev/tmp0 (-d is ignored)\n" "\n", progname, VID, PID); exit(!!errs); @@ -291,7 +392,6 @@ static void usb_findit(uint16_t vid, uint16_t pid, struct usb_endpoint *uep) printf("READY\n-------\n"); } -#define SIGNED_TRANSFER_SIZE 1024 struct upgrade_command { uint32_t block_digest; uint32_t block_base; @@ -366,7 +466,7 @@ static int transfer_block(struct usb_endpoint *uep, struct update_pdu *updu, return 0; } -static void transfer_and_reboot(struct usb_endpoint *uep, +static void transfer_and_reboot(struct transfer_endpoint *tep, uint8_t *data, uint32_t data_len) { uint32_t out; @@ -376,6 +476,7 @@ static void transfer_and_reboot(struct usb_endpoint *uep, struct update_pdu updu; struct startup_resp first_resp; int rxed_size; + struct usb_endpoint *uep = &tep->uep; /* Send start/erase request */ printf("erase\n"); @@ -383,8 +484,17 @@ static void transfer_and_reboot(struct usb_endpoint *uep, memset(&updu, 0, sizeof(updu)); updu.block_size = htobe32(sizeof(updu)); - do_xfer(uep, &updu, sizeof(updu), &first_resp, sizeof(first_resp), - 1, &rxed_size); + if (tep->ep_type == usb_xfer) { + do_xfer(uep, &updu, sizeof(updu), &first_resp, + sizeof(first_resp), 1, &rxed_size); + } else { + rxed_size = sizeof(first_resp); + if (tpm_send_pkt(tep->tpm_fd, 0, 0, NULL, 0, + &first_resp, &rxed_size) < 0) { + perror("Failed to start transfer"); + return; + } + } if (rxed_size == sizeof(uint32_t)) protocol_version = 0; @@ -401,8 +511,6 @@ static void transfer_and_reboot(struct usb_endpoint *uep, } next_offset = reply - FLASH_BASE; - printf("Updating at offset 0x%08x\n", next_offset); - data_ptr = data + next_offset; data_len = CONFIG_RW_SIZE; @@ -410,7 +518,8 @@ static void transfer_and_reboot(struct usb_endpoint *uep, while (data_len && (data_ptr[data_len - 1] == 0xff)) data_len--; - printf("sending 0x%x/0x%x bytes\n", data_len, CONFIG_RW_SIZE); + printf("sending 0x%x/0x%x bytes to %#x\n", data_len, CONFIG_RW_SIZE, + next_offset); while (data_len) { size_t payload_size; SHA_CTX ctx; @@ -434,15 +543,38 @@ static void transfer_and_reboot(struct usb_endpoint *uep, /* Copy the first few bytes. */ memcpy(&updu.cmd.block_digest, digest, sizeof(updu.cmd.block_digest)); + if (tep->ep_type == usb_xfer) { + for (max_retries = 10; max_retries; max_retries--) + if (!transfer_block(uep, &updu, + data_ptr, payload_size)) + break; + + if (!max_retries) { + fprintf(stderr, + "Failed to trasfer block, %d to go\n", + data_len); + exit(1); + } + } else { + rxed_size = sizeof(first_resp); + if (tpm_send_pkt(tep->tpm_fd, + updu.cmd.block_digest, + next_offset + FLASH_BASE, + data_ptr, + payload_size, &first_resp, + &rxed_size) < 0) { + fprintf(stderr, + "Failed to trasfer block, %d to go\n", + data_len); + exit(1); + } + if ((rxed_size != 1) || *((uint8_t *)&first_resp)) { + fprintf(stderr, + "got response of size %d, value %#x\n", + rxed_size, first_resp.value); - for (max_retries = 10; max_retries; max_retries--) - if (!transfer_block(uep, &updu, data_ptr, payload_size)) - break; - - if (!max_retries) { - fprintf(stderr, "Failed to trasfer block, %d to go\n", - data_len); - exit(1); + exit(1); + } } data_len -= payload_size; data_ptr += payload_size; @@ -450,6 +582,8 @@ static void transfer_and_reboot(struct usb_endpoint *uep, } printf("-------\nupdate complete\n"); + if (tep->ep_type != usb_xfer) + return; /* Send stop request, ignorign reply. */ out = htobe32(UPGRADE_DONE); @@ -463,7 +597,7 @@ static void transfer_and_reboot(struct usb_endpoint *uep, int main(int argc, char *argv[]) { - struct usb_endpoint uep; + struct transfer_endpoint tep; int errorcnt; uint8_t *data = 0; uint32_t data_len = 0; @@ -476,6 +610,10 @@ int main(int argc, char *argv[]) else progname = argv[0]; + /* Usb transfer - default mode. */ + memset(&tep, 0, sizeof(tep)); + tep.ep_type = usb_xfer; + errorcnt = 0; opterr = 0; /* quiet, you */ while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) { @@ -489,6 +627,9 @@ int main(int argc, char *argv[]) case 'h': usage(errorcnt); break; + case 's': + tep.ep_type = spi_xfer; + break; case 0: /* auto-handled option */ break; case '?': @@ -525,14 +666,24 @@ int main(int argc, char *argv[]) exit(1); } - usb_findit(vid, pid, &uep); + if (tep.ep_type == usb_xfer) { + usb_findit(vid, pid, &tep.uep); + } else { + tep.tpm_fd = open("/dev/tpm0", O_RDWR); + if (tep.tpm_fd < 0) { + perror("Could not open TPM"); + exit(1); + } + } - transfer_and_reboot(&uep, data, data_len); + transfer_and_reboot(&tep, data, data_len); printf("bye\n"); free(data); - libusb_close(uep.devh); - libusb_exit(NULL); + if (tep.ep_type == usb_xfer) { + libusb_close(tep.uep.devh); + libusb_exit(NULL); + } return 0; } |