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);