Boot: Extend flash layout for multiple images

This patch introduces the BOOT_IMAGE_NUMBER macro and current_image
variable to support multiple updatable images and the associated
extended flash layout.

The FLASH_AREA_IMAGE_* object-like macros are replaced with
function-like ones and therefore some functions have been updated,
because the labels of a switch statement and the initialization
values of objects with static storage duration have to be constant
expressions.

Change-Id: Ib7b26ec3c94233e52db4f97825ddb6a3e55bb1d3
Signed-off-by: David Vincze <david.vincze@arm.com>
diff --git a/NOTICE b/NOTICE
index 4ba7354..f7af8ab 100644
--- a/NOTICE
+++ b/NOTICE
@@ -6,3 +6,6 @@
 
 Portions of this software were developed at
 Runtime Inc, copyright 2015.
+
+Portions of this software were developed at
+Arm Limited, copyright 2019.
diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c
index 041f6a2..b2a07d0 100644
--- a/boot/bootutil/src/bootutil_misc.c
+++ b/boot/bootutil/src/bootutil_misc.c
@@ -172,13 +172,12 @@
 int
 boot_status_entries(const struct flash_area *fap)
 {
-    switch (fap->fa_id) {
-    case FLASH_AREA_IMAGE_PRIMARY:
-    case FLASH_AREA_IMAGE_SECONDARY:
-        return BOOT_STATUS_STATE_COUNT * BOOT_STATUS_MAX_ENTRIES;
-    case FLASH_AREA_IMAGE_SCRATCH:
+    if (fap->fa_id == FLASH_AREA_IMAGE_SCRATCH) {
         return BOOT_STATUS_STATE_COUNT;
-    default:
+    } else if ((fap->fa_id == FLASH_AREA_IMAGE_PRIMARY) ||
+               (fap->fa_id == FLASH_AREA_IMAGE_SECONDARY)) {
+        return BOOT_STATUS_STATE_COUNT * BOOT_STATUS_MAX_ENTRIES;
+    } else {
         return BOOT_EBADARGS;
     }
 }
@@ -301,16 +300,14 @@
     const struct flash_area *fap;
     int rc;
 
-    switch (flash_area_id) {
-    case FLASH_AREA_IMAGE_SCRATCH:
-    case FLASH_AREA_IMAGE_PRIMARY:
-    case FLASH_AREA_IMAGE_SECONDARY:
+    if (flash_area_id == FLASH_AREA_IMAGE_SCRATCH ||
+        flash_area_id == FLASH_AREA_IMAGE_PRIMARY ||
+        flash_area_id == FLASH_AREA_IMAGE_SECONDARY) {
         rc = flash_area_open(flash_area_id, &fap);
         if (rc != 0) {
             return BOOT_EFLASH;
         }
-        break;
-    default:
+    } else {
         return BOOT_EBADARGS;
     }
 
diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h
index bc35a72..11d0a54 100644
--- a/boot/bootutil/src/bootutil_priv.h
+++ b/boot/bootutil/src/bootutil_priv.h
@@ -134,6 +134,12 @@
     uint8_t image_num;  /* Boot status belongs to this image */
 };
 
+#ifdef MCUBOOT_IMAGE_NUMBER
+#define BOOT_IMAGE_NUMBER          MCUBOOT_IMAGE_NUMBER
+#else
+#define BOOT_IMAGE_NUMBER          1
+#endif
+
 #define BOOT_MAX_IMG_SECTORS       MCUBOOT_MAX_IMG_SECTORS
 
 /*
@@ -302,23 +308,21 @@
     int num_sectors = BOOT_MAX_IMG_SECTORS;
     int rc;
 
-    switch (flash_area) {
-    case FLASH_AREA_IMAGE_PRIMARY:
+    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;
-        break;
-    case FLASH_AREA_IMAGE_SECONDARY:
+
+    } 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;
-        break;
-    case FLASH_AREA_IMAGE_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;
-        break;
-    default:
+    } else {
         return BOOT_EFLASH;
     }
 
@@ -350,24 +354,19 @@
     size_t *out_num_sectors;
     int rc;
 
-    switch (flash_area) {
-    case FLASH_AREA_IMAGE_PRIMARY:
-        num_sectors = BOOT_MAX_IMG_SECTORS;
+    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;
-        break;
-    case FLASH_AREA_IMAGE_SECONDARY:
-        num_sectors = BOOT_MAX_IMG_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;
-        break;
-    case FLASH_AREA_IMAGE_SCRATCH:
-        num_sectors = BOOT_MAX_IMG_SECTORS;
+    } else if (flash_area == FLASH_AREA_IMAGE_SCRATCH) {
         out_sectors = state->scratch.sectors;
         out_num_sectors = &state->scratch.num_sectors;
-        break;
-    default:
-        return -1;
+    } else {
+        return BOOT_EFLASH;
     }
 
     rc = flash_area_get_sectors(flash_area, &num_sectors, out_sectors);
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index fcbf082..a843d73 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -47,6 +47,7 @@
 MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
 
 static struct boot_loader_state boot_data;
+uint8_t current_image = 0;
 
 #if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) && !defined(MCUBOOT_OVERWRITE_ONLY)
 static int boot_status_fails = 0;
@@ -1021,18 +1022,20 @@
     uint32_t total_sz;
     uint32_t off;
     uint32_t sz;
+    int fa_id_primary;
+    int fa_id_secondary;
     int rc;
 
     BOOT_LOG_DBG("erasing trailer; fa_id=%d", fap->fa_id);
 
-    switch (fap->fa_id) {
-    case FLASH_AREA_IMAGE_PRIMARY:
+    fa_id_primary   = flash_area_id_from_image_slot(BOOT_PRIMARY_SLOT);
+    fa_id_secondary = flash_area_id_from_image_slot(BOOT_SECONDARY_SLOT);
+
+    if (fap->fa_id == fa_id_primary) {
         slot = BOOT_PRIMARY_SLOT;
-        break;
-    case FLASH_AREA_IMAGE_SECONDARY:
+    } else if (fap->fa_id == fa_id_secondary) {
         slot = BOOT_SECONDARY_SLOT;
-        break;
-    default:
+    } else {
         return BOOT_EFLASH;
     }
 
diff --git a/boot/zephyr/flash_map_extended.c b/boot/zephyr/flash_map_extended.c
index 543a467..535e69a 100644
--- a/boot/zephyr/flash_map_extended.c
+++ b/boot/zephyr/flash_map_extended.c
@@ -55,9 +55,12 @@
  */
 int flash_area_id_from_image_slot(int slot)
 {
-    static const int area_id_tab[] = {FLASH_AREA_IMAGE_PRIMARY,
-                                      FLASH_AREA_IMAGE_SECONDARY,
-                                      FLASH_AREA_IMAGE_SCRATCH};
+#if (MCUBOOT_IMAGE_NUMBER == 1)
+    static
+#endif
+    const int area_id_tab[] = {FLASH_AREA_IMAGE_PRIMARY,
+                               FLASH_AREA_IMAGE_SECONDARY,
+                               FLASH_AREA_IMAGE_SCRATCH};
 
     if (slot >= 0 && slot < ARRAY_SIZE(area_id_tab)) {
         return area_id_tab[slot];
@@ -68,15 +71,15 @@
 
 int flash_area_id_to_image_slot(int area_id)
 {
-    switch (area_id) {
-    case FLASH_AREA_IMAGE_PRIMARY:
+    if (area_id == FLASH_AREA_IMAGE_PRIMARY) {
         return 0;
-    case FLASH_AREA_IMAGE_SECONDARY:
-        return 1;
-    default:
-        BOOT_LOG_ERR("invalid flash area ID");
-        return -1;
     }
+    if (area_id == FLASH_AREA_IMAGE_SECONDARY) {
+        return 1;
+    }
+
+    BOOT_LOG_ERR("invalid flash area ID");
+    return -1;
 }
 
 int flash_area_sector_from_off(off_t off, struct flash_sector *sector)
diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h
index d79d556..925591c 100644
--- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h
+++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2018 Open Source Foundries Limited
+ * Copyright (c) 2019 Arm Limited
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -65,6 +66,12 @@
 #define MCUBOOT_BOOTSTRAP 1
 #endif
 
+#ifdef CONFIG_UPDATEABLE_IMAGE_NUMBER
+#define MCUBOOT_IMAGE_NUMBER    CONFIG_UPDATEABLE_IMAGE_NUMBER
+#else
+#define MCUBOOT_IMAGE_NUMBER    1
+#endif
+
 /*
  * Enabling this option uses newer flash map APIs. This saves RAM and
  * avoids deprecated API usage.
diff --git a/boot/zephyr/include/sysflash/sysflash.h b/boot/zephyr/include/sysflash/sysflash.h
index 2492244..d4617bb 100644
--- a/boot/zephyr/include/sysflash/sysflash.h
+++ b/boot/zephyr/include/sysflash/sysflash.h
@@ -4,9 +4,32 @@
 #define __SYSFLASH_H__
 
 #include <generated_dts_board.h>
+#include <mcuboot_config/mcuboot_config.h>
 
+extern uint8_t current_image;
+
+#if (MCUBOOT_IMAGE_NUMBER == 1)
 #define FLASH_AREA_IMAGE_PRIMARY    DT_FLASH_AREA_IMAGE_0_ID
 #define FLASH_AREA_IMAGE_SECONDARY  DT_FLASH_AREA_IMAGE_1_ID
+#elif (MCUBOOT_IMAGE_NUMBER == 2)
+/* MCUBoot currently supports only up to 2 updateable firmware images.
+ * If the number of the current image is greater than MCUBOOT_IMAGE_NUMBER - 1
+ * then a dummy value will be assigned to the flash area macros.
+ */
+#define FLASH_AREA_IMAGE_PRIMARY    ((current_image == 0) ?         \
+                                         DT_FLASH_AREA_IMAGE_0_ID : \
+                                     (current_image == 1) ?         \
+                                         DT_FLASH_AREA_IMAGE_2_ID : \
+                                         255 )
+#define FLASH_AREA_IMAGE_SECONDARY  ((current_image == 0) ?         \
+                                         DT_FLASH_AREA_IMAGE_1_ID : \
+                                     (current_image == 1) ?         \
+                                         DT_FLASH_AREA_IMAGE_3_ID : \
+                                         255 )
+#else
+#error "Image slot and flash area mapping is not defined"
+#endif
+
 #define FLASH_AREA_IMAGE_SCRATCH    DT_FLASH_AREA_IMAGE_SCRATCH_ID
 
 #endif /* __SYSFLASH_H__ */
diff --git a/boot/zephyr/include/target.h b/boot/zephyr/include/target.h
index 6e777fb..a11616f 100644
--- a/boot/zephyr/include/target.h
+++ b/boot/zephyr/include/target.h
@@ -1,5 +1,7 @@
 /*
  *  Copyright (C) 2017, Linaro Ltd
+ *  Copyright (c) 2019, Arm Limited
+ *
  *  SPDX-License-Identifier: Apache-2.0
  */
 
@@ -45,4 +47,11 @@
 #error "Target support is incomplete; cannot build mcuboot."
 #endif
 
+#if ((MCUBOOT_IMAGE_NUMBER == 2) && (!defined(FLASH_AREA_IMAGE_2_OFFSET) || \
+                                     !defined(FLASH_AREA_IMAGE_2_SIZE)   || \
+                                     !defined(FLASH_AREA_IMAGE_3_OFFSET) || \
+                                     !defined(FLASH_AREA_IMAGE_3_SIZE)))
+#error "Target support is incomplete; cannot build mcuboot."
 #endif
+
+#endif /* H_TARGETS_TARGET_ */
diff --git a/docs/PORTING.md b/docs/PORTING.md
index 2a63d50..ad2b885 100644
--- a/docs/PORTING.md
+++ b/docs/PORTING.md
@@ -86,9 +86,19 @@
 `fa_id` is can be one of the following options:
 
 ```c
-#define FLASH_AREA_IMAGE_PRIMARY    1
-#define FLASH_AREA_IMAGE_SECONDARY  2
-#define FLASH_AREA_IMAGE_SCRATCH    3
+/* Independent from multiple image boot */
+#define FLASH_AREA_BOOTLOADER         0
+#define FLASH_AREA_IMAGE_SCRATCH      3
+```
+```c
+/* Flash area IDs of the first image in case of multiple images */
+#define FLASH_AREA_IMAGE_PRIMARY      1
+#define FLASH_AREA_IMAGE_SECONDARY    2
+```
+```c
+/* Flash area IDs of the second image in case of multiple images */
+#define FLASH_AREA_IMAGE_PRIMARY      5
+#define FLASH_AREA_IMAGE_SECONDARY    6
 ```
 
 The functions that must be defined for working with the `flash_area`s are:
diff --git a/docs/design.md b/docs/design.md
index d86d89d..edb98c6 100644
--- a/docs/design.md
+++ b/docs/design.md
@@ -116,33 +116,44 @@
 2. A write to one area does not restrict writes to other areas.
 
 The boot loader uses the following flash area IDs:
-
-``` c
+```c
+/* Independent from multiple image boot */
 #define FLASH_AREA_BOOTLOADER         0
+#define FLASH_AREA_IMAGE_SCRATCH      3
+```
+```c
+/* If the boot loader is working with the first image */
 #define FLASH_AREA_IMAGE_PRIMARY      1
 #define FLASH_AREA_IMAGE_SECONDARY    2
-#define FLASH_AREA_IMAGE_SCRATCH      3
+```
+```c
+/* If the boot loader is working with the second image */
+#define FLASH_AREA_IMAGE_PRIMARY      5
+#define FLASH_AREA_IMAGE_SECONDARY    6
 ```
 
 The bootloader area contains the bootloader image itself. The other areas are
-described in subsequent sections.
+described in subsequent sections. The flash could contain multiple executable
+images therefore the flash area IDs of primary and secondary areas are mapped
+based on the number of the active image (on which the bootloader is currently
+working).
 
 ## Image Slots
 
-A portion of the flash memory is partitioned into two image slots: a primary
-slot (0) and a secondary slot (1).  The boot loader will only run an image from
-the primary slot, so images must be built such that they can run from that
-fixed location in flash.  If the boot loader needs to run the image resident in
-the secondary slot, it must copy its contents into the primary slot before doing
-so, either by swapping the two images or by overwriting the contents of the
-primary slot. The bootloader supports either swap- or overwrite-based image
-upgrades, but must be configured at build time to choose one of these two
-strategies.
+A portion of the flash memory can be partitioned into multiple image areas, each
+contains two image slots: a primary slot and a secondary slot.
+The boot loader will only run an image from the primary slot, so images must be
+built such that they can run from that fixed location in flash.  If the boot
+loader needs to run the image resident in the secondary slot, it must copy its
+contents into the primary slot before doing so, either by swapping the two
+images or by overwriting the contents of the primary slot. The bootloader
+supports either swap- or overwrite-based image upgrades, but must be configured
+at build time to choose one of these two strategies.
 
-In addition to the two image slots, the boot loader requires a scratch area to
-allow for reliable image swapping. The scratch area must have a size that is
-enough to store at least the largest sector that is going to be swapped. Many
-devices have small equally sized flash sectors, eg 4K, while others have
+In addition to the slots of image areas, the boot loader requires a scratch
+area to allow for reliable image swapping. The scratch area must have a size
+that is enough to store at least the largest sector that is going to be swapped.
+Many devices have small equally sized flash sectors, eg 4K, while others have
 variable sized sectors where the largest sectors might be 128K or 256K, so the
 scratch must be big enough to store that. The scratch is only ever used when
 swapping firmware, which means only when doing an upgrade. Given that, the main
diff --git a/samples/mcuboot_config/mcuboot_config.template.h b/samples/mcuboot_config/mcuboot_config.template.h
index 14ad218..3038a93 100644
--- a/samples/mcuboot_config/mcuboot_config.template.h
+++ b/samples/mcuboot_config/mcuboot_config.template.h
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2018 Open Source Foundries Limited
+ * Copyright (c) 2019 Arm Limited
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -82,6 +83,10 @@
  * as desirable. */
 #define MCUBOOT_MAX_IMG_SECTORS 128
 
+/* Default number of separately updateable images; change in case of
+ * multiple images. */
+#define MCUBOOT_IMAGE_NUMBER 1
+
 /*
  * Logging
  */
diff --git a/sim/mcuboot-sys/csupport/run.c b/sim/mcuboot-sys/csupport/run.c
index 8f2a01c..da2d232 100644
--- a/sim/mcuboot-sys/csupport/run.c
+++ b/sim/mcuboot-sys/csupport/run.c
@@ -397,15 +397,15 @@
 
 int flash_area_id_to_image_slot(int area_id)
 {
-    switch (area_id) {
-    case FLASH_AREA_IMAGE_PRIMARY:
+    if (area_id == FLASH_AREA_IMAGE_PRIMARY) {
         return 0;
-    case FLASH_AREA_IMAGE_SECONDARY:
-        return 1;
-    default:
-        printf("Unsupported image area ID\n");
-        abort();
     }
+    if (area_id == FLASH_AREA_IMAGE_SECONDARY) {
+        return 1;
+    }
+
+    printf("Unsupported image area ID\n");
+    abort();
 }
 
 void sim_assert(int x, const char *assertion, const char *file, unsigned int line, const char *function)