boot: Multi-image boot for direct-xip and ram-load
Multiple image boot support is implemented for direct-xip and ram-load
strategies. Dependency checking is also added, and for ram-load more
executable ram regions can be set.
Wrapper functions are intoduced to all boot strategies.
Change-Id: I59b346277dcb2c17cef4800f1e556b4ea9144845
Signed-off-by: Mark Horvath <mark.horvath@arm.com>
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index 5b78c70..7f4261b 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2016-2020 Linaro LTD
* Copyright (c) 2016-2019 JUUL Labs
- * Copyright (c) 2019-2020 Arm Limited
+ * Copyright (c) 2019-2021 Arm Limited
*
* Original license:
*
@@ -44,6 +44,7 @@
#include "bootutil/security_cnt.h"
#include "bootutil/boot_record.h"
#include "bootutil/fault_injection_hardening.h"
+#include "bootutil/ramload.h"
#ifdef MCUBOOT_ENC_IMAGES
#include "bootutil/enc_key.h"
@@ -61,6 +62,23 @@
#define IMAGES_ITER(x)
#endif
+#if defined(MCUBOOT_DIRECT_XIP) || defined(MCUBOOT_RAM_LOAD)
+struct slot_usage_t {
+ /* Index of the slot chosen to be loaded */
+ uint32_t active_slot;
+ bool slot_available[BOOT_NUM_SLOTS];
+#ifdef MCUBOOT_RAM_LOAD
+ /* Image destination and size for the active slot */
+ uint32_t img_dst;
+ uint32_t img_sz;
+#endif /* MCUBOOT_RAM_LOAD */
+#ifdef MCUBOOT_DIRECT_XIP_REVERT
+ /* Swap status for the active slot */
+ struct boot_swap_state swap_state
+#endif /* MCUBOOT_DIRECT_XIP_REVERT */
+};
+#endif
+
/*
* This macro allows some control on the allocation of local variables.
* When running natively on a target, we don't want to allocated huge
@@ -101,6 +119,101 @@
return 0;
}
+/**
+ * Saves boot status and shared data for current image.
+ *
+ * @param state Boot loader status information.
+ * @param active_slot Index of the slot will be loaded for current image.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+boot_add_shared_data(struct boot_loader_state *state,
+ uint32_t active_slot)
+{
+#if defined(MCUBOOT_MEASURED_BOOT) || defined(MCUBOOT_DATA_SHARING)
+ int rc;
+
+#ifdef MCUBOOT_MEASURED_BOOT
+ rc = boot_save_boot_status(BOOT_CURR_IMG(state),
+ boot_img_hdr(state, active_slot),
+ BOOT_IMG_AREA(state, active_slot));
+ if (rc != 0) {
+ BOOT_LOG_ERR("Failed to add image data to shared area");
+ return rc;
+ }
+#endif /* MCUBOOT_MEASURED_BOOT */
+
+#ifdef MCUBOOT_DATA_SHARING
+ rc = boot_save_shared_data(boot_img_hdr(state, active_slot),
+ BOOT_IMG_AREA(state, active_slot));
+ if (rc != 0) {
+ BOOT_LOG_ERR("Failed to add data to shared memory area.");
+ return rc;
+ }
+#endif /* MCUBOOT_DATA_SHARING */
+
+ return 0;
+
+#else /* MCUBOOT_MEASURED_BOOT || MCUBOOT_DATA_SHARING */
+ (void) (state);
+ (void) (active_slot);
+
+ return 0;
+#endif
+}
+
+/**
+ * Fills rsp to indicate how booting should occur.
+ *
+ * @param state Boot loader status information.
+ * @param slot_usage Information about the active and available slots.
+ * Only used in MCUBOOT_DIRECT_XIP and MCUBOOT_RAM_LOAD
+ * @param rsp boot_rsp struct to fill.
+ */
+static void
+fill_rsp(struct boot_loader_state *state, void *slot_usage,
+ struct boot_rsp *rsp)
+{
+ uint32_t active_slot;
+
+#if (BOOT_IMAGE_NUMBER > 1)
+ /* Always boot from Image 0. */
+ BOOT_CURR_IMG(state) = 0;
+#endif
+
+#if defined(MCUBOOT_DIRECT_XIP) || defined(MCUBOOT_RAM_LOAD)
+ active_slot = ((struct slot_usage_t *)slot_usage)[BOOT_CURR_IMG(state)].active_slot;
+#else
+ (void) (slot_usage);
+ active_slot = BOOT_PRIMARY_SLOT;
+#endif
+
+ rsp->br_flash_dev_id = BOOT_IMG_AREA(state, active_slot)->fa_device_id;
+ rsp->br_image_off = boot_img_slot_off(state, active_slot);
+ rsp->br_hdr = boot_img_hdr(state, active_slot);
+}
+
+/**
+ * Closes all flash areas.
+ *
+ * @param state Boot loader status information.
+ */
+static void
+close_all_flash_areas(struct boot_loader_state *state)
+{
+ uint32_t slot;
+
+ 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));
+ }
+ }
+}
+
#if !defined(MCUBOOT_DIRECT_XIP)
/*
* Compute the total size of the given image. Includes the size of
@@ -578,13 +691,21 @@
* does not match the slot address.
*/
static bool
-boot_rom_address_check(struct boot_loader_state *state, size_t slot)
+boot_rom_address_check(struct boot_loader_state *state,
+ struct slot_usage_t slot_usage[])
{
- const struct image_header *hdr = boot_img_hdr(state, slot);
- uint32_t f_off = boot_img_slot_off(state, slot);
+ uint32_t active_slot;
+ const struct image_header *hdr;
+ uint32_t f_off;
+
+ active_slot = slot_usage[BOOT_CURR_IMG(state)].active_slot;
+ hdr = boot_img_hdr(state, active_slot);
+ f_off = boot_img_slot_off(state, active_slot);
+
if (hdr->ih_flags & IMAGE_F_ROM_FIXED && hdr->ih_load_addr != f_off) {
BOOT_LOG_WRN("Image in %s slot at 0x%x has been built for offset 0x%x"\
- ", skipping", slot == 0 ? "primary" : "secondary", f_off,
+ ", skipping",
+ active_slot == 0 ? "primary" : "secondary", f_off,
hdr->ih_load_addr);
/* If there is address mismatch, the image is not bootable from this
@@ -1695,6 +1816,50 @@
}
}
+/**
+ * Updates the security counter for the current image.
+ *
+ * @param state Boot loader status information.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+boot_update_hw_rollback_protection(struct boot_loader_state *state)
+{
+#ifdef MCUBOOT_HW_ROLLBACK_PROT
+ int rc;
+
+ /* Update the stored security counter with the active image's security
+ * counter value. It will only be updated if the new security counter is
+ * greater than the stored value.
+ *
+ * In case of a successful image swapping when the swap type is TEST the
+ * security counter can be increased only after a reset, when the swap
+ * type is NONE and the image has marked itself "OK" (the image_ok flag
+ * has been set). This way a "revert" can be performed when it's
+ * necessary.
+ */
+ if (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_NONE) {
+ rc = boot_update_security_counter(
+ BOOT_CURR_IMG(state),
+ BOOT_PRIMARY_SLOT,
+ boot_img_hdr(state, BOOT_PRIMARY_SLOT));
+ if (rc != 0) {
+ BOOT_LOG_ERR("Security counter update failed after image "
+ "validation.");
+ return rc;
+ }
+ }
+
+ return 0;
+
+#else /* MCUBOOT_HW_ROLLBACK_PROT */
+ (void) (state);
+
+ return 0;
+#endif
+}
+
fih_int
context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
{
@@ -1887,56 +2052,17 @@
}
#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */
-#ifdef MCUBOOT_HW_ROLLBACK_PROT
- /* Update the stored security counter with the active image's security
- * counter value. It will only be updated if the new security counter is
- * greater than the stored value.
- *
- * In case of a successful image swapping when the swap type is TEST the
- * security counter can be increased only after a reset, when the swap
- * type is NONE and the image has marked itself "OK" (the image_ok flag
- * has been set). This way a "revert" can be performed when it's
- * necessary.
- */
- if (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_NONE) {
- rc = boot_update_security_counter(
- BOOT_CURR_IMG(state),
- BOOT_PRIMARY_SLOT,
- boot_img_hdr(state, BOOT_PRIMARY_SLOT));
- if (rc != 0) {
- BOOT_LOG_ERR("Security counter update failed after image "
- "validation.");
- goto out;
- }
- }
-#endif /* MCUBOOT_HW_ROLLBACK_PROT */
-
-#ifdef MCUBOOT_MEASURED_BOOT
- rc = boot_save_boot_status(BOOT_CURR_IMG(state),
- boot_img_hdr(state, BOOT_PRIMARY_SLOT),
- BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT));
+ rc = boot_update_hw_rollback_protection(state);
if (rc != 0) {
- BOOT_LOG_ERR("Failed to add Image %u data to shared memory area",
- BOOT_CURR_IMG(state));
goto out;
}
-#endif /* MCUBOOT_MEASURED_BOOT */
-#ifdef MCUBOOT_DATA_SHARING
- rc = boot_save_shared_data(boot_img_hdr(state, BOOT_PRIMARY_SLOT),
- BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT));
+ rc = boot_add_shared_data(state, BOOT_PRIMARY_SLOT);
if (rc != 0) {
- BOOT_LOG_ERR("Failed to add data to shared memory area.");
goto out;
}
-#endif /* MCUBOOT_DATA_SHARING */
}
-#if (BOOT_IMAGE_NUMBER > 1)
- /* Always boot from the primary slot of Image 0. */
- BOOT_CURR_IMG(state) = 0;
-#endif
-
/*
* Since the boot_status struct stores plaintext encryption keys, reset
* them here to avoid the possibility of jumping into an image that could
@@ -1944,20 +2070,11 @@
*/
memset(&bs, 0, sizeof(struct boot_status));
- rsp->br_flash_dev_id = BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)->fa_device_id;
- rsp->br_image_off = boot_img_slot_off(state, BOOT_PRIMARY_SLOT);
- rsp->br_hdr = boot_img_hdr(state, BOOT_PRIMARY_SLOT);
+ fill_rsp(state, NULL, rsp);
fih_rc = FIH_SUCCESS;
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));
- }
- }
+ close_all_flash_areas(state);
if (rc) {
fih_rc = fih_int_encode(rc);
@@ -2036,77 +2153,163 @@
#else /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */
+#define NO_ACTIVE_SLOT UINT32_MAX
+
/**
- * Iterates over all slots and determines which contain a firmware image.
+ * Opens all flash areas and checks which contain an image with a valid header.
*
- * @param state Boot loader status information.
- * @param slot_usage Pointer to an array, which aim is to carry information
- * about slots that contain an image. After return the
- * corresponding array elements are set to a non-zero
- * value if the given slots are in use (contain a firmware
- * image), otherwise they are set to zero.
- * @param slot_cnt The number of slots, which can contain firmware images.
- * (Equal to or smaller than the size of the
- * slot_usage array.)
+ * @param state Boot loader status information.
+ * @param slot_usage Structure to fill with information about the available
+ * slots.
*
- * @return The number of found images.
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+boot_get_slot_usage(struct boot_loader_state *state,
+ struct slot_usage_t slot_usage[])
+{
+ uint32_t slot;
+ int fa_id;
+ int rc;
+ struct image_header *hdr = NULL;
+
+ IMAGES_ITER(BOOT_CURR_IMG(state)) {
+ /* Open all the slots */
+ for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
+ fa_id = flash_area_id_from_multi_image_slot(
+ BOOT_CURR_IMG(state), slot);
+ rc = flash_area_open(fa_id, &BOOT_IMG_AREA(state, slot));
+ assert(rc == 0);
+ }
+
+ /* Attempt to read an image header from each slot. */
+ rc = boot_read_image_headers(state, false, NULL);
+ if (rc != 0) {
+ BOOT_LOG_WRN("Failed reading image headers.");
+ return rc;
+ }
+
+ /* Check headers in all slots */
+ for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
+ hdr = boot_img_hdr(state, slot);
+
+ if (boot_is_header_valid(hdr, BOOT_IMG_AREA(state, slot))) {
+ slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = true;
+ BOOT_LOG_IMAGE_INFO(slot, hdr);
+ } else {
+ slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
+ BOOT_LOG_INF("Image %d %s slot: Image not found",
+ BOOT_CURR_IMG(state),
+ (slot == BOOT_PRIMARY_SLOT)
+ ? "Primary" : "Secondary");
+ }
+ }
+
+ slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
+ }
+
+ return 0;
+}
+
+/**
+ * Finds the slot containing the image with the highest version number for the
+ * current image.
+ *
+ * @param state Boot loader status information.
+ * @param slot_usage Information about the active and available slots.
+ *
+ * @return NO_ACTIVE_SLOT if no available slot found, number of
+ * the found slot otherwise.
*/
static uint32_t
-boot_get_slot_usage(struct boot_loader_state *state, uint8_t slot_usage[],
- uint32_t slot_cnt)
+find_slot_with_highest_version(struct boot_loader_state *state,
+ struct slot_usage_t slot_usage[])
{
- struct image_header *hdr = NULL;
- uint32_t image_cnt = 0;
uint32_t slot;
+ uint32_t candidate_slot = NO_ACTIVE_SLOT;
+ int rc;
- memset(slot_usage, 0, slot_cnt);
-
- for (slot = 0; slot < slot_cnt; slot++) {
- hdr = boot_img_hdr(state, slot);
-
- if (boot_is_header_valid(hdr, BOOT_IMG_AREA(state, slot))) {
- slot_usage[slot] = 1;
- image_cnt++;
- BOOT_LOG_IMAGE_INFO(slot, hdr);
- } else {
- BOOT_LOG_INF("%s slot: Image not found", (slot == BOOT_PRIMARY_SLOT)
- ? "Primary" : "Secondary");
+ for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
+ if (slot_usage[BOOT_CURR_IMG(state)].slot_available[slot]) {
+ if (candidate_slot == NO_ACTIVE_SLOT) {
+ candidate_slot = slot;
+ } else {
+ rc = boot_version_cmp(
+ &boot_img_hdr(state, slot)->ih_ver,
+ &boot_img_hdr(state, candidate_slot)->ih_ver);
+ if (rc == 1) {
+ /* The version of the image being examined is greater than
+ * the version of the current candidate.
+ */
+ candidate_slot = slot;
+ }
+ }
}
}
- return image_cnt;
+ return candidate_slot;
}
+#ifdef MCUBOOT_HAVE_LOGGING
+/**
+ * Prints the state of the loaded images.
+ *
+ * @param state Boot loader status information.
+ * @param slot_usage Information about the active and available slots.
+ */
+static void
+print_loaded_images(struct boot_loader_state *state,
+ struct slot_usage_t slot_usage[])
+{
+ uint32_t active_slot;
+
+ IMAGES_ITER(BOOT_CURR_IMG(state)) {
+ active_slot = slot_usage[BOOT_CURR_IMG(state)].active_slot;
+
+ BOOT_LOG_INF("Image %d loaded from the %s slot",
+ BOOT_CURR_IMG(state),
+ (active_slot == BOOT_PRIMARY_SLOT) ?
+ "primary" : "secondary");
+ }
+}
+#endif
+
#ifdef MCUBOOT_DIRECT_XIP_REVERT
/**
- * Checks whether the image in the given slot was previously selected to run.
- * Erases the image if it was selected but its execution failed, otherwise marks
- * it as selected if it has not been before.
+ * Checks whether the active slot of the current image was previously selected
+ * to run. Erases the image if it was selected but its execution failed,
+ * otherwise marks it as selected if it has not been before.
*
- * @param state Image metadata from the image trailer. This function fills
- * this struct with the data read from the image trailer.
- * @param slot Image slot number.
+ * @param state Boot loader status information.
+ * @param slot_usage Information about the active and available slots.
*
- * @return 0 on success; nonzero on failure.
+ * @return 0 on success; nonzero on failure.
*/
static int
-boot_select_or_erase(struct boot_swap_state *state, uint32_t slot)
+boot_select_or_erase(struct boot_loader_state *state,
+ struct slot_usage_t slot_usage[])
{
const struct flash_area *fap;
int fa_id;
int rc;
+ uint32_t active_slot;
+ struct boot_swap_state* active_swap_state;
- fa_id = flash_area_id_from_image_slot(slot);
+ active_slot = slot_usage[BOOT_CURR_IMG(state)].active_slot;
+
+ fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), active_slot);
rc = flash_area_open(fa_id, &fap);
assert(rc == 0);
- memset(state, 0, sizeof(struct boot_swap_state));
- rc = boot_read_swap_state(fap, state);
+ active_swap_state = &(slot_usage[BOOT_CURR_IMG(state)].swap_state);
+
+ memset(active_swap_state, 0, sizeof(struct boot_swap_state));
+ rc = boot_read_swap_state(fap, active_swap_state);
assert(rc == 0);
- if (state->magic != BOOT_MAGIC_GOOD ||
- (state->copy_done == BOOT_FLAG_SET &&
- state->image_ok != BOOT_FLAG_SET)) {
+ if (active_swap_state->magic != BOOT_MAGIC_GOOD ||
+ (active_swap_state->copy_done == BOOT_FLAG_SET &&
+ active_swap_state->image_ok != BOOT_FLAG_SET)) {
/*
* A reboot happened without the image being confirmed at
* runtime or its trailer is corrupted/invalid. Erase the image
@@ -2120,8 +2323,8 @@
flash_area_close(fap);
rc = -1;
} else {
- if (state->copy_done != BOOT_FLAG_SET) {
- if (state->copy_done == BOOT_FLAG_BAD) {
+ if (active_swap_state->copy_done != BOOT_FLAG_SET) {
+ if (active_swap_state->copy_done == BOOT_FLAG_BAD) {
BOOT_LOG_DBG("The copy_done flag had an unexpected value. Its "
"value was neither 'set' nor 'unset', but 'bad'.");
}
@@ -2148,25 +2351,47 @@
#ifdef MCUBOOT_RAM_LOAD
+#ifndef MULTIPLE_EXECUTABLE_RAM_REGIONS
#if !defined(IMAGE_EXECUTABLE_RAM_START) || !defined(IMAGE_EXECUTABLE_RAM_SIZE)
#error "Platform MUST define executable RAM bounds in case of RAM_LOAD"
#endif
+#endif
/**
- * Verifies that the image in a slot will be loaded within the predefined bounds
- * that are allowed to be used by executable images.
+ * Verifies that the active slot of the current image can be loaded within the
+ * predefined bounds that are allowed to be used by executable images.
*
- * @param img_dst The address to which the image is going to be copied.
- * @param img_sz The size of the image.
+ * @param state Boot loader status information.
+ * @param slot_usage Information about the active and available slots.
*
- * @return 0 on success; nonzero on failure.
+ * @return 0 on success; nonzero on failure.
*/
static int
-boot_verify_ram_load_address(uint32_t img_dst, uint32_t img_sz)
+boot_verify_ram_load_address(struct boot_loader_state *state,
+ struct slot_usage_t slot_usage[])
{
+ uint32_t img_dst;
+ uint32_t img_sz;
uint32_t img_end_addr;
+ uint32_t exec_ram_start;
+ uint32_t exec_ram_size;
+#ifdef MULTIPLE_EXECUTABLE_RAM_REGIONS
+ int rc;
- if (img_dst < IMAGE_EXECUTABLE_RAM_START) {
+ rc = boot_get_image_exec_ram_info(BOOT_CURR_IMG(state), &exec_ram_start,
+ &exec_ram_size);
+ if (rc != 0) {
+ return BOOT_EBADSTATUS;
+ }
+#else
+ exec_ram_start = IMAGE_EXECUTABLE_RAM_START;
+ exec_ram_size = IMAGE_EXECUTABLE_RAM_SIZE;
+#endif
+
+ img_dst = slot_usage[BOOT_CURR_IMG(state)].img_dst;
+ img_sz = slot_usage[BOOT_CURR_IMG(state)].img_sz;
+
+ if (img_dst < exec_ram_start) {
return BOOT_EBADIMAGE;
}
@@ -2174,8 +2399,7 @@
return BOOT_EBADIMAGE;
}
- if (img_end_addr > (IMAGE_EXECUTABLE_RAM_START +
- IMAGE_EXECUTABLE_RAM_SIZE)) {
+ if (img_end_addr > (exec_ram_start + exec_ram_size)) {
return BOOT_EBADIMAGE;
}
@@ -2183,8 +2407,9 @@
}
/**
- * Copies an image from a slot in the flash to an SRAM address.
+ * Copies a slot of the current image into SRAM.
*
+ * @param state Boot loader status information.
* @param slot The flash slot of the image to be copied to SRAM.
* @param img_dst The address at which the image needs to be copied to
* SRAM.
@@ -2193,12 +2418,20 @@
* @return 0 on success; nonzero on failure.
*/
static int
-boot_copy_image_to_sram(int slot, uint32_t img_dst, uint32_t img_sz)
+boot_copy_image_to_sram(struct boot_loader_state *state, int slot,
+ uint32_t img_dst, uint32_t img_sz)
{
int rc;
const struct flash_area *fap_src = NULL;
+ int area_id;
- rc = flash_area_open(flash_area_id_from_image_slot(slot), &fap_src);
+#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_src);
if (rc != 0) {
return BOOT_EFLASH;
}
@@ -2214,50 +2447,134 @@
return rc;
}
+#if (BOOT_IMAGE_NUMBER > 1)
/**
- * Copies an image from a slot in the flash to an SRAM address. The load
- * address and image size is extracted from the image header.
+ * Checks if two memory regions (A and B) are overlap or not.
*
- * @param state Boot loader status information.
- * @param slot The flash slot of the image to be copied to SRAM.
- * @param hdr Pointer to the image header structure of the image
- * @param img_dst Pointer to the address at which the image needs to be
- * copied to SRAM.
- * @param img_sz Pointer to the size of the image that needs to be
- * copied to SRAM.
+ * @param start_a Start of the A region.
+ * @param end_a End of the A region.
+ * @param start_b Start of the B region.
+ * @param end_b End of the B region.
*
- * @return 0 on success; nonzero on failure.
+ * @return true if there is overlap; false otherwise.
+ */
+static bool
+do_regions_overlap(uint32_t start_a, uint32_t end_a,
+ uint32_t start_b, uint32_t end_b)
+{
+ if (start_b > end_a) {
+ return false;
+ } else if (start_b >= start_a) {
+ return true;
+ } else if (end_b > start_a) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Checks if the image we want to load to memory overlap with an already
+ * ramloaded image.
+ *
+ * @param slot_usage Information about the active and available slots.
+ * @param image_id_to_check The ID of the image we would like to load.
+ *
+ * @return 0 if there is no overlap; nonzero otherwise.
*/
static int
-boot_load_image_to_sram(struct boot_loader_state *state, uint32_t slot,
- struct image_header *hdr, uint32_t *img_dst,
- uint32_t *img_sz)
+boot_check_ram_load_overlapping(struct slot_usage_t slot_usage[],
+ uint32_t image_id_to_check)
{
+ uint32_t i;
+
+ uint32_t start_a;
+ uint32_t end_a;
+ uint32_t start_b;
+ uint32_t end_b;
+
+ start_a = slot_usage[image_id_to_check].img_dst;
+ /* Safe to add here, values are already verified in
+ * boot_verify_ram_load_address() */
+ end_a = start_a + slot_usage[image_id_to_check].img_sz;
+
+ for (i = 0; i < BOOT_IMAGE_NUMBER; i++) {
+ if (slot_usage[i].active_slot == NO_ACTIVE_SLOT
+ || i == image_id_to_check) {
+ continue;
+ }
+
+ start_b = slot_usage[i].img_dst;
+ /* Safe to add here, values are already verified in
+ * boot_verify_ram_load_address() */
+ end_b = start_b + slot_usage[i].img_sz;
+
+ if (do_regions_overlap(start_a, end_a, start_b, end_b)) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+/**
+ * Loads the active slot of the current image into SRAM. The load address and
+ * image size is extracted from the image header.
+ *
+ * @param state Boot loader status information.
+ * @param slot_usage Information about the active and available slots.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+boot_load_image_to_sram(struct boot_loader_state *state,
+ struct slot_usage_t slot_usage[])
+{
+ uint32_t active_slot;
+ struct image_header *hdr = NULL;
+ uint32_t img_dst;
+ uint32_t img_sz;
int rc;
+ active_slot = slot_usage[BOOT_CURR_IMG(state)].active_slot;
+ hdr = boot_img_hdr(state, active_slot);
+
if (hdr->ih_flags & IMAGE_F_RAM_LOAD) {
- *img_dst = hdr->ih_load_addr;
+ img_dst = hdr->ih_load_addr;
- rc = boot_read_image_size(state, slot, img_sz);
+ rc = boot_read_image_size(state, active_slot, &img_sz);
if (rc != 0) {
return rc;
}
- rc = boot_verify_ram_load_address(*img_dst, *img_sz);
+ slot_usage[BOOT_CURR_IMG(state)].img_dst = img_dst;
+ slot_usage[BOOT_CURR_IMG(state)].img_sz = img_sz;
+
+ rc = boot_verify_ram_load_address(state, slot_usage);
if (rc != 0) {
- BOOT_LOG_INF("Image RAM load address 0x%x is invalid.", *img_dst);
+ BOOT_LOG_INF("Image RAM load address 0x%x is invalid.", img_dst);
return rc;
}
+#if (BOOT_IMAGE_NUMBER > 1)
+ rc = boot_check_ram_load_overlapping(slot_usage, BOOT_CURR_IMG(state));
+ if (rc != 0) {
+ BOOT_LOG_INF("Image RAM loading to address 0x%x would overlap with\
+ another image.", img_dst);
+ return rc;
+ }
+#endif
+
/* Copy image to the load address from where it currently resides in
* flash.
*/
- rc = boot_copy_image_to_sram(slot, *img_dst, *img_sz);
+ rc = boot_copy_image_to_sram(state, active_slot, img_dst, img_sz);
if (rc != 0) {
- BOOT_LOG_INF("RAM loading to 0x%x is failed.", *img_dst);
+ BOOT_LOG_INF("RAM loading to 0x%x is failed.", img_dst);
} else {
- BOOT_LOG_INF("RAM loading to 0x%x is succeeded.", *img_dst);
+ BOOT_LOG_INF("RAM loading to 0x%x is succeeded.", img_dst);
}
} else {
/* Only images that support IMAGE_F_RAM_LOAD are allowed if
@@ -2266,114 +2583,270 @@
rc = BOOT_EBADIMAGE;
}
+ if (rc != 0) {
+ slot_usage[BOOT_CURR_IMG(state)].img_dst = 0;
+ slot_usage[BOOT_CURR_IMG(state)].img_sz = 0;
+ }
+
return rc;
}
/**
* Removes an image from SRAM, by overwriting it with zeros.
*
- * @param img_dst The address of the image that needs to be removed from
- * SRAM.
- * @param img_sz The size of the image that needs to be removed from
- * SRAM.
+ * @param state Boot loader status information.
+ * @param slot_usage Information about the active and available slots.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static inline int
+boot_remove_image_from_sram(struct boot_loader_state *state,
+ struct slot_usage_t slot_usage[])
+{
+ BOOT_LOG_INF("Removing image from SRAM at address 0x%x",
+ slot_usage[BOOT_CURR_IMG(state)].img_dst);
+
+ memset((void*)slot_usage[BOOT_CURR_IMG(state)].img_dst, 0,
+ slot_usage[BOOT_CURR_IMG(state)].img_sz);
+
+ slot_usage[BOOT_CURR_IMG(state)].img_dst = 0;
+ slot_usage[BOOT_CURR_IMG(state)].img_sz = 0;
+
+ return 0;
+}
+
+/**
+ * Removes an image from flash by erasing the corresponding flash area
+ *
+ * @param state Boot loader status information.
+ * @param slot The flash slot of the image to be erased.
*
* @return 0 on success; nonzero on failure.
*/
static inline int
-boot_remove_image_from_sram(uint32_t img_dst, uint32_t img_sz)
+boot_remove_image_from_flash(struct boot_loader_state *state, uint32_t slot)
{
- BOOT_LOG_INF("Removing image from SRAM at address 0x%x", img_dst);
- memset((void*)img_dst, 0, img_sz);
+ int area_id;
+ int rc;
+ const struct flash_area *fap;
- return 0;
+ BOOT_LOG_INF("Removing image %d slot %d from flash", BOOT_CURR_IMG(state),
+ slot);
+ area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot);
+ rc = flash_area_open(area_id, &fap);
+ if (rc == 0) {
+ flash_area_erase(fap, 0, fap->fa_size);
+ }
+
+ return rc;
}
#endif /* MCUBOOT_RAM_LOAD */
-fih_int
-context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
+#if (BOOT_IMAGE_NUMBER > 1)
+/**
+ * Checks the image dependency whether it is satisfied.
+ *
+ * @param state Boot loader status information.
+ * @param slot_usage Information about the active and available slots.
+ * @param dep Image dependency which has to be verified.
+ *
+ * @return 0 if dependencies are met; nonzero otherwise.
+ */
+static int
+boot_verify_slot_dependency(struct boot_loader_state *state,
+ struct slot_usage_t slot_usage[],
+ struct image_dependency *dep)
{
- struct image_header *hdr = NULL;
- struct image_header *selected_image_header = NULL;
- uint8_t slot_usage[BOOT_NUM_SLOTS];
- uint32_t selected_slot;
- uint32_t slot;
- uint32_t img_cnt;
- uint32_t i;
- int fa_id;
+ struct image_version *dep_version;
+ uint32_t dep_slot;
int rc;
-#ifdef MCUBOOT_RAM_LOAD
- uint32_t img_dst;
- uint32_t img_sz;
- uint32_t img_loaded = 0;
-#endif /* MCUBOOT_RAM_LOAD */
-#ifdef MCUBOOT_DIRECT_XIP_REVERT
- struct boot_swap_state slot_state;
-#endif /* MCUBOOT_DIRECT_XIP_REVERT */
- fih_int fih_rc = FIH_FAILURE;
- memset(state, 0, sizeof(struct boot_loader_state));
-
- /* Open primary and secondary image areas for the duration
- * of this call.
+ /* Determine the source of the image which is the subject of
+ * the dependency and get it's version.
*/
- 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(state, slot));
- assert(rc == 0);
+ dep_slot = slot_usage[dep->image_id].active_slot;
+ dep_version = &state->imgs[dep->image_id][dep_slot].hdr.ih_ver;
+
+ rc = boot_version_cmp(dep_version, &dep->image_min_version);
+ if (rc >= 0) {
+ /* Dependency satisfied. */
+ rc = 0;
}
- /* Attempt to read an image header from each slot. */
- rc = boot_read_image_headers(state, false, NULL);
+ return rc;
+}
+
+/**
+ * Reads all dependency TLVs of an image and verifies one after another to see
+ * if they are all satisfied.
+ *
+ * @param state Boot loader status information.
+ * @param slot_usage Information about the active and available slots.
+ *
+ * @return 0 if dependencies are met; nonzero otherwise.
+ */
+static int
+boot_verify_slot_dependencies(struct boot_loader_state *state,
+ struct slot_usage_t slot_usage[])
+{
+ uint32_t active_slot;
+ const struct flash_area *fap;
+ struct image_tlv_iter it;
+ struct image_dependency dep;
+ uint32_t off;
+ uint16_t len;
+ int area_id;
+ int rc;
+
+ active_slot = slot_usage[BOOT_CURR_IMG(state)].active_slot;
+
+ area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state),
+ active_slot);
+ rc = flash_area_open(area_id, &fap);
if (rc != 0) {
- BOOT_LOG_WRN("Failed reading image headers.");
- goto out;
+ rc = BOOT_EFLASH;
+ goto done;
}
- img_cnt = boot_get_slot_usage(state, slot_usage,
- sizeof(slot_usage)/sizeof(slot_usage[0]));
+ rc = bootutil_tlv_iter_begin(&it, boot_img_hdr(state, active_slot), fap,
+ IMAGE_TLV_DEPENDENCY, true);
+ if (rc != 0) {
+ goto done;
+ }
- if (img_cnt) {
- /* Select the newest and valid image. */
- for (i = 0; i < img_cnt; i++) {
- selected_slot = 0;
+ while (true) {
+ rc = bootutil_tlv_iter_next(&it, &off, &len, NULL);
+ if (rc < 0) {
+ return -1;
+ } else if (rc > 0) {
+ rc = 0;
+ break;
+ }
- /* Iterate over all the slots that are in use (contain an image)
- * and select the one that holds the newest image.
- */
- for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
- if (slot_usage[slot]) {
- hdr = boot_img_hdr(state, slot);
- if (selected_image_header != NULL) {
- rc = boot_version_cmp(&hdr->ih_ver,
- &selected_image_header->ih_ver);
- if (rc < 1) {
- /* The version of the image being examined wasn't
- * greater than the currently selected image's
- * version.
- */
- continue;
- }
- }
- /* Check if image has IMAGE_F_ROM_FIXED flag set and
- * is in proper slot.
- */
- if (boot_rom_address_check(state, slot) != 0) {
- continue;
- }
- selected_slot = slot;
- selected_image_header = hdr;
- }
+ if (len != sizeof(dep)) {
+ rc = BOOT_EBADIMAGE;
+ goto done;
+ }
+
+ rc = LOAD_IMAGE_DATA(boot_img_hdr(state, active_slot),
+ fap, off, &dep, len);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ if (dep.image_id >= BOOT_IMAGE_NUMBER) {
+ rc = BOOT_EBADARGS;
+ goto done;
+ }
+
+ rc = boot_verify_slot_dependency(state, slot_usage, &dep);
+ if (rc != 0) {
+ /* Dependency not satisfied. */
+ goto done;
+ }
+ }
+
+done:
+ flash_area_close(fap);
+ return rc;
+}
+
+/**
+ * Checks the dependency of all the active slots. If an image found with
+ * invalid or not satisfied dependencies the image is removed from SRAM (in
+ * case of MCUBOOT_RAM_LOAD strategy) and its slot is set to unavailable.
+ *
+ * @param state Boot loader status information.
+ * @param slot_usage Information about the active and available slots.
+ *
+ * @return 0 if dependencies are met; nonzero otherwise.
+ */
+static int
+boot_verify_dependencies(struct boot_loader_state *state,
+ struct slot_usage_t slot_usage[])
+{
+ int rc = -1;
+ uint32_t active_slot;
+
+ IMAGES_ITER(BOOT_CURR_IMG(state)) {
+ rc = boot_verify_slot_dependencies(state, slot_usage);
+ if (rc != 0) {
+ /* Dependencies not met or invalid dependencies. */
+
+#ifdef MCUBOOT_RAM_LOAD
+ boot_remove_image_from_sram(state, slot_usage);
+#endif /* MCUBOOT_RAM_LOAD */
+
+ active_slot = slot_usage[BOOT_CURR_IMG(state)].active_slot;
+ slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false;
+ slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
+
+ return rc;
+ }
+ }
+
+ return rc;
+}
+#endif /* (BOOT_IMAGE_NUMBER > 1) */
+
+/**
+ * Tries to load a slot for all the images with validation.
+ *
+ * @param state Boot loader status information.
+ * @param slot_usage Information about the active and available slots.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+fih_int
+boot_load_and_validate_images(struct boot_loader_state *state,
+ struct slot_usage_t slot_usage[])
+{
+ uint32_t active_slot;
+ int rc;
+ fih_int fih_rc;
+
+ /* Go over all the images and try to load one */
+ IMAGES_ITER(BOOT_CURR_IMG(state)) {
+ /* All slots tried until a valid image found. Breaking from this loop
+ * means that a valid image found or already loaded. If no slot is
+ * found the function returns with error code. */
+ while (true) {
+
+ /* Go over all the slots and try to load one */
+ active_slot = slot_usage[BOOT_CURR_IMG(state)].active_slot;
+ if (active_slot != NO_ACTIVE_SLOT){
+ /* A slot is already active, go to next image. */
+ break;
}
- /* Reset rc since we use it above for boot_version_cmp */
- rc = 0;
+ active_slot = find_slot_with_highest_version(state,
+ slot_usage);
+ if (active_slot == NO_ACTIVE_SLOT) {
+ BOOT_LOG_INF("No slot to load for image %d",
+ BOOT_CURR_IMG(state));
+ FIH_RET(FIH_FAILURE);
+ }
+
+ /* Save the number of the active slot. */
+ slot_usage[BOOT_CURR_IMG(state)].active_slot = active_slot;
+
+#ifdef MCUBOOT_DIRECT_XIP
+ rc = boot_rom_address_check(state, slot_usage);
+ if (rc != 0) {
+ /* The image is placed in an unsuitable slot. */
+ slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false;
+ slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
+ continue;
+ }
+#endif /* MCUBOOT_DIRECT_XIP */
#ifdef MCUBOOT_DIRECT_XIP_REVERT
- rc = boot_select_or_erase(&slot_state, selected_slot);
+ rc = boot_select_or_erase(state, slot_usage);
if (rc != 0) {
/* The selected image slot has been erased. */
- slot_usage[selected_slot] = 0;
- selected_image_header = NULL;
+ slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false;
+ slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
continue;
}
#endif /* MCUBOOT_DIRECT_XIP_REVERT */
@@ -2384,111 +2857,150 @@
* when loading images from external (untrusted) flash to internal
* (trusted) RAM and image is authenticated before copying.
*/
- rc = boot_load_image_to_sram(state, selected_slot,
- selected_image_header, &img_dst,
- &img_sz);
+ rc = boot_load_image_to_sram(state, slot_usage);
if (rc != 0 ) {
- /* Image loading failed try the next one. */
- slot_usage[selected_slot] = 0;
- selected_image_header = NULL;
+ /* Image cannot be ramloaded. */
+ boot_remove_image_from_flash(state, active_slot);
+ slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false;
+ slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
continue;
- } else {
- img_loaded = 1;
}
#endif /* MCUBOOT_RAM_LOAD */
- FIH_CALL(boot_validate_slot, fih_rc, state, selected_slot, NULL);
- if (fih_eq(fih_rc, FIH_SUCCESS)) {
- /* If a valid image is found then there is no reason to check
- * the rest of the images, as each of them has a smaller version
- * number.
- */
- break;
- }
+
+ FIH_CALL(boot_validate_slot, fih_rc, state, active_slot, NULL);
+ if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
+ /* Image is invalid. */
#ifdef MCUBOOT_RAM_LOAD
- else if (img_loaded) {
- /* If an image is found to be invalid then it is removed from
- * RAM to prevent it being a shellcode vector.
- */
- boot_remove_image_from_sram(img_dst, img_sz);
- img_loaded = 0;
- }
+ boot_remove_image_from_sram(state, slot_usage);
#endif /* MCUBOOT_RAM_LOAD */
- /* The selected image is invalid, mark its slot as "unused"
- * and start over.
- */
- slot_usage[selected_slot] = 0;
- selected_image_header = NULL;
- }
-
- if (fih_not_eq(fih_rc, FIH_SUCCESS) || (selected_image_header == NULL)) {
- /* If there was no valid image at all */
- goto out;
- }
-
-#ifdef MCUBOOT_HW_ROLLBACK_PROT
- /* Update the stored security counter with the newer (active) image's
- * security counter value.
- */
-#ifdef MCUBOOT_DIRECT_XIP_REVERT
- /* When the 'revert' mechanism is enabled in direct-xip mode, the
- * security counter can be increased only after reboot, if the image
- * has been confirmed at runtime (the image_ok flag has been set).
- * This way a 'revert' can be performed when it's necessary.
- */
- if (slot_state.image_ok == BOOT_FLAG_SET) {
-#endif
- rc = boot_update_security_counter(0, selected_slot,
- selected_image_header);
- if (rc != 0) {
- BOOT_LOG_ERR("Security counter update failed after image "
- "validation.");
- goto out;
+ slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false;
+ slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
+ continue;
}
+
+ /* Valid image loaded from a slot, go to next image. */
+ break;
+ }
+ }
+
+ FIH_RET(FIH_SUCCESS);
+}
+
+/**
+ * Updates the security counter for the current image.
+ *
+ * @param state Boot loader status information.
+ * @param active_slot Index of the slot will be loaded for current image.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+boot_update_hw_rollback_protection(struct boot_loader_state *state,
+ uint32_t active_slot)
+{
+#ifdef MCUBOOT_HW_ROLLBACK_PROT
+ int rc;
+
+ /* Update the stored security counter with the newer (active) image's
+ * security counter value.
+ */
#ifdef MCUBOOT_DIRECT_XIP_REVERT
- }
+ /* When the 'revert' mechanism is enabled in direct-xip mode, the
+ * security counter can be increased only after reboot, if the image
+ * has been confirmed at runtime (the image_ok flag has been set).
+ * This way a 'revert' can be performed when it's necessary.
+ */
+ if (slot_usage[BOOT_CURR_IMG(state)].swap_state.image_ok == BOOT_FLAG_SET) {
#endif
-#endif /* MCUBOOT_HW_ROLLBACK_PROT */
-
-#ifdef MCUBOOT_MEASURED_BOOT
- rc = boot_save_boot_status(0, selected_image_header,
- BOOT_IMG_AREA(state, selected_slot));
+ rc = boot_update_security_counter(BOOT_CURR_IMG(state), active_slot,
+ boot_img_hdr(state, active_slot));
if (rc != 0) {
- BOOT_LOG_ERR("Failed to add image data to shared area");
+ BOOT_LOG_ERR("Security counter update failed after image "
+ "validation.");
+ return rc;
}
-#endif /* MCUBOOT_MEASURED_BOOT */
+#ifdef MCUBOOT_DIRECT_XIP_REVERT
+ }
+#endif
-#ifdef MCUBOOT_DATA_SHARING
- rc = boot_save_shared_data(selected_image_header,
- BOOT_IMG_AREA(state, selected_slot));
- if (rc != 0) {
- BOOT_LOG_ERR("Failed to add data to shared memory area.");
- }
-#endif /* MCUBOOT_DATA_SHARING */
+ return 0;
- BOOT_LOG_INF("Booting image from the %s slot",
- (selected_slot == BOOT_PRIMARY_SLOT) ?
- "primary" : "secondary");
+#else /* MCUBOOT_HW_ROLLBACK_PROT */
+ (void) (state);
+ (void) (active_slot);
- rsp->br_flash_dev_id =
- BOOT_IMG_AREA(state, selected_slot)->fa_device_id;
- rsp->br_image_off = boot_img_slot_off(state, selected_slot);
- rsp->br_hdr = selected_image_header;
- } else {
- /* No candidate image available */
- rc = BOOT_EBADIMAGE;
+ return 0;
+#endif
+}
+
+fih_int
+context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
+{
+ struct slot_usage_t slot_usage[BOOT_IMAGE_NUMBER];
+ int rc;
+ fih_int fih_rc;
+ uint32_t active_slot;
+
+ memset(state, 0, sizeof(struct boot_loader_state));
+ memset(slot_usage, 0, sizeof(struct slot_usage_t) * BOOT_IMAGE_NUMBER);
+
+ rc = boot_get_slot_usage(state, slot_usage);
+ if (rc != 0) {
goto out;
}
+#if (BOOT_IMAGE_NUMBER > 1)
+ while (true) {
+#endif
+ FIH_CALL(boot_load_and_validate_images, fih_rc, state, slot_usage);
+ if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
+ goto out;
+ }
+
+#if (BOOT_IMAGE_NUMBER > 1)
+ rc = boot_verify_dependencies(state, slot_usage);
+ if (rc != 0) {
+ /* Dependency check failed for an image, it has been removed from
+ * SRAM in case of MCUBOOT_RAM_LOAD strategy, and set to
+ * unavailable. Try to load an image from another slot.
+ */
+ continue;
+ }
+ /* Dependency check was successful. */
+ break;
+ }
+#endif
+
+ IMAGES_ITER(BOOT_CURR_IMG(state)) {
+
+ active_slot = slot_usage[BOOT_CURR_IMG(state)].active_slot;
+
+ rc = boot_update_hw_rollback_protection(state, active_slot);
+ if (rc != 0) {
+ goto out;
+ }
+
+ rc = boot_add_shared_data(state, active_slot);
+ if (rc != 0) {
+ goto out;
+ }
+ }
+
+ /* All image loaded successfully. */
+#ifdef MCUBOOT_HAVE_LOGGING
+ print_loaded_images(state, slot_usage);
+#endif
+
+ fill_rsp(state, slot_usage, rsp);
+
out:
- for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
- flash_area_close(BOOT_IMG_AREA(state, BOOT_NUM_SLOTS - 1 - slot));
- }
+ close_all_flash_areas(state);
- if (rc) {
- fih_rc = fih_int_encode(rc);
- }
+ if (fih_eq(fih_rc, FIH_SUCCESS)) {
+ fih_rc = fih_int_encode(rc);
+ }
- FIH_RET(fih_rc);
+ FIH_RET(fih_rc);
}
#endif /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */