Add support for swapping only fw sectors in use
Before this patch, the swapping would process all sectors in a slot
not matter what the size of the binary firmware was. This changes the
swap process to swap only sectors that are in use by firmware.
Also, if the last slot sector, which stores the trailer, is actually not
in use by the binary firmware, now trailer is never written to scratch.
`use_scratch` temp variable was added to boot_status struct to control
this (this var is never written to disk).
Random other small refactorings were applied.
diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c
index cfc9ba5..ba8fa7f 100644
--- a/boot/bootutil/src/bootutil_misc.c
+++ b/boot/bootutil/src/bootutil_misc.c
@@ -166,16 +166,18 @@
boot_slots_trailer_sz(uint8_t min_write_sz)
{
return BOOT_MAGIC_SZ +
+ /* state for all sectors */
BOOT_STATUS_MAX_ENTRIES * BOOT_STATUS_STATE_COUNT * min_write_sz +
+ /* copy_done + image_ok */
min_write_sz * 2;
}
static uint32_t
boot_scratch_trailer_sz(uint8_t min_write_sz)
{
- return BOOT_MAGIC_SZ +
- BOOT_STATUS_STATE_COUNT * min_write_sz +
- min_write_sz * 2;
+ return BOOT_MAGIC_SZ + /* magic */
+ BOOT_STATUS_STATE_COUNT * min_write_sz + /* state for one sector */
+ min_write_sz; /* image_ok */
}
static uint32_t
@@ -205,6 +207,7 @@
static uint32_t
boot_copy_done_off(const struct flash_area *fap)
{
+ assert(fap->fa_id != FLASH_AREA_IMAGE_SCRATCH);
return fap->fa_size - flash_area_align(fap) * 2;
}
@@ -229,10 +232,12 @@
}
state->magic = boot_magic_code(magic);
- off = boot_copy_done_off(fap);
- rc = flash_area_read(fap, off, &state->copy_done, 1);
- if (rc != 0) {
- return BOOT_EFLASH;
+ if (fap->fa_id != FLASH_AREA_IMAGE_SCRATCH) {
+ off = boot_copy_done_off(fap);
+ rc = flash_area_read(fap, off, &state->copy_done, 1);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
}
off = boot_image_ok_off(fap);
@@ -248,56 +253,31 @@
* Reads the image trailer from the scratch area.
*/
int
-boot_read_swap_state_scratch(struct boot_swap_state *state)
+boot_read_swap_state_by_id(int flash_area_id, struct boot_swap_state *state)
{
const struct flash_area *fap;
int rc;
- rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap);
+ switch (flash_area_id) {
+ case FLASH_AREA_IMAGE_SCRATCH:
+ case FLASH_AREA_IMAGE_0:
+ case FLASH_AREA_IMAGE_1:
+ rc = flash_area_open(flash_area_id, &fap);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+ break;
+ default:
+ return BOOT_EFLASH;
+ }
+
+ rc = boot_read_swap_state(fap, state);
if (rc) {
- rc = BOOT_EFLASH;
- goto done;
+ flash_area_close(fap);
+ return rc;
}
- rc = boot_read_swap_state(fap, state);
- if (rc != 0) {
- goto done;
- }
-
- rc = 0;
-
-done:
- flash_area_close(fap);
- return rc;
-}
-
-/**
- * Reads the image trailer from a given image slot.
- */
-int
-boot_read_swap_state_img(int slot, struct boot_swap_state *state)
-{
- const struct flash_area *fap;
- int area_id;
- int rc;
-
- area_id = flash_area_id_from_image_slot(slot);
- rc = flash_area_open(area_id, &fap);
- if (rc != 0) {
- rc = BOOT_EFLASH;
- goto done;
- }
-
- rc = boot_read_swap_state(fap, state);
- if (rc != 0) {
- goto done;
- }
-
- rc = 0;
-
-done:
- flash_area_close(fap);
- return rc;
+ return 0;
}
int
@@ -316,15 +296,24 @@
return 0;
}
-int
-boot_write_copy_done(const struct flash_area *fap)
+static int
+boot_write_flag(int flag, const struct flash_area *fap)
{
uint32_t off;
int rc;
uint8_t buf[BOOT_MAX_ALIGN];
uint8_t align;
- off = boot_copy_done_off(fap);
+ switch (flag) {
+ case BOOT_FLAG_COPY_DONE:
+ off = boot_copy_done_off(fap);
+ break;
+ case BOOT_FLAG_IMAGE_OK:
+ off = boot_image_ok_off(fap);
+ break;
+ default:
+ return BOOT_EFLASH;
+ }
align = hal_flash_align(fap->fa_device_id);
assert(align <= BOOT_MAX_ALIGN);
@@ -340,26 +329,15 @@
}
int
+boot_write_copy_done(const struct flash_area *fap)
+{
+ return boot_write_flag(BOOT_FLAG_COPY_DONE, fap);
+}
+
+int
boot_write_image_ok(const struct flash_area *fap)
{
- uint32_t off;
- int rc;
- uint8_t buf[BOOT_MAX_ALIGN];
- uint8_t align;
-
- off = boot_image_ok_off(fap);
-
- align = hal_flash_align(fap->fa_device_id);
- assert(align <= BOOT_MAX_ALIGN);
- memset(buf, 0xFF, BOOT_MAX_ALIGN);
- buf[0] = 1;
-
- rc = flash_area_write(fap, off, buf, align);
- if (rc != 0) {
- return BOOT_EFLASH;
- }
-
- return 0;
+ return boot_write_flag(BOOT_FLAG_IMAGE_OK, fap);
}
int
@@ -371,10 +349,10 @@
int rc;
int i;
- rc = boot_read_swap_state_img(0, &state_slot0);
+ rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_0, &state_slot0);
assert(rc == 0);
- rc = boot_read_swap_state_img(1, &state_slot1);
+ rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_1, &state_slot1);
assert(rc == 0);
for (i = 0; i < BOOT_SWAP_TABLES_COUNT; i++) {
@@ -419,10 +397,9 @@
{
const struct flash_area *fap;
struct boot_swap_state state_slot1;
- int area_id;
int rc;
- rc = boot_read_swap_state_img(1, &state_slot1);
+ rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_1, &state_slot1);
if (rc != 0) {
return rc;
}
@@ -433,8 +410,7 @@
return 0;
case BOOT_MAGIC_UNSET:
- area_id = flash_area_id_from_image_slot(1);
- rc = flash_area_open(area_id, &fap);
+ rc = flash_area_open(FLASH_AREA_IMAGE_1, &fap);
if (rc != 0) {
rc = BOOT_EFLASH;
} else {
@@ -467,7 +443,7 @@
struct boot_swap_state state_slot0;
int rc;
- rc = boot_read_swap_state_img(0, &state_slot0);
+ rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_0, &state_slot0);
if (rc != 0) {
return rc;
}
diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h
index 6de8d4e..45ce625 100644
--- a/boot/bootutil/src/bootutil_priv.h
+++ b/boot/bootutil/src/bootutil_priv.h
@@ -45,8 +45,9 @@
* Maintain state of copy progress.
*/
struct boot_status {
- uint32_t idx; /* Which area we're operating on */
- uint8_t state; /* Which part of the swapping process are we at */
+ uint32_t idx; /* Which area we're operating on */
+ uint8_t state; /* Which part of the swapping process are we at */
+ uint8_t use_scratch; /* Are status bytes ever written to scratch? */
};
#define BOOT_MAGIC_GOOD 1
@@ -86,6 +87,9 @@
#define BOOT_STATUS_SOURCE_SCRATCH 1
#define BOOT_STATUS_SOURCE_SLOT0 2
+#define BOOT_FLAG_IMAGE_OK 0
+#define BOOT_FLAG_COPY_DONE 1
+
extern const uint32_t BOOT_MAGIC_SZ;
int bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, int slen,
@@ -95,8 +99,8 @@
uint32_t boot_status_off(const struct flash_area *fap);
int boot_read_swap_state(const struct flash_area *fap,
struct boot_swap_state *state);
-int boot_read_swap_state_img(int slot, struct boot_swap_state *state);
-int boot_read_swap_state_scratch(struct boot_swap_state *state);
+int boot_read_swap_state_by_id(int flash_area_id,
+ struct boot_swap_state *state);
int boot_write_magic(const struct flash_area *fap);
int boot_write_status(struct boot_status *bs);
int boot_schedule_test_swap(void);
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index 604f790..782ab6c 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -179,10 +179,10 @@
int i;
uint8_t source;
- rc = boot_read_swap_state_img(0, &state_slot0);
+ rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_0, &state_slot0);
assert(rc == 0);
- rc = boot_read_swap_state_scratch(&state_scratch);
+ rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH, &state_scratch);
assert(rc == 0);
BOOT_LOG_SWAP_STATE("Image 0", &state_slot0);
@@ -371,7 +371,6 @@
boot_status_internal_off(int idx, int state, int elem_sz)
{
int idx_sz;
- uint32_t status_off;
idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
@@ -418,6 +417,7 @@
if (found) {
i--;
+ /* FIXME: is this test required? */
if (fap->fa_id != FLASH_AREA_IMAGE_SCRATCH) {
bs->idx = i / BOOT_STATUS_STATE_COUNT;
bs->state = i % BOOT_STATUS_STATE_COUNT;
@@ -495,7 +495,7 @@
* two status writes go to the scratch which will be copied to SLOT 0!
*/
- if (bs->idx == 0) {
+ if (bs->use_scratch) {
/* Write to scratch. */
area_id = FLASH_AREA_IMAGE_SCRATCH;
} else {
@@ -785,6 +785,61 @@
return rc;
}
+static inline int
+boot_status_init_by_id(int flash_area_id)
+{
+ const struct flash_area *fap;
+ struct boot_swap_state swap_state;
+ int rc;
+
+ rc = flash_area_open(flash_area_id, &fap);
+ assert(rc == 0);
+
+ rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_1, &swap_state);
+ assert(rc == 0);
+
+ if (swap_state.image_ok == 0x01) {
+ rc = boot_write_image_ok(fap);
+ assert(rc == 0);
+ }
+
+ rc = boot_write_magic(fap);
+ assert(rc == 0);
+
+ flash_area_close(fap);
+
+ return 0;
+}
+
+static int
+boot_erase_last_sector_by_id(int flash_area_id)
+{
+ uint8_t slot;
+ uint32_t last_sector;
+ struct flash_area *sectors;
+ int rc;
+
+ switch (flash_area_id) {
+ case FLASH_AREA_IMAGE_0:
+ slot = 0;
+ break;
+ case FLASH_AREA_IMAGE_1:
+ slot = 1;
+ break;
+ default:
+ return BOOT_EFLASH;
+ }
+
+ last_sector = boot_data.imgs[slot].num_sectors - 1;
+ sectors = boot_data.imgs[slot].sectors;
+ rc = boot_erase_sector(flash_area_id,
+ sectors[last_sector].fa_off - sectors[0].fa_off,
+ sectors[last_sector].fa_size);
+ assert(rc == 0);
+
+ return rc;
+}
+
/**
* Swaps the contents of two flash regions within the two image slots.
*
@@ -820,6 +875,8 @@
copy_sz -= trailer_sz;
}
+ bs->use_scratch = (bs->idx == 0 && copy_sz != sz);
+
if (bs->state == 0) {
rc = boot_erase_sector(FLASH_AREA_IMAGE_SCRATCH, 0, sz);
assert(rc == 0);
@@ -828,22 +885,19 @@
img_off, 0, copy_sz);
assert(rc == 0);
- if (copy_sz != sz) {
- rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap);
- assert(rc == 0);
-
- rc = boot_read_swap_state_img(1, &swap_state);
- assert(rc == 0);
-
- if (swap_state.image_ok == 0x01) {
- rc = boot_write_image_ok(fap);
+ if (bs->idx == 0) {
+ if (bs->use_scratch) {
+ boot_status_init_by_id(FLASH_AREA_IMAGE_SCRATCH);
+ } else {
+ /* Prepare the status area... here it is known that the
+ * last sector is not being used by the image data so it's
+ * safe to erase.
+ */
+ rc = boot_erase_last_sector_by_id(FLASH_AREA_IMAGE_0);
assert(rc == 0);
+
+ boot_status_init_by_id(FLASH_AREA_IMAGE_0);
}
-
- rc = boot_write_magic(fap);
- assert(rc == 0);
-
- flash_area_close(fap);
}
bs->state = 1;
@@ -859,6 +913,14 @@
img_off, img_off, copy_sz);
assert(rc == 0);
+ if (bs->idx == 0 && !bs->use_scratch) {
+ /* If not all sectors of the slot are being swapped,
+ * guarantee here that only slot0 will have the state.
+ */
+ rc = boot_erase_last_sector_by_id(FLASH_AREA_IMAGE_1);
+ assert(rc == 0);
+ }
+
bs->state = 2;
rc = boot_write_status(bs);
assert(rc == 0);
@@ -873,7 +935,7 @@
0, img_off, copy_sz);
assert(rc == 0);
- if (copy_sz != sz) {
+ if (bs->idx == 0 && bs->use_scratch) {
rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap);
assert(rc == 0);
@@ -891,7 +953,8 @@
BOOT_STATUS_STATE_COUNT * boot_data.write_sz);
assert(rc == 0);
- rc = boot_read_swap_state_scratch(&swap_state);
+ rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH,
+ &swap_state);
assert(rc == 0);
if (swap_state.image_ok == 0x01) {
@@ -907,6 +970,7 @@
bs->idx++;
bs->state = 0;
+ bs->use_scratch = 0;
rc = boot_write_status(bs);
assert(rc == 0);
}
@@ -970,9 +1034,55 @@
int first_sector_idx;
int last_sector_idx;
int swap_idx;
+ struct image_header *hdr;
+ uint32_t size;
+ uint32_t copy_size;
+ struct image_header tmp_hdr;
+ int rc;
+
+ /* FIXME: just do this if asked by user? */
+
+ size = copy_size = 0;
+
+ hdr = &boot_data.imgs[0].hdr;
+ if (hdr->ih_magic == IMAGE_MAGIC) {
+ copy_size = hdr->ih_hdr_size + hdr->ih_img_size + hdr->ih_tlv_size;
+ }
+
+ hdr = &boot_data.imgs[1].hdr;
+ if (hdr->ih_magic == IMAGE_MAGIC) {
+ size = hdr->ih_hdr_size + hdr->ih_img_size + hdr->ih_tlv_size;
+ }
+
+ if (!size || !copy_size || size == copy_size) {
+ rc = boot_read_image_header(2, &tmp_hdr);
+ assert(rc == 0);
+
+ hdr = &tmp_hdr;
+ if (hdr->ih_magic == IMAGE_MAGIC) {
+ if (!size) {
+ size = hdr->ih_hdr_size + hdr->ih_img_size + hdr->ih_tlv_size;
+ } else {
+ copy_size = hdr->ih_hdr_size + hdr->ih_img_size + hdr->ih_tlv_size;
+ }
+ }
+ }
+
+ if (size > copy_size) {
+ copy_size = size;
+ }
+
+ size = 0;
+ last_sector_idx = 0;
+ while (1) {
+ size += boot_data.imgs[0].sectors[last_sector_idx].fa_size;
+ if (size >= copy_size) {
+ break;
+ }
+ last_sector_idx++;
+ }
swap_idx = 0;
- last_sector_idx = boot_data.imgs[0].num_sectors - 1;
while (last_sector_idx >= 0) {
sz = boot_copy_sz(last_sector_idx, &first_sector_idx);
if (swap_idx >= bs->idx) {