boot: bootutil: move scratch swap functionality
This moves the functionality that is unique to a scratch based swap
upgrade into a separate file. Later other upgrade strategies can be
added by reimplementing those functions.
Signed-off-by: Fabio Utzig <utzig@apache.org>
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index 43bc487..1635efc 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -36,6 +36,7 @@
#include "bootutil/bootutil.h"
#include "bootutil/image.h"
#include "bootutil_priv.h"
+#include "swap_priv.h"
#include "bootutil/bootutil_log.h"
#ifdef MCUBOOT_ENC_IMAGES
@@ -67,181 +68,6 @@
#define TARGET_STATIC
#endif
-#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) && !defined(MCUBOOT_OVERWRITE_ONLY)
-/*
- * FIXME: this might have to be updated for threaded sim
- */
-static int boot_status_fails = 0;
-#define BOOT_STATUS_ASSERT(x) \
- do { \
- if (!(x)) { \
- boot_status_fails++; \
- } \
- } while (0)
-#else
-#define BOOT_STATUS_ASSERT(x) ASSERT(x)
-#endif
-
-struct boot_status_table {
- uint8_t bst_magic_primary_slot;
- uint8_t bst_magic_scratch;
- uint8_t bst_copy_done_primary_slot;
- uint8_t bst_status_source;
-};
-
-/**
- * This set of tables maps swap state contents to boot status location.
- * When searching for a match, these tables must be iterated in order.
- */
-static const struct boot_status_table boot_status_tables[] = {
- {
- /* | primary slot | scratch |
- * ----------+--------------+--------------|
- * magic | Good | Any |
- * copy-done | Set | N/A |
- * ----------+--------------+--------------'
- * source: none |
- * ----------------------------------------'
- */
- .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
- .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
- .bst_copy_done_primary_slot = BOOT_FLAG_SET,
- .bst_status_source = BOOT_STATUS_SOURCE_NONE,
- },
-
- {
- /* | primary slot | scratch |
- * ----------+--------------+--------------|
- * magic | Good | Any |
- * copy-done | Unset | N/A |
- * ----------+--------------+--------------'
- * source: primary slot |
- * ----------------------------------------'
- */
- .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
- .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
- .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
- .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
- },
-
- {
- /* | primary slot | scratch |
- * ----------+--------------+--------------|
- * magic | Any | Good |
- * copy-done | Any | N/A |
- * ----------+--------------+--------------'
- * source: scratch |
- * ----------------------------------------'
- */
- .bst_magic_primary_slot = BOOT_MAGIC_ANY,
- .bst_magic_scratch = BOOT_MAGIC_GOOD,
- .bst_copy_done_primary_slot = BOOT_FLAG_ANY,
- .bst_status_source = BOOT_STATUS_SOURCE_SCRATCH,
- },
- {
- /* | primary slot | scratch |
- * ----------+--------------+--------------|
- * magic | Unset | Any |
- * copy-done | Unset | N/A |
- * ----------+--------------+--------------|
- * source: varies |
- * ----------------------------------------+--------------------------+
- * This represents one of two cases: |
- * o No swaps ever (no status to read, so no harm in checking). |
- * o Mid-revert; status in primary slot. |
- * -------------------------------------------------------------------'
- */
- .bst_magic_primary_slot = BOOT_MAGIC_UNSET,
- .bst_magic_scratch = BOOT_MAGIC_ANY,
- .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
- .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
- },
-};
-
-#define BOOT_STATUS_TABLES_COUNT \
- (sizeof boot_status_tables / sizeof boot_status_tables[0])
-
-#define BOOT_LOG_SWAP_STATE(area, state) \
- BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, " \
- "image_ok=0x%x", \
- (area), \
- ((state)->magic == BOOT_MAGIC_GOOD ? "good" : \
- (state)->magic == BOOT_MAGIC_UNSET ? "unset" : \
- "bad"), \
- (state)->swap_type, \
- (state)->copy_done, \
- (state)->image_ok)
-
-/**
- * Determines where in flash the most recent boot status is stored. The boot
- * status is necessary for completing a swap that was interrupted by a boot
- * loader reset.
- *
- * @return A BOOT_STATUS_SOURCE_[...] code indicating where status should
- * be read from.
- */
-static int
-boot_status_source(struct boot_loader_state *state)
-{
- const struct boot_status_table *table;
- struct boot_swap_state state_scratch;
- struct boot_swap_state state_primary_slot;
- int rc;
- size_t i;
- uint8_t source;
- uint8_t image_index;
-
-#if (BOOT_IMAGE_NUMBER == 1)
- (void)state;
-#endif
-
- image_index = BOOT_CURR_IMG(state);
- rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index),
- &state_primary_slot);
- assert(rc == 0);
-
- rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH, &state_scratch);
- assert(rc == 0);
-
- BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot);
- BOOT_LOG_SWAP_STATE("Scratch", &state_scratch);
-
- for (i = 0; i < BOOT_STATUS_TABLES_COUNT; i++) {
- table = &boot_status_tables[i];
-
- if (boot_magic_compatible_check(table->bst_magic_primary_slot,
- state_primary_slot.magic) &&
- boot_magic_compatible_check(table->bst_magic_scratch,
- state_scratch.magic) &&
- (table->bst_copy_done_primary_slot == BOOT_FLAG_ANY ||
- table->bst_copy_done_primary_slot == state_primary_slot.copy_done))
- {
- source = table->bst_status_source;
-
-#if (BOOT_IMAGE_NUMBER > 1)
- /* In case of multi-image boot it can happen that if boot status
- * info is found on scratch area then it does not belong to the
- * currently examined image.
- */
- if (source == BOOT_STATUS_SOURCE_SCRATCH &&
- state_scratch.image_num != BOOT_CURR_IMG(state)) {
- source = BOOT_STATUS_SOURCE_NONE;
- }
-#endif
-
- BOOT_LOG_INF("Boot source: %s",
- source == BOOT_STATUS_SOURCE_NONE ? "none" :
- source == BOOT_STATUS_SOURCE_SCRATCH ? "scratch" :
- source == BOOT_STATUS_SOURCE_PRIMARY_SLOT ?
- "primary slot" : "BUG; can't happen");
- return source;
- }
- }
-
- BOOT_LOG_INF("Boot source: none");
- return BOOT_STATUS_SOURCE_NONE;
-}
-
/*
* Compute the total size of the given image. Includes the size of
* the TLVs.
@@ -306,45 +132,14 @@
#endif /* !MCUBOOT_OVERWRITE_ONLY */
static int
-boot_read_image_header(struct boot_loader_state *state, int slot,
- struct image_header *out_hdr)
-{
- const struct flash_area *fap;
- int area_id;
- int rc;
-
-#if (BOOT_IMAGE_NUMBER == 1)
- (void)state;
-#endif
-
- area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot);
- rc = flash_area_open(area_id, &fap);
- if (rc != 0) {
- rc = BOOT_EFLASH;
- goto done;
- }
-
- rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr);
- if (rc != 0) {
- rc = BOOT_EFLASH;
- goto done;
- }
-
- rc = 0;
-
-done:
- flash_area_close(fap);
- return rc;
-}
-
-static int
-boot_read_image_headers(struct boot_loader_state *state, bool require_all)
+boot_read_image_headers(struct boot_loader_state *state, bool require_all,
+ struct boot_status *bs)
{
int rc;
int i;
for (i = 0; i < BOOT_NUM_SLOTS; i++) {
- rc = boot_read_image_header(state, i, boot_img_hdr(state, i));
+ rc = boot_read_image_header(state, i, boot_img_hdr(state, i), bs);
if (rc != 0) {
/* If `require_all` is set, fail on any single fail, otherwise
* if at least the first slot's header was read successfully,
@@ -367,111 +162,25 @@
boot_write_sz(struct boot_loader_state *state)
{
uint32_t elem_sz;
+#if MCUBOOT_SWAP_USING_SCRATCH
uint32_t align;
+#endif
/* Figure out what size to write update status update as. The size depends
* 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 = flash_area_align(BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT));
+#if MCUBOOT_SWAP_USING_SCRATCH
align = flash_area_align(BOOT_SCRATCH_AREA(state));
if (align > elem_sz) {
elem_sz = align;
}
+#endif
return elem_sz;
}
-/*
- * Slots are compatible when all sectors that store up to to size of the image
- * round up to sector size, in both slot's are able to fit in the scratch
- * area, and have sizes that are a multiple of each other (powers of two
- * presumably!).
- */
-static int
-boot_slots_compatible(struct boot_loader_state *state)
-{
- size_t num_sectors_primary;
- size_t num_sectors_secondary;
- size_t sz0, sz1;
- size_t primary_slot_sz, secondary_slot_sz;
- size_t scratch_sz;
- size_t i, j;
- int8_t smaller;
-
- num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
- num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
- if ((num_sectors_primary > BOOT_MAX_IMG_SECTORS) ||
- (num_sectors_secondary > BOOT_MAX_IMG_SECTORS)) {
- BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
- return 0;
- }
-
- scratch_sz = boot_scratch_area_size(state);
-
- /*
- * The following loop scans all sectors in a linear fashion, assuring that
- * for each possible sector in each slot, it is able to fit in the other
- * slot's sector or sectors. Slot's should be compatible as long as any
- * number of a slot's sectors are able to fit into another, which only
- * excludes cases where sector sizes are not a multiple of each other.
- */
- i = sz0 = primary_slot_sz = 0;
- j = sz1 = secondary_slot_sz = 0;
- smaller = 0;
- while (i < num_sectors_primary || j < num_sectors_secondary) {
- if (sz0 == sz1) {
- sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
- sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
- i++;
- j++;
- } else if (sz0 < sz1) {
- sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
- /* Guarantee that multiple sectors of the secondary slot
- * fit into the primary slot.
- */
- if (smaller == 2) {
- BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
- return 0;
- }
- smaller = 1;
- i++;
- } else {
- sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
- /* Guarantee that multiple sectors of the primary slot
- * fit into the secondary slot.
- */
- if (smaller == 1) {
- BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
- return 0;
- }
- smaller = 2;
- j++;
- }
- if (sz0 == sz1) {
- primary_slot_sz += sz0;
- secondary_slot_sz += sz1;
- /* Scratch has to fit each swap operation to the size of the larger
- * sector among the primary slot and the secondary slot.
- */
- if (sz0 > scratch_sz || sz1 > scratch_sz) {
- BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch");
- return 0;
- }
- smaller = sz0 = sz1 = 0;
- }
- }
-
- if ((i != num_sectors_primary) ||
- (j != num_sectors_secondary) ||
- (primary_slot_sz != secondary_slot_sz)) {
- BOOT_LOG_WRN("Cannot upgrade: slots are not compatible");
- return 0;
- }
-
- return 1;
-}
-
#ifndef MCUBOOT_USE_FLASH_AREA_GET_SECTORS
static int
boot_initialize_area(struct boot_loader_state *state, int flash_area)
@@ -489,10 +198,13 @@
BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors);
BOOT_IMG(state, BOOT_SECONDARY_SLOT).num_sectors = (size_t)num_sectors;
+#if MCUBOOT_SWAP_USING_SCRATCH
} else if (flash_area == FLASH_AREA_IMAGE_SCRATCH) {
rc = flash_area_to_sectors(flash_area, &num_sectors,
state->scratch.sectors);
state->scratch.num_sectors = (size_t)num_sectors;
+#endif
+
} else {
return BOOT_EFLASH;
}
@@ -516,9 +228,11 @@
} else if (flash_area == FLASH_AREA_IMAGE_SECONDARY(BOOT_CURR_IMG(state))) {
out_sectors = BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors;
out_num_sectors = &BOOT_IMG(state, BOOT_SECONDARY_SLOT).num_sectors;
+#if MCUBOOT_SWAP_USING_SCRATCH
} else if (flash_area == FLASH_AREA_IMAGE_SCRATCH) {
out_sectors = state->scratch.sectors;
out_num_sectors = &state->scratch.num_sectors;
+#endif
} else {
return BOOT_EFLASH;
}
@@ -556,163 +270,31 @@
return BOOT_EFLASH;
}
+#if MCUBOOT_SWAP_USING_SCRATCH
rc = boot_initialize_area(state, FLASH_AREA_IMAGE_SCRATCH);
if (rc != 0) {
return BOOT_EFLASH;
}
+#endif
BOOT_WRITE_SZ(state) = boot_write_sz(state);
return 0;
}
-static uint32_t
-boot_status_internal_off(int idx, int state, int elem_sz)
+void
+boot_status_reset(struct boot_status *bs)
{
- int idx_sz;
-
- idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
-
- return (idx - BOOT_STATUS_IDX_0) * idx_sz +
- (state - BOOT_STATUS_STATE_0) * elem_sz;
-}
-
-/**
- * Reads the status of a partially-completed swap, if any. This is necessary
- * to recover in case the boot lodaer was reset in the middle of a swap
- * operation.
- */
-static int
-boot_read_status_bytes(const struct flash_area *fap,
- struct boot_loader_state *state, struct boot_status *bs)
-{
- uint32_t off;
- uint8_t status;
- int max_entries;
- int found;
- int found_idx;
- int invalid;
- int rc;
- int i;
-
- off = boot_status_off(fap);
- max_entries = boot_status_entries(BOOT_CURR_IMG(state), fap);
- if (max_entries < 0) {
- return BOOT_EBADARGS;
- }
-
- found = 0;
- found_idx = 0;
- invalid = 0;
- for (i = 0; i < max_entries; i++) {
- rc = flash_area_read_is_empty(fap, off + i * BOOT_WRITE_SZ(state),
- &status, 1);
- if (rc < 0) {
- return BOOT_EFLASH;
- }
-
- if (rc == 1) {
- if (found && !found_idx) {
- found_idx = i;
- }
- } else if (!found) {
- found = 1;
- } else if (found_idx) {
- invalid = 1;
- break;
- }
- }
-
- if (invalid) {
- /* This means there was an error writing status on the last
- * swap. Tell user and move on to validation!
- */
- BOOT_LOG_ERR("Detected inconsistent status!");
-
-#if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
- /* With validation of the primary slot disabled, there is no way
- * to be sure the swapped primary slot is OK, so abort!
- */
- assert(0);
-#endif
- }
-
- if (found) {
- if (!found_idx) {
- found_idx = i;
- }
- bs->idx = (found_idx / BOOT_STATUS_STATE_COUNT) + 1;
- bs->state = (found_idx % BOOT_STATUS_STATE_COUNT) + 1;
- }
-
- return 0;
-}
-
-/**
- * Reads the boot status from the flash. The boot status contains
- * the current state of an interrupted image copy operation. If the boot
- * status is not present, or it indicates that previous copy finished,
- * there is no operation in progress.
- */
-static int
-boot_read_status(struct boot_loader_state *state, struct boot_status *bs)
-{
- const struct flash_area *fap;
- uint32_t off;
- uint8_t swap_info;
- int status_loc;
- int area_id;
- int rc;
-
memset(bs, 0, sizeof *bs);
bs->idx = BOOT_STATUS_IDX_0;
bs->state = BOOT_STATUS_STATE_0;
bs->swap_type = BOOT_SWAP_TYPE_NONE;
+}
-#ifdef MCUBOOT_OVERWRITE_ONLY
- /* Overwrite-only doesn't make use of the swap status area. */
- return 0;
-#endif
-
- status_loc = boot_status_source(state);
- switch (status_loc) {
- case BOOT_STATUS_SOURCE_NONE:
- return 0;
-
- case BOOT_STATUS_SOURCE_SCRATCH:
- area_id = FLASH_AREA_IMAGE_SCRATCH;
- break;
-
- case BOOT_STATUS_SOURCE_PRIMARY_SLOT:
- area_id = FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state));
- break;
-
- default:
- assert(0);
- return BOOT_EBADARGS;
- }
-
- rc = flash_area_open(area_id, &fap);
- if (rc != 0) {
- return BOOT_EFLASH;
- }
-
- rc = boot_read_status_bytes(fap, state, bs);
- if (rc == 0) {
- off = boot_swap_info_off(fap);
- rc = flash_area_read_is_empty(fap, off, &swap_info, sizeof swap_info);
- if (rc == 1) {
- BOOT_SET_SWAP_INFO(swap_info, 0, BOOT_SWAP_TYPE_NONE);
- rc = 0;
- }
-
- /* Extract the swap type info */
- bs->swap_type = BOOT_GET_SWAP_TYPE(swap_info);
- }
-
- flash_area_close(fap);
-
- return rc;
+bool
+boot_status_is_reset(const struct boot_status *bs)
+{
+ return (bs->idx == BOOT_STATUS_IDX_0 && bs->state == BOOT_STATUS_STATE_0);
}
/**
@@ -724,7 +306,7 @@
* @return 0 on success; nonzero on failure.
*/
int
-boot_write_status(struct boot_loader_state *state, struct boot_status *bs)
+boot_write_status(const struct boot_loader_state *state, struct boot_status *bs)
{
const struct flash_area *fap;
uint32_t off;
@@ -740,13 +322,17 @@
* the primary slot!
*/
+#if MCUBOOT_SWAP_USING_SCRATCH
if (bs->use_scratch) {
/* Write to scratch. */
area_id = FLASH_AREA_IMAGE_SCRATCH;
} else {
+#endif
/* Write to the primary slot. */
area_id = FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state));
+#if MCUBOOT_SWAP_USING_SCRATCH
}
+#endif
rc = flash_area_open(area_id, &fap);
if (rc != 0) {
@@ -755,7 +341,7 @@
}
off = boot_status_off(fap) +
- boot_status_internal_off(bs->idx, bs->state, BOOT_WRITE_SZ(state));
+ boot_status_internal_off(bs, BOOT_WRITE_SZ(state));
align = flash_area_align(fap);
erased_val = flash_area_erased_val(fap);
memset(buf, erased_val, BOOT_MAX_ALIGN);
@@ -995,53 +581,6 @@
}
/**
- * Calculates the number of sectors the scratch area can contain. A "last"
- * source sector is specified because images are copied backwards in flash
- * (final index to index number 0).
- *
- * @param last_sector_idx The index of the last source sector
- * (inclusive).
- * @param out_first_sector_idx The index of the first source sector
- * (inclusive) gets written here.
- *
- * @return The number of bytes comprised by the
- * [first-sector, last-sector] range.
- */
-#ifndef MCUBOOT_OVERWRITE_ONLY
-static uint32_t
-boot_copy_sz(struct boot_loader_state *state, int last_sector_idx,
- int *out_first_sector_idx)
-{
- size_t scratch_sz;
- uint32_t new_sz;
- uint32_t sz;
- int i;
-
- sz = 0;
-
- scratch_sz = boot_scratch_area_size(state);
- for (i = last_sector_idx; i >= 0; i--) {
- new_sz = sz + boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
- /*
- * The secondary slot is not being checked here, because
- * `boot_slots_compatible` already provides assurance that the copy size
- * will be compatible with the primary slot and scratch.
- */
- if (new_sz > scratch_sz) {
- break;
- }
- sz = new_sz;
- }
-
- /* i currently refers to a sector that doesn't fit or it is -1 because all
- * sectors have been processed. In both cases, exclude sector i.
- */
- *out_first_sector_idx = i + 1;
- return sz;
-}
-#endif /* !MCUBOOT_OVERWRITE_ONLY */
-
-/**
* Erases a region of flash.
*
* @param flash_area The flash_area containing the region to erase.
@@ -1051,7 +590,7 @@
*
* @return 0 on success; nonzero on failure.
*/
-static inline int
+int
boot_erase_region(const struct flash_area *fap, uint32_t off, uint32_t sz)
{
return flash_area_erase(fap, off, sz);
@@ -1071,7 +610,7 @@
*
* @return 0 on success; nonzero on failure.
*/
-static int
+int
boot_copy_region(struct boot_loader_state *state,
const struct flash_area *fap_src,
const struct flash_area *fap_dst,
@@ -1161,306 +700,6 @@
return 0;
}
-#ifndef MCUBOOT_OVERWRITE_ONLY
-static inline int
-boot_status_init(const struct boot_loader_state *state,
- const struct flash_area *fap,
- const struct boot_status *bs)
-{
- struct boot_swap_state swap_state;
- uint8_t image_index;
- int rc;
-
-#if (BOOT_IMAGE_NUMBER == 1)
- (void)state;
-#endif
-
- image_index = BOOT_CURR_IMG(state);
-
- BOOT_LOG_DBG("initializing status; fa_id=%d", fap->fa_id);
-
- rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SECONDARY(image_index),
- &swap_state);
- assert(rc == 0);
-
- if (bs->swap_type != BOOT_SWAP_TYPE_NONE) {
- rc = boot_write_swap_info(fap, bs->swap_type, image_index);
- assert(rc == 0);
- }
-
- if (swap_state.image_ok == BOOT_FLAG_SET) {
- rc = boot_write_image_ok(fap);
- assert(rc == 0);
- }
-
- rc = boot_write_swap_size(fap, bs->swap_size);
- assert(rc == 0);
-
-#ifdef MCUBOOT_ENC_IMAGES
- rc = boot_write_enc_key(fap, 0, bs->enckey[0]);
- assert(rc == 0);
-
- rc = boot_write_enc_key(fap, 1, bs->enckey[1]);
- assert(rc == 0);
-#endif
-
- rc = boot_write_magic(fap);
- assert(rc == 0);
-
- return 0;
-}
-
-#endif
-
-#ifndef MCUBOOT_OVERWRITE_ONLY
-static int
-boot_erase_trailer_sectors(const struct boot_loader_state *state,
- const struct flash_area *fap)
-{
- uint8_t slot;
- uint32_t sector;
- uint32_t trailer_sz;
- uint32_t total_sz;
- uint32_t off;
- uint32_t sz;
- int fa_id_primary;
- int fa_id_secondary;
- uint8_t image_index;
- int rc;
-
- BOOT_LOG_DBG("erasing trailer; fa_id=%d", fap->fa_id);
-
- image_index = BOOT_CURR_IMG(state);
- fa_id_primary = flash_area_id_from_multi_image_slot(image_index,
- BOOT_PRIMARY_SLOT);
- fa_id_secondary = flash_area_id_from_multi_image_slot(image_index,
- BOOT_SECONDARY_SLOT);
-
- if (fap->fa_id == fa_id_primary) {
- slot = BOOT_PRIMARY_SLOT;
- } else if (fap->fa_id == fa_id_secondary) {
- slot = BOOT_SECONDARY_SLOT;
- } else {
- return BOOT_EFLASH;
- }
-
- /* delete starting from last sector and moving to beginning */
- sector = boot_img_num_sectors(state, slot) - 1;
- trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
- total_sz = 0;
- do {
- sz = boot_img_sector_size(state, slot, sector);
- off = boot_img_sector_off(state, slot, sector);
- rc = boot_erase_region(fap, off, sz);
- assert(rc == 0);
-
- sector--;
- total_sz += sz;
- } while (total_sz < trailer_sz);
-
- return rc;
-}
-#endif /* !MCUBOOT_OVERWRITE_ONLY */
-
-/**
- * Swaps the contents of two flash regions within the two image slots.
- *
- * @param idx The index of the first sector in the range of
- * sectors being swapped.
- * @param sz The number of bytes to swap.
- * @param bs The current boot status. This struct gets
- * updated according to the outcome.
- *
- * @return 0 on success; nonzero on failure.
- */
-#ifndef MCUBOOT_OVERWRITE_ONLY
-static void
-boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
- struct boot_status *bs)
-{
- const struct flash_area *fap_primary_slot;
- const struct flash_area *fap_secondary_slot;
- const struct flash_area *fap_scratch;
- uint32_t copy_sz;
- uint32_t trailer_sz;
- uint32_t img_off;
- uint32_t scratch_trailer_off;
- struct boot_swap_state swap_state;
- size_t last_sector;
- bool erase_scratch;
- uint8_t image_index;
- int rc;
-
- /* Calculate offset from start of image area. */
- img_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
-
- copy_sz = sz;
- trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
-
- /* sz in this function is always sized on a multiple of the sector size.
- * The check against the start offset of the last sector
- * is to determine if we're swapping the last sector. The last sector
- * needs special handling because it's where the trailer lives. If we're
- * copying it, we need to use scratch to write the trailer temporarily.
- *
- * NOTE: `use_scratch` is a temporary flag (never written to flash) which
- * controls if special handling is needed (swapping last sector).
- */
- last_sector = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1;
- if ((img_off + sz) >
- boot_img_sector_off(state, BOOT_PRIMARY_SLOT, last_sector)) {
- copy_sz -= trailer_sz;
- }
-
- bs->use_scratch = (bs->idx == BOOT_STATUS_IDX_0 && copy_sz != sz);
-
- image_index = BOOT_CURR_IMG(state);
-
- rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index),
- &fap_primary_slot);
- assert (rc == 0);
-
- rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index),
- &fap_secondary_slot);
- assert (rc == 0);
-
- rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap_scratch);
- assert (rc == 0);
-
- if (bs->state == BOOT_STATUS_STATE_0) {
- BOOT_LOG_DBG("erasing scratch area");
- rc = boot_erase_region(fap_scratch, 0, fap_scratch->fa_size);
- assert(rc == 0);
-
- if (bs->idx == BOOT_STATUS_IDX_0) {
- /* Write a trailer to the scratch area, even if we don't need the
- * scratch area for status. We need a temporary place to store the
- * `swap-type` while we erase the primary trailer.
- */
- rc = boot_status_init(state, fap_scratch, bs);
- assert(rc == 0);
-
- if (!bs->use_scratch) {
- /* Prepare the primary 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_trailer_sectors(state, fap_primary_slot);
- assert(rc == 0);
-
- rc = boot_status_init(state, fap_primary_slot, bs);
- assert(rc == 0);
-
- /* Erase the temporary trailer from the scratch area. */
- rc = boot_erase_region(fap_scratch, 0, fap_scratch->fa_size);
- assert(rc == 0);
- }
- }
-
- rc = boot_copy_region(state, fap_secondary_slot, fap_scratch,
- img_off, 0, copy_sz);
- assert(rc == 0);
-
- rc = boot_write_status(state, bs);
- bs->state = BOOT_STATUS_STATE_1;
- BOOT_STATUS_ASSERT(rc == 0);
- }
-
- if (bs->state == BOOT_STATUS_STATE_1) {
- rc = boot_erase_region(fap_secondary_slot, img_off, sz);
- assert(rc == 0);
-
- rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot,
- img_off, img_off, copy_sz);
- assert(rc == 0);
-
- if (bs->idx == BOOT_STATUS_IDX_0 && !bs->use_scratch) {
- /* If not all sectors of the slot are being swapped,
- * guarantee here that only the primary slot will have the state.
- */
- rc = boot_erase_trailer_sectors(state, fap_secondary_slot);
- assert(rc == 0);
- }
-
- rc = boot_write_status(state, bs);
- bs->state = BOOT_STATUS_STATE_2;
- BOOT_STATUS_ASSERT(rc == 0);
- }
-
- if (bs->state == BOOT_STATUS_STATE_2) {
- rc = boot_erase_region(fap_primary_slot, img_off, sz);
- assert(rc == 0);
-
- /* NOTE: If this is the final sector, we exclude the image trailer from
- * this copy (copy_sz was truncated earlier).
- */
- rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
- 0, img_off, copy_sz);
- assert(rc == 0);
-
- if (bs->use_scratch) {
- scratch_trailer_off = boot_status_off(fap_scratch);
-
- /* copy current status that is being maintained in scratch */
- rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
- scratch_trailer_off, img_off + copy_sz,
- (BOOT_STATUS_STATE_COUNT - 1) * BOOT_WRITE_SZ(state));
- BOOT_STATUS_ASSERT(rc == 0);
-
- rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH,
- &swap_state);
- assert(rc == 0);
-
- if (swap_state.image_ok == BOOT_FLAG_SET) {
- rc = boot_write_image_ok(fap_primary_slot);
- assert(rc == 0);
- }
-
- if (swap_state.swap_type != BOOT_SWAP_TYPE_NONE) {
- rc = boot_write_swap_info(fap_primary_slot,
- swap_state.swap_type, image_index);
- assert(rc == 0);
- }
-
- rc = boot_write_swap_size(fap_primary_slot, bs->swap_size);
- assert(rc == 0);
-
-#ifdef MCUBOOT_ENC_IMAGES
- rc = boot_write_enc_key(fap_primary_slot, 0, bs->enckey[0]);
- assert(rc == 0);
-
- rc = boot_write_enc_key(fap_primary_slot, 1, bs->enckey[1]);
- assert(rc == 0);
-#endif
- rc = boot_write_magic(fap_primary_slot);
- assert(rc == 0);
- }
-
- /* If we wrote a trailer to the scratch area, erase it after we persist
- * a trailer to the primary slot. We do this to prevent mcuboot from
- * reading a stale status from the scratch area in case of immediate
- * reset.
- */
- erase_scratch = bs->use_scratch;
- bs->use_scratch = 0;
-
- rc = boot_write_status(state, bs);
- bs->idx++;
- bs->state = BOOT_STATUS_STATE_0;
- BOOT_STATUS_ASSERT(rc == 0);
-
- if (erase_scratch) {
- rc = boot_erase_region(fap_scratch, 0, sz);
- assert(rc == 0);
- }
- }
-
- flash_area_close(fap_primary_slot);
- flash_area_close(fap_secondary_slot);
- flash_area_close(fap_scratch);
-}
-#endif /* !MCUBOOT_OVERWRITE_ONLY */
-
/**
* Overwrite primary slot with the image contained in the secondary slot.
* If a prior copy operation was interrupted by a system reset, this function
@@ -1587,11 +826,6 @@
static int
boot_swap_image(struct boot_loader_state *state, struct boot_status *bs)
{
- uint32_t sz;
- int first_sector_idx;
- int last_sector_idx;
- int last_idx_secondary_slot;
- uint32_t swap_idx;
struct image_header *hdr;
#ifdef MCUBOOT_ENC_IMAGES
const struct flash_area *fap;
@@ -1600,8 +834,6 @@
#endif
uint32_t size;
uint32_t copy_size;
- uint32_t primary_slot_size;
- uint32_t secondary_slot_size;
uint8_t image_index;
int rc;
@@ -1610,7 +842,7 @@
size = copy_size = 0;
image_index = BOOT_CURR_IMG(state);
- if (bs->idx == BOOT_STATUS_IDX_0 && bs->state == BOOT_STATUS_STATE_0) {
+ if (boot_status_is_reset(bs)) {
/*
* No swap ever happened, so need to find the largest image which
* will be used to determine the amount of sectors to swap.
@@ -1695,51 +927,10 @@
#endif
}
- primary_slot_size = 0;
- secondary_slot_size = 0;
- last_sector_idx = 0;
- last_idx_secondary_slot = 0;
-
- /*
- * Knowing the size of the largest image between both slots, here we
- * find what is the last sector in the primary slot that needs swapping.
- * Since we already know that both slots are compatible, the secondary
- * slot's last sector is not really required after this check is finished.
- */
- while (1) {
- if ((primary_slot_size < copy_size) ||
- (primary_slot_size < secondary_slot_size)) {
- primary_slot_size += boot_img_sector_size(state,
- BOOT_PRIMARY_SLOT,
- last_sector_idx);
- }
- if ((secondary_slot_size < copy_size) ||
- (secondary_slot_size < primary_slot_size)) {
- secondary_slot_size += boot_img_sector_size(state,
- BOOT_SECONDARY_SLOT,
- last_idx_secondary_slot);
- }
- if (primary_slot_size >= copy_size &&
- secondary_slot_size >= copy_size &&
- primary_slot_size == secondary_slot_size) {
- break;
- }
- last_sector_idx++;
- last_idx_secondary_slot++;
- }
-
- swap_idx = 0;
- while (last_sector_idx >= 0) {
- sz = boot_copy_sz(state, last_sector_idx, &first_sector_idx);
- if (swap_idx >= (bs->idx - BOOT_STATUS_IDX_0)) {
- boot_swap_sectors(first_sector_idx, sz, state, bs);
- }
-
- last_sector_idx = first_sector_idx - 1;
- swap_idx++;
- }
+ swap_run(state, bs, copy_size);
#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
+ extern int boot_status_fails;
if (boot_status_fails > 0) {
BOOT_LOG_WRN("%d status write fails performing the swap",
boot_status_fails);
@@ -1750,67 +941,6 @@
}
#endif
-/**
- * Marks the image in the primary slot as fully copied.
- */
-#ifndef MCUBOOT_OVERWRITE_ONLY
-static int
-boot_set_copy_done(uint8_t image_index)
-{
- const struct flash_area *fap;
- int rc;
-
- rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index),
- &fap);
- if (rc != 0) {
- return BOOT_EFLASH;
- }
-
- rc = boot_write_copy_done(fap);
- flash_area_close(fap);
- return rc;
-}
-#endif /* !MCUBOOT_OVERWRITE_ONLY */
-
-/**
- * Marks a reverted image in the primary slot as confirmed. This is necessary to
- * ensure the status bytes from the image revert operation don't get processed
- * on a subsequent boot.
- *
- * NOTE: image_ok is tested before writing because if there's a valid permanent
- * image installed on the primary slot and the new image to be upgrade to has a
- * bad sig, image_ok would be overwritten.
- */
-#ifndef MCUBOOT_OVERWRITE_ONLY
-static int
-boot_set_image_ok(uint8_t image_index)
-{
- const struct flash_area *fap;
- struct boot_swap_state state;
- int rc;
-
- rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index),
- &fap);
- if (rc != 0) {
- return BOOT_EFLASH;
- }
-
- rc = boot_read_swap_state(fap, &state);
- if (rc != 0) {
- rc = BOOT_EFLASH;
- goto out;
- }
-
- if (state.image_ok == BOOT_FLAG_UNSET) {
- rc = boot_write_image_ok(fap);
- }
-
-out:
- flash_area_close(fap);
- return rc;
-}
-#endif /* !MCUBOOT_OVERWRITE_ONLY */
-
#if (BOOT_IMAGE_NUMBER > 1)
/**
* Check if the version of the image is not older than required.
@@ -2045,14 +1175,14 @@
swap_type = BOOT_SWAP_TYPE(state);
if (swap_type == BOOT_SWAP_TYPE_REVERT ||
swap_type == BOOT_SWAP_TYPE_PERM) {
- rc = boot_set_image_ok(BOOT_CURR_IMG(state));
+ rc = swap_set_image_ok(BOOT_CURR_IMG(state));
if (rc != 0) {
BOOT_SWAP_TYPE(state) = swap_type = BOOT_SWAP_TYPE_PANIC;
}
}
if (BOOT_IS_UPGRADE(swap_type)) {
- rc = boot_set_copy_done(BOOT_CURR_IMG(state));
+ rc = swap_set_copy_done(BOOT_CURR_IMG(state));
if (rc != 0) {
BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC;
}
@@ -2089,14 +1219,14 @@
*/
if (bs->swap_type == BOOT_SWAP_TYPE_REVERT ||
bs->swap_type == BOOT_SWAP_TYPE_PERM) {
- rc = boot_set_image_ok(BOOT_CURR_IMG(state));
+ rc = swap_set_image_ok(BOOT_CURR_IMG(state));
if (rc != 0) {
BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC;
}
}
if (BOOT_IS_UPGRADE(bs->swap_type)) {
- rc = boot_set_copy_done(BOOT_CURR_IMG(state));
+ rc = swap_set_copy_done(BOOT_CURR_IMG(state));
if (rc != 0) {
BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC;
}
@@ -2201,7 +1331,7 @@
}
/* Attempt to read an image header from each slot. */
- rc = boot_read_image_headers(state, false);
+ rc = boot_read_image_headers(state, false, NULL);
if (rc != 0) {
/* Continue with next image if there is one. */
BOOT_LOG_WRN("Failed reading image headers; Image=%u",
@@ -2214,7 +1344,10 @@
* Just boot into primary slot.
*/
if (boot_slots_compatible(state)) {
- rc = boot_read_status(state, bs);
+ boot_status_reset(bs);
+
+#ifndef MCUBOOT_OVERWRITE_ONLY
+ rc = swap_read_status(state, bs);
if (rc != 0) {
BOOT_LOG_WRN("Failed reading boot status; Image=%u",
BOOT_CURR_IMG(state));
@@ -2222,11 +1355,12 @@
BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE;
return;
}
+#endif
/* Determine if we rebooted in the middle of an image swap
* operation. If a partial swap was detected, complete it.
*/
- if (bs->idx != BOOT_STATUS_IDX_0 || bs->state != BOOT_STATUS_STATE_0) {
+ if (!boot_status_is_reset(bs)) {
#if (BOOT_IMAGE_NUMBER > 1)
boot_review_image_swap_types(state, true);
@@ -2247,7 +1381,7 @@
/* Attempt to read an image header from each slot. Ensure that
* image headers in slots are aligned with headers in boot_data.
*/
- rc = boot_read_image_headers(state, false);
+ rc = boot_read_image_headers(state, false, bs);
assert(rc == 0);
/* Swap has finished set to NONE */
@@ -2313,7 +1447,9 @@
*/
TARGET_STATIC boot_sector_t primary_slot_sectors[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS];
TARGET_STATIC boot_sector_t secondary_slot_sectors[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS];
+#if MCUBOOT_SWAP_USING_SCRATCH
TARGET_STATIC boot_sector_t scratch_sectors[BOOT_MAX_IMG_SECTORS];
+#endif
memset(state, 0, sizeof(struct boot_loader_state));
has_upgrade = false;
@@ -2342,7 +1478,9 @@
primary_slot_sectors[image_index];
BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors =
secondary_slot_sectors[image_index];
+#if MCUBOOT_SWAP_USING_SCRATCH
state->scratch.sectors = scratch_sectors;
+#endif
/* Open primary and secondary image areas for the duration
* of this call.
@@ -2352,9 +1490,11 @@
rc = flash_area_open(fa_id, &BOOT_IMG_AREA(state, slot));
assert(rc == 0);
}
+#if MCUBOOT_SWAP_USING_SCRATCH
rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH,
&BOOT_SCRATCH_AREA(state));
assert(rc == 0);
+#endif
/* Determine swap type and complete swap if it has been aborted. */
boot_prepare_image_for_update(state, &bs);
@@ -2398,9 +1538,7 @@
#endif /* MCUBOOT_ENC_IMAGES */
/* Indicate that swap is not aborted */
- memset(&bs, 0, sizeof bs);
- bs.idx = BOOT_STATUS_IDX_0;
- bs.state = BOOT_STATUS_STATE_0;
+ boot_status_reset(&bs);
#endif /* (BOOT_IMAGE_NUMBER > 1) */
/* Set the previously determined swap type */
@@ -2424,7 +1562,7 @@
*/
#ifndef MCUBOOT_OVERWRITE_ONLY
/* image_ok needs to be explicitly set to avoid a new revert. */
- rc = boot_set_image_ok(BOOT_CURR_IMG(state));
+ rc = swap_set_image_ok(BOOT_CURR_IMG(state));
if (rc != 0) {
BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC;
}
@@ -2453,7 +1591,7 @@
/* Attempt to read an image header from each slot. Ensure that image
* headers in slots are aligned with headers in boot_data.
*/
- rc = boot_read_image_headers(state, false);
+ rc = boot_read_image_headers(state, false, &bs);
if (rc != 0) {
goto out;
}
@@ -2496,7 +1634,9 @@
out:
IMAGES_ITER(BOOT_CURR_IMG(state)) {
+#if MCUBOOT_SWAP_USING_SCRATCH
flash_area_close(BOOT_SCRATCH_AREA(state));
+#endif
for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
flash_area_close(BOOT_IMG_AREA(state, BOOT_NUM_SLOTS - 1 - slot));
}
@@ -2550,7 +1690,7 @@
goto done;
}
- rc = boot_read_image_headers(&boot_data, true);
+ rc = boot_read_image_headers(&boot_data, true, NULL);
if (rc != 0) {
goto done;
}