summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJett Rink <jettrink@chromium.org>2020-12-10 13:58:35 -0700
committerCommit Bot <commit-bot@chromium.org>2021-01-06 00:31:35 +0000
commitc677f06db48a415c982b10f9dd6a57096cfd7570 (patch)
tree530e7244c288219242359199fed7d4149b332774
parentafa4ade1e47289956506fea5ace9fb7194d001bc (diff)
downloadchrome-ec-c677f06db48a415c982b10f9dd6a57096cfd7570.tar.gz
gsctool: add ti50 image support
Based on the input image, we determine if this is a CR50 image or a Dauntless image. The magic lets us determine which type of image it is. For D2 images, we scan for the RW header instead of using hard coded offsets as this will allow us to change where the RW is located (if RO contracts or expands). BUG=b:172465629 TEST=sent image to D2 from gsctool via CCD TEST=flash new H1 image to volteer using to slot B Signed-off-by: Jett Rink <jettrink@chromium.org> Change-Id: I7554c978a9ba83b423fbaf43c62f9f0d6711d071 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2585926 Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
-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);