Add support for flash devices erased at 0
This extends mcuboot to allow use on devices which don't follow the
typical erased at 0xff. This was tested on some previously supported
devices (erased at 0xff) and STM32L1/L0 which are erased at 0.
Signed-off-by: Fabio Utzig <utzig@apache.org>
diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c
index f50d693..b1bea5d 100644
--- a/boot/bootutil/src/bootutil_misc.c
+++ b/boot/bootutil/src/bootutil_misc.c
@@ -47,7 +47,6 @@
const uint32_t BOOT_MAX_ALIGN = MAX_FLASH_ALIGN;
struct boot_swap_table {
- /** * For each field, a value of 0 means "any". */
uint8_t magic_slot0;
uint8_t magic_slot1;
uint8_t image_ok_slot0;
@@ -69,27 +68,27 @@
*/
static const struct boot_swap_table boot_swap_tables[] = {
{
- .magic_slot0 = 0,
+ .magic_slot0 = BOOT_MAGIC_ANY,
.magic_slot1 = BOOT_MAGIC_GOOD,
- .image_ok_slot0 = 0,
- .image_ok_slot1 = 0xff,
- .copy_done_slot0 = 0,
+ .image_ok_slot0 = BOOT_FLAG_ANY,
+ .image_ok_slot1 = BOOT_FLAG_UNSET,
+ .copy_done_slot0 = BOOT_FLAG_ANY,
.swap_type = BOOT_SWAP_TYPE_TEST,
},
{
- .magic_slot0 = 0,
+ .magic_slot0 = BOOT_MAGIC_ANY,
.magic_slot1 = BOOT_MAGIC_GOOD,
- .image_ok_slot0 = 0,
- .image_ok_slot1 = 0x01,
- .copy_done_slot0 = 0,
+ .image_ok_slot0 = BOOT_FLAG_ANY,
+ .image_ok_slot1 = BOOT_FLAG_SET,
+ .copy_done_slot0 = BOOT_FLAG_ANY,
.swap_type = BOOT_SWAP_TYPE_PERM,
},
{
.magic_slot0 = BOOT_MAGIC_GOOD,
.magic_slot1 = BOOT_MAGIC_UNSET,
- .image_ok_slot0 = 0xff,
- .image_ok_slot1 = 0,
- .copy_done_slot0 = 0x01,
+ .image_ok_slot0 = BOOT_FLAG_UNSET,
+ .image_ok_slot1 = BOOT_FLAG_ANY,
+ .copy_done_slot0 = BOOT_FLAG_SET,
.swap_type = BOOT_SWAP_TYPE_REVERT,
},
};
@@ -97,17 +96,19 @@
#define BOOT_SWAP_TABLES_COUNT \
(sizeof boot_swap_tables / sizeof boot_swap_tables[0])
-int
-boot_magic_code(const uint32_t *magic)
+static int
+boot_magic_decode(const struct flash_area *fap, const uint32_t *magic)
{
size_t i;
+ uint8_t erased_val;
if (memcmp(magic, boot_img_magic, BOOT_MAGIC_SZ) == 0) {
return BOOT_MAGIC_GOOD;
}
- for (i = 0; i < BOOT_MAGIC_SZ / sizeof *magic; i++) {
- if (magic[i] != 0xffffffff) {
+ erased_val = flash_area_erased_val(fap);
+ for (i = 0; i < BOOT_MAGIC_SZ; i++) {
+ if (((uint8_t *)magic)[i] != erased_val) {
return BOOT_MAGIC_BAD;
}
}
@@ -115,6 +116,21 @@
return BOOT_MAGIC_UNSET;
}
+static int
+boot_flag_decode(const struct flash_area *fap, uint8_t flag)
+{
+ uint8_t erased_val;
+
+ erased_val = flash_area_erased_val(fap);
+ if (flag == erased_val) {
+ return BOOT_FLAG_UNSET;
+ }
+ if (flag != BOOT_FLAG_SET) {
+ return BOOT_FLAG_BAD;
+ }
+ return BOOT_FLAG_SET;
+}
+
uint32_t
boot_slots_trailer_sz(uint8_t min_write_sz)
{
@@ -213,7 +229,7 @@
if (rc != 0) {
return BOOT_EFLASH;
}
- state->magic = boot_magic_code(magic);
+ state->magic = boot_magic_decode(fap, magic);
if (fap->fa_id != FLASH_AREA_IMAGE_SCRATCH) {
off = boot_copy_done_off(fap);
@@ -221,6 +237,7 @@
if (rc != 0) {
return BOOT_EFLASH;
}
+ state->copy_done = boot_flag_decode(fap, state->copy_done);
}
off = boot_image_ok_off(fap);
@@ -228,6 +245,7 @@
if (rc != 0) {
return BOOT_EFLASH;
}
+ state->image_ok = boot_flag_decode(fap, state->image_ok);
return 0;
}
@@ -344,6 +362,7 @@
int rc;
uint8_t buf[BOOT_MAX_ALIGN];
uint8_t align;
+ uint8_t erased_val;
switch (flag) {
case BOOT_FLAG_COPY_DONE:
@@ -358,7 +377,8 @@
align = flash_area_align(fap);
assert(align <= BOOT_MAX_ALIGN);
- memset(buf, 0xFF, BOOT_MAX_ALIGN);
+ erased_val = flash_area_erased_val(fap);
+ memset(buf, erased_val, BOOT_MAX_ALIGN);
buf[0] = BOOT_FLAG_SET;
rc = flash_area_write(fap, off, buf, align);
@@ -388,6 +408,7 @@
int rc;
uint8_t buf[BOOT_MAX_ALIGN];
uint8_t align;
+ uint8_t erased_val;
off = boot_swap_size_off(fap);
align = flash_area_align(fap);
@@ -395,7 +416,8 @@
if (align < sizeof swap_size) {
align = sizeof swap_size;
}
- memset(buf, 0xFF, BOOT_MAX_ALIGN);
+ erased_val = flash_area_erased_val(fap);
+ memset(buf, erased_val, BOOT_MAX_ALIGN);
memcpy(buf, (uint8_t *)&swap_size, sizeof swap_size);
rc = flash_area_write(fap, off, buf, align);
@@ -428,11 +450,16 @@
for (i = 0; i < BOOT_SWAP_TABLES_COUNT; i++) {
table = boot_swap_tables + i;
- if ((!table->magic_slot0 || table->magic_slot0 == slot0.magic ) &&
- (!table->magic_slot1 || table->magic_slot1 == slot1.magic ) &&
- (!table->image_ok_slot0 || table->image_ok_slot0 == slot0.image_ok ) &&
- (!table->image_ok_slot1 || table->image_ok_slot1 == slot1.image_ok ) &&
- (!table->copy_done_slot0 || table->copy_done_slot0 == slot0.copy_done)) {
+ if ((table->magic_slot0 == BOOT_MAGIC_ANY ||
+ table->magic_slot0 == slot0.magic) &&
+ (table->magic_slot1 == BOOT_MAGIC_ANY ||
+ table->magic_slot1 == slot1.magic) &&
+ (table->image_ok_slot0 == BOOT_FLAG_ANY ||
+ table->image_ok_slot0 == slot0.image_ok) &&
+ (table->image_ok_slot1 == BOOT_FLAG_ANY ||
+ table->image_ok_slot1 == slot1.image_ok) &&
+ (table->copy_done_slot0 == BOOT_FLAG_ANY ||
+ table->copy_done_slot0 == slot0.copy_done)) {
BOOT_LOG_INF("Swap type: %s",
table->swap_type == BOOT_SWAP_TYPE_TEST ? "test" :
table->swap_type == BOOT_SWAP_TYPE_PERM ? "perm" :
@@ -509,6 +536,7 @@
{
const struct flash_area *fap;
struct boot_swap_state state_slot0;
+ uint8_t erased_val;
int rc;
rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_0, &state_slot0);
@@ -530,28 +558,25 @@
return BOOT_EBADVECT;
}
- if (state_slot0.copy_done == BOOT_FLAG_UNSET) {
- /* Swap never completed. This is unexpected. */
- return BOOT_EBADVECT;
- }
-
- if (state_slot0.image_ok != BOOT_FLAG_UNSET) {
- /* Already confirmed. */
- return 0;
- }
-
rc = flash_area_open(FLASH_AREA_IMAGE_0, &fap);
if (rc) {
rc = BOOT_EFLASH;
goto done;
}
- rc = boot_write_image_ok(fap);
- if (rc != 0) {
+ erased_val = flash_area_erased_val(fap);
+ if (state_slot0.copy_done == erased_val) {
+ /* Swap never completed. This is unexpected. */
+ rc = BOOT_EBADVECT;
goto done;
}
- rc = 0;
+ if (state_slot0.image_ok != erased_val) {
+ /* Already confirmed. */
+ goto done;
+ }
+
+ rc = boot_write_image_ok(fap);
done:
flash_area_close(fap);
diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h
index c5c23aa..8cb7533 100644
--- a/boot/bootutil/src/bootutil_priv.h
+++ b/boot/bootutil/src/bootutil_priv.h
@@ -59,9 +59,24 @@
uint32_t swap_size; /* Total size of swapped image */
};
-#define BOOT_MAGIC_GOOD 1
-#define BOOT_MAGIC_BAD 2
-#define BOOT_MAGIC_UNSET 3
+#define BOOT_MAGIC_GOOD 1
+#define BOOT_MAGIC_BAD 2
+#define BOOT_MAGIC_UNSET 3
+#define BOOT_MAGIC_ANY 4 /* NOTE: control only, not dependent on sector */
+
+/*
+ * NOTE: leave BOOT_FLAG_SET equal to one, this is written to flash!
+ */
+#define BOOT_FLAG_SET 1
+#define BOOT_FLAG_BAD 2
+#define BOOT_FLAG_UNSET 3
+#define BOOT_FLAG_ANY 4 /* NOTE: control only, not dependent on sector */
+
+#define BOOT_STATUS_IDX_0 1
+
+#define BOOT_STATUS_STATE_0 1
+#define BOOT_STATUS_STATE_1 2
+#define BOOT_STATUS_STATE_2 3
/**
* End-of-image slot structure.
@@ -75,11 +90,11 @@
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Swap size |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * ~ 0xff padding (MAX ALIGN - 4) ~
+ * ~ padding with erased val (MAX ALIGN - 4) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Copy done | 0xff padding (MAX ALIGN - 1) ~
+ * | Copy done | padding with erased val (MAX ALIGN - 1) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Image OK | 0xff padding (MAX ALIGN - 1) ~
+ * | Image OK | padding with erased val (MAX ALIGN - 1) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ~ MAGIC (16 octets) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -120,9 +135,6 @@
#define BOOT_FLAG_IMAGE_OK 0
#define BOOT_FLAG_COPY_DONE 1
-#define BOOT_FLAG_SET 0x01
-#define BOOT_FLAG_UNSET 0xff
-
extern const uint32_t BOOT_MAGIC_SZ;
/**
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index 9ddf82d..66c8843 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -28,7 +28,6 @@
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
-#include <hal/hal_flash.h>
#include <os/os_malloc.h>
#include "bootutil/bootutil.h"
#include "bootutil/image.h"
@@ -52,9 +51,6 @@
#endif
struct boot_status_table {
- /**
- * For each field, a value of 0 means "any".
- */
uint8_t bst_magic_slot0;
uint8_t bst_magic_scratch;
uint8_t bst_copy_done_slot0;
@@ -70,14 +66,14 @@
/* | slot-0 | scratch |
* ----------+------------+------------|
* magic | Good | Any |
- * copy-done | 0x01 | N/A |
+ * copy-done | Set | N/A |
* ----------+------------+------------'
* source: none |
* ------------------------------------'
*/
.bst_magic_slot0 = BOOT_MAGIC_GOOD,
- .bst_magic_scratch = 0,
- .bst_copy_done_slot0 = 0x01,
+ .bst_magic_scratch = BOOT_MAGIC_ANY,
+ .bst_copy_done_slot0 = BOOT_FLAG_SET,
.bst_status_source = BOOT_STATUS_SOURCE_NONE,
},
@@ -85,14 +81,14 @@
/* | slot-0 | scratch |
* ----------+------------+------------|
* magic | Good | Any |
- * copy-done | 0xff | N/A |
+ * copy-done | Unset | N/A |
* ----------+------------+------------'
* source: slot 0 |
* ------------------------------------'
*/
.bst_magic_slot0 = BOOT_MAGIC_GOOD,
- .bst_magic_scratch = 0,
- .bst_copy_done_slot0 = 0xff,
+ .bst_magic_scratch = BOOT_MAGIC_ANY,
+ .bst_copy_done_slot0 = BOOT_FLAG_UNSET,
.bst_status_source = BOOT_STATUS_SOURCE_SLOT0,
},
@@ -105,17 +101,16 @@
* source: scratch |
* ------------------------------------'
*/
- .bst_magic_slot0 = 0,
+ .bst_magic_slot0 = BOOT_MAGIC_ANY,
.bst_magic_scratch = BOOT_MAGIC_GOOD,
- .bst_copy_done_slot0 = 0,
+ .bst_copy_done_slot0 = BOOT_FLAG_ANY,
.bst_status_source = BOOT_STATUS_SOURCE_SCRATCH,
},
-
{
/* | slot-0 | scratch |
* ----------+------------+------------|
* magic | Unset | Any |
- * copy-done | 0xff | N/A |
+ * copy-done | Unset | N/A |
* ----------+------------+------------|
* source: varies |
* ------------------------------------+------------------------------+
@@ -125,8 +120,8 @@
* -------------------------------------------------------------------'
*/
.bst_magic_slot0 = BOOT_MAGIC_UNSET,
- .bst_magic_scratch = 0,
- .bst_copy_done_slot0 = 0xff,
+ .bst_magic_scratch = BOOT_MAGIC_ANY,
+ .bst_copy_done_slot0 = BOOT_FLAG_UNSET,
.bst_status_source = BOOT_STATUS_SOURCE_SLOT0,
},
};
@@ -172,11 +167,11 @@
for (i = 0; i < BOOT_STATUS_TABLES_COUNT; i++) {
table = &boot_status_tables[i];
- if ((table->bst_magic_slot0 == 0 ||
+ if ((table->bst_magic_slot0 == BOOT_MAGIC_ANY ||
table->bst_magic_slot0 == state_slot0.magic) &&
- (table->bst_magic_scratch == 0 ||
+ (table->bst_magic_scratch == BOOT_MAGIC_ANY ||
table->bst_magic_scratch == state_scratch.magic) &&
- (table->bst_copy_done_slot0 == 0 ||
+ (table->bst_copy_done_slot0 == BOOT_FLAG_ANY ||
table->bst_copy_done_slot0 == state_slot0.copy_done)) {
source = table->bst_status_source;
BOOT_LOG_INF("Boot source: %s",
@@ -389,7 +384,8 @@
idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
- return idx * idx_sz + state * elem_sz;
+ return (idx - BOOT_STATUS_IDX_0) * idx_sz +
+ (state - BOOT_STATUS_STATE_0) * elem_sz;
}
/**
@@ -408,9 +404,11 @@
int invalid;
int rc;
int i;
+ uint8_t erased_val;
off = boot_status_off(fap);
max_entries = boot_status_entries(fap);
+ erased_val = flash_area_erased_val(fap);
found = 0;
found_idx = 0;
@@ -422,7 +420,7 @@
return BOOT_EFLASH;
}
- if (status == 0xff) {
+ if (status == erased_val) {
if (found && !found_idx) {
found_idx = i;
}
@@ -453,8 +451,8 @@
found_idx = i;
}
found_idx--;
- bs->idx = found_idx / BOOT_STATUS_STATE_COUNT;
- bs->state = found_idx % BOOT_STATUS_STATE_COUNT;
+ bs->idx = (found_idx / BOOT_STATUS_STATE_COUNT) + 1;
+ bs->state = (found_idx % BOOT_STATUS_STATE_COUNT) + 1;
}
return 0;
@@ -475,6 +473,8 @@
int rc;
memset(bs, 0, sizeof *bs);
+ bs->idx = BOOT_STATUS_IDX_0;
+ bs->state = BOOT_STATUS_STATE_0;
#ifdef MCUBOOT_OVERWRITE_ONLY
/* Overwrite-only doesn't make use of the swap status area. */
@@ -528,6 +528,7 @@
int rc;
uint8_t buf[BOOT_MAX_ALIGN];
uint8_t align;
+ uint8_t erased_val;
/* NOTE: The first sector copied (that is the last sector on slot) contains
* the trailer. Since in the last step SLOT 0 is erased, the first
@@ -552,7 +553,8 @@
boot_status_internal_off(bs->idx, bs->state,
BOOT_WRITE_SZ(&boot_data));
align = flash_area_align(fap);
- memset(buf, 0xFF, BOOT_MAX_ALIGN);
+ erased_val = flash_area_erased_val(fap);
+ memset(buf, erased_val, BOOT_MAX_ALIGN);
buf[0] = bs->state;
rc = flash_area_write(fap, off, buf, align);
@@ -612,6 +614,18 @@
return 0;
}
+static inline int
+boot_magic_is_erased(uint8_t erased_val, uint32_t magic)
+{
+ uint8_t i;
+ for (i = 0; i < sizeof(magic); i++) {
+ if (erased_val != *(((uint8_t *)&magic) + i)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
static int
boot_validate_slot(int slot)
{
@@ -619,17 +633,18 @@
struct image_header *hdr;
int rc;
- hdr = boot_img_hdr(&boot_data, slot);
- if (hdr->ih_magic == 0xffffffff || hdr->ih_flags & IMAGE_F_NON_BOOTABLE) {
- /* No bootable image in slot; continue booting from slot 0. */
- return -1;
- }
-
rc = flash_area_open(flash_area_id_from_image_slot(slot), &fap);
if (rc != 0) {
return BOOT_EFLASH;
}
+ hdr = boot_img_hdr(&boot_data, slot);
+ if (boot_magic_is_erased(flash_area_erased_val(fap), hdr->ih_magic) ||
+ hdr->ih_flags & IMAGE_F_NON_BOOTABLE) {
+ /* No bootable image in slot; continue booting from slot 0. */
+ return -1;
+ }
+
if ((hdr->ih_magic != IMAGE_MAGIC || boot_image_check(hdr, fap) != 0)) {
if (slot != 0) {
flash_area_erase(fap, 0, fap->fa_size);
@@ -931,9 +946,9 @@
copy_sz -= trailer_sz;
}
- bs->use_scratch = (bs->idx == 0 && copy_sz != sz);
+ bs->use_scratch = (bs->idx == BOOT_STATUS_IDX_0 && copy_sz != sz);
- if (bs->state == 0) {
+ if (bs->state == BOOT_STATUS_STATE_0) {
rc = boot_erase_sector(FLASH_AREA_IMAGE_SCRATCH, 0, sz);
assert(rc == 0);
@@ -941,7 +956,7 @@
img_off, 0, copy_sz);
assert(rc == 0);
- if (bs->idx == 0) {
+ if (bs->idx == BOOT_STATUS_IDX_0) {
if (bs->use_scratch) {
boot_status_init_by_id(FLASH_AREA_IMAGE_SCRATCH, bs);
} else {
@@ -956,12 +971,12 @@
}
}
- bs->state = 1;
+ bs->state = BOOT_STATUS_STATE_1;
rc = boot_write_status(bs);
BOOT_STATUS_ASSERT(rc == 0);
}
- if (bs->state == 1) {
+ if (bs->state == BOOT_STATUS_STATE_1) {
rc = boot_erase_sector(FLASH_AREA_IMAGE_1, img_off, sz);
assert(rc == 0);
@@ -969,7 +984,7 @@
img_off, img_off, copy_sz);
assert(rc == 0);
- if (bs->idx == 0 && !bs->use_scratch) {
+ if (bs->idx == BOOT_STATUS_IDX_0 && !bs->use_scratch) {
/* If not all sectors of the slot are being swapped,
* guarantee here that only slot0 will have the state.
*/
@@ -977,12 +992,12 @@
assert(rc == 0);
}
- bs->state = 2;
+ bs->state = BOOT_STATUS_STATE_2;
rc = boot_write_status(bs);
BOOT_STATUS_ASSERT(rc == 0);
}
- if (bs->state == 2) {
+ if (bs->state == BOOT_STATUS_STATE_2) {
rc = boot_erase_sector(FLASH_AREA_IMAGE_0, img_off, sz);
assert(rc == 0);
@@ -1028,7 +1043,7 @@
}
bs->idx++;
- bs->state = 0;
+ bs->state = BOOT_STATUS_STATE_0;
bs->use_scratch = 0;
rc = boot_write_status(bs);
BOOT_STATUS_ASSERT(rc == 0);
@@ -1127,7 +1142,7 @@
size = copy_size = 0;
- if (bs->idx == 0 && bs->state == 0) {
+ if (bs->idx == BOOT_STATUS_IDX_0 && bs->state == BOOT_STATUS_STATE_0) {
/*
* No swap ever happened, so need to find the largest image which
* will be used to determine the amount of sectors to swap.
@@ -1173,7 +1188,7 @@
swap_idx = 0;
while (last_sector_idx >= 0) {
sz = boot_copy_sz(last_sector_idx, &first_sector_idx);
- if (swap_idx >= bs->idx) {
+ if (swap_idx >= (bs->idx - BOOT_STATUS_IDX_0)) {
boot_swap_sectors(first_sector_idx, sz, bs);
}
@@ -1275,7 +1290,7 @@
}
/* If a partial swap was detected, complete it. */
- if (bs.idx != 0 || bs.state != 0) {
+ if (bs.idx != BOOT_STATUS_IDX_0 || bs.state != BOOT_STATUS_STATE_0) {
rc = boot_copy_image(&bs);
assert(rc == 0);