diff options
-rw-r--r-- | extra/usb_updater/Makefile | 28 | ||||
-rw-r--r-- | extra/usb_updater/usb_updater2.c | 961 |
2 files changed, 980 insertions, 9 deletions
diff --git a/extra/usb_updater/Makefile b/extra/usb_updater/Makefile index 9120b7d4dc..0a2b3a035e 100644 --- a/extra/usb_updater/Makefile +++ b/extra/usb_updater/Makefile @@ -4,8 +4,7 @@ CC ?= gcc PKG_CONFIG ?= pkg-config -PROGRAM := usb_updater -SOURCE := $(PROGRAM).c +PROGRAMS := usb_updater usb_updater2 LIBS := LFLAGS := CFLAGS := -std=gnu99 \ @@ -29,17 +28,28 @@ endif # # Add libusb-1.0 required flags # -LIBS += $(shell $(PKG_CONFIG) --libs libusb-1.0 libcrypto) -CFLAGS += $(shell $(PKG_CONFIG) --cflags libusb-1.0 libcrypto) +LIBS += $(shell $(PKG_CONFIG) --libs libusb-1.0) +CFLAGS += $(shell $(PKG_CONFIG) --cflags libusb-1.0) +CFLAGS += -I../../include -I../../util -# NOTE: This may be board-specific BOARD ?= cr50 -CFLAGS += -I../../include -I../../board/$(BOARD) -I ../../chip/g -I../../util +LIBS_g = $(shell $(PKG_CONFIG) --libs libcrypto) +CFLAGS_g = $(shell $(PKG_CONFIG) --cflags libcrypto) +CFLAGS_g += -I../../board/$(BOARD) -I ../../chip/g -$(PROGRAM): $(SOURCE) Makefile - $(CC) $(CFLAGS) $(SOURCE) $(LFLAGS) $(LIBS) -o $@ +LIBS_common = -lfmap + +all: $(PROGRAMS) + +# chip/g updater +usb_updater: usb_updater.c Makefile + $(CC) $(CFLAGS) $(CFLAGS_g) $< $(LFLAGS) $(LIBS) $(LIBS_g) -o $@ + +# common EC code USB updater +usb_updater2: usb_updater2.c Makefile + $(CC) $(CFLAGS) $< $(LFLAGS) $(LIBS) $(LIBS_common) -o $@ .PHONY: clean clean: - rm -rf $(PROGRAM) *~ + rm -rf $(PROGRAMS) *~ diff --git a/extra/usb_updater/usb_updater2.c b/extra/usb_updater/usb_updater2.c new file mode 100644 index 0000000000..3e676c0334 --- /dev/null +++ b/extra/usb_updater/usb_updater2.c @@ -0,0 +1,961 @@ +/* + * Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <asm/byteorder.h> +#include <endian.h> +#include <fcntl.h> +#include <getopt.h> +#include <libusb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <fmap.h> + +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +#include "compile_time_macros.h" +#include "misc_util.h" +#include "usb_descriptor.h" +#include "update_fw.h" +#include "vb21_struct.h" + +#ifdef DEBUG +#define debug printf +#else +#define debug(fmt, args...) +#endif + +/* + * This file contains the source code of a Linux application used to update + * EC device firmware (common code only, usb_updater takes care of cr50). + */ + +#define VID USB_VID_GOOGLE +#define PID 0x5022 +#define SUBCLASS USB_SUBCLASS_GOOGLE_UPDATE +#define PROTOCOL USB_PROTOCOL_GOOGLE_UPDATE + +enum exit_values { + noop = 0, /* All up to date, no update needed. */ + all_updated = 1, /* Update completed, reboot required. */ + rw_updated = 2, /* RO was not updated, reboot required. */ + update_error = 3 /* Something went wrong. */ +}; + +struct usb_endpoint { + struct libusb_device_handle *devh; + uint8_t ep_num; + int chunk_len; +}; + +struct transfer_descriptor { + /* + * offsets of section available for update (not currently active). + */ + uint32_t offset; + + struct usb_endpoint uep; +}; + +/* Information about the target */ +static struct first_response_pdu targ; + +static uint16_t protocol_version; +static uint16_t header_type; +static char *progname; +static char *short_opts = "bd:fhjrsuw"; +static const struct option long_opts[] = { + /* name hasarg *flag val */ + {"binvers", 1, NULL, 'b'}, + {"device", 1, NULL, 'd'}, + {"fwver", 0, NULL, 'f'}, + {"help", 0, NULL, 'h'}, + {"jump_to_rw", 0, NULL, 'j'}, + {"reboot", 0, NULL, 'r'}, + {"stay_in_ro", 0, NULL, 's'}, + {"unlock_rollback", 0, NULL, 'u'}, + {"unlock_rw", 0, NULL, 'w'}, + {}, +}; + +/* Release USB device and return error to the OS. */ +static void shut_down(struct usb_endpoint *uep) +{ + libusb_close(uep->devh); + libusb_exit(NULL); + exit(update_error); +} + +static void usage(int errs) +{ + printf("\nUsage: %s [options] <binary image>\n" + "\n" + "This updates EC firmware over USB (common code EC, no cr50).\n" + "The required argument is the full RO+RW image.\n" + "\n" + "Options:\n" + "\n" + " -b,--binvers Report versions of image's " + "RW and RO, do not update\n" + " -d,--device VID:PID USB device (default %04x:%04x)\n" + " -f,--fwver Report running firmware versions.\n" + " -h,--help Show this message\n" + " -j,--jump_to_rw Tell EC to jump to RW\n" + " -r,--reboot Tell EC to reboot\n" + " -s,--stay_in_ro Tell EC to stay in RO\n" + " -u,--unlock_rollback Tell EC to unlock the rollback region\n" + " -w,--unlock_rw Tell EC to unlock the RW region\n" + "\n", progname, VID, PID); + + exit(errs ? update_error : noop); +} + +/* Read file into buffer */ +static uint8_t *get_file_or_die(const char *filename, size_t *len_ptr) +{ + FILE *fp; + struct stat st; + uint8_t *data; + size_t len; + + fp = fopen(filename, "rb"); + if (!fp) { + perror(filename); + exit(update_error); + } + if (fstat(fileno(fp), &st)) { + perror("stat"); + exit(update_error); + } + + len = st.st_size; + + data = malloc(len); + if (!data) { + perror("malloc"); + exit(update_error); + } + + if (fread(data, st.st_size, 1, fp) != 1) { + perror("fread"); + exit(update_error); + } + + fclose(fp); + + *len_ptr = len; + return data; +} + +#define USB_ERROR(m, r) \ + fprintf(stderr, "%s:%d, %s returned %d (%s)\n", __FILE__, __LINE__, \ + m, r, libusb_strerror(r)) + +/* + * Actual USB transfer function, the 'allow_less' flag indicates that the + * valid response could be shortef than allotted memory, the 'rxed_count' + * pointer, if provided along with 'allow_less' lets the caller know how mavy + * bytes were received. + */ +static void do_xfer(struct usb_endpoint *uep, void *outbuf, int outlen, + void *inbuf, int inlen, int allow_less, + size_t *rxed_count) +{ + + int r, actual; + + /* Send data out */ + if (outbuf && outlen) { + actual = 0; + r = libusb_bulk_transfer(uep->devh, uep->ep_num, + outbuf, outlen, + &actual, 1000); + if (r < 0) { + USB_ERROR("libusb_bulk_transfer", r); + exit(update_error); + } + if (actual != outlen) { + fprintf(stderr, "%s:%d, only sent %d/%d bytes\n", + __FILE__, __LINE__, actual, outlen); + shut_down(uep); + } + } + + /* Read reply back */ + if (inbuf && inlen) { + + actual = 0; + r = libusb_bulk_transfer(uep->devh, uep->ep_num | 0x80, + inbuf, inlen, + &actual, 1000); + if (r < 0) { + USB_ERROR("libusb_bulk_transfer", r); + exit(update_error); + } + if ((actual != inlen) && !allow_less) { + fprintf(stderr, "%s:%d, only received %d/%d bytes\n", + __FILE__, __LINE__, actual, inlen); + shut_down(uep); + } + + if (rxed_count) + *rxed_count = actual; + } +} + +static void xfer(struct usb_endpoint *uep, void *outbuf, + size_t outlen, void *inbuf, size_t inlen) +{ + do_xfer(uep, outbuf, outlen, inbuf, inlen, 0, NULL); +} + +/* Return 0 on error, since it's never gonna be EP 0 */ +static int find_endpoint(const struct libusb_interface_descriptor *iface, + struct usb_endpoint *uep) +{ + const struct libusb_endpoint_descriptor *ep; + + if (iface->bInterfaceClass == 255 && + iface->bInterfaceSubClass == SUBCLASS && + iface->bInterfaceProtocol == PROTOCOL && + iface->bNumEndpoints) { + ep = &iface->endpoint[0]; + uep->ep_num = ep->bEndpointAddress & 0x7f; + uep->chunk_len = ep->wMaxPacketSize; + return 1; + } + + return 0; +} + +/* Return -1 on error */ +static int find_interface(struct usb_endpoint *uep) +{ + int iface_num = -1; + int r, i, j; + struct libusb_device *dev; + struct libusb_config_descriptor *conf = 0; + const struct libusb_interface *iface0; + const struct libusb_interface_descriptor *iface; + + dev = libusb_get_device(uep->devh); + r = libusb_get_active_config_descriptor(dev, &conf); + if (r < 0) { + USB_ERROR("libusb_get_active_config_descriptor", r); + goto out; + } + + for (i = 0; i < conf->bNumInterfaces; i++) { + iface0 = &conf->interface[i]; + for (j = 0; j < iface0->num_altsetting; j++) { + iface = &iface0->altsetting[j]; + if (find_endpoint(iface, uep)) { + iface_num = i; + goto out; + } + } + } + +out: + libusb_free_config_descriptor(conf); + return iface_num; +} + +/* Returns true if parsed. */ +static int parse_vidpid(const char *input, uint16_t *vid_ptr, uint16_t *pid_ptr) +{ + char *copy, *s, *e = 0; + + copy = strdup(input); + + s = strchr(copy, ':'); + if (!s) + return 0; + *s++ = '\0'; + + *vid_ptr = (uint16_t) strtoul(copy, &e, 16); + if (!*optarg || (e && *e)) + return 0; + + *pid_ptr = (uint16_t) strtoul(s, &e, 16); + if (!*optarg || (e && *e)) + return 0; + + return 1; +} + + +static void usb_findit(uint16_t vid, uint16_t pid, struct usb_endpoint *uep) +{ + int iface_num, r; + + memset(uep, 0, sizeof(*uep)); + + r = libusb_init(NULL); + if (r < 0) { + USB_ERROR("libusb_init", r); + exit(update_error); + } + + printf("open_device %04x:%04x\n", vid, pid); + /* NOTE: This doesn't handle multiple matches! */ + uep->devh = libusb_open_device_with_vid_pid(NULL, vid, pid); + if (!uep->devh) { + fprintf(stderr, "Can't find device\n"); + exit(update_error); + } + + iface_num = find_interface(uep); + if (iface_num < 0) { + fprintf(stderr, "USB FW update not supported by that device\n"); + shut_down(uep); + } + if (!uep->chunk_len) { + fprintf(stderr, "wMaxPacketSize isn't valid\n"); + shut_down(uep); + } + + printf("found interface %d endpoint %d, chunk_len %d\n", + iface_num, uep->ep_num, uep->chunk_len); + + libusb_set_auto_detach_kernel_driver(uep->devh, 1); + r = libusb_claim_interface(uep->devh, iface_num); + if (r < 0) { + USB_ERROR("libusb_claim_interface", r); + shut_down(uep); + } + + printf("READY\n-------\n"); +} + +static int transfer_block(struct usb_endpoint *uep, + struct update_frame_header *ufh, + uint8_t *transfer_data_ptr, size_t payload_size) +{ + size_t transfer_size; + uint32_t reply; + int actual; + int r; + + /* First send the header. */ + xfer(uep, ufh, sizeof(*ufh), NULL, 0); + + /* Now send the block, chunk by chunk. */ + for (transfer_size = 0; transfer_size < payload_size;) { + int chunk_size; + + chunk_size = MIN(uep->chunk_len, payload_size - transfer_size); + xfer(uep, transfer_data_ptr, chunk_size, NULL, 0); + transfer_data_ptr += chunk_size; + transfer_size += chunk_size; + } + + /* Now get the reply. */ + r = libusb_bulk_transfer(uep->devh, uep->ep_num | 0x80, + (void *) &reply, sizeof(reply), + &actual, 1000); + if (r) { + if (r == -7) { + fprintf(stderr, "Timeout!\n"); + return r; + } + USB_ERROR("libusb_bulk_transfer", r); + shut_down(uep); + } + + reply = *((uint8_t *)&reply); + if (reply) { + fprintf(stderr, "Error: status %#x\n", reply); + exit(update_error); + } + + return 0; +} + +/** + * Transfer an image section (typically RW or RO). + * + * td - transfer descriptor to use to communicate with the target + * data_ptr - pointer at the section base in the image + * section_addr - address of the section in the target memory space + * data_len - section size + */ +static void transfer_section(struct transfer_descriptor *td, + uint8_t *data_ptr, + uint32_t section_addr, + size_t data_len) +{ + /* + * Actually, we can skip trailing chunks of 0xff, as the entire + * section space must be erased before the update is attempted. + * + * FIXME: We can be smarter than this and skip blocks within the image. + */ + while (data_len && (data_ptr[data_len - 1] == 0xff)) + data_len--; + + printf("sending 0x%zx bytes to %#x\n", data_len, section_addr); + while (data_len) { + size_t payload_size; + uint32_t block_base; + int max_retries; + + /* prepare the header to prepend to the block. */ + payload_size = MIN(data_len, targ.common.maximum_pdu_size); + + block_base = htobe32(section_addr); + + struct update_frame_header ufh; + + ufh.block_size = htobe32(payload_size + + sizeof(struct update_frame_header)); + ufh.cmd.block_base = block_base; + ufh.cmd.block_digest = 0; + for (max_retries = 10; max_retries; max_retries--) + if (!transfer_block(&td->uep, &ufh, + data_ptr, payload_size)) + break; + + if (!max_retries) { + fprintf(stderr, + "Failed to transfer block, %zd to go\n", + data_len); + exit(update_error); + } + data_len -= payload_size; + data_ptr += payload_size; + section_addr += payload_size; + } +} + +/* + * Each RO or RW section of the new image can be in one of the following + * states. + */ +enum upgrade_status { + not_needed = 0, /* Version below or equal that on the target. */ + not_possible, /* + * RO is newer, but can't be transferred due to + * target RW shortcomings. + */ + needed /* + * This section needs to be transferred to the + * target. + */ +}; + +/* This array describes all sections of the new image. */ +static struct { + const char *name; + uint32_t offset; + uint32_t size; + enum upgrade_status ustatus; + char version[32]; + int32_t rollback; + uint32_t key_version; +} sections[] = { + {"RO"}, + {"RW"} +}; + +static const struct fmap_area *fmap_find_area_or_die(const struct fmap *fmap, + const char *name) +{ + const struct fmap_area *fmaparea; + + fmaparea = fmap_find_area(fmap, name); + if (!fmaparea) { + fprintf(stderr, "Cannot find FMAP area %s\n", name); + exit(update_error); + } + + return fmaparea; +} + +/* + * Scan the new image and retrieve versions of all sections. + */ +static void fetch_header_versions(const uint8_t *image, size_t len) +{ + const struct fmap *fmap; + const struct fmap_area *fmaparea; + long int offset; + size_t i; + + offset = fmap_find(image, len); + if (offset < 0) { + fprintf(stderr, "Cannot find FMAP in image\n"); + exit(update_error); + } + fmap = (const struct fmap *)(image+offset); + + /* FIXME: validate fmap struct more than this? */ + if (fmap->size != len) { + fprintf(stderr, "Mismatch between FMAP size and image size\n"); + exit(update_error); + } + + for (i = 0; i < ARRAY_SIZE(sections); i++) { + const char *fmap_name; + const char *fmap_fwid_name; + const char *fmap_rollback_name = NULL; + const char *fmap_key_name = NULL; + + if (!strcmp(sections[i].name, "RO")) { + fmap_name = "EC_RO"; + fmap_fwid_name = "RO_FRID"; + } else if (!strcmp(sections[i].name, "RW")) { + fmap_name = "EC_RW"; + fmap_fwid_name = "RW_FWID"; + fmap_rollback_name = "RW_RBVER"; + /* + * Key version comes from key RO (RW signature does not + * contain the key version. + */ + fmap_key_name = "KEY_RO"; + } else { + fprintf(stderr, "Invalid section name\n"); + exit(update_error); + } + + fmaparea = fmap_find_area_or_die(fmap, fmap_name); + + /* FIXME: endianness? */ + sections[i].offset = fmaparea->offset; + sections[i].size = fmaparea->size; + + fmaparea = fmap_find_area_or_die(fmap, fmap_fwid_name); + + if (fmaparea->size != sizeof(sections[i].version)) { + fprintf(stderr, "Invalid fwid size\n"); + exit(update_error); + } + memcpy(sections[i].version, image+fmaparea->offset, + fmaparea->size); + + sections[i].rollback = -1; + if (fmap_rollback_name) { + fmaparea = fmap_find_area(fmap, fmap_rollback_name); + if (fmaparea) + memcpy(§ions[i].rollback, + image+fmaparea->offset, + sizeof(sections[i].rollback)); + } + + sections[i].key_version = -1; + if (fmap_key_name) { + fmaparea = fmap_find_area(fmap, fmap_key_name); + if (fmaparea) { + const struct vb21_packed_key *key = + (const void *)(image+fmaparea->offset); + sections[i].key_version = key->key_version; + } + } + } +} + +static int show_headers_versions(const void *image) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(sections); i++) { + printf("%s off=%08x/%08x v=%.32s rb=%d kv=%d\n", + sections[i].name, sections[i].offset, sections[i].size, + sections[i].version, sections[i].rollback, + sections[i].key_version); + } + return 0; +} + +/* + * Pick sections to transfer based on information retrieved from the target, + * the new image, and the protocol version the target is running. + */ +static void pick_sections(struct transfer_descriptor *td) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(sections); i++) { + uint32_t offset = sections[i].offset; + + /* Skip currently active section. */ + if (offset != td->offset) + continue; + + sections[i].ustatus = needed; + } +} + +static void setup_connection(struct transfer_descriptor *td) +{ + size_t rxed_size; + size_t i; + uint32_t error_code; + + /* + * Need to be backwards compatible, communicate with targets running + * different protocol versions. + */ + union { + struct first_response_pdu rpdu; + uint32_t legacy_resp; + } start_resp; + + /* Send start request. */ + printf("start\n"); + + struct update_frame_header ufh; + uint8_t inbuf[td->uep.chunk_len]; + int actual = 0; + + /* Flush all data from endpoint to recover in case of error. */ + while (!libusb_bulk_transfer(td->uep.devh, + td->uep.ep_num | 0x80, + (void *)&inbuf, td->uep.chunk_len, + &actual, 10)) { + printf("flush\n"); + } + + memset(&ufh, 0, sizeof(ufh)); + ufh.block_size = htobe32(sizeof(ufh)); + do_xfer(&td->uep, &ufh, sizeof(ufh), &start_resp, + sizeof(start_resp), 1, &rxed_size); + + /* We got something. Check for errors in response */ + if (rxed_size < 8) { + fprintf(stderr, "Unexpected response size %zd: ", rxed_size); + for (i = 0; i < rxed_size; i++) + fprintf(stderr, " %02x", ((uint8_t *)&start_resp)[i]); + fprintf(stderr, "\n"); + exit(update_error); + } + + protocol_version = be16toh(start_resp.rpdu.protocol_version); + if (protocol_version < 5 || protocol_version > 6) { + fprintf(stderr, "Unsupported protocol version %d\n", + protocol_version); + exit(update_error); + } + + header_type = be16toh(start_resp.rpdu.header_type); + + printf("target running protocol version %d (type %d)\n", + protocol_version, header_type); + if (header_type != UPDATE_HEADER_TYPE_COMMON) { + fprintf(stderr, "Unsupported header type %d\n", + header_type); + exit(update_error); + } + + error_code = be32toh(start_resp.rpdu.return_value); + + if (error_code) { + fprintf(stderr, "Target reporting error %d\n", error_code); + shut_down(&td->uep); + exit(update_error); + } + + td->offset = be32toh(start_resp.rpdu.common.offset); + memcpy(targ.common.version, start_resp.rpdu.common.version, + sizeof(start_resp.rpdu.common.version)); + targ.common.maximum_pdu_size = + be32toh(start_resp.rpdu.common.maximum_pdu_size); + targ.common.flash_protection = + be32toh(start_resp.rpdu.common.flash_protection); + targ.common.min_rollback = be32toh(start_resp.rpdu.common.min_rollback); + targ.common.key_version = be32toh(start_resp.rpdu.common.key_version); + + printf("maximum PDU size: %d\n", targ.common.maximum_pdu_size); + printf("Flash protection status: %04x\n", targ.common.flash_protection); + printf("version: %32s\n", targ.common.version); + printf("key_version: %d\n", targ.common.key_version); + printf("min_rollback: %d\n", targ.common.min_rollback); + printf("offset: writable at %#x\n", td->offset); + + pick_sections(td); +} + +/* + * Channel TPM extension/vendor command over USB. The payload of the USB frame + * in this case consists of the 2 byte subcommand code concatenated with the + * command body. The caller needs to indicate if a response is expected, and + * if it is - of what maximum size. + */ +static int ext_cmd_over_usb(struct usb_endpoint *uep, uint16_t subcommand, + void *cmd_body, size_t body_size, + void *resp, size_t *resp_size) +{ + struct update_frame_header *ufh; + uint16_t *frame_ptr; + size_t usb_msg_size; + + usb_msg_size = sizeof(struct update_frame_header) + + sizeof(subcommand) + body_size; + + ufh = malloc(usb_msg_size); + if (!ufh) { + printf("%s: failed to allocate %zd bytes\n", + __func__, usb_msg_size); + return -1; + } + + ufh->block_size = htobe32(usb_msg_size); + ufh->cmd.block_digest = 0; + ufh->cmd.block_base = htobe32(UPDATE_EXTRA_CMD); + frame_ptr = (uint16_t *)(ufh + 1); + *frame_ptr = htobe16(subcommand); + + if (body_size) + memcpy(frame_ptr + 1, cmd_body, body_size); + + xfer(uep, ufh, usb_msg_size, resp, resp_size ? *resp_size : 0); + + free(ufh); + return 0; +} + +/* + * Indicate to the target that update image transfer has been completed. Upon + * receiveing of this message the target state machine transitions into the + * 'rx_idle' state. The host may send an extension command to reset the target + * after this. + */ +static void send_done(struct usb_endpoint *uep) +{ + uint32_t out; + + /* Send stop request, ignoring reply. */ + out = htobe32(UPDATE_DONE); + xfer(uep, &out, sizeof(out), &out, 1); +} + +static void send_subcommand(struct transfer_descriptor *td, uint16_t subcommand) +{ + send_done(&td->uep); + + if (protocol_version > 5) { + uint8_t response = -1; + size_t response_size = sizeof(response); + + ext_cmd_over_usb(&td->uep, subcommand, + NULL, 0, + &response, &response_size); + printf("sent command %x, resp %x\n", subcommand, response); + } +} + +/* Returns number of successfully transmitted image sections. */ +static int transfer_image(struct transfer_descriptor *td, + uint8_t *data, size_t data_len) +{ + size_t i; + int num_txed_sections = 0; + + for (i = 0; i < ARRAY_SIZE(sections); i++) + if (sections[i].ustatus == needed) { + transfer_section(td, + data + sections[i].offset, + sections[i].offset, + sections[i].size); + num_txed_sections++; + } + + /* + * Move USB receiver sate machine to idle state so that vendor + * commands can be processed later, if any. + */ + send_done(&td->uep); + + if (!num_txed_sections) + printf("nothing to do\n"); + else + printf("-------\nupdate complete\n"); + return num_txed_sections; +} + +static void generate_reset_request(struct transfer_descriptor *td) +{ + size_t response_size; + uint8_t response; + uint16_t subcommand; + uint8_t command_body[2]; /* Max command body size. */ + size_t command_body_size; + + if (protocol_version < 6) { + /* + * Send a second stop request, which should reboot + * without replying. + */ + send_done(&td->uep); + /* Nothing we can do over /dev/tpm0 running versions below 6. */ + return; + } + + /* + * If the user explicitly wants it, request post reset instead of + * immediate reset. In this case next time the target reboots, the h1 + * will reboot as well, and will consider running the uploaded code. + * + * In case target RW version is 19 or above, to reset the target the + * host is supposed to send the command to enable the uploaded image + * disabled by default. + * + * Otherwise the immediate reset command would suffice. + */ + /* Most common case. */ + command_body_size = 0; + response_size = 1; + subcommand = UPDATE_EXTRA_CMD_IMMEDIATE_RESET; + ext_cmd_over_usb(&td->uep, subcommand, + command_body, command_body_size, + &response, &response_size); + + printf("reboot not triggered\n"); +} + +int main(int argc, char *argv[]) +{ + struct transfer_descriptor td; + int errorcnt; + uint8_t *data = 0; + size_t data_len = 0; + uint16_t vid = VID, pid = PID; + int i; + size_t j; + int transferred_sections = 0; + int binary_vers = 0; + int show_fw_ver = 0; + int extra_command = -1; + + progname = strrchr(argv[0], '/'); + if (progname) + progname++; + else + progname = argv[0]; + + /* Usb transfer - default mode. */ + memset(&td, 0, sizeof(td)); + + errorcnt = 0; + opterr = 0; /* quiet, you */ + while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) { + switch (i) { + case 'b': + binary_vers = 1; + break; + case 'd': + if (!parse_vidpid(optarg, &vid, &pid)) { + printf("Invalid argument: \"%s\"\n", optarg); + errorcnt++; + } + break; + case 'f': + show_fw_ver = 1; + break; + case 'h': + usage(errorcnt); + break; + case 'j': + extra_command = UPDATE_EXTRA_CMD_JUMP_TO_RW; + break; + case 'r': + extra_command = UPDATE_EXTRA_CMD_IMMEDIATE_RESET; + break; + case 's': + extra_command = UPDATE_EXTRA_CMD_STAY_IN_RO; + break; + case 'u': + extra_command = UPDATE_EXTRA_CMD_UNLOCK_ROLLBACK; + break; + case 'w': + extra_command = UPDATE_EXTRA_CMD_UNLOCK_RW; + break; + case 0: /* auto-handled option */ + break; + case '?': + if (optopt) + printf("Unrecognized option: -%c\n", optopt); + else + printf("Unrecognized option: %s\n", + argv[optind - 1]); + errorcnt++; + break; + case ':': + printf("Missing argument to %s\n", argv[optind - 1]); + errorcnt++; + break; + default: + printf("Internal error at %s:%d\n", __FILE__, __LINE__); + exit(update_error); + } + } + + if (errorcnt) + usage(errorcnt); + + if (!show_fw_ver && extra_command == -1) { + if (optind >= argc) { + fprintf(stderr, + "\nERROR: Missing required <binary image>\n\n"); + usage(1); + } + + data = get_file_or_die(argv[optind], &data_len); + printf("read %zd(%#zx) bytes from %s\n", + data_len, data_len, argv[optind]); + + fetch_header_versions(data, data_len); + + if (binary_vers) + exit(show_headers_versions(data)); + } else { + if (optind < argc) + printf("Ignoring binary image %s\n", argv[optind]); + } + + usb_findit(vid, pid, &td.uep); + + setup_connection(&td); + + if (show_fw_ver) { + printf("Current versions:\n"); + printf("Writable %32s\n", targ.common.version); + } + + if (data) { + transferred_sections = transfer_image(&td, data, data_len); + free(data); + + if (transferred_sections) + generate_reset_request(&td); + } else if (extra_command > -1) + send_subcommand(&td, extra_command); + + libusb_close(td.uep.devh); + libusb_exit(NULL); + + if (!transferred_sections) + return noop; + /* + * We should indicate if RO update was not done because of the + * insufficient RW version. + */ + for (j = 0; j < ARRAY_SIZE(sections); j++) + if (sections[j].ustatus == not_possible) { + /* This will allow scripting repeat attempts. */ + printf("Failed to update RO, run the command again\n"); + return rw_updated; + } + + printf("image updated\n"); + return all_updated; +} |