Boot: Enable multi-image boot

This patch adds the capability to handle multiple firmware images,
to update them independently. Also update the design documentation.
It separates the completion of aborted image swap operations and the
update of images even more as these should be happening at different
stages of the boot process according to the design proposal of
the multiple image support:
https://github.com/JuulLabs-OSS/mcuboot/pull/317.

Change-Id: I7eb5f632298bb08c805bfaee0359703b2ae19e9d
Signed-off-by: David Vincze <david.vincze@arm.com>
diff --git a/.travis.yml b/.travis.yml
index 3bb0751..089bfa9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -23,7 +23,7 @@
     # separated by ',' and each list of values is run sequentially in the
     # defined order.
     - os: linux
-      env: MULTI_FEATURES="sig-rsa overwrite-only,sig-ecdsa overwrite-only" TEST=sim
+      env: MULTI_FEATURES="sig-rsa overwrite-only,sig-ecdsa overwrite-only,multiimage overwrite-only" TEST=sim
     - os: linux
       env: MULTI_FEATURES="sig-rsa validate-primary-slot,sig-ecdsa validate-primary-slot" TEST=sim
     - os: linux
diff --git a/boot/bootutil/include/bootutil/enc_key.h b/boot/bootutil/include/bootutil/enc_key.h
index 0f4ab57..51232b4 100644
--- a/boot/bootutil/include/bootutil/enc_key.h
+++ b/boot/bootutil/include/bootutil/enc_key.h
@@ -53,6 +53,7 @@
 int boot_enc_load(const struct image_header *hdr, const struct flash_area *fap,
         uint8_t *enckey);
 bool boot_enc_valid(const struct flash_area *fap);
+void boot_enc_mark_keys_invalid(void);
 void boot_encrypt(const struct flash_area *fap, uint32_t off, uint32_t sz,
         uint32_t blk_off, uint8_t *buf);
 void boot_enc_zeroize(void);
diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h
index 11d0a54..09d5fac 100644
--- a/boot/bootutil/src/bootutil_priv.h
+++ b/boot/bootutil/src/bootutil_priv.h
@@ -124,6 +124,7 @@
  *      (`MCUBOOT_ENC_IMAGES`).
  */
 
+extern uint8_t current_image;
 extern const uint32_t boot_img_magic[4];
 
 struct boot_swap_state {
@@ -202,7 +203,7 @@
         const struct flash_area *area;
         boot_sector_t *sectors;
         size_t num_sectors;
-    } imgs[BOOT_NUM_SLOTS];
+    } imgs[BOOT_IMAGE_NUMBER][BOOT_NUM_SLOTS];
 
     struct {
         const struct flash_area *area;
@@ -210,6 +211,7 @@
         size_t num_sectors;
     } scratch;
 
+    uint8_t swap_type[BOOT_IMAGE_NUMBER];
     uint8_t write_sz;
 };
 
@@ -245,20 +247,22 @@
  */
 
 /* These are macros so they can be used as lvalues. */
-#define BOOT_IMG_AREA(state, slot) ((state)->imgs[(slot)].area)
+#define BOOT_IMG(state, slot) ((state)->imgs[current_image][(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[current_image])
 
 static inline struct image_header*
 boot_img_hdr(struct boot_loader_state *state, size_t slot)
 {
-    return &state->imgs[slot].hdr;
+    return &BOOT_IMG(state, slot).hdr;
 }
 
 static inline size_t
 boot_img_num_sectors(struct boot_loader_state *state, size_t slot)
 {
-    return state->imgs[slot].num_sectors;
+    return BOOT_IMG(state, slot).num_sectors;
 }
 
 static inline size_t
@@ -273,7 +277,7 @@
 static inline uint32_t
 boot_img_slot_off(struct boot_loader_state *state, size_t slot)
 {
-    return state->imgs[slot].area->fa_off;
+    return BOOT_IMG(state, slot).area->fa_off;
 }
 
 static inline size_t boot_scratch_area_size(struct boot_loader_state *state)
@@ -287,7 +291,7 @@
 boot_img_sector_size(struct boot_loader_state *state,
                      size_t slot, size_t sector)
 {
-    return state->imgs[slot].sectors[sector].fa_size;
+    return BOOT_IMG(state, slot).sectors[sector].fa_size;
 }
 
 /*
@@ -298,8 +302,8 @@
 boot_img_sector_off(struct boot_loader_state *state, size_t slot,
                     size_t sector)
 {
-    return state->imgs[slot].sectors[sector].fa_off -
-           state->imgs[slot].sectors[0].fa_off;
+    return BOOT_IMG(state, slot).sectors[sector].fa_off -
+           BOOT_IMG(state, slot).sectors[0].fa_off;
 }
 
 static inline int
@@ -310,13 +314,13 @@
 
     if (flash_area == FLASH_AREA_IMAGE_PRIMARY) {
         rc = flash_area_to_sectors(flash_area, &num_sectors,
-                                   state->imgs[BOOT_PRIMARY_SLOT].sectors);
-        state->imgs[BOOT_PRIMARY_SLOT].num_sectors = (size_t)num_sectors;
+                                   BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors);
+        BOOT_IMG(state, BOOT_PRIMARY_SLOT).num_sectors = (size_t)num_sectors;
 
     } else if (flash_area == FLASH_AREA_IMAGE_SECONDARY) {
         rc = flash_area_to_sectors(flash_area, &num_sectors,
-                                   state->imgs[BOOT_SECONDARY_SLOT].sectors);
-        state->imgs[BOOT_SECONDARY_SLOT].num_sectors = (size_t)num_sectors;
+                                 BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors);
+        BOOT_IMG(state, BOOT_SECONDARY_SLOT).num_sectors = (size_t)num_sectors;
 
     } else if (flash_area == FLASH_AREA_IMAGE_SCRATCH) {
         rc = flash_area_to_sectors(flash_area, &num_sectors,
@@ -335,15 +339,15 @@
 boot_img_sector_size(struct boot_loader_state *state,
                      size_t slot, size_t sector)
 {
-    return state->imgs[slot].sectors[sector].fs_size;
+    return BOOT_IMG(state, slot).sectors[sector].fs_size;
 }
 
 static inline uint32_t
 boot_img_sector_off(struct boot_loader_state *state, size_t slot,
                     size_t sector)
 {
-    return state->imgs[slot].sectors[sector].fs_off -
-           state->imgs[slot].sectors[0].fs_off;
+    return BOOT_IMG(state, slot).sectors[sector].fs_off -
+           BOOT_IMG(state, slot).sectors[0].fs_off;
 }
 
 static inline int
@@ -357,11 +361,11 @@
     num_sectors = BOOT_MAX_IMG_SECTORS;
 
     if (flash_area == FLASH_AREA_IMAGE_PRIMARY) {
-        out_sectors = state->imgs[BOOT_PRIMARY_SLOT].sectors;
-        out_num_sectors = &state->imgs[BOOT_PRIMARY_SLOT].num_sectors;
+        out_sectors = BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors;
+        out_num_sectors = &BOOT_IMG(state, BOOT_PRIMARY_SLOT).num_sectors;
     } else if (flash_area == FLASH_AREA_IMAGE_SECONDARY) {
-        out_sectors = state->imgs[BOOT_SECONDARY_SLOT].sectors;
-        out_num_sectors = &state->imgs[BOOT_SECONDARY_SLOT].num_sectors;
+        out_sectors = BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors;
+        out_num_sectors = &BOOT_IMG(state, BOOT_SECONDARY_SLOT).num_sectors;
     } else if (flash_area == FLASH_AREA_IMAGE_SCRATCH) {
         out_sectors = state->scratch.sectors;
         out_num_sectors = &state->scratch.num_sectors;
diff --git a/boot/bootutil/src/encrypted.c b/boot/bootutil/src/encrypted.c
index 6ac10d6..6dcaa22 100644
--- a/boot/bootutil/src/encrypted.c
+++ b/boot/bootutil/src/encrypted.c
@@ -322,6 +322,16 @@
 }
 
 void
+boot_enc_mark_keys_invalid(void)
+{
+    size_t slot;
+
+    for(slot = 0; slot < BOOT_NUM_SLOTS; ++slot) {
+        enc_state[slot].valid = 0;
+    }
+}
+
+void
 boot_encrypt(const struct flash_area *fap, uint32_t off, uint32_t sz,
         uint32_t blk_off, uint8_t *buf)
 {
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index a843d73..4c94fa7 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -190,6 +190,18 @@
              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 != current_image) {
+                source = BOOT_STATUS_SOURCE_NONE;
+            }
+#endif
+
             BOOT_LOG_INF("Boot source: %s",
                          source == BOOT_STATUS_SOURCE_NONE ? "none" :
                          source == BOOT_STATUS_SOURCE_SCRATCH ? "scratch" :
@@ -305,8 +317,8 @@
      * 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_data.imgs[BOOT_PRIMARY_SLOT].area);
-    align = flash_area_align(boot_data.scratch.area);
+    elem_sz = flash_area_align(BOOT_IMG_AREA(&boot_data, BOOT_PRIMARY_SLOT));
+    align = flash_area_align(BOOT_SCRATCH_AREA(&boot_data));
     if (align > elem_sz) {
         elem_sz = align;
     }
@@ -984,7 +996,7 @@
     if (bs->swap_type != BOOT_SWAP_TYPE_NONE) {
         rc = boot_write_swap_info(fap,
                                   bs->swap_type,
-                                  0);
+                                  current_image);
         assert(rc == 0);
     }
 
@@ -1100,8 +1112,8 @@
      * controls if special handling is needed (swapping last sector).
      */
     last_sector = boot_img_num_sectors(&boot_data, BOOT_PRIMARY_SLOT) - 1;
-    if (img_off + sz > boot_img_sector_off(&boot_data, BOOT_PRIMARY_SLOT,
-                                           last_sector)) {
+    if ((img_off + sz) >
+        boot_img_sector_off(&boot_data, BOOT_PRIMARY_SLOT, last_sector)) {
         copy_sz -= trailer_sz;
     }
 
@@ -1208,7 +1220,7 @@
             if (swap_state.swap_type != BOOT_SWAP_TYPE_NONE) {
                 rc = boot_write_swap_info(fap_primary_slot,
                                           swap_state.swap_type,
-                                          0);
+                                          current_image);
                 assert(rc == 0);
             }
 
@@ -1597,83 +1609,292 @@
 #endif /* !MCUBOOT_OVERWRITE_ONLY */
 
 /**
- * Performs an image swap if one is required.
+ * Performs a clean (not aborted) image update.
  *
- * @param out_swap_type         On success, the type of swap performed gets
- *                                  written here.
+ * @param bs                    The current boot status.
  *
  * @return                      0 on success; nonzero on failure.
  */
 static int
-boot_swap_if_needed(int *out_swap_type)
+boot_perform_update(struct boot_status *bs)
 {
-    struct boot_status bs;
     int rc;
 
-    /* Determine if we rebooted in the middle of an image swap operation. */
-    rc = boot_read_status(&bs);
+    /* At this point there are no aborted swaps. */
+#if defined(MCUBOOT_OVERWRITE_ONLY)
+    rc = boot_copy_image(bs);
+#elif defined(MCUBOOT_BOOTSTRAP)
+    /* Check if the image update was triggered by a bad image in the
+     * primary slot (the validity of the image in the secondary slot had
+     * already been checked).
+     */
+    if (boot_check_header_erased(BOOT_PRIMARY_SLOT) == 0 ||
+        boot_validate_slot(BOOT_PRIMARY_SLOT, bs) != 0) {
+        rc = boot_copy_image(bs);
+    } else {
+        rc = boot_swap_image(bs);
+    }
+#else
+        rc = boot_swap_image(bs);
+#endif
     assert(rc == 0);
-    if (rc != 0) {
-        return rc;
+
+#ifndef MCUBOOT_OVERWRITE_ONLY
+    /* The following state needs image_ok be explicitly set after the
+     * swap was finished to avoid a new revert.
+     */
+    if (BOOT_SWAP_TYPE(&boot_data) == BOOT_SWAP_TYPE_REVERT ||
+        BOOT_SWAP_TYPE(&boot_data) == BOOT_SWAP_TYPE_PERM) {
+        rc = boot_set_image_ok();
+        if (rc != 0) {
+            BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_PANIC;
+        }
     }
 
-    /* If a partial swap was detected, complete it. */
-    if (bs.idx != BOOT_STATUS_IDX_0 || bs.state != BOOT_STATUS_STATE_0) {
-#ifdef MCUBOOT_OVERWRITE_ONLY
-        /* Should never arrive here, overwrite-only mode has no swap state. */
-        assert(0);
-#else
-        /* Determine the type of swap operation being resumed from the
-         * `swap-type` trailer field.
-         */
-        rc = boot_swap_image(&bs);
-        assert(rc == 0);
-#endif
-
-    } else {
-        if (bs.swap_type == BOOT_SWAP_TYPE_NONE) {
-            bs.swap_type = boot_validated_swap_type(&bs);
-        } else if (boot_validate_slot(BOOT_SECONDARY_SLOT, &bs) != 0) {
-            bs.swap_type = BOOT_SWAP_TYPE_FAIL;
+    if (BOOT_SWAP_TYPE(&boot_data) == BOOT_SWAP_TYPE_TEST ||
+        BOOT_SWAP_TYPE(&boot_data) == BOOT_SWAP_TYPE_PERM ||
+        BOOT_SWAP_TYPE(&boot_data) == BOOT_SWAP_TYPE_REVERT) {
+        rc = boot_set_copy_done();
+        if (rc != 0) {
+            BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_PANIC;
         }
-        switch (bs.swap_type) {
-        case BOOT_SWAP_TYPE_TEST:
-        case BOOT_SWAP_TYPE_PERM:
-        case BOOT_SWAP_TYPE_REVERT:
-#ifdef MCUBOOT_OVERWRITE_ONLY
-            rc = boot_copy_image(&bs);
-#else
-            rc = boot_swap_image(&bs);
-#endif
-            assert(rc == 0);
-            break;
-#ifdef MCUBOOT_BOOTSTRAP
-        case BOOT_SWAP_TYPE_NONE:
-            /*
-             * Header checks are done first because they are inexpensive.
-             * Since overwrite-only copies starting from offset 0, if
-             * interrupted, it might leave a valid header magic, so also
-             * run validation on the primary slot to be sure it's not OK.
-             */
-            if (boot_check_header_erased(BOOT_PRIMARY_SLOT) == 0 ||
-                boot_validate_slot(BOOT_PRIMARY_SLOT, &bs) != 0) {
-                if ((boot_img_hdr(&boot_data, BOOT_SECONDARY_SLOT)->ih_magic
-                     == IMAGE_MAGIC ) &&
-                    (boot_validate_slot(BOOT_SECONDARY_SLOT, &bs) == 0)) {
-                    rc = boot_copy_image(&bs);
-                    assert(rc == 0);
+    }
+#endif /* !MCUBOOT_OVERWRITE_ONLY */
 
-                    /* Returns fail here to trigger a re-read of the headers. */
-                    bs.swap_type = BOOT_SWAP_TYPE_FAIL;
+    return rc;
+}
+
+/**
+ * Completes a previously aborted image swap.
+ *
+ * @param bs                    The current boot status.
+ *
+ * @return                      0 on success; nonzero on failure.
+ */
+#if !defined(MCUBOOT_OVERWRITE_ONLY)
+static int
+boot_complete_partial_swap(struct boot_status *bs)
+{
+    int rc;
+
+    /* Determine the type of swap operation being resumed from the
+     * `swap-type` trailer field.
+     */
+    rc = boot_swap_image(bs);
+    assert(rc == 0);
+
+    BOOT_SWAP_TYPE(&boot_data) = bs->swap_type;
+
+    /* The following states need image_ok be explicitly set after the
+     * swap was finished to avoid a new revert.
+     */
+    if (bs->swap_type == BOOT_SWAP_TYPE_REVERT ||
+        bs->swap_type == BOOT_SWAP_TYPE_PERM) {
+        rc = boot_set_image_ok();
+        if (rc != 0) {
+            BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_PANIC;
+        }
+    }
+
+    if (bs->swap_type == BOOT_SWAP_TYPE_TEST ||
+        bs->swap_type == BOOT_SWAP_TYPE_PERM ||
+        bs->swap_type == BOOT_SWAP_TYPE_REVERT) {
+        rc = boot_set_copy_done();
+        if (rc != 0) {
+            BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_PANIC;
+        }
+    }
+
+    if (BOOT_SWAP_TYPE(&boot_data) == BOOT_SWAP_TYPE_PANIC) {
+        BOOT_LOG_ERR("panic!");
+        assert(0);
+
+        /* Loop forever... */
+        while (1) {}
+    }
+
+    return rc;
+}
+#endif /* !MCUBOOT_OVERWRITE_ONLY */
+
+#if (BOOT_IMAGE_NUMBER > 1)
+/**
+ * Review the validity of previously determined swap types of other images.
+ *
+ * @param aborted_swap          The current image upgrade is a
+ *                              partial/aborted swap.
+ */
+static void
+boot_review_image_swap_types(bool aborted_swap)
+{
+    /* In that case if we rebooted in the middle of an image upgrade process, we
+     * must review the validity of swap types, that were previously determined
+     * for other images. The image_ok flag had not been set before the reboot
+     * for any of the updated images (only the copy_done flag) and thus falsely
+     * the REVERT swap type has been determined for the previous images that had
+     * been updated before the reboot.
+     *
+     * There are two separate scenarios that we have to deal with:
+     *
+     * 1. The reboot has happened during swapping an image:
+     *      The current image upgrade has been determined as a
+     *      partial/aborted swap.
+     * 2. The reboot has happened between two separate image upgrades:
+     *      In this scenario we must check the swap type of the current image.
+     *      In those cases if it is NONE or REVERT we cannot certainly determine
+     *      the fact of a reboot. In a consistent state images must move in the
+     *      same direction or stay in place, e.g. in practice REVERT and TEST
+     *      swap types cannot be present at the same time. If the swap type of
+     *      the current image is either TEST, PERM or FAIL we must review the
+     *      already determined swap types of other images and set each false
+     *      REVERT swap types to NONE (these images had been successfully
+     *      updated before the system rebooted between two separate image
+     *      upgrades).
+     */
+
+    if (current_image == 0) {
+        /* Nothing to do */
+        return;
+    }
+
+    if (!aborted_swap) {
+        if ((BOOT_SWAP_TYPE(&boot_data) == BOOT_SWAP_TYPE_NONE) ||
+            (BOOT_SWAP_TYPE(&boot_data) == BOOT_SWAP_TYPE_REVERT)) {
+            /* Nothing to do */
+            return;
+        }
+    }
+
+    for (uint8_t i = 0; i < current_image; i++) {
+        if (boot_data.swap_type[i] == BOOT_SWAP_TYPE_REVERT) {
+            boot_data.swap_type[i] = BOOT_SWAP_TYPE_NONE;
+        }
+    }
+}
+#endif
+
+/**
+ * Prepare image to be updated if required.
+ *
+ * Prepare image to be updated if required with completing an image swap
+ * operation if one was aborted and/or determining the type of the
+ * swap operation. In case of any error set the swap type to NONE.
+ *
+ * @param bs                    Pointer where the read and possibly updated
+ *                              boot status can be written to.
+ */
+static void
+boot_prepare_image_for_update(struct boot_status *bs)
+{
+    int rc;
+
+    /* Determine the sector layout of the image slots and scratch area. */
+    rc = boot_read_sectors();
+    if (rc != 0) {
+        BOOT_LOG_WRN("Failed reading sectors; BOOT_MAX_IMG_SECTORS=%d"
+                     " - too small?", BOOT_MAX_IMG_SECTORS);
+        /* Unable to determine sector layout, continue with next image
+         * if there is one.
+         */
+        BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_NONE;
+        return;
+    }
+
+    /* Attempt to read an image header from each slot. */
+    rc = boot_read_image_headers(false);
+    if (rc != 0) {
+        /* Continue with next image if there is one. */
+        BOOT_LOG_WRN("Failed reading image headers; Image=%u", current_image);
+        BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_NONE;
+        return;
+    }
+
+    /* If the current image's slots aren't compatible, no swap is possible.
+     * Just boot into primary slot.
+     */
+    if (boot_slots_compatible()) {
+
+        rc = boot_read_status(bs);
+        if (rc != 0) {
+            BOOT_LOG_WRN("Failed reading boot status; Image=%u",
+                         current_image);
+            /* Continue with next image if there is one. */
+            BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_NONE;
+            return;
+        }
+
+        /* 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_IMAGE_NUMBER > 1)
+            boot_review_image_swap_types(true);
+#endif
+
+#ifdef MCUBOOT_OVERWRITE_ONLY
+            /* Should never arrive here, overwrite-only mode has
+             * no swap state.
+             */
+            assert(0);
+#else
+            /* Determine the type of swap operation being resumed from the
+             * `swap-type` trailer field.
+             */
+            rc = boot_complete_partial_swap(bs);
+            assert(rc == 0);
+#endif
+            /* 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(false);
+            assert(rc == 0);
+
+            /* Swap has finished set to NONE */
+            BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_NONE;
+        } else {
+            /* There was no partial swap, determine swap type. */
+            if (bs->swap_type == BOOT_SWAP_TYPE_NONE) {
+                BOOT_SWAP_TYPE(&boot_data) = boot_validated_swap_type(bs);
+            } else if (boot_validate_slot(BOOT_SECONDARY_SLOT, bs) != 0) {
+                BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_FAIL;
+            } else {
+                BOOT_SWAP_TYPE(&boot_data) = bs->swap_type;
+            }
+
+#if (BOOT_IMAGE_NUMBER > 1)
+            boot_review_image_swap_types(false);
+#endif
+
+#ifdef MCUBOOT_BOOTSTRAP
+            if (BOOT_SWAP_TYPE(&boot_data) == BOOT_SWAP_TYPE_NONE) {
+                /* Header checks are done first because they are
+                 * inexpensive. Since overwrite-only copies starting from
+                 * offset 0, if interrupted, it might leave a valid header
+                 * magic, so also run validation on the primary slot to be
+                 * sure it's not OK.
+                 */
+                 if (boot_check_header_erased(BOOT_PRIMARY_SLOT) == 0 ||
+                     boot_validate_slot(BOOT_PRIMARY_SLOT, bs) != 0) {
+                     if (boot_img_hdr(&boot_data,
+                          BOOT_SECONDARY_SLOT)->ih_magic == IMAGE_MAGIC &&
+                         boot_validate_slot(BOOT_SECONDARY_SLOT, bs) == 0)
+                     {
+                        /* Set swap type to REVERT to overwrite the primary
+                         * slot with the image contained in secondary slot
+                         * and to trigger the explicit setting of the
+                         * image_ok flag.
+                         */
+                         BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_REVERT;
+                    }
                 }
             }
-            break;
 #endif
         }
+    } else {
+        /* In that case if slots are not compatible. */
+        BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_NONE;
     }
-
-    *out_swap_type = bs.swap_type;
-    return 0;
 }
 
 /**
@@ -1687,161 +1908,187 @@
 int
 boot_go(struct boot_rsp *rsp)
 {
-    int swap_type;
     size_t slot;
+    struct boot_status bs;
     int rc;
     int fa_id;
-    bool reload_headers = false;
 
     /* The array of slot sectors are defined here (as opposed to file scope) so
      * that they don't get allocated for non-boot-loader apps.  This is
      * necessary because the gcc option "-fdata-sections" doesn't seem to have
      * any effect in older gcc versions (e.g., 4.8.4).
      */
-    static boot_sector_t primary_slot_sectors[BOOT_MAX_IMG_SECTORS];
-    static boot_sector_t secondary_slot_sectors[BOOT_MAX_IMG_SECTORS];
+    static boot_sector_t
+        primary_slot_sectors[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS];
+    static boot_sector_t
+        secondary_slot_sectors[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS];
     static boot_sector_t scratch_sectors[BOOT_MAX_IMG_SECTORS];
-    boot_data.imgs[BOOT_PRIMARY_SLOT].sectors = primary_slot_sectors;
-    boot_data.imgs[BOOT_SECONDARY_SLOT].sectors = secondary_slot_sectors;
-    boot_data.scratch.sectors = scratch_sectors;
 
 #ifdef MCUBOOT_ENC_IMAGES
     /* FIXME: remove this after RAM is cleared by sim */
     boot_enc_zeroize();
 #endif
 
-    /* Open boot_data image areas for the duration of this call. */
-    for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
-        fa_id = flash_area_id_from_image_slot(slot);
-        rc = flash_area_open(fa_id, &BOOT_IMG_AREA(&boot_data, slot));
-        assert(rc == 0);
-    }
-    rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH,
-                         &BOOT_SCRATCH_AREA(&boot_data));
-    assert(rc == 0);
-
-    /* Determine the sector layout of the image slots and scratch area. */
-    rc = boot_read_sectors();
-    if (rc != 0) {
-        BOOT_LOG_WRN("Failed reading sectors; BOOT_MAX_IMG_SECTORS=%d - too small?",
-                BOOT_MAX_IMG_SECTORS);
-        goto out;
-    }
-
-    /* Attempt to read an image header from each slot. */
-    rc = boot_read_image_headers(false);
-    if (rc != 0) {
-        goto out;
-    }
-
-    /* If the image slots aren't compatible, no swap is possible.  Just boot
-     * into the primary slot.
+    /* Iterate over all the images. By the end of the loop the swap type has
+     * to be determined for each image and all aborted swaps have to be
+     * completed.
      */
-    if (boot_slots_compatible()) {
-        rc = boot_swap_if_needed(&swap_type);
-        assert(rc == 0);
-        if (rc != 0) {
-            goto out;
-        }
+    for (current_image = 0; current_image < BOOT_IMAGE_NUMBER; ++current_image)
+    {
 
-        /*
-         * The following states need image_ok be explicitly set after the
-         * swap was finished to avoid a new revert.
+#if defined(MCUBOOT_ENC_IMAGES) && (BOOT_IMAGE_NUMBER > 1)
+        /* The keys used for encryption may no longer be valid (could belong to
+         * another images). Therefore, mark them as invalid to force their reload
+         * by boot_enc_load().
          */
-        if (swap_type == BOOT_SWAP_TYPE_REVERT ||
-            swap_type == BOOT_SWAP_TYPE_FAIL ||
-            swap_type == BOOT_SWAP_TYPE_PERM) {
-#ifndef MCUBOOT_OVERWRITE_ONLY
-            rc = boot_set_image_ok();
-            if (rc != 0) {
-                swap_type = BOOT_SWAP_TYPE_PANIC;
-            }
-#endif /* !MCUBOOT_OVERWRITE_ONLY */
-        }
-    } else {
-        swap_type = BOOT_SWAP_TYPE_NONE;
-    }
-
-    switch (swap_type) {
-    case BOOT_SWAP_TYPE_NONE:
-        slot = BOOT_PRIMARY_SLOT;
-        break;
-
-    case BOOT_SWAP_TYPE_TEST:          /* fallthrough */
-    case BOOT_SWAP_TYPE_PERM:          /* fallthrough */
-    case BOOT_SWAP_TYPE_REVERT:
-        slot = BOOT_SECONDARY_SLOT;
-        reload_headers = true;
-#ifndef MCUBOOT_OVERWRITE_ONLY
-        rc = boot_set_copy_done();
-        if (rc != 0) {
-            swap_type = BOOT_SWAP_TYPE_PANIC;
-        }
-#endif /* !MCUBOOT_OVERWRITE_ONLY */
-        break;
-
-    case BOOT_SWAP_TYPE_FAIL:
-        /* The image in the secondary slot was invalid and is now erased.
-         * Ensure we don't try to boot into it again on the next reboot.
-         * Do this by pretending we just reverted back to the primary slot.
-         */
-        slot = BOOT_PRIMARY_SLOT;
-        reload_headers = true;
-        break;
-
-    default:
-        swap_type = BOOT_SWAP_TYPE_PANIC;
-    }
-
-    if (swap_type == BOOT_SWAP_TYPE_PANIC) {
-        BOOT_LOG_ERR("panic!");
-        assert(0);
-
-        /* Loop forever... */
-        while (1) {}
-    }
-
-    if (reload_headers) {
-        rc = boot_read_image_headers(false);
-        if (rc != 0) {
-            goto out;
-        }
-        /* Since headers were reloaded, it can be assumed we just performed a
-         * swap or overwrite. Now the header info that should be used to
-         * provide the data for the bootstrap, which previously was at the
-         * secondary slot, was updated to the primary slot.
-         */
-        slot = BOOT_PRIMARY_SLOT;
-    }
-
-#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
-    rc = boot_validate_slot(BOOT_PRIMARY_SLOT, NULL);
-    if (rc != 0) {
-        rc = BOOT_EBADIMAGE;
-        goto out;
-    }
-#else
-    /* Even if we're not re-validating the primary slot, we could be booting
-     * onto an empty flash chip. At least do a basic sanity check that
-     * the magic number on the image is OK.
-     */
-    if (boot_data.imgs[BOOT_PRIMARY_SLOT].hdr.ih_magic != IMAGE_MAGIC) {
-        BOOT_LOG_ERR("bad image magic 0x%lx",
-                (unsigned long)boot_data.imgs[BOOT_PRIMARY_SLOT].hdr.ih_magic);
-        rc = BOOT_EBADIMAGE;
-        goto out;
-    }
+        boot_enc_mark_keys_invalid();
 #endif
 
-    /* Always boot from the primary slot. */
-    rsp->br_flash_dev_id = boot_data.imgs[BOOT_PRIMARY_SLOT].area->fa_device_id;
-    rsp->br_image_off = boot_img_slot_off(&boot_data, BOOT_PRIMARY_SLOT);
-    rsp->br_hdr = boot_img_hdr(&boot_data, slot);
+        BOOT_IMG(&boot_data, BOOT_PRIMARY_SLOT).sectors =
+                                        primary_slot_sectors[current_image];
+        BOOT_IMG(&boot_data, BOOT_SECONDARY_SLOT).sectors =
+                                        secondary_slot_sectors[current_image];
+        boot_data.scratch.sectors = scratch_sectors;
+
+        /* Open primary and secondary image areas for the duration
+         * of this call.
+         */
+        for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
+            fa_id = flash_area_id_from_image_slot(slot);
+            rc = flash_area_open(fa_id, &BOOT_IMG_AREA(&boot_data, slot));
+            assert(rc == 0);
+        }
+        rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH,
+                             &BOOT_SCRATCH_AREA(&boot_data));
+        assert(rc == 0);
+
+        /* Determine swap type and complete swap if it has been aborted. */
+        boot_prepare_image_for_update(&bs);
+    }
+
+    /* Iterate over all the images. At this point there are no aborted swaps
+     * and the swap types are determined for each image. By the end of the loop
+     * all required update operations will have been finished.
+     */
+    for (current_image = 0; current_image < BOOT_IMAGE_NUMBER; ++current_image)
+    {
+
+#if (BOOT_IMAGE_NUMBER > 1)
+#ifdef MCUBOOT_ENC_IMAGES
+        /* The keys used for encryption may no longer be valid (could belong to
+         * another images). Therefore, mark them as invalid to force their reload
+         * by boot_enc_load().
+         */
+        boot_enc_mark_keys_invalid();
+#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;
+#endif /* (BOOT_IMAGE_NUMBER > 1) */
+
+        /* Set the previously determined swap type */
+        bs.swap_type = BOOT_SWAP_TYPE(&boot_data);
+
+        switch (BOOT_SWAP_TYPE(&boot_data)) {
+        case BOOT_SWAP_TYPE_NONE:
+            break;
+
+        case BOOT_SWAP_TYPE_TEST:          /* fallthrough */
+        case BOOT_SWAP_TYPE_PERM:          /* fallthrough */
+        case BOOT_SWAP_TYPE_REVERT:
+            rc = boot_perform_update(&bs);
+            assert(rc == 0);
+            break;
+
+        case BOOT_SWAP_TYPE_FAIL:
+            /* The image in secondary slot was invalid and is now erased. Ensure
+             * we don't try to boot into it again on the next reboot. Do this by
+             * pretending we just reverted back to primary slot.
+             */
+#ifndef MCUBOOT_OVERWRITE_ONLY
+            /* image_ok needs to be explicitly set to avoid a new revert. */
+            rc = boot_set_image_ok();
+            if (rc != 0) {
+                BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_PANIC;
+            }
+#endif /* !MCUBOOT_OVERWRITE_ONLY */
+            break;
+
+        default:
+            BOOT_SWAP_TYPE(&boot_data) = BOOT_SWAP_TYPE_PANIC;
+        }
+
+        if (BOOT_SWAP_TYPE(&boot_data) == BOOT_SWAP_TYPE_PANIC) {
+            BOOT_LOG_ERR("panic!");
+            assert(0);
+
+            /* Loop forever... */
+            while (1) {}
+        }
+    }
+
+    /* Iterate over all the images. At this point all required update operations
+     * have finished. By the end of the loop each image in the primary slot will
+     * have been re-validated.
+     */
+    for (current_image = 0; current_image < BOOT_IMAGE_NUMBER; ++current_image)
+    {
+        if (BOOT_SWAP_TYPE(&boot_data) != BOOT_SWAP_TYPE_NONE) {
+            /* 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(false);
+            if (rc != 0) {
+                goto out;
+            }
+            /* Since headers were reloaded, it can be assumed we just performed
+             * a swap or overwrite. Now the header info that should be used to
+             * provide the data for the bootstrap, which previously was at
+             * secondary slot, was updated to primary slot.
+             */
+        }
+
+#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
+        rc = boot_validate_slot(BOOT_PRIMARY_SLOT, NULL);
+        if (rc != 0) {
+            rc = BOOT_EBADIMAGE;
+            goto out;
+        }
+#else
+        /* Even if we're not re-validating the primary slot, we could be booting
+         * onto an empty flash chip. At least do a basic sanity check that
+         * the magic number on the image is OK.
+         */
+        if (BOOT_IMG(&boot_data, BOOT_PRIMARY_SLOT).hdr.ih_magic !=
+                IMAGE_MAGIC) {
+            BOOT_LOG_ERR("bad image magic 0x%lx; Image=%u", (unsigned long)
+                         &boot_img_hdr(&boot_data,BOOT_PRIMARY_SLOT)->ih_magic,
+                         current_image);
+            rc = BOOT_EBADIMAGE;
+            goto out;
+        }
+#endif
+    }
+
+    /* Always boot from the primary slot of Image 0. */
+    current_image = 0;
+    rsp->br_flash_dev_id =
+            BOOT_IMG_AREA(&boot_data, BOOT_PRIMARY_SLOT)->fa_device_id;
+    rsp->br_image_off =
+            boot_img_slot_off(&boot_data, BOOT_PRIMARY_SLOT);
+    rsp->br_hdr =
+            boot_img_hdr(&boot_data, BOOT_PRIMARY_SLOT);
 
  out:
-    flash_area_close(BOOT_SCRATCH_AREA(&boot_data));
-    for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
-        flash_area_close(BOOT_IMG_AREA(&boot_data, BOOT_NUM_SLOTS - 1 - slot));
+    for (current_image = 0; current_image < BOOT_IMAGE_NUMBER; ++current_image)
+    {
+        flash_area_close(BOOT_SCRATCH_AREA(&boot_data));
+        for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
+            flash_area_close(BOOT_IMG_AREA(&boot_data,
+                                           BOOT_NUM_SLOTS - 1 - slot));
+        }
     }
     return rc;
 }
@@ -1859,8 +2106,8 @@
     if (sectors == NULL) {
         return SPLIT_GO_ERR;
     }
-    boot_data.imgs[loader_slot].sectors = sectors + 0;
-    boot_data.imgs[split_slot].sectors = sectors + BOOT_MAX_IMG_SECTORS;
+    BOOT_IMG(&boot_data, loader_slot).sectors = sectors + 0;
+    BOOT_IMG(&boot_data, split_slot).sectors = sectors + BOOT_MAX_IMG_SECTORS;
 
     loader_flash_id = flash_area_id_from_image_slot(loader_slot);
     rc = flash_area_open(loader_flash_id,
diff --git a/docs/design.md b/docs/design.md
index edb98c6..6573c25 100644
--- a/docs/design.md
+++ b/docs/design.md
@@ -17,6 +17,10 @@
   under the License.
 -->
 
+<!--
+  Modifications are Copyright (c) 2019 Arm Limited.
+-->
+
 # Boot Loader
 
 ## Summary
@@ -200,19 +204,19 @@
 ## Boot Swap Types
 
 When the device first boots under normal circumstances, there is an up-to-date
-firmware image in the primary slot, which mcuboot can validate and then
+firmware image in each primary slot, which mcuboot can validate and then
 chain-load. In this case, no image swaps are necessary. During device upgrades,
-however, new candidate images are present in the secondary slot, which mcuboot
-must swap into the primary slot before booting as discussed above.
+however, new candidate image(s) is present in the secondary slot(s), which
+mcuboot must swap into the primary slot(s) before booting as discussed above.
 
 Upgrading an old image with a new one by swapping can be a two-step process. In
 this process, mcuboot performs a "test" swap of image data in flash and boots
-the new image. The new image can then update the contents of flash at runtime
-to mark itself "OK", and mcuboot will then still choose to run it during the
-next boot. When this happens, the swap is made "permanent". If this doesn't
-happen, mcuboot will perform a "revert" swap during the next boot by swapping
-the images back into their original locations, and attempting to boot the old
-image.
+the new image or it will be executed during operation. The new image can then
+update the contents of flash at runtime to mark itself "OK", and mcuboot will
+then still choose to run it during the next boot. When this happens, the swap is
+made "permanent". If this doesn't happen, mcuboot will perform a "revert" swap
+during the next boot by swapping the image(s) back into its original location(s)
+, and attempting to boot the old image(s).
 
 Depending on the use case, the first swap can also be made permanent directly.
 In this case, mcuboot will never attempt to revert the images on the next reset.
@@ -224,8 +228,9 @@
 device firmware to make test swaps permanent only after performing a self-test
 routine.
 
-On startup, mcuboot inspects the contents of flash to decide which of these
-"swap types" to perform; this decision determines how it proceeds.
+On startup, mcuboot inspects the contents of flash to decide for each images
+which of these "swap types" to perform; this decision determines how it
+proceeds.
 
 The possible swap types, and their meanings, are:
 
@@ -488,6 +493,89 @@
 
 3. Boot into image in primary slot.
 
+### Multiple Image Boot
+
+When the flash contains multiple executable images the boot loader's operation
+is a bit more complex but similar to the previously described procedure with
+one image. Every image can be updated independently therefore the flash is
+partitioned further to arrange two slots for each image.
+```
++--------------------+
+| MCUBoot            |
++--------------------+
+        ~~~~~            <- memory might be not contiguous
++--------------------+
+| Image 0            |
+| primary   slot     |
++--------------------+
+| Image 0            |
+| secondary slot     |
++--------------------+
+        ~~~~~            <- memory might be not contiguous
++--------------------+
+| Image N            |
+| primary   slot     |
++--------------------+
+| Image N            |
+| secondary slot     |
++--------------------+
+| Scratch            |
++--------------------+
+```
+The multiple image boot procedure is organized in loops which iterate over all
+the firmware images. The high-level overview of the boot process is presented
+below.
+
+Procedure:
+
++ ###### Loop 1. Iterate over all images
+    1. Inspect swap status region of current image; is an interrupted swap being
+       resumed?
+        + Yes:
+            + Review the validity of previously determined swap types
+              of other images.
+            + Complete the partial swap operation.
+            + Mark the swap type as `None`.
+            + Skip to next image.
+        + No: Proceed to step 2.
+
+    2. Inspect image trailers in the primary and secondary slot; is an image
+       swap requested?
+        + Yes: Review the validity of previously determined swap types of other
+               images. Is the requested image valid (integrity and security
+               check)?
+            + Yes:
+                + Set the previously determined swap type for the current image.
+                + Skip to next image.
+            + No:
+                + Erase invalid image.
+                + Persist failure of swap procedure to image trailers.
+                + Mark the swap type as `Fail`.
+                + Skip to next image.
+        + No:
+            + Mark the swap type as `None`.
+            + Skip to next image.
+
++ ###### Loop 2. Iterate over all images
+    At this point there are no aborted swaps and the swap types are determined
+    for each image.
+
+    1. Is an image swap requested?
+        + Yes:
+            + Perform image update operation.
+            + Persist completion of swap procedure to image trailers.
+            + Skip to next image.
+        + No: Skip to next image.
+
++ ###### Loop 3. Iterate over all images
+
+    1. Validate image in the primary slot (integrity and security check) or
+       at least do a basic sanity check to avoid booting into an empty flash
+       area.
+
++ Boot into image in the primary slot of the 0th image position\
+  (other image in the boot chain is started by another image).
+
 ## Image Swapping
 
 The boot loader swaps the contents of the two image slots for two reasons:
@@ -662,7 +750,11 @@
 Because this region is embedded within the image slots, its location in flash
 changes during a swap operation.  The below set of tables map image trailers
 contents to swap status location.  In these tables, the "source" field
-indicates where the swap status region is located.
+indicates where the swap status region is located. In case of multi image boot
+the images primary area and the single scratch area is always examined in pairs.
+If swap status found on scratch area then it might not belong to the current
+image. The swap_info field of swap status stores the corresponding image number.
+If it does not match then "source: none" is returned.
 
 ```
               | primary slot | scratch      |
diff --git a/docs/readme-zephyr.md b/docs/readme-zephyr.md
index ac183c5..26bc519 100644
--- a/docs/readme-zephyr.md
+++ b/docs/readme-zephyr.md
@@ -13,13 +13,16 @@
 partitions defined in its device tree. These partitions are:
 
 - `boot_partition`: for MCUboot itself
-- `primary_slot_partition`: the primary image slot
-- `secondary_slot_partition`: the secondary image slot
+- `image_0_primary_partition`: the primary slot of Image 0
+- `image_0_secondary_partition`: the secondary slot of Image 0
 - `scratch_partition`: the scratch slot
 
 Currently, the two image slots must be contiguous. If you are running
 MCUboot as your stage 1 bootloader, `boot_partition` must be configured
-so your SoC runs it out of reset.
+so your SoC runs it out of reset. If there are multiple updateable images
+then the corresponding primary and secondary partitions must be defined for
+the rest of the images too (e.g. `image_1_primary_partition` and
+`image_1_secondary_partition` for Image 1).
 
 The flash partitions are typically defined in the Zephyr boards folder, in a
 file named `boards/<arch>/<board>/<board>.dts`. An example `.dts` file with