summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/g/signed_header.h3
-rw-r--r--extra/usb_updater/gsctool.c276
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(&sections[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);