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/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c
index fe806c6..64882d2 100644
--- a/boot/bootutil/src/bootutil_misc.c
+++ b/boot/bootutil/src/bootutil_misc.c
@@ -164,9 +164,12 @@
 int
 boot_status_entries(int image_index, const struct flash_area *fap)
 {
+#if MCUBOOT_SWAP_USING_SCRATCH
     if (fap->fa_id == FLASH_AREA_IMAGE_SCRATCH) {
         return BOOT_STATUS_STATE_COUNT;
-    } else if (fap->fa_id == FLASH_AREA_IMAGE_PRIMARY(image_index) ||
+    } else
+#endif
+    if (fap->fa_id == FLASH_AREA_IMAGE_PRIMARY(image_index) ||
                fap->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) {
         return BOOT_STATUS_STATE_COUNT * BOOT_STATUS_MAX_ENTRIES;
     }
@@ -321,7 +324,9 @@
     uint32_t magic[BOOT_MAGIC_ARR_SZ];
     uint32_t off;
     uint8_t areas[2] = {
+#if MCUBOOT_SWAP_USING_SCRATCH
         FLASH_AREA_IMAGE_SCRATCH,
+#endif
         FLASH_AREA_IMAGE_PRIMARY(image_index),
     };
     unsigned int i;
@@ -376,7 +381,6 @@
     return rc;
 }
 
-
 #ifdef MCUBOOT_ENC_IMAGES
 int
 boot_read_enc_key(int image_index, uint8_t slot, uint8_t *enckey)
diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h
index 24fe7e9..70a1b0d 100644
--- a/boot/bootutil/src/bootutil_priv.h
+++ b/boot/bootutil/src/bootutil_priv.h
@@ -62,6 +62,10 @@
 /** Number of image slots in flash; currently limited to two. */
 #define BOOT_NUM_SLOTS                  2
 
+#if !defined(MCUBOOT_OVERWRITE_ONLY)
+#define MCUBOOT_SWAP_USING_SCRATCH 1
+#endif
+
 /*
  * Maintain state of copy progress.
  */
@@ -207,11 +211,13 @@
         size_t num_sectors;
     } imgs[BOOT_IMAGE_NUMBER][BOOT_NUM_SLOTS];
 
+#if MCUBOOT_SWAP_USING_SCRATCH
     struct {
         const struct flash_area *area;
         boot_sector_t *sectors;
         size_t num_sectors;
     } scratch;
+#endif
 
     uint8_t swap_type[BOOT_IMAGE_NUMBER];
     uint32_t write_sz;
@@ -238,13 +244,23 @@
 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_loader_state *state, struct boot_status *bs);
+int boot_write_status(const struct boot_loader_state *state, struct boot_status *bs);
 int boot_write_copy_done(const struct flash_area *fap);
 int boot_write_image_ok(const struct flash_area *fap);
 int boot_write_swap_info(const struct flash_area *fap, uint8_t swap_type,
                          uint8_t image_num);
 int boot_write_swap_size(const struct flash_area *fap, uint32_t swap_size);
 int boot_read_swap_size(int image_index, uint32_t *swap_size);
+int boot_slots_compatible(struct boot_loader_state *state);
+uint32_t boot_status_internal_off(const struct boot_status *bs, int elem_sz);
+int boot_read_image_header(struct boot_loader_state *state, int slot,
+                           struct image_header *out_hdr, struct boot_status *bs);
+int boot_copy_region(struct boot_loader_state *state,
+                     const struct flash_area *fap_src,
+                     const struct flash_area *fap_dst,
+                     uint32_t off_src, uint32_t off_dst, uint32_t sz);
+int boot_erase_region(const struct flash_area *fap, uint32_t off, uint32_t sz);
+
 #ifdef MCUBOOT_ENC_IMAGES
 int boot_write_enc_key(const struct flash_area *fap, uint8_t slot,
                        const uint8_t *enckey);
@@ -303,7 +319,6 @@
 #endif
 #define BOOT_IMG(state, slot) ((state)->imgs[BOOT_CURR_IMG(state)][(slot)])
 #define BOOT_IMG_AREA(state, slot) (BOOT_IMG(state, slot).area)
-#define BOOT_SCRATCH_AREA(state) ((state)->scratch.area)
 #define BOOT_WRITE_SZ(state) ((state)->write_sz)
 #define BOOT_SWAP_TYPE(state) ((state)->swap_type[BOOT_CURR_IMG(state)])
 #define BOOT_TLV_OFF(hdr) ((hdr)->ih_hdr_size + (hdr)->ih_img_size)
@@ -325,12 +340,6 @@
     return BOOT_IMG(state, slot).num_sectors;
 }
 
-static inline size_t
-boot_scratch_num_sectors(struct boot_loader_state *state)
-{
-    return state->scratch.num_sectors;
-}
-
 /*
  * Offset of the slot from the beginning of the flash device.
  */
@@ -340,11 +349,6 @@
     return BOOT_IMG(state, slot).area->fa_off;
 }
 
-static inline size_t boot_scratch_area_size(struct boot_loader_state *state)
-{
-    return BOOT_SCRATCH_AREA(state)->fa_size;
-}
-
 #ifndef MCUBOOT_USE_FLASH_AREA_GET_SECTORS
 
 static inline size_t
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;
     }
diff --git a/boot/bootutil/src/swap_misc.c b/boot/bootutil/src/swap_misc.c
new file mode 100644
index 0000000..f194538
--- /dev/null
+++ b/boot/bootutil/src/swap_misc.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2019 JUUL Labs
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include "bootutil/bootutil.h"
+#include "bootutil_priv.h"
+#include "swap_priv.h"
+#include "bootutil/bootutil_log.h"
+
+#include "mcuboot_config/mcuboot_config.h"
+
+MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
+
+#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE)
+
+int
+swap_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;
+}
+
+int
+swap_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;
+}
+
+int
+swap_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;
+
+    status_loc = swap_status_source(state);
+    switch (status_loc) {
+    case BOOT_STATUS_SOURCE_NONE:
+        return 0;
+
+#if MCUBOOT_SWAP_USING_SCRATCH
+    case BOOT_STATUS_SOURCE_SCRATCH:
+        area_id = FLASH_AREA_IMAGE_SCRATCH;
+        break;
+#endif
+
+    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 = swap_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;
+}
+
+int
+swap_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;
+}
+
+int
+swap_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 /* defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) */
diff --git a/boot/bootutil/src/swap_priv.h b/boot/bootutil/src/swap_priv.h
new file mode 100644
index 0000000..7507595
--- /dev/null
+++ b/boot/bootutil/src/swap_priv.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2019 JUUL Labs
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef H_SWAP_PRIV_
+#define H_SWAP_PRIV_
+
+#include "mcuboot_config/mcuboot_config.h"
+
+#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE)
+
+/**
+ * Calculates the amount of space required to store the trailer, and erases
+ * all sectors required for this storage in the given flash_area.
+ */
+int swap_erase_trailer_sectors(const struct boot_loader_state *state,
+                               const struct flash_area *fap);
+
+/**
+ * Initialize the given flash_area with the metadata required to start a new
+ * swap upgrade.
+ */
+int swap_status_init(const struct boot_loader_state *state,
+                     const struct flash_area *fap,
+                     const struct boot_status *bs);
+
+/**
+ * Tries to locate an interrupted swap status (metadata). If not metadata
+ * was found returns BOOT_STATUS_SOURCE_NONE.
+ *
+ * Must return one of:
+ *   - BOOT_STATUS_SOURCE_NONE
+ *   - BOOT_STATUS_SOURCE_SCRATCH
+ *   - BOOT_STATUS_SOURCE_PRIMARY_SLOT
+ */
+int swap_status_source(struct boot_loader_state *state);
+
+/**
+ * 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.
+ */
+int swap_read_status(struct boot_loader_state *state, struct boot_status *bs);
+
+/**
+ * Iterate over the swap status bytes in the given flash_area and populate
+ * the given boot_status with the calculated index where a swap upgrade was
+ * interrupted.
+ */
+int swap_read_status_bytes(const struct flash_area *fap,
+                           struct boot_loader_state *state,
+                           struct boot_status *bs);
+
+/**
+ * Marks the image in the primary slot as fully copied.
+ */
+int swap_set_copy_done(uint8_t image_index);
+
+/**
+ * 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.
+ */
+int swap_set_image_ok(uint8_t image_index);
+
+/**
+ * Start a new or resume an interrupted swap according to the parameters
+ * found in the given boot_status.
+ */
+void swap_run(struct boot_loader_state *state,
+              struct boot_status *bs,
+              uint32_t copy_size);
+
+#if MCUBOOT_SWAP_USING_SCRATCH
+#define BOOT_SCRATCH_AREA(state) ((state)->scratch.area)
+
+static inline size_t boot_scratch_area_size(const struct boot_loader_state *state)
+{
+    return BOOT_SCRATCH_AREA(state)->fa_size;
+}
+#endif
+
+#endif /* defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) */
+
+#endif /* H_SWAP_PRIV_ */
diff --git a/boot/bootutil/src/swap_scratch.c b/boot/bootutil/src/swap_scratch.c
new file mode 100644
index 0000000..bf638bf
--- /dev/null
+++ b/boot/bootutil/src/swap_scratch.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright (c) 2019 JUUL Labs
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include "bootutil/bootutil.h"
+#include "bootutil_priv.h"
+#include "swap_priv.h"
+#include "bootutil/bootutil_log.h"
+
+#include "mcuboot_config/mcuboot_config.h"
+
+MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
+
+#if MCUBOOT_SWAP_USING_SCRATCH
+
+#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
+/*
+ * FIXME: this might have to be updated for threaded sim
+ */
+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
+
+int
+boot_read_image_header(struct boot_loader_state *state, int slot,
+                       struct image_header *out_hdr, struct boot_status *bs)
+{
+    const struct flash_area *fap;
+    int area_id;
+    int rc;
+
+    (void)bs;
+
+#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;
+}
+
+/**
+ * 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.
+ */
+int
+swap_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;
+}
+
+uint32_t
+boot_status_internal_off(const struct boot_status *bs, int elem_sz)
+{
+    int idx_sz;
+
+    idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
+
+    return (bs->idx - BOOT_STATUS_IDX_0) * idx_sz +
+           (bs->state - BOOT_STATUS_STATE_0) * 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!).
+ */
+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;
+}
+
+#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)
+
+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])
+
+/**
+ * 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.
+ */
+int
+swap_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;
+}
+
+/**
+ * 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.
+ */
+static uint32_t
+boot_copy_sz(const 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;
+}
+
+/**
+ * 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.
+ */
+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 = swap_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 = swap_erase_trailer_sectors(state, fap_primary_slot);
+                assert(rc == 0);
+
+                rc = swap_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 = swap_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);
+}
+
+void
+swap_run(struct boot_loader_state *state, struct boot_status *bs,
+         uint32_t copy_size)
+{
+    uint32_t sz;
+    int first_sector_idx;
+    int last_sector_idx;
+    uint32_t swap_idx;
+    int last_idx_secondary_slot;
+    uint32_t primary_slot_size;
+    uint32_t secondary_slot_size;
+    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++;
+    }
+
+}
+
+#endif