bootutil: use flash_area_get_sectors()
The flash map API has added a new routine, flash_area_get_sectors().
Use that instead of flash_area_to_sectors(), which is now deprecated.
This exposed a bug in boot_swap_sectors() (a large sz would still lead
to copying past the end of the area, including the trailer); fix it.
This also exposed a bug in split_go(). Calling boot_read_sectors()
there makes a potentially invalid assumption, namely:
(loader_slot == FLASH_AREA_IMAGE_0 &&
split_slot == FLASH_AREA_IMAGE_1)
We make this slightly better by making sure that loader_slot and
split_slot in split_go() get placed into boot_data at indices
mynewt-core currently sets LOADER_SLOT and SPLIT_SLOT to, but a real
fix is left to future work.
This patch also frees up about 650B of RAM, since struct flash_sector
is smaller than struct flash_area.
Signed-off-by: Marti Bolivar <marti.bolivar@linaro.org>
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index da72f78..788992b 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -39,6 +39,7 @@
#include "bootutil/bootutil_log.h"
#define BOOT_MAX_IMG_SECTORS 120
+#define BOOT_MAX_SCRATCH_SECTORS 35
/** Number of image slots in flash; currently limited to two. */
#define BOOT_NUM_SLOTS 2
@@ -46,11 +47,14 @@
static struct {
struct {
struct image_header hdr;
- struct flash_area *sectors;
- int num_sectors;
+ const struct flash_area *area;
+ struct flash_sector *sectors;
+ uint32_t num_sectors;
} imgs[BOOT_NUM_SLOTS];
- struct flash_area scratch_sector;
+ const struct flash_area *scratch_area;
+ struct flash_sector *scratch_sectors;
+ uint32_t scratch_num_sectors;
uint8_t write_sz;
} boot_data;
@@ -300,8 +304,8 @@
* on what the minimum write size is for scratch area, active image slot.
* We need to use the bigger of those 2 values.
*/
- elem_sz = hal_flash_align(boot_data.imgs[0].sectors[0].fa_device_id);
- align = hal_flash_align(boot_data.scratch_sector.fa_device_id);
+ elem_sz = hal_flash_align(boot_data.imgs[0].area->fa_device_id);
+ align = hal_flash_align(boot_data.scratch_area->fa_device_id);
if (align > elem_sz) {
elem_sz = align;
}
@@ -312,8 +316,8 @@
static int
boot_slots_compatible(void)
{
- const struct flash_area *sector0;
- const struct flash_area *sector1;
+ const struct flash_sector *sector0;
+ const struct flash_sector *sector1;
int i;
/* Ensure both image slots have identical sector layouts. */
@@ -323,7 +327,7 @@
for (i = 0; i < boot_data.imgs[0].num_sectors; i++) {
sector0 = boot_data.imgs[0].sectors + i;
sector1 = boot_data.imgs[1].sectors + i;
- if (sector0->fa_size != sector1->fa_size) {
+ if (sector0->fs_size != sector1->fs_size) {
return 0;
}
}
@@ -340,32 +344,34 @@
static int
boot_read_sectors(void)
{
- const struct flash_area *scratch;
- int num_sectors_slot0;
- int num_sectors_slot1;
+ uint32_t num_sectors_slot0;
+ uint32_t num_sectors_slot1;
+ uint32_t num_sectors_scratch;
int rc;
num_sectors_slot0 = BOOT_MAX_IMG_SECTORS;
- rc = flash_area_to_sectors(FLASH_AREA_IMAGE_0, &num_sectors_slot0,
- boot_data.imgs[0].sectors);
+ rc = flash_area_get_sectors(FLASH_AREA_IMAGE_0, &num_sectors_slot0,
+ boot_data.imgs[0].sectors);
if (rc != 0) {
return BOOT_EFLASH;
}
boot_data.imgs[0].num_sectors = num_sectors_slot0;
num_sectors_slot1 = BOOT_MAX_IMG_SECTORS;
- rc = flash_area_to_sectors(FLASH_AREA_IMAGE_1, &num_sectors_slot1,
- boot_data.imgs[1].sectors);
+ rc = flash_area_get_sectors(FLASH_AREA_IMAGE_1, &num_sectors_slot1,
+ boot_data.imgs[1].sectors);
if (rc != 0) {
return BOOT_EFLASH;
}
boot_data.imgs[1].num_sectors = num_sectors_slot1;
- rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &scratch);
+ num_sectors_scratch = BOOT_MAX_SCRATCH_SECTORS;
+ rc = flash_area_get_sectors(FLASH_AREA_IMAGE_SCRATCH, &num_sectors_scratch,
+ boot_data.scratch_sectors);
if (rc != 0) {
return BOOT_EFLASH;
}
- boot_data.scratch_sector = *scratch;
+ boot_data.scratch_num_sectors = num_sectors_scratch;
boot_data.write_sz = boot_write_sz();
@@ -654,8 +660,8 @@
sz = 0;
for (i = last_sector_idx; i >= 0; i--) {
- new_sz = sz + boot_data.imgs[0].sectors[i].fa_size;
- if (new_sz > boot_data.scratch_sector.fa_size) {
+ new_sz = sz + boot_data.imgs[0].sectors[i].fs_size;
+ if (new_sz > boot_data.scratch_area->fa_size) {
break;
}
sz = new_sz;
@@ -792,11 +798,12 @@
{
uint32_t copy_sz;
uint32_t img_off;
+ uint32_t max_off;
int rc;
- /* Calculate offset from start of image area. */
- img_off = boot_data.imgs[0].sectors[idx].fa_off -
- boot_data.imgs[0].sectors[0].fa_off;
+ /* Sector offsets are from start of image area. */
+ img_off = boot_data.imgs[0].sectors[idx].fs_off;
+ max_off = boot_data.imgs[0].area->fa_size;
if (bs->state == 0) {
rc = boot_erase_sector(FLASH_AREA_IMAGE_SCRATCH, 0, sz);
@@ -815,13 +822,14 @@
assert(rc == 0);
copy_sz = sz;
- if (boot_data.imgs[0].sectors[idx].fa_off + sz >=
- boot_data.imgs[1].sectors[0].fa_off) {
-
- /* This is the end of the area. Don't copy the image state into
+ if (img_off >= max_off - sz) {
+ /*
+ * This is the end of the area. Don't copy the image state into
* slot 1.
*/
- copy_sz -= boot_trailer_sz(boot_data.write_sz);
+ BOOT_LOG_WRN("long write: idx=%d (off=0x%x), size=%u, max_off=%u",
+ idx, img_off, sz, max_off);
+ copy_sz = max_off - img_off - boot_trailer_sz(boot_data.write_sz);
}
rc = boot_copy_sector(FLASH_AREA_IMAGE_0, FLASH_AREA_IMAGE_1,
@@ -1012,27 +1020,40 @@
int swap_type;
int slot;
int rc;
+ int fa_id;
/* The array of slot sectors are defined here (as opposed to file scope) so
* that they don't get allocated for non-boot-loader apps. This is
* necessary because the gcc option "-fdata-sections" doesn't seem to have
* any effect in older gcc versions (e.g., 4.8.4).
*/
- static struct flash_area slot0_sectors[BOOT_MAX_IMG_SECTORS];
- static struct flash_area slot1_sectors[BOOT_MAX_IMG_SECTORS];
+ static struct flash_sector slot0_sectors[BOOT_MAX_IMG_SECTORS];
+ static struct flash_sector slot1_sectors[BOOT_MAX_IMG_SECTORS];
+ static struct flash_sector scratch_sectors[BOOT_MAX_SCRATCH_SECTORS];
+
boot_data.imgs[0].sectors = slot0_sectors;
boot_data.imgs[1].sectors = slot1_sectors;
+ boot_data.scratch_sectors = scratch_sectors;
+
+ /* Open boot_data's flash areas for use in this file. */
+ for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
+ fa_id = flash_area_id_from_image_slot(slot);
+ rc = flash_area_open(fa_id, &boot_data.imgs[slot].area);
+ assert(rc == 0);
+ }
+ rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &boot_data.scratch_area);
+ assert(rc == 0);
/* Determine the sector layout of the image slots and scratch area. */
rc = boot_read_sectors();
if (rc != 0) {
- return rc;
+ goto out;
}
/* Attempt to read an image header from each slot. */
rc = boot_read_image_headers();
if (rc != 0) {
- return rc;
+ goto out;
}
/* If the image slots aren't compatible, no swap is possible. Just boot
@@ -1041,7 +1062,7 @@
if (boot_slots_compatible()) {
rc = boot_swap_if_needed(&swap_type);
if (rc != 0) {
- return rc;
+ goto out;
}
} else {
swap_type = BOOT_SWAP_TYPE_NONE;
@@ -1052,7 +1073,8 @@
#ifdef BOOTUTIL_VALIDATE_SLOT0
rc = boot_validate_slot(0);
if (rc != 0) {
- return BOOT_EBADIMAGE;
+ rc = BOOT_EBADIMAGE;
+ goto out;
}
#endif
slot = 0;
@@ -1085,82 +1107,104 @@
}
/* Always boot from the primary slot. */
- rsp->br_flash_id = boot_data.imgs[0].sectors[0].fa_device_id;
- rsp->br_image_addr = boot_data.imgs[0].sectors[0].fa_off;
+ rsp->br_flash_id = boot_data.imgs[0].area->fa_device_id;
+ rsp->br_image_addr = boot_data.imgs[0].area->fa_off;
rsp->br_hdr = &boot_data.imgs[slot].hdr;
+ out:
+ /*
+ * We're done using the flash areas now; the boot response
+ * contains the information the caller needs.
+ */
+ for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
+ flash_area_close(boot_data.imgs[slot].area);
+ }
+ flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &boot_data.scratch_area);
+
return 0;
}
int
split_go(int loader_slot, int split_slot, void **entry)
{
- const struct flash_area *loader_fap;
- const struct flash_area *app_fap;
- struct flash_area *sectors;
+ struct flash_sector *sectors;
uintptr_t entry_val;
int loader_flash_id;
- int app_flash_id;
+ int split_flash_id;
+ uint32_t loader_num_sectors;
+ uint32_t split_num_sectors;
int rc;
- app_fap = NULL;
- loader_fap = NULL;
-
sectors = malloc(BOOT_MAX_IMG_SECTORS * 2 * sizeof *sectors);
if (sectors == NULL) {
rc = SPLIT_GO_ERR;
goto done;
}
- boot_data.imgs[0].sectors = sectors + 0;
- boot_data.imgs[1].sectors = sectors + BOOT_MAX_IMG_SECTORS;
+ boot_data.imgs[loader_slot].sectors = sectors + 0;
+ boot_data.imgs[split_slot].sectors = sectors + BOOT_MAX_IMG_SECTORS;
- /* Determine the sector layout of the image slots and scratch area. */
- rc = boot_read_sectors();
+ loader_flash_id = flash_area_id_from_image_slot(loader_slot);
+ rc = flash_area_open(loader_flash_id, &boot_data.imgs[loader_slot].area);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ split_flash_id = flash_area_id_from_image_slot(split_slot);
+ rc = flash_area_open(split_flash_id, &boot_data.imgs[split_slot].area);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ /* Determine the sector layout of the image slots.
+ *
+ * A scratch area is not meaningful for split booting, so don't
+ * try to initialize it.
+ */
+ loader_num_sectors = BOOT_MAX_IMG_SECTORS;
+ rc = flash_area_get_sectors(loader_flash_id, &loader_num_sectors,
+ boot_data.imgs[loader_slot].sectors);
if (rc != 0) {
rc = SPLIT_GO_ERR;
goto done;
}
+ boot_data.imgs[loader_slot].num_sectors = loader_num_sectors;
+ split_num_sectors = BOOT_MAX_IMG_SECTORS;
+ rc = flash_area_get_sectors(split_flash_id, &split_num_sectors,
+ boot_data.imgs[split_slot].sectors);
+ if (rc != 0) {
+ rc = SPLIT_GO_ERR;
+ goto done;
+ }
+ boot_data.imgs[split_slot].num_sectors = split_num_sectors;
rc = boot_read_image_headers();
if (rc != 0) {
goto done;
}
- app_flash_id = flash_area_id_from_image_slot(split_slot);
- rc = flash_area_open(app_flash_id, &app_fap);
- if (rc != 0) {
- rc = BOOT_EFLASH;
- goto done;
- }
-
- loader_flash_id = flash_area_id_from_image_slot(loader_slot);
- rc = flash_area_open(loader_flash_id, &loader_fap);
- if (rc != 0) {
- rc = BOOT_EFLASH;
- goto done;
- }
-
/* Don't check the bootable image flag because we could really call a
* bootable or non-bootable image. Just validate that the image check
* passes which is distinct from the normal check.
*/
rc = split_image_check(&boot_data.imgs[split_slot].hdr,
- app_fap,
+ boot_data.imgs[split_slot].area,
&boot_data.imgs[loader_slot].hdr,
- loader_fap);
+ boot_data.imgs[loader_slot].area);
if (rc != 0) {
rc = SPLIT_GO_NON_MATCHING;
goto done;
}
- entry_val = boot_data.imgs[split_slot].sectors[0].fa_off +
+ entry_val = boot_data.imgs[split_slot].area->fa_off +
boot_data.imgs[split_slot].hdr.ih_hdr_size;
*entry = (void *) entry_val;
rc = SPLIT_GO_OK;
done:
free(sectors);
- flash_area_close(app_fap);
- flash_area_close(loader_fap);
+ flash_area_close(boot_data.imgs[loader_slot].area);
+ flash_area_close(boot_data.imgs[split_slot].area);
return rc;
}