bootutil: add swap without scratch strategy

This implements a swap upgrade that does not use a scratch area. It
works by first moving all sectors in the primary slot up one position,
and then looping on moving sector of index X of the secondary slot to
index X of the primary slot, followed by moving sector X+1 of the
primary slot to X on the secondary slot, for each sector X.

The idea behind this implementation was initially suggested by Jehudi
Maes (@Laczen) and implemented on his own bootloader (ZEPboot).

Signed-off-by: Fabio Utzig <utzig@apache.org>
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index 1635efc..36ea754 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -286,6 +286,7 @@
 boot_status_reset(struct boot_status *bs)
 {
     memset(bs, 0, sizeof *bs);
+    bs->op = BOOT_STATUS_OP_MOVE;
     bs->idx = BOOT_STATUS_IDX_0;
     bs->state = BOOT_STATUS_STATE_0;
     bs->swap_type = BOOT_SWAP_TYPE_NONE;
@@ -294,7 +295,9 @@
 bool
 boot_status_is_reset(const struct boot_status *bs)
 {
-    return (bs->idx == BOOT_STATUS_IDX_0 && bs->state == BOOT_STATUS_STATE_0);
+    return (bs->op == BOOT_STATUS_OP_MOVE &&
+            bs->idx == BOOT_STATUS_IDX_0 &&
+            bs->state == BOOT_STATUS_STATE_0);
 }
 
 /**
@@ -650,16 +653,25 @@
 
 #ifdef MCUBOOT_ENC_IMAGES
         image_index = BOOT_CURR_IMG(state);
-        if (fap_src->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index) ||
-            fap_dst->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) {
+        if ((fap_src->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index) ||
+            fap_dst->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) &&
+            !(fap_src->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index) &&
+              fap_dst->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index))) {
             /* assume the secondary slot as src, needs decryption */
             hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT);
+#if !defined(MCUBOOT_SWAP_USING_MOVE)
             off = off_src;
             if (fap_dst->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) {
                 /* might need encryption (metadata from the primary slot) */
                 hdr = boot_img_hdr(state, BOOT_PRIMARY_SLOT);
                 off = off_dst;
             }
+#else
+            off = off_dst;
+            if (fap_dst->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) {
+                hdr = boot_img_hdr(state, BOOT_PRIMARY_SLOT);
+            }
+#endif
             if (IS_ENCRYPTED(hdr)) {
                 blk_sz = chunk_sz;
                 idx = 0;
@@ -1357,6 +1369,21 @@
         }
 #endif
 
+#ifdef MCUBOOT_SWAP_USING_MOVE
+        /*
+         * Must re-read image headers because the boot status might
+         * have been updated in the previous function call.
+         */
+        rc = boot_read_image_headers(state, !boot_status_is_reset(bs), bs);
+        if (rc != 0) {
+            /* Continue with next image if there is one. */
+            BOOT_LOG_WRN("Failed reading image headers; Image=%u",
+                    BOOT_CURR_IMG(state));
+            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.
          */