diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c index 53f40c33a..f35ec7864 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -105,6 +105,17 @@ static struct sector_buffer_t sector_buffers; #endif #endif +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 && defined(MCUBOOT_OVERWRITE_ONLY) && \ + defined(MCUBOOT_DOWNGRADE_PREVENTION) +/* s0/s1 package version of the current MCUboot image */ +static const struct image_version mcuboot_s0_s1_image_version = { + .iv_major = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_MAJOR, + .iv_minor = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_MINOR, + .iv_revision = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_REVISION, + .iv_build_num = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_BUILD_NUMBER, +}; +#endif + #if (BOOT_IMAGE_NUMBER > 1) #define IMAGES_ITER(x) for ((x) = 0; (x) < BOOT_IMAGE_NUMBER; ++(x)) #else @@ -153,15 +164,15 @@ boot_read_image_headers(struct boot_loader_state *state, bool require_all, * * Failure to read any headers is a fatal error. */ -#ifdef PM_S1_ADDRESS +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 /* Patch needed for NCS. The primary slot of the second image * (image 1) will not contain a valid image header until an upgrade * of mcuboot has happened (filling S1 with the new version). */ - if (BOOT_CURR_IMG(state) == 1 && i == 0) { + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER && i == 0) { continue; } -#endif /* PM_S1_ADDRESS */ +#endif /* CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 */ if (i > 0 && !require_all) { return 0; } else { @@ -1160,17 +1171,51 @@ boot_validate_slot(struct boot_loader_state *state, int slot, #if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) \ && defined(CONFIG_PCD_APP) && defined(CONFIG_PCD_READ_NETCORE_APP_VERSION) - if (BOOT_CURR_IMG(state) == 1) { + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { rc = pcd_version_cmp_net(fap, boot_img_hdr(state, BOOT_SECONDARY_SLOT)); } else { rc = boot_version_cmp( &boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (rc >= 0 && BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + /* Also check the new version of MCUboot against that of the current s0/s1 MCUboot + * trailer version to prevent downgrades + */ + int version_check; + + version_check = boot_version_cmp(&boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &mcuboot_s0_s1_image_version); + + /* Only update rc if the currently running version is newer */ + if (version_check < rc) { + rc = version_check; + } + } +#endif } #else rc = boot_version_cmp( &boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (rc >= 0 && BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + /* Also check the new version of MCUboot against that of the current s0/s1 MCUboot + * trailer version to prevent downgrades + */ + int version_check; + + version_check = boot_version_cmp(&boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &mcuboot_s0_s1_image_version); + + /* Only update rc if the currently running version is newer */ + if (version_check < rc) { + rc = version_check; + } + } +#endif #endif if (rc < 0 && boot_check_header_erased(state, BOOT_PRIMARY_SLOT)) { BOOT_LOG_ERR("insufficient version in secondary slot"); @@ -1229,6 +1274,8 @@ boot_validate_slot(struct boot_loader_state *state, int slot, struct image_header *secondary_hdr = boot_img_hdr(state, slot); uint32_t reset_value = 0; uint32_t reset_addr = secondary_hdr->ih_hdr_size + sizeof(reset_value); + uint32_t min_addr, max_addr; + bool check_addresses = false; rc = flash_area_read(fap, reset_addr, &reset_value, sizeof(reset_value)); if (rc != 0) { @@ -1236,29 +1283,46 @@ boot_validate_slot(struct boot_loader_state *state, int slot, goto out; } - uint32_t min_addr, max_addr; - #ifdef PM_CPUNET_APP_ADDRESS /* The primary slot for the network core is emulated in RAM. * Its flash_area hasn't got relevant boundaries. * Therfore need to override its boundaries for the check. */ - if (BOOT_CURR_IMG(state) == 1) { + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { min_addr = PM_CPUNET_APP_ADDRESS; max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE; -#ifdef PM_S1_ADDRESS - } else if (BOOT_CURR_IMG(state) == 0) { + check_addresses = true; + } else +#endif +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { +#if (CONFIG_NCS_IS_VARIANT_IMAGE) min_addr = PM_S0_ADDRESS; - max_addr = pri_fa->fa_off + pri_fa->fa_size; + max_addr = (PM_S0_ADDRESS + PM_S0_SIZE); +#else + min_addr = PM_S1_ADDRESS; + max_addr = (PM_S1_ADDRESS + PM_S1_SIZE); #endif + check_addresses = true; } else #endif - { + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + min_addr = MIN(pri_fa->fa_off, PM_S0_ADDRESS); + max_addr = MAX((pri_fa->fa_off + pri_fa->fa_size), (PM_S0_ADDRESS + PM_S0_SIZE)); +#else + min_addr = MIN(pri_fa->fa_off, PM_S1_ADDRESS); + max_addr = MAX((pri_fa->fa_off + pri_fa->fa_size), (PM_S1_ADDRESS + PM_S1_SIZE)); +#endif +#else min_addr = pri_fa->fa_off; max_addr = pri_fa->fa_off + pri_fa->fa_size; +#endif + check_addresses = true; } - if (reset_value < min_addr || reset_value> (max_addr)) { + if (check_addresses == true && (reset_value < min_addr || reset_value > max_addr)) { BOOT_LOG_ERR("Reset address of image in secondary slot is not in the primary slot"); BOOT_LOG_ERR("Erasing image from secondary slot"); @@ -1335,36 +1399,42 @@ boot_update_security_counter(uint8_t image_index, int slot, #define SEC_SLOT_TOUCHED 1 #define SEC_SLOT_ASSIGNED 2 -#if (MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ - !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) -/* This configuration is peculiar - the one physical secondary slot is - * mocking two logical secondary - */ -#define SEC_SLOT_PHYSICAL_CNT 1 -#else -#define SEC_SLOT_PHYSICAL_CNT MCUBOOT_IMAGE_NUMBER -#endif - -static uint8_t sec_slot_assignmnet[SEC_SLOT_PHYSICAL_CNT] = {0}; +static uint8_t sec_slot_assignmnet[MCUBOOT_IMAGE_NUMBER] = {0}; static inline void sec_slot_touch(struct boot_loader_state *state) { - uint8_t idx = (SEC_SLOT_PHYSICAL_CNT == 1) ? 0 : BOOT_CURR_IMG(state); +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + if (sec_slot_assignmnet[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] == SEC_SLOT_VIRGIN) { + sec_slot_assignmnet[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] = SEC_SLOT_TOUCHED; + } + } else if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { + if (sec_slot_assignmnet[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] == SEC_SLOT_VIRGIN) { + sec_slot_assignmnet[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] = SEC_SLOT_TOUCHED; + } + } +#endif - if (SEC_SLOT_VIRGIN == sec_slot_assignmnet[idx]) { - sec_slot_assignmnet[idx] = SEC_SLOT_TOUCHED; + if (sec_slot_assignmnet[BOOT_CURR_IMG(state)] == SEC_SLOT_VIRGIN) { + sec_slot_assignmnet[BOOT_CURR_IMG(state)] = SEC_SLOT_TOUCHED; } } static inline void sec_slot_mark_assigned(struct boot_loader_state *state) { - uint8_t idx = (SEC_SLOT_PHYSICAL_CNT == 1) ? 0 : BOOT_CURR_IMG(state); +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + sec_slot_assignmnet[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] = SEC_SLOT_ASSIGNED; + } else if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { + sec_slot_assignmnet[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] = SEC_SLOT_ASSIGNED; + } +#endif - sec_slot_assignmnet[idx] = SEC_SLOT_ASSIGNED; + sec_slot_assignmnet[BOOT_CURR_IMG(state)] = SEC_SLOT_ASSIGNED; } /** - * Cleanu up all secondary slot which couldn't be assigned to any primary slot. + * Cleanup up all secondary slot which couldn't be assigned to any primary slot. * * This function erases content of each secondary slot which contains valid * header but couldn't be assigned to any of supported primary images. @@ -1376,7 +1446,7 @@ static void sec_slot_cleanup_if_unusable(void) { uint8_t idx; - for (idx = 0; idx < SEC_SLOT_PHYSICAL_CNT; idx++) { + for (idx = 0; idx < MCUBOOT_IMAGE_NUMBER; idx++) { if (SEC_SLOT_TOUCHED == sec_slot_assignmnet[idx]) { const struct flash_area *secondary_fa; int rc; @@ -1386,12 +1456,12 @@ static void sec_slot_cleanup_if_unusable(void) if (!rc) { rc = flash_area_erase(secondary_fa, 0, secondary_fa->fa_size); if (!rc) { - BOOT_LOG_ERR("Cleaned-up secondary slot of %d. image.", idx); + BOOT_LOG_ERR("Cleaned-up secondary slot of image %d", idx); } } if (rc) { - BOOT_LOG_ERR("Can not cleanup secondary slot of %d. image.", idx); + BOOT_LOG_ERR("Failed to clean-up secondary slot of image %d: %d", idx, rc); } } } @@ -1428,7 +1498,7 @@ boot_validated_swap_type(struct boot_loader_state *state, owner_nsib[BOOT_CURR_IMG(state)] = false; #endif -#if defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP) +#if defined(PM_S1_ADDRESS) || defined(PM_CPUNET_B0N_ADDRESS) const struct flash_area *secondary_fa = BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT); struct image_header *hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); @@ -1466,31 +1536,29 @@ boot_validated_swap_type(struct boot_loader_state *state, } /* Check start and end of primary slot for current image */ - if (reset_addr < primary_fa->fa_off) { -#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) - const struct flash_area *nsib_fa; - - /* NSIB upgrade slot */ - rc = flash_area_open((uint32_t)_image_1_primary_slot_id, - &nsib_fa); - - if (rc != 0) { - return BOOT_SWAP_TYPE_FAIL; - } - - /* Image is placed before Primary and within the NSIB slot */ - if (reset_addr > nsib_fa->fa_off - && reset_addr < (nsib_fa->fa_off + nsib_fa->fa_size)) { - /* Set primary to be NSIB upgrade slot */ - BOOT_IMG_AREA(state, 0) = nsib_fa; - owner_nsib[BOOT_CURR_IMG(state)] = true; - } +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) { #else - return BOOT_SWAP_TYPE_NONE; - + if (reset_addr >= PM_S1_ADDRESS && reset_addr <= (PM_S1_ADDRESS + PM_S1_SIZE)) { #endif + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { + /* This is not the s0/s1 upgrade image but the application image, pretend + * there is no image so the NSIB update can be loaded + */ + return BOOT_SWAP_TYPE_NONE; + } - } else if (reset_addr > (primary_fa->fa_off + primary_fa->fa_size)) { + owner_nsib[BOOT_CURR_IMG(state)] = true; +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + } else if (reset_addr >= PM_S1_ADDRESS && reset_addr <= (PM_S1_ADDRESS + PM_S1_SIZE)) { +#else + } else if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) { +#endif + /* NSIB upgrade but for the wrong slot, must be erased */ + BOOT_LOG_ERR("Image in slot is for wrong s0/s1 image"); + flash_area_erase(secondary_fa, 0, secondary_fa->fa_size); + return BOOT_SWAP_TYPE_FAIL; + } else if (reset_addr < primary_fa->fa_off || reset_addr > (primary_fa->fa_off + primary_fa->fa_size)) { /* The image in the secondary slot is not intended for any */ return BOOT_SWAP_TYPE_NONE; } @@ -1503,7 +1571,7 @@ boot_validated_swap_type(struct boot_loader_state *state, sec_slot_mark_assigned(state); } -#endif /* PM_S1_ADDRESS || CONFIG_SOC_NRF5340_CPUAPP */ +#endif /* PM_S1_ADDRESS || PM_CPUNET_B0N_ADDRESS */ swap_type = boot_swap_type_multi(BOOT_CURR_IMG(state)); if (BOOT_IS_UPGRADE(swap_type)) { @@ -2035,7 +2103,22 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) flash_area_close(fap); } - swap_run(state, bs, copy_size); +#if defined(PM_S1_ADDRESS) && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (owner_nsib[BOOT_CURR_IMG(state)]) { + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + /* For NSIB, move the image instead of swapping it */ + nsib_swap_run(state, bs); + +#if defined(CONFIG_REBOOT) + /* Should also reboot at this point so the new S0/S1 update is applied */ + sys_reboot(SYS_REBOOT_COLD); +#endif + } + } else +#endif + { + swap_run(state, bs, copy_size); + } #ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT extern int boot_status_fails; @@ -2701,12 +2784,6 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) rc = boot_perform_update(state, &bs); } assert(rc == 0); -#if defined(PM_S1_ADDRESS) && defined(CONFIG_REBOOT) - if (owner_nsib[BOOT_CURR_IMG(state)]) { - sys_reboot(SYS_REBOOT_COLD); - - } -#endif break; case BOOT_SWAP_TYPE_FAIL: @@ -2780,7 +2857,8 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) * executing MCUBoot image, and is therefore already validated by NSIB and * does not need to also be validated by MCUBoot. */ - bool image_validated_by_nsib = BOOT_CURR_IMG(state) == 1; + bool image_validated_by_nsib = BOOT_CURR_IMG(state) == + CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER; if (!image_validated_by_nsib) #endif { diff --git a/boot/bootutil/src/loader_legacy_child_parent.c b/boot/bootutil/src/loader_legacy_child_parent.c new file mode 100644 index 000000000..53f40c33a --- /dev/null +++ b/boot/bootutil/src/loader_legacy_child_parent.c @@ -0,0 +1,3896 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2016-2020 Linaro LTD + * Copyright (c) 2016-2019 JUUL Labs + * Copyright (c) 2019-2023 Arm Limited + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * This file provides an interface to the boot loader. Functions defined in + * this file should only be called while the boot loader is running. + */ + +#include +#include +#include +#include +#include +#include "bootutil/bootutil.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/image.h" +#include "bootutil_priv.h" +#include "swap_priv.h" +#include "bootutil/bootutil_log.h" +#include "bootutil/security_cnt.h" +#include "bootutil/boot_record.h" +#include "bootutil/fault_injection_hardening.h" +#include "bootutil/ramload.h" +#include "bootutil/boot_hooks.h" +#include "bootutil/mcuboot_status.h" + +#if defined(MCUBOOT_DECOMPRESS_IMAGES) +#include +#include +#endif + +#ifdef __ZEPHYR__ +#include +#endif + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) +#include +#ifdef CONFIG_PCD_READ_NETCORE_APP_VERSION +#include +int pcd_version_cmp_net(const struct flash_area *fap, struct image_header *hdr); +#endif +#endif + +#ifdef MCUBOOT_ENC_IMAGES +#include "bootutil/enc_key.h" +#endif + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) +#include +#endif + +#include "mcuboot_config/mcuboot_config.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +static struct boot_loader_state boot_data; +#ifdef PM_S1_ADDRESS +static bool owner_nsib[BOOT_IMAGE_NUMBER] = {false}; +#endif + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) +static struct image_max_size image_max_sizes[BOOT_IMAGE_NUMBER] = {0}; +#endif + +#if (!defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)) || \ +defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +#if !defined(__BOOTSIM__) +/* Used for holding static buffers in multiple functions to work around issues + * in older versions of gcc (e.g. 4.8.4) + */ +struct sector_buffer_t { + boot_sector_t primary[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; + boot_sector_t secondary[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; +#if MCUBOOT_SWAP_USING_SCRATCH + boot_sector_t scratch[BOOT_MAX_IMG_SECTORS]; +#endif +}; + +static struct sector_buffer_t sector_buffers; +#endif +#endif + +#if (BOOT_IMAGE_NUMBER > 1) +#define IMAGES_ITER(x) for ((x) = 0; (x) < BOOT_IMAGE_NUMBER; ++(x)) +#else +#define IMAGES_ITER(x) +#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 + * variables on the stack, so make them global instead. For the simulator + * we want to run as many threads as there are tests, and it's safer + * to just make those variables stack allocated. + */ +#if !defined(__BOOTSIM__) +#define TARGET_STATIC static +#else +#define TARGET_STATIC +#endif + +#if BOOT_MAX_ALIGN > 1024 +#define BUF_SZ BOOT_MAX_ALIGN +#else +#define BUF_SZ 1024 +#endif + +#define NO_ACTIVE_SLOT UINT32_MAX + +static int +boot_read_image_headers(struct boot_loader_state *state, bool require_all, + struct boot_status *bs) +{ + int rc; + int i; + + for (i = 0; i < BOOT_NUM_SLOTS; i++) { + rc = BOOT_HOOK_CALL(boot_read_image_header_hook, BOOT_HOOK_REGULAR, + BOOT_CURR_IMG(state), i, boot_img_hdr(state, i)); + if (rc == BOOT_HOOK_REGULAR) + { + rc = boot_read_image_header(state, i, boot_img_hdr(state, i), bs); + } + if (rc != 0) { + /* If `require_all` is set, fail on any single fail, otherwise + * if at least the first slot's header was read successfully, + * then the boot loader can attempt a boot. + * + * Failure to read any headers is a fatal error. + */ +#ifdef PM_S1_ADDRESS + /* Patch needed for NCS. The primary slot of the second image + * (image 1) will not contain a valid image header until an upgrade + * of mcuboot has happened (filling S1 with the new version). + */ + if (BOOT_CURR_IMG(state) == 1 && i == 0) { + continue; + } +#endif /* PM_S1_ADDRESS */ + if (i > 0 && !require_all) { + return 0; + } else { + return rc; + } + } + } + + 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, + uint8_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), + active_slot, image_max_sizes); + 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 rsp boot_rsp struct to fill. + */ +static void +fill_rsp(struct boot_loader_state *state, struct boot_rsp *rsp) +{ + uint32_t active_slot; + +#if (BOOT_IMAGE_NUMBER > 1) + /* Always boot from the first enabled image. */ + BOOT_CURR_IMG(state) = 0; + IMAGES_ITER(BOOT_CURR_IMG(state)) { + if (!state->img_mask[BOOT_CURR_IMG(state)]) { + break; + } + } + /* At least one image must be active, otherwise skip the execution */ + if (BOOT_CURR_IMG(state) >= BOOT_IMAGE_NUMBER) { + return; + } +#endif + +#if defined(MCUBOOT_DIRECT_XIP) || defined(MCUBOOT_RAM_LOAD) + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; +#else + active_slot = BOOT_PRIMARY_SLOT; +#endif + + rsp->br_flash_dev_id = flash_area_get_device_id(BOOT_IMG_AREA(state, active_slot)); + 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 BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif +#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 (BOOT_IMAGE_NUMBER > 1) || \ + defined(MCUBOOT_DIRECT_XIP) || \ + defined(MCUBOOT_RAM_LOAD) || \ + defined(MCUBOOT_DOWNGRADE_PREVENTION) +/** + * Compare image version numbers + * + * By default, the comparison does not take build number into account. + * Enable MCUBOOT_VERSION_CMP_USE_BUILD_NUMBER to take the build number into account. + * + * @param ver1 Pointer to the first image version to compare. + * @param ver2 Pointer to the second image version to compare. + * + * @retval -1 If ver1 is less than ver2. + * @retval 0 If the image version numbers are equal. + * @retval 1 If ver1 is greater than ver2. + */ +static int +boot_version_cmp(const struct image_version *ver1, + const struct image_version *ver2) +{ + if (ver1->iv_major > ver2->iv_major) { + return 1; + } + if (ver1->iv_major < ver2->iv_major) { + return -1; + } + /* The major version numbers are equal, continue comparison. */ + if (ver1->iv_minor > ver2->iv_minor) { + return 1; + } + if (ver1->iv_minor < ver2->iv_minor) { + return -1; + } + /* The minor version numbers are equal, continue comparison. */ + if (ver1->iv_revision > ver2->iv_revision) { + return 1; + } + if (ver1->iv_revision < ver2->iv_revision) { + return -1; + } + +#if defined(MCUBOOT_VERSION_CMP_USE_BUILD_NUMBER) + /* The revisions are equal, continue comparison. */ + if (ver1->iv_build_num > ver2->iv_build_num) { + return 1; + } + if (ver1->iv_build_num < ver2->iv_build_num) { + return -1; + } +#endif + + return 0; +} +#endif + +#if (!defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)) || \ +defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +static int +boot_initialize_area(struct boot_loader_state *state, int flash_area) +{ + uint32_t num_sectors = BOOT_MAX_IMG_SECTORS; + boot_sector_t *out_sectors; + uint32_t *out_num_sectors; + int rc; + + num_sectors = BOOT_MAX_IMG_SECTORS; + + if (flash_area == FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state))) { + 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(BOOT_CURR_IMG(state))) { + out_sectors = BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors; + out_num_sectors = &BOOT_IMG(state, BOOT_SECONDARY_SLOT).num_sectors; +#if MCUBOOT_SWAP_USING_SCRATCH + } else if (flash_area == FLASH_AREA_IMAGE_SCRATCH) { + out_sectors = state->scratch.sectors; + out_num_sectors = &state->scratch.num_sectors; +#endif + } else { + return BOOT_EFLASH; + } + +#ifdef MCUBOOT_USE_FLASH_AREA_GET_SECTORS + rc = flash_area_get_sectors(flash_area, &num_sectors, out_sectors); +#else + _Static_assert(sizeof(int) <= sizeof(uint32_t), "Fix needed"); + rc = flash_area_to_sectors(flash_area, (int *)&num_sectors, out_sectors); +#endif /* defined(MCUBOOT_USE_FLASH_AREA_GET_SECTORS) */ + if (rc != 0) { + return rc; + } + *out_num_sectors = num_sectors; + return 0; +} +#endif + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +static int +boot_read_sectors_recovery(struct boot_loader_state *state) +{ + uint8_t image_index; + int rc; + + image_index = BOOT_CURR_IMG(state); + + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_PRIMARY(image_index)); + if (rc != 0) { + return BOOT_EFLASH; + } + + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_SECONDARY(image_index)); + if (rc != 0) { + /* We need to differentiate from the primary image issue */ + return BOOT_EFLASH_SEC; + } + + return 0; +} +#endif + + +#if (BOOT_IMAGE_NUMBER > 1) + +static int +boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot); + +/** + * Check the image dependency whether it is satisfied and modify + * the swap type if necessary. + * + * @param dep Image dependency which has to be verified. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_verify_slot_dependency(struct boot_loader_state *state, + struct image_dependency *dep) +{ + struct image_version *dep_version; + size_t dep_slot; + int rc; + + /* Determine the source of the image which is the subject of + * the dependency and get it's version. */ +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) + uint8_t swap_type = state->swap_type[dep->image_id]; + dep_slot = BOOT_IS_UPGRADE(swap_type) ? BOOT_SECONDARY_SLOT + : BOOT_PRIMARY_SLOT; +#else + dep_slot = state->slot_usage[dep->image_id].active_slot; +#endif + + dep_version = &state->imgs[dep->image_id][dep_slot].hdr.ih_ver; + + rc = boot_version_cmp(dep_version, &dep->image_min_version); +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) + if (rc < 0) { + /* Dependency not satisfied. + * Modify the swap type to decrease the version number of the image + * (which will be located in the primary slot after the boot process), + * consequently the number of unsatisfied dependencies will be + * decreased or remain the same. + */ + switch (BOOT_SWAP_TYPE(state)) { + case BOOT_SWAP_TYPE_TEST: + case BOOT_SWAP_TYPE_PERM: + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + break; + case BOOT_SWAP_TYPE_NONE: + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_REVERT; + break; + default: + break; + } + } else { + /* Dependency satisfied. */ + rc = 0; + } +#else + if (rc >= 0) { + /* Dependency satisfied. */ + rc = 0; + } +#endif + + return rc; +} + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) +/** + * Iterate over all the images and verify whether the image dependencies in the + * TLV area are all satisfied and update the related swap type if necessary. + */ +static int +boot_verify_dependencies(struct boot_loader_state *state) +{ + int rc = -1; + uint8_t slot; + + BOOT_CURR_IMG(state) = 0; + while (BOOT_CURR_IMG(state) < BOOT_IMAGE_NUMBER) { + if (state->img_mask[BOOT_CURR_IMG(state)]) { + BOOT_CURR_IMG(state)++; + continue; + } + if (BOOT_SWAP_TYPE(state) != BOOT_SWAP_TYPE_NONE && + BOOT_SWAP_TYPE(state) != BOOT_SWAP_TYPE_FAIL) { + slot = BOOT_SECONDARY_SLOT; + } else { + slot = BOOT_PRIMARY_SLOT; + } + + rc = boot_verify_slot_dependencies(state, slot); + if (rc == 0) { + /* All dependencies've been satisfied, continue with next image. */ + BOOT_CURR_IMG(state)++; + } else if (rc == BOOT_EBADIMAGE) { + /* Cannot upgrade due to non-met dependencies, so disable all + * image upgrades. + */ + for (int idx = 0; idx < BOOT_IMAGE_NUMBER; idx++) { + BOOT_CURR_IMG(state) = idx; + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + } + break; + } else { + /* Other error happened, images are inconsistent */ + return rc; + } + } + return rc; +} +#else + +#if defined MCUBOOT_RAM_LOAD +static inline int +boot_remove_image_from_sram(struct boot_loader_state *state); +#endif + +/** + * 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. + * + * @return 0 if dependencies are met; nonzero otherwise. + */ +static int +boot_verify_dependencies(struct boot_loader_state *state) +{ + int rc = -1; + uint32_t active_slot; + + IMAGES_ITER(BOOT_CURR_IMG(state)) { + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + rc = boot_verify_slot_dependencies(state, active_slot); + if (rc != 0) { + /* Dependencies not met or invalid dependencies. */ + +#ifdef MCUBOOT_RAM_LOAD + boot_remove_image_from_sram(state); +#endif /* MCUBOOT_RAM_LOAD */ + + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + + return rc; + } + } + + return rc; +} +#endif + +/** + * Read all dependency TLVs of an image from the flash and verify + * one after another to see if they are all satisfied. + * + * @param slot Image slot number. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t 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; + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + rc = bootutil_tlv_iter_begin(&it, boot_img_hdr(state, slot), fap, + IMAGE_TLV_DEPENDENCY, true); + if (rc != 0) { + goto done; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, NULL); + if (rc < 0) { + return -1; + } else if (rc > 0) { + rc = 0; + break; + } + + if (len != sizeof(dep)) { + rc = BOOT_EBADIMAGE; + goto done; + } + + rc = LOAD_IMAGE_DATA(boot_img_hdr(state, 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; + } + + /* Verify dependency and modify the swap type if not satisfied. */ + rc = boot_verify_slot_dependency(state, &dep); + if (rc != 0) { + /* Dependency not satisfied */ + goto done; + } + } + +done: + flash_area_close(fap); + return rc; +} + +#endif /* (BOOT_IMAGE_NUMBER > 1) */ + +#if !defined(MCUBOOT_DIRECT_XIP) +/* + * Compute the total size of the given image. Includes the size of + * the TLVs. + */ +#if !defined(MCUBOOT_OVERWRITE_ONLY) || defined(MCUBOOT_OVERWRITE_ONLY_FAST) +static int +boot_read_image_size(struct boot_loader_state *state, int slot, uint32_t *size) +{ + const struct flash_area *fap; + struct image_tlv_info info; + uint32_t off; + uint32_t protect_tlv_size; + int area_id; + int rc; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + +#ifdef MCUBOOT_DECOMPRESS_IMAGES + if (MUST_DECOMPRESS(fap, BOOT_CURR_IMG(state), boot_img_hdr(state, slot))) { + uint32_t tmp_size = 0; + + rc = bootutil_get_img_decomp_size(boot_img_hdr(state, slot), fap, &tmp_size); + + if (rc) { + rc = BOOT_EBADIMAGE; + goto done; + } + + off = boot_img_hdr(state, slot)->ih_hdr_size + tmp_size; + + rc = boot_size_protected_tlvs(boot_img_hdr(state, slot), fap, &tmp_size); + + if (rc) { + rc = BOOT_EBADIMAGE; + goto done; + } + + off += tmp_size; + + if (flash_area_read(fap, (BOOT_TLV_OFF(boot_img_hdr(state, slot)) + + boot_img_hdr(state, slot)->ih_protect_tlv_size), &info, + sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + + if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { + rc = BOOT_EBADIMAGE; + goto done; + } + + *size = off + info.it_tlv_tot; + } else { +#else + if (1) { +#endif + off = BOOT_TLV_OFF(boot_img_hdr(state, slot)); + + if (flash_area_read(fap, off, &info, sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + + protect_tlv_size = boot_img_hdr(state, slot)->ih_protect_tlv_size; + if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) { + if (protect_tlv_size != info.it_tlv_tot) { + rc = BOOT_EBADIMAGE; + goto done; + } + + if (flash_area_read(fap, off + info.it_tlv_tot, &info, sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + } else if (protect_tlv_size != 0) { + rc = BOOT_EBADIMAGE; + goto done; + } + + if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { + rc = BOOT_EBADIMAGE; + goto done; + } + + *size = off + protect_tlv_size + info.it_tlv_tot; + } + + rc = 0; + +done: + flash_area_close(fap); + return rc; +} +#endif /* !MCUBOOT_OVERWRITE_ONLY */ + +#if !defined(MCUBOOT_RAM_LOAD) +static uint32_t +boot_write_sz(struct boot_loader_state *state) +{ + uint32_t elem_sz; +#if MCUBOOT_SWAP_USING_SCRATCH + uint32_t align; +#endif + + /* Figure out what size to write update status update as. The size depends + * on what the minimum write size is for scratch area, active image slot. + * We need to use the bigger of those 2 values. + */ + elem_sz = flash_area_align(BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)); +#if MCUBOOT_SWAP_USING_SCRATCH + align = flash_area_align(BOOT_SCRATCH_AREA(state)); + if (align > elem_sz) { + elem_sz = align; + } +#endif + + return elem_sz; +} + +/** + * Determines the sector layout of both image slots and the scratch area. + * This information is necessary for calculating the number of bytes to erase + * and copy during an image swap. The information collected during this + * function is used to populate the state. + */ +static int +boot_read_sectors(struct boot_loader_state *state) +{ + uint8_t image_index; + int rc; + + image_index = BOOT_CURR_IMG(state); + + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_PRIMARY(image_index)); + if (rc != 0) { + return BOOT_EFLASH; + } + + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_SECONDARY(image_index)); + if (rc != 0) { + /* We need to differentiate from the primary image issue */ + return BOOT_EFLASH_SEC; + } + +#if MCUBOOT_SWAP_USING_SCRATCH + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_SCRATCH); + if (rc != 0) { + return BOOT_EFLASH; + } +#endif + + BOOT_WRITE_SZ(state) = boot_write_sz(state); + + return 0; +} + +void +boot_status_reset(struct boot_status *bs) +{ +#ifdef MCUBOOT_ENC_IMAGES + memset(&bs->enckey, 0xff, BOOT_NUM_SLOTS * BOOT_ENC_KEY_ALIGN_SIZE); +#if MCUBOOT_SWAP_SAVE_ENCTLV + memset(&bs->enctlv, 0xff, BOOT_NUM_SLOTS * BOOT_ENC_TLV_ALIGN_SIZE); +#endif +#endif /* MCUBOOT_ENC_IMAGES */ + + bs->use_scratch = 0; + bs->swap_size = 0; + bs->source = 0; + + bs->op = BOOT_STATUS_OP_MOVE; + bs->idx = BOOT_STATUS_IDX_0; + bs->state = BOOT_STATUS_STATE_0; + bs->swap_type = BOOT_SWAP_TYPE_NONE; +} + +bool +boot_status_is_reset(const struct boot_status *bs) +{ + return (bs->op == BOOT_STATUS_OP_MOVE && + bs->idx == BOOT_STATUS_IDX_0 && + bs->state == BOOT_STATUS_STATE_0); +} + +/** + * Writes the supplied boot status to the flash file system. The boot status + * contains the current state of an in-progress image copy operation. + * + * @param bs The boot status to write. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_write_status(const struct boot_loader_state *state, struct boot_status *bs) +{ + const struct flash_area *fap; + uint32_t off; + int area_id; + int rc = 0; + uint8_t buf[BOOT_MAX_ALIGN]; + uint32_t align; + uint8_t erased_val; + + /* NOTE: The first sector copied (that is the last sector on slot) contains + * the trailer. Since in the last step the primary slot is erased, the + * first two status writes go to the scratch which will be copied to + * the primary slot! + */ + +#if MCUBOOT_SWAP_USING_SCRATCH + if (bs->use_scratch) { + /* Write to scratch. */ + area_id = FLASH_AREA_IMAGE_SCRATCH; + } else { +#endif + /* Write to the primary slot. */ + area_id = FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state)); +#if MCUBOOT_SWAP_USING_SCRATCH + } +#endif + + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + return BOOT_EFLASH; + } + + off = boot_status_off(fap) + + boot_status_internal_off(bs, BOOT_WRITE_SZ(state)); + align = flash_area_align(fap); + erased_val = flash_area_erased_val(fap); + memset(buf, erased_val, BOOT_MAX_ALIGN); + buf[0] = bs->state; + + BOOT_LOG_DBG("writing swap status; fa_id=%d off=0x%lx (0x%lx)", + flash_area_get_id(fap), (unsigned long)off, + (unsigned long)flash_area_get_off(fap) + off); + + rc = flash_area_write(fap, off, buf, align); + if (rc != 0) { + rc = BOOT_EFLASH; + } + + flash_area_close(fap); + + return rc; +} +#endif /* !MCUBOOT_RAM_LOAD */ +#endif /* !MCUBOOT_DIRECT_XIP */ + +/* + * Validate image hash/signature and optionally the security counter in a slot. + */ +static fih_ret +boot_image_check(struct boot_loader_state *state, struct image_header *hdr, + const struct flash_area *fap, struct boot_status *bs) +{ + TARGET_STATIC uint8_t tmpbuf[BOOT_TMPBUF_SZ]; + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + (void)bs; + (void)rc; + +/* In the case of ram loading the image has already been decrypted as it is + * decrypted when copied in ram */ +#if defined(MCUBOOT_ENC_IMAGES) && !defined(MCUBOOT_RAM_LOAD) + if (MUST_DECRYPT(fap, BOOT_CURR_IMG(state), hdr)) { + rc = boot_enc_load(BOOT_CURR_ENC(state), 1, hdr, fap, bs); + if (rc < 0) { + FIH_RET(fih_rc); + } + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs)) { + FIH_RET(fih_rc); + } + } +#endif + + FIH_CALL(bootutil_img_validate, fih_rc, BOOT_CURR_ENC(state), + BOOT_CURR_IMG(state), hdr, fap, tmpbuf, BOOT_TMPBUF_SZ, + NULL, 0, NULL); + + FIH_RET(fih_rc); +} + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) +static fih_ret +split_image_check(struct image_header *app_hdr, + const struct flash_area *app_fap, + struct image_header *loader_hdr, + const struct flash_area *loader_fap) +{ + static void *tmpbuf; + uint8_t loader_hash[32]; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + if (!tmpbuf) { + tmpbuf = malloc(BOOT_TMPBUF_SZ); + if (!tmpbuf) { + goto out; + } + } + + FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, loader_hdr, loader_fap, + tmpbuf, BOOT_TMPBUF_SZ, NULL, 0, loader_hash); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_RET(fih_rc); + } + + FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, app_hdr, app_fap, + tmpbuf, BOOT_TMPBUF_SZ, loader_hash, 32, NULL); + +out: + FIH_RET(fih_rc); +} +#endif /* !MCUBOOT_DIRECT_XIP && !MCUBOOT_RAM_LOAD */ + +/* + * Check that this is a valid header. Valid means that the magic is + * correct, and that the sizes/offsets are "sane". Sane means that + * there is no overflow on the arithmetic, and that the result fits + * within the flash area we are in. Also check the flags in the image + * and class the image as invalid if flags for encryption/compression + * are present but these features are not enabled. + */ +static bool +boot_is_header_valid(const struct image_header *hdr, const struct flash_area *fap, + struct boot_loader_state *state) +{ + uint32_t size; + + (void)state; + + if (hdr->ih_magic != IMAGE_MAGIC) { + return false; + } + + if (!boot_u32_safe_add(&size, hdr->ih_img_size, hdr->ih_hdr_size)) { + return false; + } + +#ifdef MCUBOOT_DECOMPRESS_IMAGES + if (!MUST_DECOMPRESS(fap, BOOT_CURR_IMG(state), hdr)) { +#else + if (1) { +#endif + if (!boot_u32_safe_add(&size, size, hdr->ih_protect_tlv_size)) { + return false; + } + } + + if (size >= flash_area_get_size(fap)) { + return false; + } + +#if !defined(MCUBOOT_ENC_IMAGES) + if (IS_ENCRYPTED(hdr)) { + return false; + } +#else + if ((hdr->ih_flags & IMAGE_F_ENCRYPTED_AES128) && + (hdr->ih_flags & IMAGE_F_ENCRYPTED_AES256)) + { + return false; + } +#endif + +#if !defined(MCUBOOT_DECOMPRESS_IMAGES) + if (IS_COMPRESSED(hdr)) { + return false; + } +#else + if (MUST_DECOMPRESS(fap, BOOT_CURR_IMG(state), hdr)) { + if (!boot_is_compressed_header_valid(hdr, fap, state)) { + return false; + } + } +#endif + + return true; +} + +/* + * Check that a memory area consists of a given value. + */ +static inline bool +boot_data_is_set_to(uint8_t val, void *data, size_t len) +{ + uint8_t i; + uint8_t *p = (uint8_t *)data; + for (i = 0; i < len; i++) { + if (val != p[i]) { + return false; + } + } + return true; +} + +static int +boot_check_header_erased(struct boot_loader_state *state, int slot) +{ + const struct flash_area *fap; + struct image_header *hdr; + uint8_t erased_val; + int area_id; + int rc; + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + return -1; + } + + erased_val = flash_area_erased_val(fap); + flash_area_close(fap); + + hdr = boot_img_hdr(state, slot); + if (!boot_data_is_set_to(erased_val, &hdr->ih_magic, sizeof(hdr->ih_magic))) { + return -1; + } + + return 0; +} + +#if defined(MCUBOOT_DIRECT_XIP) +/** + * Check if image in slot has been set with specific ROM address to run from + * and whether the slot starts at that address. + * + * @returns 0 if IMAGE_F_ROM_FIXED flag is not set; + * 0 if IMAGE_F_ROM_FIXED flag is set and ROM address specified in + * header matches the slot address; + * 1 if IMF_F_ROM_FIXED flag is set but ROM address specified in header + * does not match the slot address. + */ +static bool +boot_rom_address_check(struct boot_loader_state *state) +{ + uint32_t active_slot; + const struct image_header *hdr; + uint32_t f_off; + + active_slot = state->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", + active_slot == 0 ? "primary" : "secondary", f_off, + hdr->ih_load_addr); + + /* If there is address mismatch, the image is not bootable from this + * slot. + */ + return 1; + } + return 0; +} +#endif + +/* + * Check that there is a valid image in a slot + * + * @returns + * FIH_SUCCESS if image was successfully validated + * FIH_NO_BOOTABLE_IMAGE if no bootloable image was found + * FIH_FAILURE on any errors + */ +static fih_ret +boot_validate_slot(struct boot_loader_state *state, int slot, + struct boot_status *bs) +{ + const struct flash_area *fap; + struct image_header *hdr; + int area_id; + FIH_DECLARE(fih_rc, FIH_FAILURE); + int rc; + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + FIH_RET(fih_rc); + } + + hdr = boot_img_hdr(state, slot); + if (boot_check_header_erased(state, slot) == 0 || + (hdr->ih_flags & IMAGE_F_NON_BOOTABLE)) { + +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) + /* + * This fixes an issue where an image might be erased, but a trailer + * be left behind. It can happen if the image is in the secondary slot + * and did not pass validation, in which case the whole slot is erased. + * If during the erase operation, a reset occurs, parts of the slot + * might have been erased while some did not. The concerning part is + * the trailer because it might disable a new image from being loaded + * through mcumgr; so we just get rid of the trailer here, if the header + * is erased. + */ + if (slot != BOOT_PRIMARY_SLOT) { + swap_erase_trailer_sectors(state, fap); + } +#endif + + /* No bootable image in slot; continue booting from the primary slot. */ + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + +#if defined(MCUBOOT_OVERWRITE_ONLY) && defined(MCUBOOT_DOWNGRADE_PREVENTION) + if (slot != BOOT_PRIMARY_SLOT) { + /* Check if version of secondary slot is sufficient */ + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) \ + && defined(CONFIG_PCD_APP) && defined(CONFIG_PCD_READ_NETCORE_APP_VERSION) + if (BOOT_CURR_IMG(state) == 1) { + rc = pcd_version_cmp_net(fap, boot_img_hdr(state, BOOT_SECONDARY_SLOT)); + } else { + rc = boot_version_cmp( + &boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); + } +#else + rc = boot_version_cmp( + &boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); +#endif + if (rc < 0 && boot_check_header_erased(state, BOOT_PRIMARY_SLOT)) { + BOOT_LOG_ERR("insufficient version in secondary slot"); + flash_area_erase(fap, 0, flash_area_get_size(fap)); + /* Image in the secondary slot does not satisfy version requirement. + * Erase the image and continue booting from the primary slot. + */ + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + } +#endif + if (!boot_is_header_valid(hdr, fap, state)) { + fih_rc = FIH_FAILURE; + } else { + BOOT_HOOK_CALL_FIH(boot_image_check_hook, FIH_BOOT_HOOK_REGULAR, + fih_rc, BOOT_CURR_IMG(state), slot); + if (FIH_EQ(fih_rc, FIH_BOOT_HOOK_REGULAR)) { + FIH_CALL(boot_image_check, fih_rc, state, hdr, fap, bs); + } + } + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + if ((slot != BOOT_PRIMARY_SLOT) || ARE_SLOTS_EQUIVALENT()) { + flash_area_erase(fap, 0, flash_area_get_size(fap)); + /* Image is invalid, erase it to prevent further unnecessary + * attempts to validate and boot it. + */ + } + +#if !defined(__BOOTSIM__) + BOOT_LOG_ERR("Image in the %s slot is not valid!", + (slot == BOOT_PRIMARY_SLOT) ? "primary" : "secondary"); +#endif + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + +#if MCUBOOT_IMAGE_NUMBER > 1 && !defined(MCUBOOT_ENC_IMAGES) && defined(MCUBOOT_VERIFY_IMG_ADDRESS) + /* Verify that the image in the secondary slot has a reset address + * located in the primary slot. This is done to avoid users incorrectly + * overwriting an application written to the incorrect slot. + * This feature is only supported by ARM platforms. + */ +#if MCUBOOT_IMAGE_NUMBER >= 3 + /* Currently the MCUboot can be configured for up to 3 image, where image number 2 is + * designated for XIP, where it is the second part of image stored in slots of image + * 0. This part of image is not bootable, as the XIP setup is done by the app in + * image 0 slot, and it does not carry the reset vector. + */ + if (area_id == FLASH_AREA_IMAGE_SECONDARY(2)) { + goto out; + } +#endif + if (area_id == FLASH_AREA_IMAGE_SECONDARY(BOOT_CURR_IMG(state))) { + const struct flash_area *pri_fa = BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT); + struct image_header *secondary_hdr = boot_img_hdr(state, slot); + uint32_t reset_value = 0; + uint32_t reset_addr = secondary_hdr->ih_hdr_size + sizeof(reset_value); + + rc = flash_area_read(fap, reset_addr, &reset_value, sizeof(reset_value)); + if (rc != 0) { + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + + uint32_t min_addr, max_addr; + +#ifdef PM_CPUNET_APP_ADDRESS + /* The primary slot for the network core is emulated in RAM. + * Its flash_area hasn't got relevant boundaries. + * Therfore need to override its boundaries for the check. + */ + if (BOOT_CURR_IMG(state) == 1) { + min_addr = PM_CPUNET_APP_ADDRESS; + max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE; +#ifdef PM_S1_ADDRESS + } else if (BOOT_CURR_IMG(state) == 0) { + min_addr = PM_S0_ADDRESS; + max_addr = pri_fa->fa_off + pri_fa->fa_size; +#endif + } else +#endif + { + min_addr = pri_fa->fa_off; + max_addr = pri_fa->fa_off + pri_fa->fa_size; + } + + if (reset_value < min_addr || reset_value> (max_addr)) { + BOOT_LOG_ERR("Reset address of image in secondary slot is not in the primary slot"); + BOOT_LOG_ERR("Erasing image from secondary slot"); + + /* The vector table in the image located in the secondary + * slot does not target the primary slot. This might + * indicate that the image was loaded to the wrong slot. + * + * Erase the image and continue booting from the primary slot. + */ + flash_area_erase(fap, 0, fap->fa_size); + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + } +#endif + +out: + flash_area_close(fap); + + FIH_RET(fih_rc); +} + +#ifdef MCUBOOT_HW_ROLLBACK_PROT +/** + * Updates the stored security counter value with the image's security counter + * value which resides in the given slot, only if it's greater than the stored + * value. + * + * @param image_index Index of the image to determine which security + * counter to update. + * @param slot Slot number of the image. + * @param hdr Pointer to the image header structure of the image + * that is currently stored in the given slot. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_update_security_counter(uint8_t image_index, int slot, + struct image_header *hdr) +{ + const struct flash_area *fap = NULL; + uint32_t img_security_cnt; + int rc; + + rc = flash_area_open(flash_area_id_from_multi_image_slot(image_index, slot), + &fap); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + rc = bootutil_get_img_security_cnt(hdr, fap, &img_security_cnt); + if (rc != 0) { + goto done; + } + + rc = boot_nv_security_counter_update(image_index, img_security_cnt); + if (rc != 0) { + goto done; + } + +done: + flash_area_close(fap); + return rc; +} +#endif /* MCUBOOT_HW_ROLLBACK_PROT */ + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) + +#if defined(CONFIG_MCUBOOT_CLEANUP_UNUSABLE_SECONDARY) &&\ +(defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP)) + +#define SEC_SLOT_VIRGIN 0 +#define SEC_SLOT_TOUCHED 1 +#define SEC_SLOT_ASSIGNED 2 + +#if (MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ + !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) +/* This configuration is peculiar - the one physical secondary slot is + * mocking two logical secondary + */ +#define SEC_SLOT_PHYSICAL_CNT 1 +#else +#define SEC_SLOT_PHYSICAL_CNT MCUBOOT_IMAGE_NUMBER +#endif + +static uint8_t sec_slot_assignmnet[SEC_SLOT_PHYSICAL_CNT] = {0}; + +static inline void sec_slot_touch(struct boot_loader_state *state) +{ + uint8_t idx = (SEC_SLOT_PHYSICAL_CNT == 1) ? 0 : BOOT_CURR_IMG(state); + + if (SEC_SLOT_VIRGIN == sec_slot_assignmnet[idx]) { + sec_slot_assignmnet[idx] = SEC_SLOT_TOUCHED; + } +} + +static inline void sec_slot_mark_assigned(struct boot_loader_state *state) +{ + uint8_t idx = (SEC_SLOT_PHYSICAL_CNT == 1) ? 0 : BOOT_CURR_IMG(state); + + sec_slot_assignmnet[idx] = SEC_SLOT_ASSIGNED; +} + +/** + * Cleanu up all secondary slot which couldn't be assigned to any primary slot. + * + * This function erases content of each secondary slot which contains valid + * header but couldn't be assigned to any of supported primary images. + * + * This function is supposed to be called after boot_validated_swap_type() + * iterates over all the images in context_boot_go(). + */ +static void sec_slot_cleanup_if_unusable(void) +{ + uint8_t idx; + + for (idx = 0; idx < SEC_SLOT_PHYSICAL_CNT; idx++) { + if (SEC_SLOT_TOUCHED == sec_slot_assignmnet[idx]) { + const struct flash_area *secondary_fa; + int rc; + + rc = flash_area_open(flash_area_id_from_multi_image_slot(idx, BOOT_SECONDARY_SLOT), + &secondary_fa); + if (!rc) { + rc = flash_area_erase(secondary_fa, 0, secondary_fa->fa_size); + if (!rc) { + BOOT_LOG_ERR("Cleaned-up secondary slot of %d. image.", idx); + } + } + + if (rc) { + BOOT_LOG_ERR("Can not cleanup secondary slot of %d. image.", idx); + } + } + } +} +#else +static inline void sec_slot_touch(struct boot_loader_state *state) +{ +} +static inline void sec_slot_mark_assigned(struct boot_loader_state *state) +{ +} +static inline void sec_slot_cleanup_if_unusable(void) +{ +} +#endif /* defined(CONFIG_MCUBOOT_CLEANUP_UNUSABLE_SECONDARY) &&\ + defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP) */ + +/** + * Determines which swap operation to perform, if any. If it is determined + * that a swap operation is required, the image in the secondary slot is checked + * for validity. If the image in the secondary slot is invalid, it is erased, + * and a swap type of "none" is indicated. + * + * @return The type of swap to perform (BOOT_SWAP_TYPE...) + */ +static int +boot_validated_swap_type(struct boot_loader_state *state, + struct boot_status *bs) +{ + int swap_type; + FIH_DECLARE(fih_rc, FIH_FAILURE); + bool upgrade_valid = false; +#if defined(PM_S1_ADDRESS) + owner_nsib[BOOT_CURR_IMG(state)] = false; +#endif + +#if defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP) + const struct flash_area *secondary_fa = + BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT); + struct image_header *hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + uint32_t reset_addr = 0; + int rc = 0; + /* Patch needed for NCS. Since image 0 (the app) and image 1 (the other + * B1 slot S0 or S1) share the same secondary slot, we need to check + * whether the update candidate in the secondary slot is intended for + * image 0 or image 1 primary by looking at the address of the reset + * vector. Note that there are good reasons for not using img_num from + * the swap info. + */ + + if (hdr->ih_magic == IMAGE_MAGIC) { + rc = flash_area_read(secondary_fa, hdr->ih_hdr_size + + sizeof(uint32_t), &reset_addr, + sizeof(reset_addr)); + if (rc != 0) { + return BOOT_SWAP_TYPE_FAIL; + } + + sec_slot_touch(state); + +#ifdef PM_S1_ADDRESS +#ifdef PM_CPUNET_B0N_ADDRESS + if(!(reset_addr >= PM_CPUNET_APP_ADDRESS && reset_addr < PM_CPUNET_APP_END_ADDRESS)) +#endif + { + const struct flash_area *primary_fa; + rc = flash_area_open(flash_area_id_from_multi_image_slot( + BOOT_CURR_IMG(state), BOOT_PRIMARY_SLOT), + &primary_fa); + if (rc != 0) { + return BOOT_SWAP_TYPE_FAIL; + } + + /* Check start and end of primary slot for current image */ + if (reset_addr < primary_fa->fa_off) { +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) + const struct flash_area *nsib_fa; + + /* NSIB upgrade slot */ + rc = flash_area_open((uint32_t)_image_1_primary_slot_id, + &nsib_fa); + + if (rc != 0) { + return BOOT_SWAP_TYPE_FAIL; + } + + /* Image is placed before Primary and within the NSIB slot */ + if (reset_addr > nsib_fa->fa_off + && reset_addr < (nsib_fa->fa_off + nsib_fa->fa_size)) { + /* Set primary to be NSIB upgrade slot */ + BOOT_IMG_AREA(state, 0) = nsib_fa; + owner_nsib[BOOT_CURR_IMG(state)] = true; + } +#else + return BOOT_SWAP_TYPE_NONE; + +#endif + + } else if (reset_addr > (primary_fa->fa_off + primary_fa->fa_size)) { + /* The image in the secondary slot is not intended for any */ + return BOOT_SWAP_TYPE_NONE; + } + + if ((primary_fa->fa_off == PM_S0_ADDRESS) || (primary_fa->fa_off == PM_S1_ADDRESS)) { + owner_nsib[BOOT_CURR_IMG(state)] = true; + } + } +#endif /* PM_S1_ADDRESS */ + sec_slot_mark_assigned(state); + } + +#endif /* PM_S1_ADDRESS || CONFIG_SOC_NRF5340_CPUAPP */ + + swap_type = boot_swap_type_multi(BOOT_CURR_IMG(state)); + if (BOOT_IS_UPGRADE(swap_type)) { + /* Boot loader wants to switch to the secondary slot. + * Ensure image is valid. + */ + FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_SECONDARY_SLOT, bs); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + if (FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) { + swap_type = BOOT_SWAP_TYPE_NONE; + } else { + swap_type = BOOT_SWAP_TYPE_FAIL; + } + } else { + upgrade_valid = true; + } + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) \ + && !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) && defined(CONFIG_PCD_APP) + /* If the update is valid, and it targets the network core: perform the + * update and indicate to the caller of this function that no update is + * available + */ + if (upgrade_valid && reset_addr >= PM_CPUNET_APP_ADDRESS && + reset_addr < PM_CPUNET_APP_END_ADDRESS) { + struct image_header *hdr = (struct image_header *)secondary_fa->fa_off; + uint32_t vtable_addr = (uint32_t)hdr + hdr->ih_hdr_size; + uint32_t *net_core_fw_addr = (uint32_t *)(vtable_addr); + uint32_t fw_size = hdr->ih_img_size; + BOOT_LOG_INF("Starting network core update"); + rc = pcd_network_core_update(net_core_fw_addr, fw_size); + + if (rc != 0) { + swap_type = BOOT_SWAP_TYPE_FAIL; + } else { + BOOT_LOG_INF("Done updating network core"); +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) + /* swap_erase_trailer_sectors is undefined if upgrade only + * method is used. There is no need to erase sectors, because + * the image cannot be reverted. + */ + rc = swap_erase_trailer_sectors(state, + secondary_fa); +#endif + swap_type = BOOT_SWAP_TYPE_NONE; + } + } +#endif /* CONFIG_SOC_NRF5340_CPUAPP && PM_CPUNET_B0N_ADDRESS && + !CONFIG_NRF53_MULTI_IMAGE_UPDATE && CONFIG_PCD_APP */ + } + + return swap_type; +} +#endif + +/** + * Erases a region of flash. + * + * @param flash_area The flash_area containing the region to erase. + * @param off The offset within the flash area to start the + * erase. + * @param sz The number of bytes to erase. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_erase_region(const struct flash_area *fap, uint32_t off, uint32_t sz) +{ + return flash_area_erase(fap, off, sz); +} + +#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) + +#if defined(MCUBOOT_ENC_IMAGES) || defined(MCUBOOT_SWAP_SAVE_ENCTLV) +/* Replacement for memset(p, 0, sizeof(*p) that does not get + * optimized out. + */ +static void like_mbedtls_zeroize(void *p, size_t n) +{ + volatile unsigned char *v = (unsigned char *)p; + + for (size_t i = 0; i < n; i++) { + v[i] = 0; + } +} +#endif + +/** + * Copies the contents of one flash region to another. You must erase the + * destination region prior to calling this function. + * + * @param flash_area_id_src The ID of the source flash area. + * @param flash_area_id_dst The ID of the destination flash area. + * @param off_src The offset within the source flash area to + * copy from. + * @param off_dst The offset within the destination flash area to + * copy to. + * @param sz The number of bytes to copy. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_copy_region(struct boot_loader_state *state, + const struct flash_area *fap_src, + const struct flash_area *fap_dst, + uint32_t off_src, uint32_t off_dst, uint32_t sz) +{ + uint32_t bytes_copied; + int chunk_sz; + int rc; +#ifdef MCUBOOT_ENC_IMAGES + uint32_t off = off_dst; + uint32_t tlv_off; + size_t blk_off; + struct image_header *hdr; + uint16_t idx; + uint32_t blk_sz; + uint8_t image_index = BOOT_CURR_IMG(state); + bool encrypted_src; + bool encrypted_dst; + /* Assuming the secondary slot is source; note that 0 here not only + * means that primary slot is source, but also that there will be + * encryption happening, if it is 1 then there is decryption from + * secondary slot. + */ + int source_slot = 1; + /* In case of encryption enabled, we may have to do more work than + * just copy bytes */ + bool only_copy = false; +#else + (void)state; +#endif +#ifdef MCUBOOT_DECOMPRESS_IMAGES + struct image_header *hdr; +#endif + + TARGET_STATIC uint8_t buf[BUF_SZ] __attribute__((aligned(4))); + +#ifdef MCUBOOT_ENC_IMAGES + encrypted_src = (flash_area_get_id(fap_src) != FLASH_AREA_IMAGE_PRIMARY(image_index)); + encrypted_dst = (flash_area_get_id(fap_dst) != FLASH_AREA_IMAGE_PRIMARY(image_index)); + + if (encrypted_src != encrypted_dst) { + if (encrypted_dst) { + /* Need encryption, metadata from the primary slot */ + hdr = boot_img_hdr(state, BOOT_PRIMARY_SLOT); + source_slot = 0; + } else { + /* Need decryption, metadata from the secondary slot */ + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + source_slot = 1; + } + } else { + /* In case when source and targe is the same area, this means that we + * only have to copy bytes, no encryption or decryption. + */ + only_copy = true; + } +#endif + +#ifdef MCUBOOT_DECOMPRESS_IMAGES + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + + if (MUST_DECOMPRESS(fap_src, BOOT_CURR_IMG(state), hdr)) { + /* Use alternative function for compressed images */ + return boot_copy_region_decompress(state, fap_src, fap_dst, off_src, off_dst, sz, buf, + BUF_SZ); + } +#endif + + bytes_copied = 0; + while (bytes_copied < sz) { + if (sz - bytes_copied > sizeof buf) { + chunk_sz = sizeof buf; + } else { + chunk_sz = sz - bytes_copied; + } + + rc = flash_area_read(fap_src, off_src + bytes_copied, buf, chunk_sz); + if (rc != 0) { + return BOOT_EFLASH; + } + +#ifdef MCUBOOT_ENC_IMAGES + /* If only copy, then does not matter if header indicates need for + * encryptio/decryptio, we just copy data. */ + if (!only_copy && IS_ENCRYPTED(hdr)) { + uint32_t abs_off = off + bytes_copied; + if (abs_off < hdr->ih_hdr_size) { + /* do not decrypt header */ + if (abs_off + chunk_sz > hdr->ih_hdr_size) { + /* The lower part of the chunk contains header data */ + blk_off = 0; + blk_sz = chunk_sz - (hdr->ih_hdr_size - abs_off); + idx = hdr->ih_hdr_size - abs_off; + } else { + /* The chunk contains exclusively header data */ + blk_sz = 0; /* nothing to decrypt */ + } + } else { + idx = 0; + blk_sz = chunk_sz; + blk_off = (abs_off - hdr->ih_hdr_size) & 0xf; + } + + if (blk_sz > 0) + { + tlv_off = BOOT_TLV_OFF(hdr); + if (abs_off + chunk_sz > tlv_off) { + /* do not decrypt TLVs */ + if (abs_off >= tlv_off) { + blk_sz = 0; + } else { + blk_sz = tlv_off - abs_off; + } + } + if (source_slot == 0) { + boot_enc_encrypt(BOOT_CURR_ENC(state), source_slot, + (abs_off + idx) - hdr->ih_hdr_size, blk_sz, + blk_off, &buf[idx]); + } else { + boot_enc_decrypt(BOOT_CURR_ENC(state), source_slot, + (abs_off + idx) - hdr->ih_hdr_size, blk_sz, + blk_off, &buf[idx]); + } + } + } +#endif + + rc = flash_area_write(fap_dst, off_dst + bytes_copied, buf, chunk_sz); + if (rc != 0) { + return BOOT_EFLASH; + } + + bytes_copied += chunk_sz; + + MCUBOOT_WATCHDOG_FEED(); + } + + return 0; +} + +/** + * Overwrite primary slot with the image contained in the secondary slot. + * If a prior copy operation was interrupted by a system reset, this function + * redos the copy. + * + * @param bs The current boot status. This function reads + * this struct to determine if it is resuming + * an interrupted swap operation. This + * function writes the updated status to this + * function on return. + * + * @return 0 on success; nonzero on failure. + */ +#if defined(MCUBOOT_OVERWRITE_ONLY) || defined(MCUBOOT_BOOTSTRAP) +static int +boot_copy_image(struct boot_loader_state *state, struct boot_status *bs) +{ + size_t sect_count; + size_t sect; + int rc; + size_t size; + size_t this_size; + size_t last_sector; + const struct flash_area *fap_primary_slot; + const struct flash_area *fap_secondary_slot; + uint8_t image_index; + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + uint32_t sector; + uint32_t trailer_sz; + uint32_t off; + uint32_t sz; +#endif + + (void)bs; + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + uint32_t src_size = 0; + rc = boot_read_image_size(state, BOOT_SECONDARY_SLOT, &src_size); + assert(rc == 0); +#endif + + image_index = BOOT_CURR_IMG(state); + + BOOT_LOG_INF("Image %d upgrade secondary slot -> primary slot", image_index); + BOOT_LOG_INF("Erasing the primary slot"); + + rc = flash_area_open(flash_area_get_id(BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)), + &fap_primary_slot); + assert (rc == 0); + + rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index), + &fap_secondary_slot); + assert (rc == 0); + + sect_count = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT); + for (sect = 0, size = 0; sect < sect_count; sect++) { + this_size = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, sect); + rc = boot_erase_region(fap_primary_slot, size, this_size); + assert(rc == 0); + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + if ((size + this_size) >= src_size) { + size += src_size - size; + size += BOOT_WRITE_SZ(state) - (size % BOOT_WRITE_SZ(state)); + break; + } +#endif + + size += this_size; + } + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); + sector = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1; + sz = 0; + do { + sz += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, sector); + off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, sector); + sector--; + } while (sz < trailer_sz); + + rc = boot_erase_region(fap_primary_slot, off, sz); + assert(rc == 0); +#endif + +#ifdef MCUBOOT_ENC_IMAGES + if (IS_ENCRYPTED(boot_img_hdr(state, BOOT_SECONDARY_SLOT))) { + rc = boot_enc_load(BOOT_CURR_ENC(state), BOOT_SECONDARY_SLOT, + boot_img_hdr(state, BOOT_SECONDARY_SLOT), + fap_secondary_slot, bs); + + if (rc < 0) { + return BOOT_EBADIMAGE; + } + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs)) { + return BOOT_EBADIMAGE; + } + } +#endif + + BOOT_LOG_INF("Image %d copying the secondary slot to the primary slot: 0x%zx bytes", + image_index, size); + rc = boot_copy_region(state, fap_secondary_slot, fap_primary_slot, 0, 0, size); + if (rc != 0) { + return rc; + } + +#if defined(MCUBOOT_OVERWRITE_ONLY_FAST) + rc = boot_write_magic(fap_primary_slot); + if (rc != 0) { + return rc; + } +#endif + + rc = BOOT_HOOK_CALL(boot_copy_region_post_hook, 0, BOOT_CURR_IMG(state), + BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT), size); + if (rc != 0) { + return rc; + } + +#ifdef MCUBOOT_HW_ROLLBACK_PROT + /* Update the stored security counter with the new image's security counter + * value. Both slots hold the new image at this point, but the secondary + * slot's image header must be passed since the image headers in the + * boot_data structure have not been updated yet. + */ + rc = boot_update_security_counter(BOOT_CURR_IMG(state), BOOT_PRIMARY_SLOT, + boot_img_hdr(state, BOOT_SECONDARY_SLOT)); + if (rc != 0) { + BOOT_LOG_ERR("Security counter update failed after image upgrade."); + return rc; + } +#endif /* MCUBOOT_HW_ROLLBACK_PROT */ + +#ifndef MCUBOOT_OVERWRITE_ONLY_KEEP_BACKUP + /* + * Erases header and trailer. The trailer is erased because when a new + * image is written without a trailer as is the case when using newt, the + * trailer that was left might trigger a new upgrade. + */ + BOOT_LOG_DBG("erasing secondary header"); + rc = boot_erase_region(fap_secondary_slot, + boot_img_sector_off(state, BOOT_SECONDARY_SLOT, 0), + boot_img_sector_size(state, BOOT_SECONDARY_SLOT, 0)); + assert(rc == 0); +#endif + + last_sector = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT) - 1; + BOOT_LOG_DBG("erasing secondary trailer"); + rc = boot_erase_region(fap_secondary_slot, + boot_img_sector_off(state, BOOT_SECONDARY_SLOT, + last_sector), + boot_img_sector_size(state, BOOT_SECONDARY_SLOT, + last_sector)); + assert(rc == 0); + + flash_area_close(fap_primary_slot); + flash_area_close(fap_secondary_slot); + + /* TODO: Perhaps verify the primary slot's signature again? */ + + return 0; +} +#endif + +#if !defined(MCUBOOT_OVERWRITE_ONLY) +/** + * Swaps the two images in flash. If a prior copy operation was interrupted + * by a system reset, this function completes that operation. + * + * @param bs The current boot status. This function reads + * this struct to determine if it is resuming + * an interrupted swap operation. This + * function writes the updated status to this + * function on return. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) +{ + struct image_header *hdr; + const struct flash_area *fap; +#ifdef MCUBOOT_ENC_IMAGES + uint8_t slot; + uint8_t i; +#endif + uint32_t size; + uint32_t copy_size; + uint8_t image_index; + int rc; + + /* FIXME: just do this if asked by user? */ + + size = copy_size = 0; + image_index = BOOT_CURR_IMG(state); + + if (boot_status_is_reset(bs)) { + /* + * No swap ever happened, so need to find the largest image which + * will be used to determine the amount of sectors to swap. + */ + hdr = boot_img_hdr(state, BOOT_PRIMARY_SLOT); + if (hdr->ih_magic == IMAGE_MAGIC) { + rc = boot_read_image_size(state, BOOT_PRIMARY_SLOT, ©_size); + assert(rc == 0); + } + +#ifdef MCUBOOT_ENC_IMAGES + if (IS_ENCRYPTED(hdr)) { + fap = BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT); + rc = boot_enc_load(BOOT_CURR_ENC(state), 0, hdr, fap, bs); + assert(rc >= 0); + + if (rc == 0) { + rc = boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs); + assert(rc == 0); + } else { + rc = 0; + } + } else { + memset(bs->enckey[0], 0xff, BOOT_ENC_KEY_ALIGN_SIZE); + } +#endif + + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + if (hdr->ih_magic == IMAGE_MAGIC) { + rc = boot_read_image_size(state, BOOT_SECONDARY_SLOT, &size); + assert(rc == 0); + } + +#ifdef MCUBOOT_ENC_IMAGES + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + if (IS_ENCRYPTED(hdr)) { + fap = BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT); + rc = boot_enc_load(BOOT_CURR_ENC(state), 1, hdr, fap, bs); + assert(rc >= 0); + + if (rc == 0) { + rc = boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs); + assert(rc == 0); + } else { + rc = 0; + } + } else { + memset(bs->enckey[1], 0xff, BOOT_ENC_KEY_ALIGN_SIZE); + } +#endif + + if (size > copy_size) { + copy_size = size; + } + + bs->swap_size = copy_size; + } else { + /* + * If a swap was under way, the swap_size should already be present + * in the trailer... + */ + + rc = boot_find_status(image_index, &fap); + assert(fap != NULL); + rc = boot_read_swap_size(fap, &bs->swap_size); + assert(rc == 0); + + copy_size = bs->swap_size; + +#ifdef MCUBOOT_ENC_IMAGES + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + rc = boot_read_enc_key(fap, slot, bs); + assert(rc == 0); + + for (i = 0; i < BOOT_ENC_KEY_SIZE; i++) { + if (bs->enckey[slot][i] != 0xff) { + break; + } + } + + boot_enc_init(BOOT_CURR_ENC(state), slot); + + if (i != BOOT_ENC_KEY_SIZE) { + boot_enc_set_key(BOOT_CURR_ENC(state), slot, bs); + } + } +#endif + flash_area_close(fap); + } + + swap_run(state, bs, copy_size); + +#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT + extern int boot_status_fails; + if (boot_status_fails > 0) { + BOOT_LOG_WRN("%d status write fails performing the swap", + boot_status_fails); + } +#endif + rc = BOOT_HOOK_CALL(boot_copy_region_post_hook, 0, BOOT_CURR_IMG(state), + BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT), size); + + return 0; +} +#endif + +/** + * Performs a clean (not aborted) image update. + * + * @param bs The current boot status. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_perform_update(struct boot_loader_state *state, struct boot_status *bs) +{ + int rc; +#ifndef MCUBOOT_OVERWRITE_ONLY + uint8_t swap_type; +#endif + + /* At this point there are no aborted swaps. */ +#if defined(MCUBOOT_OVERWRITE_ONLY) + rc = boot_copy_image(state, 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). + */ + FIH_DECLARE(fih_rc, FIH_FAILURE); + rc = boot_check_header_erased(state, BOOT_PRIMARY_SLOT); + FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, bs); + if (rc == 0 || FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + rc = boot_copy_image(state, bs); + } else { + rc = boot_swap_image(state, bs); + } +#else + rc = boot_swap_image(state, bs); +#endif + assert(rc == 0); + +#ifndef MCUBOOT_OVERWRITE_ONLY + /* The following state needs image_ok be explicitly set after the + * swap was finished to avoid a new revert. + */ + swap_type = BOOT_SWAP_TYPE(state); + if (swap_type == BOOT_SWAP_TYPE_REVERT || + swap_type == BOOT_SWAP_TYPE_PERM) { + rc = swap_set_image_ok(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = swap_type = BOOT_SWAP_TYPE_PANIC; + } + } + +#ifdef MCUBOOT_HW_ROLLBACK_PROT + if (swap_type == BOOT_SWAP_TYPE_PERM) { + /* Update the stored security counter with the new image's security + * counter value. The primary slot holds the new image at this point, + * but the secondary slot's image header must be passed since image + * headers in the boot_data structure have not been updated yet. + * + * In case of a permanent image swap mcuboot will never attempt to + * revert the images on the next reboot. Therefore, the security + * counter must be increased right after the image upgrade. + */ + rc = boot_update_security_counter( + BOOT_CURR_IMG(state), + BOOT_PRIMARY_SLOT, + boot_img_hdr(state, BOOT_SECONDARY_SLOT)); + if (rc != 0) { + BOOT_LOG_ERR("Security counter update failed after " + "image upgrade."); + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + } +#endif /* MCUBOOT_HW_ROLLBACK_PROT */ + + if (BOOT_IS_UPGRADE(swap_type)) { + rc = swap_set_copy_done(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + } +#endif /* !MCUBOOT_OVERWRITE_ONLY */ + + 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_loader_state *state, + struct boot_status *bs) +{ + int rc; + + /* Determine the type of swap operation being resumed from the + * `swap-type` trailer field. + */ + rc = boot_swap_image(state, bs); + assert(rc == 0); + + BOOT_SWAP_TYPE(state) = 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 = swap_set_image_ok(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + } + + if (BOOT_IS_UPGRADE(bs->swap_type)) { + rc = swap_set_copy_done(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + } + + if (BOOT_SWAP_TYPE(state) == 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(struct boot_loader_state *state, + 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 (BOOT_CURR_IMG(state) == 0) { + /* Nothing to do */ + return; + } + + if (!aborted_swap) { + if ((BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_NONE) || + (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_REVERT)) { + /* Nothing to do */ + return; + } + } + + for (uint8_t i = 0; i < BOOT_CURR_IMG(state); i++) { + if (state->swap_type[i] == BOOT_SWAP_TYPE_REVERT) { + state->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 state TODO + * @param bs Pointer where the read and possibly updated + * boot status can be written to. + */ +static void +boot_prepare_image_for_update(struct boot_loader_state *state, + struct boot_status *bs) +{ + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) + int max_size; +#endif + + /* Determine the sector layout of the image slots and scratch area. */ + rc = boot_read_sectors(state); + 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(state) = BOOT_SWAP_TYPE_NONE; + if (rc == BOOT_EFLASH) + { + /* Only return on error from the primary image flash */ + return; + } + } + + /* Attempt to read an image header from each slot. */ + rc = boot_read_image_headers(state, false, NULL); + if (rc != 0) { + /* Continue with next image if there is one. */ + BOOT_LOG_WRN("Failed reading image headers; Image=%u", + BOOT_CURR_IMG(state)); + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + return; + } + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) + /* Fetch information on maximum sizes for later usage, if needed */ + max_size = app_max_size(state); + + if (max_size > 0) { + image_max_sizes[BOOT_CURR_IMG(state)].calculated = true; + image_max_sizes[BOOT_CURR_IMG(state)].max_size = max_size; + } +#endif + + /* If the current image's slots aren't compatible, no swap is possible. + * Just boot into primary slot. + */ + if (boot_slots_compatible(state)) { + boot_status_reset(bs); + +#ifndef MCUBOOT_OVERWRITE_ONLY + rc = swap_read_status(state, bs); + if (rc != 0) { + BOOT_LOG_WRN("Failed reading boot status; Image=%u", + BOOT_CURR_IMG(state)); + /* Continue with next image if there is one. */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + return; + } +#endif + +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) + /* + * Must re-read image headers because the boot status might + * have been updated in the previous function call. + */ + rc = boot_read_image_headers(state, !boot_status_is_reset(bs), bs); +#ifdef MCUBOOT_BOOTSTRAP + /* When bootstrapping it's OK to not have image magic in the primary slot */ + if (rc != 0 && (BOOT_CURR_IMG(state) != BOOT_PRIMARY_SLOT || + boot_check_header_erased(state, BOOT_PRIMARY_SLOT) != 0)) { +#else + if (rc != 0) { +#endif + + /* Continue with next image if there is one. */ + BOOT_LOG_WRN("Failed reading image headers; Image=%u", + BOOT_CURR_IMG(state)); + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + return; + } +#endif + + /* Determine if we rebooted in the middle of an image swap + * operation. If a partial swap was detected, complete it. + */ + if (!boot_status_is_reset(bs)) { + +#if (BOOT_IMAGE_NUMBER > 1) + boot_review_image_swap_types(state, 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(state, 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(state, false, bs); + assert(rc == 0); + + /* Swap has finished set to NONE */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + } else { + /* There was no partial swap, determine swap type. */ + if (bs->swap_type == BOOT_SWAP_TYPE_NONE) { + BOOT_SWAP_TYPE(state) = boot_validated_swap_type(state, bs); + } else { + FIH_CALL(boot_validate_slot, fih_rc, + state, BOOT_SECONDARY_SLOT, bs); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_FAIL; + } else { + BOOT_SWAP_TYPE(state) = bs->swap_type; + } + } + +#if (BOOT_IMAGE_NUMBER > 1) + boot_review_image_swap_types(state, false); +#endif + +#ifdef MCUBOOT_BOOTSTRAP + if (BOOT_SWAP_TYPE(state) == 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. + */ + rc = boot_check_header_erased(state, BOOT_PRIMARY_SLOT); + FIH_CALL(boot_validate_slot, fih_rc, + state, BOOT_PRIMARY_SLOT, bs); + + if (rc == 0 || FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + + rc = (boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_magic == IMAGE_MAGIC) ? 1: 0; + FIH_CALL(boot_validate_slot, fih_rc, + state, BOOT_SECONDARY_SLOT, bs); + + if (rc == 1 && FIH_EQ(fih_rc, FIH_SUCCESS)) { + /* 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(state) = BOOT_SWAP_TYPE_REVERT; + } + } + } +#endif + } + } else { + /* In that case if slots are not compatible. */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + } +} + +/** + * 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 +} + +/** + * Checks test swap downgrade prevention conditions. + * + * Function called only for swap upgrades test run. It may prevent + * swap if slot 1 image has <= version number or < security counter + * + * @param state Boot loader status information. + * + * @return 0 - image can be swapped, -1 downgrade prevention + */ +static int +check_downgrade_prevention(struct boot_loader_state *state) +{ +#if defined(MCUBOOT_DOWNGRADE_PREVENTION) && \ + (defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_SCRATCH)) + uint32_t security_counter[2]; + int rc; + +#if defined(PM_S1_ADDRESS) + if (owner_nsib[BOOT_CURR_IMG(state)]) { + /* Downgrade prevention on S0/S1 image is managed by NSIB */ + return 0; + } +#endif + + if (MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER) { + /* If there was security no counter in slot 0, allow swap */ + rc = bootutil_get_img_security_cnt(&(BOOT_IMG(state, 0).hdr), + BOOT_IMG(state, 0).area, + &security_counter[0]); + if (rc != 0) { + return 0; + } + /* If there is no security counter in slot 1, or it's lower than + * that of slot 0, prevent downgrade */ + rc = bootutil_get_img_security_cnt(&(BOOT_IMG(state, 1).hdr), + BOOT_IMG(state, 1).area, + &security_counter[1]); + if (rc != 0 || security_counter[0] > security_counter[1]) { + rc = -1; + } + } + else { + rc = boot_version_cmp(&boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); + } + if (rc < 0) { + /* Image in slot 0 prevents downgrade, delete image in slot 1 */ + BOOT_LOG_INF("Image %d in slot 1 erased due to downgrade prevention", BOOT_CURR_IMG(state)); + flash_area_erase(BOOT_IMG(state, 1).area, 0, + flash_area_get_size(BOOT_IMG(state, 1).area)); + } else { + rc = 0; + } + return rc; +#else + (void)state; + return 0; +#endif +} + +fih_ret +context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) +{ + size_t slot; + struct boot_status bs; + int rc = -1; + FIH_DECLARE(fih_rc, FIH_FAILURE); + int fa_id; + int image_index; + bool has_upgrade; + volatile int fih_cnt; + +#if defined(__BOOTSIM__) + /* 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). + */ + TARGET_STATIC boot_sector_t primary_slot_sectors[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; + TARGET_STATIC boot_sector_t secondary_slot_sectors[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; +#if MCUBOOT_SWAP_USING_SCRATCH + TARGET_STATIC boot_sector_t scratch_sectors[BOOT_MAX_IMG_SECTORS]; +#endif +#endif + + has_upgrade = false; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)has_upgrade; +#endif + + /* 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. + */ + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif +#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(). + */ + boot_enc_zeroize(BOOT_CURR_ENC(state)); +#endif + + image_index = BOOT_CURR_IMG(state); + +#if !defined(__BOOTSIM__) + BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors = + sector_buffers.primary[image_index]; + BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors = + sector_buffers.secondary[image_index]; +#if MCUBOOT_SWAP_USING_SCRATCH + state->scratch.sectors = sector_buffers.scratch; +#endif +#else + BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors = + primary_slot_sectors[image_index]; + BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors = + secondary_slot_sectors[image_index]; +#if MCUBOOT_SWAP_USING_SCRATCH + state->scratch.sectors = scratch_sectors; +#endif +#endif + + /* 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_multi_image_slot(image_index, slot); + rc = flash_area_open(fa_id, &BOOT_IMG_AREA(state, slot)); + assert(rc == 0); + + if (rc != 0) { + BOOT_LOG_ERR("Failed to open flash area ID %d (image %d slot %d): %d, " + "cannot continue", fa_id, image_index, (int8_t)slot, rc); + FIH_PANIC; + } + } +#if MCUBOOT_SWAP_USING_SCRATCH + rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, + &BOOT_SCRATCH_AREA(state)); + assert(rc == 0); + + if (rc != 0) { + BOOT_LOG_ERR("Failed to open scratch flash area: %d, cannot continue", rc); + FIH_PANIC; + } +#endif + + /* Determine swap type and complete swap if it has been aborted. */ + boot_prepare_image_for_update(state, &bs); + + if (BOOT_IS_UPGRADE(BOOT_SWAP_TYPE(state))) { + has_upgrade = true; + } + } + + /* cleanup secondary slots which were recognized unusable*/ + sec_slot_cleanup_if_unusable(); + +#if (BOOT_IMAGE_NUMBER > 1) + if (has_upgrade) { + /* Iterate over all the images and verify whether the image dependencies + * are all satisfied and update swap type if necessary. + */ + rc = boot_verify_dependencies(state); + if (rc != 0) { + /* + * It was impossible to upgrade because the expected dependency version + * was not available. Here we already changed the swap_type so that + * instead of asserting the bootloader, we continue and no upgrade is + * performed. + */ + rc = 0; + } + } +#endif + + /* Trigger status change callback with upgrading status */ + mcuboot_status_change(MCUBOOT_STATUS_UPGRADING); + + /* 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. + */ + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if (BOOT_IMAGE_NUMBER > 1) + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } + +#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_zeroize(BOOT_CURR_ENC(state)); +#endif /* MCUBOOT_ENC_IMAGES */ + + /* Indicate that swap is not aborted */ + boot_status_reset(&bs); +#endif /* (BOOT_IMAGE_NUMBER > 1) */ + + /* Set the previously determined swap type */ + bs.swap_type = BOOT_SWAP_TYPE(state); + + switch (BOOT_SWAP_TYPE(state)) { + case BOOT_SWAP_TYPE_NONE: + break; + + case BOOT_SWAP_TYPE_TEST: + /* fallthrough */ + case BOOT_SWAP_TYPE_PERM: + if (check_downgrade_prevention(state) != 0) { + /* Downgrade prevented */ + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; + break; + } + /* fallthrough */ + case BOOT_SWAP_TYPE_REVERT: + rc = BOOT_HOOK_CALL(boot_perform_update_hook, BOOT_HOOK_REGULAR, + BOOT_CURR_IMG(state), &(BOOT_IMG(state, 1).hdr), + BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT)); + if (rc == BOOT_HOOK_REGULAR) + { + rc = boot_perform_update(state, &bs); + } + assert(rc == 0); +#if defined(PM_S1_ADDRESS) && defined(CONFIG_REBOOT) + if (owner_nsib[BOOT_CURR_IMG(state)]) { + sys_reboot(SYS_REBOOT_COLD); + + } +#endif + 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 = swap_set_image_ok(BOOT_CURR_IMG(state)); + if (rc != 0) { + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } +#endif /* !MCUBOOT_OVERWRITE_ONLY */ + break; + + default: + BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC; + } + + if (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_PANIC) { + BOOT_LOG_ERR("panic!"); + assert(0); + + /* Loop forever... */ + FIH_PANIC; + } + } + + /* 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. + */ + FIH_SET(fih_cnt, 0); + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + /* Hardenned to prevent from skipping check of a given image, + * tmp_img_mask is declared volatile + */ + volatile bool tmp_img_mask; + FIH_SET(tmp_img_mask, state->img_mask[BOOT_CURR_IMG(state)]); + if (FIH_EQ(tmp_img_mask, true)) { + ++fih_cnt; + continue; + } +#endif + if (BOOT_SWAP_TYPE(state) != 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. + * Note: Quite complicated internal logic of boot_read_image_headers + * uses boot state, the last parm, to figure out in which slot which + * header is located; when boot state is not provided, then it + * is assumed that headers are at proper slots (we are not in + * the middle of moving images, etc). + */ + rc = boot_read_image_headers(state, false, NULL); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + 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 +#ifdef PM_S1_ADDRESS + /* Patch needed for NCS. Image 1 primary is the currently + * executing MCUBoot image, and is therefore already validated by NSIB and + * does not need to also be validated by MCUBoot. + */ + bool image_validated_by_nsib = BOOT_CURR_IMG(state) == 1; + if (!image_validated_by_nsib) +#endif + { + FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL); + /* Check for all possible values is redundant in normal operation it + * is meant to prevent FI attack. + */ + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) || + FIH_EQ(fih_rc, FIH_FAILURE) || + FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) { + FIH_SET(fih_rc, FIH_FAILURE); + 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(state, BOOT_PRIMARY_SLOT).hdr.ih_magic != IMAGE_MAGIC) { + BOOT_LOG_ERR("bad image magic 0x%lx; Image=%u", (unsigned long) + BOOT_IMG(state, BOOT_PRIMARY_SLOT).hdr.ih_magic, + BOOT_CURR_IMG(state)); + rc = BOOT_EBADIMAGE; + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } +#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */ + +#ifdef PM_S1_ADDRESS + if (!image_validated_by_nsib) +#endif + { + rc = boot_update_hw_rollback_protection(state); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + } + + rc = boot_add_shared_data(state, BOOT_PRIMARY_SLOT); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + ++fih_cnt; + } + /* + * fih_cnt should be equal to BOOT_IMAGE_NUMBER now. + * If this is not the case, at least one iteration of the loop + * has been skipped. + */ + if(FIH_NOT_EQ(fih_cnt, BOOT_IMAGE_NUMBER)) { + FIH_PANIC; + } + + fill_rsp(state, rsp); + + fih_rc = FIH_SUCCESS; +out: + /* + * Since the boot_status struct stores plaintext encryption keys, reset + * them here to avoid the possibility of jumping into an image that could + * easily recover them. + */ +#if defined(MCUBOOT_ENC_IMAGES) || defined(MCUBOOT_SWAP_SAVE_ENCTLV) + like_mbedtls_zeroize(&bs, sizeof(bs)); +#else + memset(&bs, 0, sizeof(struct boot_status)); +#endif + + close_all_flash_areas(state); + FIH_RET(fih_rc); +} + +fih_ret +split_go(int loader_slot, int split_slot, void **entry) +{ + boot_sector_t *sectors; + uintptr_t entry_val; + int loader_flash_id; + int split_flash_id; + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + sectors = malloc(BOOT_MAX_IMG_SECTORS * 2 * sizeof *sectors); + if (sectors == NULL) { + FIH_RET(FIH_FAILURE); + } + 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, + &BOOT_IMG_AREA(&boot_data, loader_slot)); + assert(rc == 0); + split_flash_id = flash_area_id_from_image_slot(split_slot); + rc = flash_area_open(split_flash_id, + &BOOT_IMG_AREA(&boot_data, split_slot)); + assert(rc == 0); + + /* Determine the sector layout of the image slots and scratch area. */ + rc = boot_read_sectors(&boot_data); + if (rc != 0) { + rc = SPLIT_GO_ERR; + goto done; + } + + rc = boot_read_image_headers(&boot_data, true, NULL); + if (rc != 0) { + goto done; + } + + /* Don't check the bootable image flag because we could really call a + * bootable or non-bootable image. Just validate that the image check + * passes which is distinct from the normal check. + */ + FIH_CALL(split_image_check, fih_rc, + boot_img_hdr(&boot_data, split_slot), + BOOT_IMG_AREA(&boot_data, split_slot), + boot_img_hdr(&boot_data, loader_slot), + BOOT_IMG_AREA(&boot_data, loader_slot)); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + goto done; + } + + entry_val = boot_img_slot_off(&boot_data, split_slot) + + boot_img_hdr(&boot_data, split_slot)->ih_hdr_size; + *entry = (void *) entry_val; + rc = SPLIT_GO_OK; + +done: + flash_area_close(BOOT_IMG_AREA(&boot_data, split_slot)); + flash_area_close(BOOT_IMG_AREA(&boot_data, loader_slot)); + free(sectors); + + if (rc) { + FIH_SET(fih_rc, FIH_FAILURE); + } + + FIH_RET(fih_rc); +} + +#else /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */ + +/** + * Opens all flash areas and checks which contain an image with a valid header. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_get_slot_usage(struct boot_loader_state *state) +{ + uint32_t slot; + int fa_id; + int rc; + struct image_header *hdr = NULL; + + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif + /* 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), state)) { + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = true; + BOOT_LOG_IMAGE_INFO(slot, hdr); + } else { + state->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"); + } + } + + state->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. + * + * @return NO_ACTIVE_SLOT if no available slot found, number of + * the found slot otherwise. + */ +static uint32_t +find_slot_with_highest_version(struct boot_loader_state *state) +{ + uint32_t slot; + uint32_t candidate_slot = NO_ACTIVE_SLOT; + int rc; + + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + if (state->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 candidate_slot; +} + +#ifdef MCUBOOT_HAVE_LOGGING +/** + * Prints the state of the loaded images. + * + * @param state Boot loader status information. + */ +static void +print_loaded_images(struct boot_loader_state *state) +{ + uint32_t active_slot; + + (void)state; + + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif + active_slot = state->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 + +#if defined(MCUBOOT_DIRECT_XIP) && defined(MCUBOOT_DIRECT_XIP_REVERT) +/** + * 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 Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_select_or_erase(struct boot_loader_state *state) +{ + const struct flash_area *fap; + int fa_id; + int rc; + uint32_t active_slot; + struct boot_swap_state* active_swap_state; + + active_slot = state->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); + + active_swap_state = &(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 (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 + * to prevent it from being selected again on the next reboot. + */ + BOOT_LOG_DBG("Erasing faulty image in the %s slot.", + (active_slot == BOOT_PRIMARY_SLOT) ? "primary" : "secondary"); + rc = flash_area_erase(fap, 0, flash_area_get_size(fap)); + assert(rc == 0); + + flash_area_close(fap); + rc = -1; + } else { + 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'."); + } + /* + * Set the copy_done flag, indicating that the image has been + * selected to boot. It can be set in advance, before even + * validating the image, because in case the validation fails, the + * entire image slot will be erased (including the trailer). + */ + rc = boot_write_copy_done(fap); + if (rc != 0) { + BOOT_LOG_WRN("Failed to set copy_done flag of the image in " + "the %s slot.", (active_slot == BOOT_PRIMARY_SLOT) ? + "primary" : "secondary"); + rc = 0; + } + } + flash_area_close(fap); + } + + return rc; +} +#endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_DIRECT_XIP_REVERT */ + +#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 active slot of the current image can be loaded within the + * predefined bounds that are allowed to be used by executable images. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_verify_ram_load_address(struct boot_loader_state *state) +{ + uint32_t img_dst; + uint32_t img_sz; + uint32_t img_end_addr; + uint32_t exec_ram_start; + uint32_t exec_ram_size; + + (void)state; + +#ifdef MULTIPLE_EXECUTABLE_RAM_REGIONS + int rc; + + 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 = state->slot_usage[BOOT_CURR_IMG(state)].img_dst; + img_sz = state->slot_usage[BOOT_CURR_IMG(state)].img_sz; + + if (img_dst < exec_ram_start) { + return BOOT_EBADIMAGE; + } + + if (!boot_u32_safe_add(&img_end_addr, img_dst, img_sz)) { + return BOOT_EBADIMAGE; + } + + if (img_end_addr > (exec_ram_start + exec_ram_size)) { + return BOOT_EBADIMAGE; + } + + return 0; +} + +#ifdef MCUBOOT_ENC_IMAGES + +/** + * Copies and decrypts an image from a slot in the flash to an SRAM address. + * + * @param state Boot loader status information. + * @param slot The flash slot of the image to be copied to SRAM. + * @param hdr The image header. + * @param src_sz Size of the image. + * @param img_dst Pointer to the address at which the image needs to be + * copied to SRAM. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_decrypt_and_copy_image_to_sram(struct boot_loader_state *state, + uint32_t slot, struct image_header *hdr, + uint32_t src_sz, uint32_t img_dst) +{ + /* The flow for the decryption and copy of the image is as follows : + * 1. The whole image is copied to the RAM (header + payload + TLV). + * 2. The encryption key is loaded from the TLV in flash. + * 3. The image is then decrypted chunk by chunk in RAM (1 chunk + * is 1024 bytes). Only the payload section is decrypted. + * 4. The image is authenticated in RAM. + */ + const struct flash_area *fap_src = NULL; + struct boot_status bs; + uint32_t blk_off; + uint32_t tlv_off; + uint32_t blk_sz; + uint32_t bytes_copied = hdr->ih_hdr_size; + uint32_t chunk_sz; + uint32_t max_sz = 1024; + uint16_t idx; + uint8_t * cur_dst; + int area_id; + int rc; + uint8_t * ram_dst = (void *)(IMAGE_RAM_BASE + img_dst); + + 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; + } + + tlv_off = BOOT_TLV_OFF(hdr); + + /* Copying the whole image in RAM */ + rc = flash_area_read(fap_src, 0, ram_dst, src_sz); + if (rc != 0) { + goto done; + } + + rc = boot_enc_load(BOOT_CURR_ENC(state), slot, hdr, fap_src, &bs); + if (rc < 0) { + goto done; + } + + /* if rc > 0 then the key has already been loaded */ + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), slot, &bs)) { + goto done; + } + + /* Starting at the end of the header as the header section is not encrypted */ + while (bytes_copied < tlv_off) { /* TLV section copied previously */ + if (src_sz - bytes_copied > max_sz) { + chunk_sz = max_sz; + } else { + chunk_sz = src_sz - bytes_copied; + } + + cur_dst = ram_dst + bytes_copied; + blk_sz = chunk_sz; + idx = 0; + blk_off = ((bytes_copied) - hdr->ih_hdr_size) & 0xf; + if (bytes_copied + chunk_sz > tlv_off) { + /* Going over TLV section + * Part of the chunk is encrypted payload */ + blk_sz = tlv_off - (bytes_copied); + } + boot_enc_decrypt(BOOT_CURR_ENC(state), slot, + (bytes_copied + idx) - hdr->ih_hdr_size, blk_sz, + blk_off, cur_dst); + bytes_copied += chunk_sz; + } + rc = 0; + +done: + flash_area_close(fap_src); + + return rc; +} + +#endif /* MCUBOOT_ENC_IMAGES */ +/** + * 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. + * @param img_sz The size of the image that needs to be copied to SRAM. + * + * @return 0 on success; nonzero on failure. + */ +static int +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; + +#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; + } + + /* Direct copy from flash to its new location in SRAM. */ + rc = flash_area_read(fap_src, 0, (void *)(IMAGE_RAM_BASE + img_dst), img_sz); + if (rc != 0) { + BOOT_LOG_INF("Error whilst copying image %d from Flash to SRAM: %d", + BOOT_CURR_IMG(state), rc); + } + + flash_area_close(fap_src); + + return rc; +} + +#if (BOOT_IMAGE_NUMBER > 1) +/** + * Checks if two memory regions (A and B) are overlap or not. + * + * @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 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 state Boot loader status information. + * + * @return 0 if there is no overlap; nonzero otherwise. + */ +static int +boot_check_ram_load_overlapping(struct boot_loader_state *state) +{ + uint32_t i; + + uint32_t start_a; + uint32_t end_a; + uint32_t start_b; + uint32_t end_b; + uint32_t image_id_to_check = BOOT_CURR_IMG(state); + + start_a = state->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 + state->slot_usage[image_id_to_check].img_sz; + + for (i = 0; i < BOOT_IMAGE_NUMBER; i++) { + if (state->slot_usage[i].active_slot == NO_ACTIVE_SLOT + || i == image_id_to_check) { + continue; + } + + start_b = state->slot_usage[i].img_dst; + /* Safe to add here, values are already verified in + * boot_verify_ram_load_address() */ + end_b = start_b + state->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. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_load_image_to_sram(struct boot_loader_state *state) +{ + uint32_t active_slot; + struct image_header *hdr = NULL; + uint32_t img_dst; + uint32_t img_sz; + int rc; + + active_slot = state->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; + + rc = boot_read_image_size(state, active_slot, &img_sz); + if (rc != 0) { + return rc; + } + + state->slot_usage[BOOT_CURR_IMG(state)].img_dst = img_dst; + state->slot_usage[BOOT_CURR_IMG(state)].img_sz = img_sz; + + rc = boot_verify_ram_load_address(state); + if (rc != 0) { + BOOT_LOG_INF("Image %d RAM load address 0x%x is invalid.", BOOT_CURR_IMG(state), img_dst); + return rc; + } + +#if (BOOT_IMAGE_NUMBER > 1) + rc = boot_check_ram_load_overlapping(state); + if (rc != 0) { + BOOT_LOG_INF("Image %d RAM loading to address 0x%x would overlap with\ + another image.", BOOT_CURR_IMG(state), img_dst); + return rc; + } +#endif +#ifdef MCUBOOT_ENC_IMAGES + /* decrypt image if encrypted and copy it to RAM */ + if (IS_ENCRYPTED(hdr)) { + rc = boot_decrypt_and_copy_image_to_sram(state, active_slot, hdr, img_sz, img_dst); + } else { + rc = boot_copy_image_to_sram(state, active_slot, img_dst, img_sz); + } +#else + /* Copy image to the load address from where it currently resides in + * flash. + */ + rc = boot_copy_image_to_sram(state, active_slot, img_dst, img_sz); +#endif + if (rc != 0) { + BOOT_LOG_INF("Image %d RAM loading to 0x%x is failed.", BOOT_CURR_IMG(state), img_dst); + } else { + BOOT_LOG_INF("Image %d RAM loading to 0x%x is succeeded.", BOOT_CURR_IMG(state), img_dst); + } + } else { + /* Only images that support IMAGE_F_RAM_LOAD are allowed if + * MCUBOOT_RAM_LOAD is set. + */ + rc = BOOT_EBADIMAGE; + } + + if (rc != 0) { + state->slot_usage[BOOT_CURR_IMG(state)].img_dst = 0; + state->slot_usage[BOOT_CURR_IMG(state)].img_sz = 0; + } + + return rc; +} + +/** + * Removes an image from SRAM, by overwriting it with zeros. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static inline int +boot_remove_image_from_sram(struct boot_loader_state *state) +{ + (void)state; + + BOOT_LOG_INF("Removing image %d from SRAM at address 0x%x", + BOOT_CURR_IMG(state), + state->slot_usage[BOOT_CURR_IMG(state)].img_dst); + + memset((void*)(IMAGE_RAM_BASE + state->slot_usage[BOOT_CURR_IMG(state)].img_dst), + 0, state->slot_usage[BOOT_CURR_IMG(state)].img_sz); + + state->slot_usage[BOOT_CURR_IMG(state)].img_dst = 0; + state->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_flash(struct boot_loader_state *state, uint32_t slot) +{ + int area_id; + int rc; + const struct flash_area *fap; + + (void)state; + + 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, flash_area_get_size(fap)); + flash_area_close(fap); + } + + return rc; +} +#endif /* MCUBOOT_RAM_LOAD */ + + +/** + * Tries to load a slot for all the images with validation. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +fih_ret +boot_load_and_validate_images(struct boot_loader_state *state) +{ + uint32_t active_slot; + int rc; + fih_ret 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 = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + if (active_slot != NO_ACTIVE_SLOT){ + /* A slot is already active, go to next image. */ + break; + } + + active_slot = find_slot_with_highest_version(state); + 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. */ + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = active_slot; + +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif + +#ifdef MCUBOOT_DIRECT_XIP + rc = boot_rom_address_check(state); + if (rc != 0) { + /* The image is placed in an unsuitable slot. */ + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + continue; + } + +#ifdef MCUBOOT_DIRECT_XIP_REVERT + rc = boot_select_or_erase(state); + if (rc != 0) { + /* The selected image slot has been erased. */ + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + continue; + } +#endif /* MCUBOOT_DIRECT_XIP_REVERT */ +#endif /* MCUBOOT_DIRECT_XIP */ + +#ifdef MCUBOOT_RAM_LOAD + /* Image is first loaded to RAM and authenticated there in order to + * prevent TOCTOU attack during image copy. This could be applied + * when loading images from external (untrusted) flash to internal + * (trusted) RAM and image is authenticated before copying. + */ + rc = boot_load_image_to_sram(state); + if (rc != 0 ) { + /* Image cannot be ramloaded. */ + boot_remove_image_from_flash(state, active_slot); + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT; + continue; + } +#endif /* MCUBOOT_RAM_LOAD */ + + 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 + boot_remove_image_from_sram(state); +#endif /* MCUBOOT_RAM_LOAD */ + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->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. + * + * @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 newer (active) image's + * security counter value. + */ +#if defined(MCUBOOT_DIRECT_XIP) && defined(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 (state->slot_usage[BOOT_CURR_IMG(state)].swap_state.image_ok == BOOT_FLAG_SET) { +#endif + rc = boot_update_security_counter(BOOT_CURR_IMG(state), + state->slot_usage[BOOT_CURR_IMG(state)].active_slot, + boot_img_hdr(state, state->slot_usage[BOOT_CURR_IMG(state)].active_slot)); + if (rc != 0) { + BOOT_LOG_ERR("Security counter update failed after image %d validation.", BOOT_CURR_IMG(state)); + return rc; + } +#if defined(MCUBOOT_DIRECT_XIP) && defined(MCUBOOT_DIRECT_XIP_REVERT) + } +#endif + + return 0; + +#else /* MCUBOOT_HW_ROLLBACK_PROT */ + (void) (state); + return 0; +#endif +} + +fih_ret +context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) +{ + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + rc = boot_get_slot_usage(state); + if (rc != 0) { + goto out; + } + +#if (BOOT_IMAGE_NUMBER > 1) + while (true) { +#endif + FIH_CALL(boot_load_and_validate_images, fih_rc, state); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + +#if (BOOT_IMAGE_NUMBER > 1) + rc = boot_verify_dependencies(state); + 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)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif + rc = boot_update_hw_rollback_protection(state); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + rc = boot_add_shared_data(state, (uint8_t)state->slot_usage[BOOT_CURR_IMG(state)].active_slot); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + } + + /* All image loaded successfully. */ +#ifdef MCUBOOT_HAVE_LOGGING + print_loaded_images(state); +#endif + + fill_rsp(state, rsp); + +out: + close_all_flash_areas(state); + + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + } + + FIH_RET(fih_rc); +} +#endif /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */ + +/** + * Prepares the booting process. This function moves images around in flash as + * appropriate, and tells you what address to boot from. + * + * @param rsp On success, indicates how booting should occur. + * + * @return FIH_SUCCESS on success; nonzero on failure. + */ +fih_ret +boot_go(struct boot_rsp *rsp) +{ + FIH_DECLARE(fih_rc, FIH_FAILURE); + + boot_state_clear(NULL); + + FIH_CALL(context_boot_go, fih_rc, &boot_data, rsp); + FIH_RET(fih_rc); +} + +/** + * Prepares the booting process, considering only a single image. This function + * moves images around in flash as appropriate, and tells you what address to + * boot from. + * + * @param rsp On success, indicates how booting should occur. + * + * @param image_id The image ID to prepare the boot process for. + * + * @return FIH_SUCCESS on success; nonzero on failure. + */ +fih_ret +boot_go_for_image_id(struct boot_rsp *rsp, uint32_t image_id) +{ + FIH_DECLARE(fih_rc, FIH_FAILURE); + + if (image_id >= BOOT_IMAGE_NUMBER) { + FIH_RET(FIH_FAILURE); + } + +#if BOOT_IMAGE_NUMBER > 1 + memset(&boot_data.img_mask, 1, BOOT_IMAGE_NUMBER); + boot_data.img_mask[image_id] = 0; +#endif + + FIH_CALL(context_boot_go, fih_rc, &boot_data, rsp); + FIH_RET(fih_rc); +} + +/** + * Clears the boot state, so that previous operations have no effect on new + * ones. + * + * @param state The state that should be cleared. If the value + * is NULL, the default bootloader state will be + * cleared. + */ +void boot_state_clear(struct boot_loader_state *state) +{ + if (state != NULL) { + memset(state, 0, sizeof(struct boot_loader_state)); + } else { + memset(&boot_data, 0, sizeof(struct boot_loader_state)); + } +} + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +/** + * Reads image data to find out the maximum application sizes. Only needs to + * be called in serial recovery mode, as the state information is unpopulated + * at that time + */ +static void boot_fetch_slot_state_sizes(void) +{ + size_t slot; + int rc = -1; + int fa_id; + int image_index; + + IMAGES_ITER(BOOT_CURR_IMG(&boot_data)) { + int max_size = 0; + + image_index = BOOT_CURR_IMG(&boot_data); + + BOOT_IMG(&boot_data, BOOT_PRIMARY_SLOT).sectors = + sector_buffers.primary[image_index]; + BOOT_IMG(&boot_data, BOOT_SECONDARY_SLOT).sectors = + sector_buffers.secondary[image_index]; +#if MCUBOOT_SWAP_USING_SCRATCH + boot_data.scratch.sectors = sector_buffers.scratch; +#endif + + /* 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_multi_image_slot(image_index, slot); + rc = flash_area_open(fa_id, &BOOT_IMG_AREA(&boot_data, slot)); + assert(rc == 0); + + if (rc != 0) { + goto finish; + } + } + +#if MCUBOOT_SWAP_USING_SCRATCH + rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, + &BOOT_SCRATCH_AREA(&boot_data)); + assert(rc == 0); + + if (rc != 0) { + goto finish; + } +#endif + + /* Determine the sector layout of the image slots and scratch area. */ + rc = boot_read_sectors_recovery(&boot_data); + + if (rc == 0) { + max_size = app_max_size(&boot_data); + + if (max_size > 0) { + image_max_sizes[image_index].calculated = true; + image_max_sizes[image_index].max_size = max_size; + } + } + } + +finish: + close_all_flash_areas(&boot_data); + memset(&boot_data, 0x00, sizeof(boot_data)); +} +#endif + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) +/** + * Fetches the maximum allowed size of the image + */ +const struct image_max_size *boot_get_max_app_size(void) +{ +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) + uint8_t i = 0; + + while (i < BOOT_IMAGE_NUMBER) { + if (image_max_sizes[i].calculated == true) { + break; + } + + ++i; + } + + if (i == BOOT_IMAGE_NUMBER) { + /* Information not available, need to fetch it */ + boot_fetch_slot_state_sizes(); + } +#endif + + return image_max_sizes; +} +#endif diff --git a/boot/bootutil/src/swap_nsib.c b/boot/bootutil/src/swap_nsib.c new file mode 100644 index 000000000..39ed4c652 --- /dev/null +++ b/boot/bootutil/src/swap_nsib.c @@ -0,0 +1,70 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include "bootutil/bootutil.h" +#include "bootutil_priv.h" +#include "swap_priv.h" +#include "bootutil/bootutil_log.h" + +#include "mcuboot_config/mcuboot_config.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +void nsib_swap_run(struct boot_loader_state *state, struct boot_status *bs) +{ + uint32_t sector_sz; + uint8_t image_index; + const struct flash_area *fap_pri; + const struct flash_area *fap_sec; + int rc; + + BOOT_LOG_INF("Starting swap using nsib algorithm."); + + sector_sz = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, 0); + +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + rc = flash_area_open(PM_S0_ID, &fap_pri); +#else + rc = flash_area_open(PM_S1_ID, &fap_pri); +#endif + assert (rc == 0); + image_index = BOOT_CURR_IMG(state); + + rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index), &fap_sec); + assert (rc == 0); + + rc = boot_erase_region(fap_pri, 0, fap_pri->fa_size); + assert(rc == 0); + + rc = boot_copy_region(state, fap_sec, fap_pri, 0, 0, fap_pri->fa_size); + assert(rc == 0); + + rc = swap_erase_trailer_sectors(state, fap_sec); + assert(rc == 0); + + rc = boot_erase_region(fap_sec, 0, MIN((fap_pri->fa_size + sector_sz), fap_sec->fa_size)); + assert(rc == 0); + + flash_area_close(fap_pri); + flash_area_close(fap_sec); +} diff --git a/boot/bootutil/src/swap_priv.h b/boot/bootutil/src/swap_priv.h index 960c72250..2734d6262 100644 --- a/boot/bootutil/src/swap_priv.h +++ b/boot/bootutil/src/swap_priv.h @@ -106,4 +106,12 @@ static inline size_t boot_scratch_area_size(const struct boot_loader_state *stat */ int app_max_size(struct boot_loader_state *state); +#if defined(PM_S1_ADDRESS) && !defined(MCUBOOT_OVERWRITE_ONLY) && \ +(CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 || defined(LEGACY_CHILD_PARENT_S0_S1_UPDATE_ENABLED)) +/** + * Performs an NSIB update + */ +void nsib_swap_run(struct boot_loader_state *state, struct boot_status *bs); +#endif + #endif /* H_SWAP_PRIV_ */ diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index 20d38b49b..e01524221 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -145,13 +145,29 @@ zephyr_library_sources( ) zephyr_library_include_directories(${BOOT_DIR}/bootutil/src) else() + if(NOT CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER EQUAL "-1") + # Sysbuild + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/loader.c + ) + else() + # Legacy child/parent image + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/loader_legacy_child_parent.c + ) + endif() zephyr_library_sources( - ${BOOT_DIR}/bootutil/src/loader.c ${BOOT_DIR}/bootutil/src/swap_misc.c ${BOOT_DIR}/bootutil/src/swap_scratch.c ${BOOT_DIR}/bootutil/src/swap_move.c ${BOOT_DIR}/bootutil/src/caps.c ) + + if(NOT CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER EQUAL "-1" AND NOT CONFIG_BOOT_UPGRADE_ONLY) + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/swap_nsib.c + ) + endif() endif() if(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256 OR CONFIG_BOOT_ENCRYPT_EC256) diff --git a/boot/zephyr/include/sysflash/pm_sysflash.h b/boot/zephyr/include/sysflash/pm_sysflash.h index db60ddd03..42f25182e 100644 --- a/boot/zephyr/include/sysflash/pm_sysflash.h +++ b/boot/zephyr/include/sysflash/pm_sysflash.h @@ -15,48 +15,36 @@ #ifndef CONFIG_SINGLE_APPLICATION_SLOT -#if (MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) -/* If B0 is present then two bootloaders are present, and we must use - * a single secondary slot for both primary slots. - */ -extern uint32_t _image_1_primary_slot_id[]; -#endif /* (MCUBOOT_IMAGE_NUMBER == 2 && defined(PM_B0_ADDRESS) */ - -#if (MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ - !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) - -#define FLASH_AREA_IMAGE_PRIMARY(x) \ - ((x == 0) ? \ - PM_MCUBOOT_PRIMARY_ID : \ - (x == 1) ? \ - (uint32_t)_image_1_primary_slot_id : \ - 255 ) - -#define FLASH_AREA_IMAGE_SECONDARY(x) \ - ((x == 0) ? \ - PM_MCUBOOT_SECONDARY_ID: \ - (x == 1) ? \ - PM_MCUBOOT_SECONDARY_ID: \ - 255 ) - -#else /* MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ - * !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) - */ - /* Each pair of slots is separated by , and there is no terminating character */ -#define FLASH_AREA_IMAGE_0_SLOTS PM_MCUBOOT_PRIMARY_ID, PM_MCUBOOT_SECONDARY_ID -#define FLASH_AREA_IMAGE_1_SLOTS PM_MCUBOOT_PRIMARY_1_ID, PM_MCUBOOT_SECONDARY_1_ID -#define FLASH_AREA_IMAGE_2_SLOTS PM_MCUBOOT_PRIMARY_2_ID, PM_MCUBOOT_SECONDARY_2_ID +#define FLASH_AREA_IMAGE_0_SLOTS PM_MCUBOOT_PRIMARY_ID, PM_MCUBOOT_SECONDARY_ID, +#define FLASH_AREA_IMAGE_1_SLOTS PM_MCUBOOT_PRIMARY_1_ID, PM_MCUBOOT_SECONDARY_1_ID, +#define FLASH_AREA_IMAGE_2_SLOTS PM_MCUBOOT_PRIMARY_2_ID, PM_MCUBOOT_SECONDARY_2_ID, +#define FLASH_AREA_IMAGE_3_SLOTS PM_MCUBOOT_PRIMARY_3_ID, PM_MCUBOOT_SECONDARY_3_ID, + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 +#ifdef CONFIG_NCS_IS_VARIANT_IMAGE +#define MCUBOOT_S0_S1_SLOTS PM_S0_ID, PM_MCUBOOT_SECONDARY_ID, +#else +#define MCUBOOT_S0_S1_SLOTS PM_S1_ID, PM_MCUBOOT_SECONDARY_ID, +#endif +#else +#define MCUBOOT_S0_S1_SLOTS +#endif -#if (MCUBOOT_IMAGE_NUMBER == 1) +#if (MCUBOOT_IMAGE_NUMBER == 1) || (MCUBOOT_IMAGE_NUMBER == 2 && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1) #define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS -#elif (MCUBOOT_IMAGE_NUMBER == 2) -#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS, \ +#elif (MCUBOOT_IMAGE_NUMBER == 2) || (MCUBOOT_IMAGE_NUMBER == 3 && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS \ FLASH_AREA_IMAGE_1_SLOTS -#elif (MCUBOOT_IMAGE_NUMBER == 3) -#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS, \ - FLASH_AREA_IMAGE_1_SLOTS, \ +#elif (MCUBOOT_IMAGE_NUMBER == 3) || (MCUBOOT_IMAGE_NUMBER == 4 && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS \ + FLASH_AREA_IMAGE_1_SLOTS \ FLASH_AREA_IMAGE_2_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 4) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS \ + FLASH_AREA_IMAGE_1_SLOTS \ + FLASH_AREA_IMAGE_2_SLOTS \ + FLASH_AREA_IMAGE_3_SLOTS #else #error Unsupported number of images #endif @@ -65,6 +53,7 @@ static inline uint32_t __flash_area_ids_for_slot(int img, int slot) { static const int all_slots[] = { ALL_AVAILABLE_SLOTS + MCUBOOT_S0_S1_SLOTS }; return all_slots[img * 2 + slot]; }; @@ -72,6 +61,8 @@ static inline uint32_t __flash_area_ids_for_slot(int img, int slot) #undef FLASH_AREA_IMAGE_0_SLOTS #undef FLASH_AREA_IMAGE_1_SLOTS #undef FLASH_AREA_IMAGE_2_SLOTS +#undef FLASH_AREA_IMAGE_3_SLOTS +#undef MCUBOOT_S0_S1_SLOTS #undef ALL_AVAILABLE_SLOTS #define FLASH_AREA_IMAGE_PRIMARY(x) __flash_area_ids_for_slot(x, 0) @@ -81,10 +72,6 @@ static inline uint32_t __flash_area_ids_for_slot(int img, int slot) #define FLASH_AREA_IMAGE_SCRATCH PM_MCUBOOT_SCRATCH_ID #endif -#endif /* MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ - * !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) - */ - #else /* CONFIG_SINGLE_APPLICATION_SLOT */ #define FLASH_AREA_IMAGE_PRIMARY(x) PM_MCUBOOT_PRIMARY_ID diff --git a/boot/zephyr/include/sysflash/pm_sysflash_legacy_child_parent.h b/boot/zephyr/include/sysflash/pm_sysflash_legacy_child_parent.h new file mode 100644 index 000000000..db60ddd03 --- /dev/null +++ b/boot/zephyr/include/sysflash/pm_sysflash_legacy_child_parent.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef __PM_SYSFLASH_H__ +#define __PM_SYSFLASH_H__ +/* Blocking the __SYSFLASH_H__ */ +#define __SYSFLASH_H__ + +#include +#include +#include + +#ifndef CONFIG_SINGLE_APPLICATION_SLOT + +#if (MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) +/* If B0 is present then two bootloaders are present, and we must use + * a single secondary slot for both primary slots. + */ +extern uint32_t _image_1_primary_slot_id[]; +#endif /* (MCUBOOT_IMAGE_NUMBER == 2 && defined(PM_B0_ADDRESS) */ + +#if (MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ + !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) + +#define FLASH_AREA_IMAGE_PRIMARY(x) \ + ((x == 0) ? \ + PM_MCUBOOT_PRIMARY_ID : \ + (x == 1) ? \ + (uint32_t)_image_1_primary_slot_id : \ + 255 ) + +#define FLASH_AREA_IMAGE_SECONDARY(x) \ + ((x == 0) ? \ + PM_MCUBOOT_SECONDARY_ID: \ + (x == 1) ? \ + PM_MCUBOOT_SECONDARY_ID: \ + 255 ) + +#else /* MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ + * !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) + */ + +/* Each pair of slots is separated by , and there is no terminating character */ +#define FLASH_AREA_IMAGE_0_SLOTS PM_MCUBOOT_PRIMARY_ID, PM_MCUBOOT_SECONDARY_ID +#define FLASH_AREA_IMAGE_1_SLOTS PM_MCUBOOT_PRIMARY_1_ID, PM_MCUBOOT_SECONDARY_1_ID +#define FLASH_AREA_IMAGE_2_SLOTS PM_MCUBOOT_PRIMARY_2_ID, PM_MCUBOOT_SECONDARY_2_ID + +#if (MCUBOOT_IMAGE_NUMBER == 1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 2) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS, \ + FLASH_AREA_IMAGE_1_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 3) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS, \ + FLASH_AREA_IMAGE_1_SLOTS, \ + FLASH_AREA_IMAGE_2_SLOTS +#else +#error Unsupported number of images +#endif + +static inline uint32_t __flash_area_ids_for_slot(int img, int slot) +{ + static const int all_slots[] = { + ALL_AVAILABLE_SLOTS + }; + return all_slots[img * 2 + slot]; +}; + +#undef FLASH_AREA_IMAGE_0_SLOTS +#undef FLASH_AREA_IMAGE_1_SLOTS +#undef FLASH_AREA_IMAGE_2_SLOTS +#undef ALL_AVAILABLE_SLOTS + +#define FLASH_AREA_IMAGE_PRIMARY(x) __flash_area_ids_for_slot(x, 0) +#define FLASH_AREA_IMAGE_SECONDARY(x) __flash_area_ids_for_slot(x, 1) + +#if !defined(CONFIG_BOOT_SWAP_USING_MOVE) +#define FLASH_AREA_IMAGE_SCRATCH PM_MCUBOOT_SCRATCH_ID +#endif + +#endif /* MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ + * !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) + */ + +#else /* CONFIG_SINGLE_APPLICATION_SLOT */ + +#define FLASH_AREA_IMAGE_PRIMARY(x) PM_MCUBOOT_PRIMARY_ID +#define FLASH_AREA_IMAGE_SECONDARY(x) PM_MCUBOOT_PRIMARY_ID +/* NOTE: Scratch parition is not used by single image DFU but some of + * functions in common files reference it, so the definitions has been + * provided to allow compilation of common units. + */ +#define FLASH_AREA_IMAGE_SCRATCH 0 + +#endif /* CONFIG_SINGLE_APPLICATION_SLOT */ + +#endif /* __PM_SYSFLASH_H__ */ diff --git a/boot/zephyr/include/sysflash/sysflash.h b/boot/zephyr/include/sysflash/sysflash.h index f231c3d02..bee101b2e 100644 --- a/boot/zephyr/include/sysflash/sysflash.h +++ b/boot/zephyr/include/sysflash/sysflash.h @@ -7,7 +7,13 @@ #if USE_PARTITION_MANAGER /* Blocking the rest of the file */ #define __SYSFLASH_H__ +#if CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER != -1 +/* Sysbuild */ #include +#else +/* Legacy child/parent */ +#include +#endif #endif #ifndef __SYSFLASH_H__ diff --git a/boot/zephyr/pm.yml b/boot/zephyr/pm.yml index eabe3d08e..13ffc44aa 100644 --- a/boot/zephyr/pm.yml +++ b/boot/zephyr/pm.yml @@ -4,6 +4,9 @@ mcuboot: size: CONFIG_PM_PARTITION_SIZE_MCUBOOT placement: before: [mcuboot_primary] +#if defined(CONFIG_HIDE_CHILD_PARENT_CONFIG) + align: {end: 0x1000} +#endif mcuboot_primary_app: # All images to be placed in MCUboot's slot 0 should be placed in this