diff options
-rw-r--r-- | chip/g/signed_header.h | 3 | ||||
-rw-r--r-- | extra/usb_updater/gsctool.c | 276 |
2 files changed, 217 insertions, 62 deletions
diff --git a/chip/g/signed_header.h b/chip/g/signed_header.h index 6096350a54..279a32081d 100644 --- a/chip/g/signed_header.h +++ b/chip/g/signed_header.h @@ -15,6 +15,9 @@ #define INFO_MAX 128 /* baked in rom! */ #define INFO_IGNORE 0xaa3c55c3 /* baked in rom! */ +#define MAGIC_HAVEN 0xFFFFFFFF +#define MAGIC_DAUNTLESS 0xFFFFFFFD + /* Default value for _pad[] words */ #define SIGNED_HEADER_PADDING 0x33333333 diff --git a/extra/usb_updater/gsctool.c b/extra/usb_updater/gsctool.c index 4deb51ccf3..6a8a2128b0 100644 --- a/extra/usb_updater/gsctool.c +++ b/extra/usb_updater/gsctool.c @@ -912,7 +912,18 @@ enum upgrade_status { */ }; -/* This array describes all four sections of the new image. */ +/* Index to refer to a section within sections array */ +enum section { + RO_A, + RW_A, + RO_B, + RW_B, +}; + +/* + * This array describes all four sections of the new image. Defaults are for + * H1 images. D2 images are scanned for SignedHeaders in the image + */ static struct { const char *name; uint32_t offset; @@ -921,13 +932,165 @@ static struct { struct signed_header_version shv; uint32_t keyid; } sections[] = { - {"RO_A", CONFIG_RO_MEM_OFF, CONFIG_RO_SIZE}, - {"RW_A", CONFIG_RW_MEM_OFF, CONFIG_RW_SIZE}, - {"RO_B", CHIP_RO_B_MEM_OFF, CONFIG_RO_SIZE}, - {"RW_B", CONFIG_RW_B_MEM_OFF, CONFIG_RW_SIZE} + [RO_A] = {"RO_A", CONFIG_RO_MEM_OFF, CONFIG_RO_SIZE}, + [RW_A] = {"RW_A", CONFIG_RW_MEM_OFF, CONFIG_RW_SIZE}, + [RO_B] = {"RO_B", CHIP_RO_B_MEM_OFF, CONFIG_RO_SIZE}, + [RW_B] = {"RW_B", CONFIG_RW_B_MEM_OFF, CONFIG_RW_SIZE} }; /* + * This is set during locate_headers and can be used to fork logic between H1 + * and D2 if needed. + */ +static uint32_t image_magic; + +/* + * Remove these definitions so a developer doesn't accidentally use them in + * the future. All lookups should go through the sections array. + */ +#undef CONFIG_RO_MEM_OFF +#undef CONFIG_RW_MEM_OFF +#undef CHIP_RO_B_MEM_OFF +#undef CONFIG_RW_B_MEM_OFF +#undef CONFIG_RO_SIZE +#undef CONFIG_RW_SIZE +#undef CONFIG_FLASH_SIZE + +/* Returns true if the specified header is valid */ +static bool valid_header(const struct SignedHeader *const h, const size_t size) +{ + if (size < sizeof(struct SignedHeader)) + return false; + + if (h->image_size > size) + return false; + + /* Only H1 and D2 are currently supported. */ + if (h->magic != MAGIC_HAVEN && h->magic != MAGIC_DAUNTLESS) + return false; + + /* + * Both Rx base and Ro base are the memory mapped address, but they + * should have the same offset. The rx section starts after the header. + */ + if (h->rx_base != h->ro_base + sizeof(struct SignedHeader)) + return false; + + /* Ensure each section falls within full size */ + if (h->ro_max - h->ro_base > size) + return false; + + if (h->rx_max - h->rx_base > size) + return false; + + return true; +} + +/* Rounds and address up to the next 16KB boundary if not one already */ +static inline uint32_t round_up_16kb(const uint32_t addr) +{ + const uint32_t mask = (16 * 1024) - 1; + + return (addr + mask) & ~mask; +} + +static const struct SignedHeader *as_header(const void *image, uint32_t offset) +{ + return (void *)((uintptr_t)image + offset); +} + +/* Returns the RW header or -1 if one cannot be found */ +static int32_t find_rw_header(const void *image, uint32_t offset, + const uint32_t end) +{ + offset = round_up_16kb(offset); + + while (offset < end) { + if (valid_header(as_header(image, offset), end - offset)) + return offset; + offset = round_up_16kb(offset + 1); + } + + return -1; +} + +/* Return true if we located headers and set sections correctly */ +static bool locate_headers(const void *image, const uint32_t size) +{ + const uint32_t slot_a_end = size / 2; + const struct SignedHeader *h; + int32_t rw_offset; + + /* + * We assume that all 512KB images are "valid" H1 images. The DBG images + * from the signer do not set the magic to -1 and no not set valid + * section offsets. We do not want to break this case as it is used in + * testing. The H1 offsets are also static, so we don't need to scan + * for RW headers. + */ + if (size == (512 * 1024)) { + image_magic = MAGIC_HAVEN; + /* Leave defaults in sections array untouched */ + return true; + } + + /* + * We know that all other image types supported (i.e. Dauntless) are + * 1MB in size. + */ + if (size != (1024 * 1024)) { + fprintf(stderr, "\nERROR: Image size (%d KB) is invalid\n", + size / 1024); + return false; + } + + /* Validate the RO_A header */ + h = as_header(image, 0); + if (!valid_header(h, slot_a_end)) { + fprintf(stderr, "\nERROR: RO_A header is invalid\n"); + return false; + } + /* Store magic so other logic can fork if needed based on H1/D2 */ + image_magic = h->magic; + sections[RO_A].offset = 0; + sections[RO_A].size = h->image_size; + + /* Find RW_A */ + rw_offset = find_rw_header( + image, sections[RO_A].offset + sections[RO_A].size, slot_a_end); + if (rw_offset == -1) { + fprintf(stderr, "\nERROR: RW_A header cannot be found\n"); + return false; + } + sections[RW_A].offset = rw_offset; + sections[RW_A].size = + round_up_16kb(as_header(image, rw_offset)->image_size); + + /* Validate the RO_B header */ + h = as_header(image, slot_a_end); + if (!valid_header(h, size - slot_a_end)) { + fprintf(stderr, "\nERROR: RO_B header is invalid\n"); + return false; + } + sections[RO_B].offset = slot_a_end; + sections[RO_B].size = h->image_size; + + /* Find RW_B */ + rw_offset = find_rw_header( + image, sections[RO_B].offset + sections[RO_B].size, size); + if (rw_offset == -1) { + fprintf(stderr, "\nERROR: RW_B header cannot be found\n"); + return false; + } + sections[RW_B].offset = rw_offset; + sections[RW_B].size = + round_up_16kb(as_header(image, rw_offset)->image_size); + + /* We found all of the headers and updated offset/size in sections */ + return true; +} + +/* * Scan the new image and retrieve versions of all four sections, two RO and * two RW. */ @@ -992,11 +1155,11 @@ static void pick_sections(struct transfer_descriptor *td) for (i = 0; i < ARRAY_SIZE(sections); i++) { uint32_t offset = sections[i].offset; - if ((offset == CONFIG_RW_MEM_OFF) || - (offset == CONFIG_RW_B_MEM_OFF)) { - - /* Skip currently active section. */ - if (offset != td->rw_offset) + if ((i == RW_A) || (i == RW_B)) { + /* Skip currently active RW section. */ + bool active_rw_slot_b = td->rw_offset < + sections[RO_B].offset; + if ((i == RW_B) == active_rw_slot_b) continue; /* * Ok, this would be the RW section to transfer to the @@ -1011,10 +1174,11 @@ static void pick_sections(struct transfer_descriptor *td) if (a_newer_than_b(§ions[i].shv, &targ.shv[1]) || !td->upstart_mode) sections[i].ustatus = needed; + /* Rest of loop is RO */ continue; } - /* Skip currently active section. */ + /* Skip currently active RO section. */ if (offset != td->ro_offset) continue; /* @@ -1187,18 +1351,28 @@ static void send_done(struct usb_endpoint *uep) */ #define NEXT_SECTION_DELAY 65 -/* Support for flashing RO immediately after RW was added in 0.3.20/0.4.20. */ +/* + * H1 support for flashing RO immediately after RW added in 0.3.20/0.4.20. + * D2 support exists in all versions. + */ static int supports_reordered_section_updates(struct signed_header_version *rw) { - return (rw->epoch || rw->major > 4 || - (rw->major >= 3 && rw->minor >= 20)); + switch (image_magic) { + case MAGIC_HAVEN: + return (rw->epoch || rw->major > 4 || + (rw->major >= 3 && rw->minor >= 20)); + case MAGIC_DAUNTLESS: + return true; + default: + return false; + } } /* Returns number of successfully transmitted image sections. */ static int transfer_image(struct transfer_descriptor *td, uint8_t *data, size_t data_len) { - size_t j; + size_t i; int num_txed_sections = 0; int needs_delay = !supports_reordered_section_updates(&targ.shv[1]); @@ -1207,41 +1381,31 @@ static int transfer_image(struct transfer_descriptor *td, * section is updated before the RO. The array below keeps sections * offsets in the required order. */ - const size_t update_order[] = {CONFIG_RW_MEM_OFF, - CONFIG_RW_B_MEM_OFF, - CONFIG_RO_MEM_OFF, - CHIP_RO_B_MEM_OFF}; - - for (j = 0; j < ARRAY_SIZE(update_order); j++) { - size_t i; + const enum section update_order[] = { RW_A, RW_B, RO_A, RO_B }; - for (i = 0; i < ARRAY_SIZE(sections); i++) { - if (sections[i].offset != update_order[j]) - continue; - - if (sections[i].ustatus != needed) - break; - if (num_txed_sections && needs_delay) { - /* - * Delays more than 5 seconds cause the update - * to timeout. End the update before the delay - * and set it up after to recover from the - * timeout. - */ - if (td->ep_type == usb_xfer) - send_done(&td->uep); - printf("Waiting %ds for %s update.\n", - NEXT_SECTION_DELAY, sections[i].name); - sleep(NEXT_SECTION_DELAY); - setup_connection(td); - } + for (i = 0; i < ARRAY_SIZE(update_order); i++) { + const enum section sect = update_order[i]; - transfer_section(td, - data + sections[i].offset, - sections[i].offset, - sections[i].size); - num_txed_sections++; + if (sections[sect].ustatus != needed) + continue; + if (num_txed_sections && needs_delay) { + /* + * Delays more than 5 seconds cause the update + * to timeout. End the update before the delay + * and set it up after to recover from the + * timeout. + */ + if (td->ep_type == usb_xfer) + send_done(&td->uep); + printf("Waiting %ds for %s update.\n", + NEXT_SECTION_DELAY, sections[sect].name); + sleep(NEXT_SECTION_DELAY); + setup_connection(td); } + + transfer_section(td, data + sections[sect].offset, + sections[sect].offset, sections[sect].size); + num_txed_sections++; } if (!num_txed_sections) @@ -1521,17 +1685,6 @@ static int show_headers_versions(const void *image, bool show_machine_output) * and RW. The 2 slots should have identical FW versions and board * IDs. */ - const struct { - const char *name; - uint32_t offset; - } sections[] = { - /* Slot A. */ - {"RO", CONFIG_RO_MEM_OFF}, - {"RW", CONFIG_RW_MEM_OFF}, - /* Slot B. */ - {"RO", CHIP_RO_B_MEM_OFF}, - {"RW", CONFIG_RW_B_MEM_OFF} - }; const size_t kNumSlots = 2; const size_t kNumSectionsPerSlot = 2; @@ -3061,11 +3214,10 @@ int main(int argc, char *argv[]) data = get_file_or_die(argv[optind], &data_len); printf("read %zd(%#zx) bytes from %s\n", data_len, data_len, argv[optind]); - if (data_len != CONFIG_FLASH_SIZE) { - fprintf(stderr, "Image file is not %d bytes\n", - CONFIG_FLASH_SIZE); + + /* Validate image size and locate headers within image */ + if (!locate_headers(data, data_len)) exit(update_error); - } fetch_header_versions(data); |