diff --git a/boot/boot_serial/src/boot_serial_encryption.c b/boot/boot_serial/src/boot_serial_encryption.c index 4bbe74dc3..de1f8b57a 100644 --- a/boot/boot_serial/src/boot_serial_encryption.c +++ b/boot/boot_serial/src/boot_serial_encryption.c @@ -31,11 +31,11 @@ boot_image_validate_encrypted(struct boot_loader_state *state, int rc; if (MUST_DECRYPT(fa_p, BOOT_CURR_IMG(state), hdr)) { - rc = boot_enc_load(state, 1, hdr, fa_p, bs); + rc = boot_enc_load(state, BOOT_SLOT_SECONDARY, hdr, fa_p, bs); if (rc < 0) { FIH_RET(fih_rc); } - rc = boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs); + rc = boot_enc_set_key(BOOT_CURR_ENC_SLOT(state, BOOT_SLOT_SECONDARY), bs->enckey[BOOT_SLOT_SECONDARY]); if (rc < 0) { FIH_RET(fih_rc); } @@ -169,7 +169,7 @@ decrypt_region_inplace(struct boot_loader_state *state, blk_sz = tlv_off - (off + bytes_copied); } } - boot_enc_decrypt(BOOT_CURR_ENC(state), slot, + boot_enc_decrypt(BOOT_CURR_ENC_SLOT(state, slot), (off + bytes_copied + idx) - hdr->ih_hdr_size, blk_sz, blk_off, &buf[idx]); } @@ -235,11 +235,11 @@ decrypt_image_inplace(const struct flash_area *fa_p, #endif memset(&boot_data, 0, sizeof(struct boot_loader_state)); /* Load the encryption keys into cache */ - rc = boot_enc_load(state, 0, hdr, fa_p, bs); + rc = boot_enc_load(state, BOOT_SLOT_PRIMARY, hdr, fa_p, bs); if (rc < 0) { FIH_RET(fih_rc); } - if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs)) { + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC_SLOT(state, BOOT_SLOT_PRIMARY), bs->enckey[BOOT_SLOT_PRIMARY])) { FIH_RET(fih_rc); } } diff --git a/boot/bootutil/CMakeLists.txt b/boot/bootutil/CMakeLists.txt index 950a4c152..6d74578d8 100644 --- a/boot/bootutil/CMakeLists.txt +++ b/boot/bootutil/CMakeLists.txt @@ -21,6 +21,8 @@ target_sources(bootutil src/bootutil_img_hash.c src/bootutil_img_security_cnt.c src/bootutil_misc.c + src/bootutil_area.c + src/bootutil_loader.c src/bootutil_public.c src/caps.c src/encrypted.c @@ -31,6 +33,7 @@ target_sources(bootutil src/image_rsa.c src/image_validate.c src/loader.c + src/loader_manifest_xip.c src/swap_misc.c src/swap_move.c src/swap_scratch.c diff --git a/boot/bootutil/include/bootutil/bootutil.h b/boot/bootutil/include/bootutil/bootutil.h index a666b2a31..48d56d1a8 100644 --- a/boot/bootutil/include/bootutil/bootutil.h +++ b/boot/bootutil/include/bootutil/bootutil.h @@ -88,9 +88,36 @@ fih_ret boot_go_for_image_id(struct boot_rsp *rsp, uint32_t image_id); void boot_state_clear(struct boot_loader_state *state); fih_ret context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp); + +/** + * Returns a pointer to the boot loader state structure. + * + * @return Pointer to the boot loader state structure. + */ struct boot_loader_state *boot_get_loader_state(void); + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) +/** + * Returns pointer to array of image maximum sizes. + * + * @note This function provides a RAW access to the structure. The sizes may not be + * calculated yet. Use boot_get_max_app_size() to ensure the sizes are calculated. + * + * @return Pointer to array of image maximum sizes. + */ struct image_max_size *boot_get_image_max_sizes(void); + +/** + * Fetches the maximum allowed size of all application images. + * + * @note In contrast to boot_get_image_max_sizes(), this function will fetch the sizes + * if they are not yet calculated. + * + * @return A pointer to the structure containing the maximum sizes of images. + */ const struct image_max_size *boot_get_max_app_size(void); +#endif /* MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO || MCUBOOT_DATA_SHARING */ + void boot_fetch_slot_state_sizes(void); uint32_t boot_get_state_secondary_offset(struct boot_loader_state *state, const struct flash_area *fap); diff --git a/boot/bootutil/include/bootutil/enc_key.h b/boot/bootutil/include/bootutil/enc_key.h index 89411bf17..6fa0db18e 100644 --- a/boot/bootutil/include/bootutil/enc_key.h +++ b/boot/bootutil/include/bootutil/enc_key.h @@ -61,18 +61,18 @@ struct boot_loader_state; /* Decrypt random, symmetric encryption key */ int boot_decrypt_key(const uint8_t *buf, uint8_t *enckey); -int boot_enc_init(struct enc_key_data *enc_state, uint8_t slot); -int boot_enc_drop(struct enc_key_data *enc_state, uint8_t slot); -int boot_enc_set_key(struct enc_key_data *enc_state, uint8_t slot, - const struct boot_status *bs); +int boot_enc_init(struct enc_key_data *enc_state); +int boot_enc_drop(struct enc_key_data *enc_state); +int boot_enc_set_key(struct enc_key_data *enc_state, const uint8_t *key); int boot_enc_load(struct boot_loader_state *state, int slot, const struct image_header *hdr, const struct flash_area *fap, struct boot_status *bs); -bool boot_enc_valid(struct enc_key_data *enc_state, int slot); -void boot_enc_encrypt(struct enc_key_data *enc_state, int slot, +bool boot_enc_valid(const struct enc_key_data *enc_state); +void boot_enc_encrypt(struct enc_key_data *enc_state, uint32_t off, uint32_t sz, uint32_t blk_off, uint8_t *buf); -void boot_enc_decrypt(struct enc_key_data *enc_state, int slot, +void boot_enc_decrypt(struct enc_key_data *enc_state, uint32_t off, uint32_t sz, uint32_t blk_off, uint8_t *buf); +/* Note that boot_enc_zeorize takes BOOT_CURR_ENC, not BOOT_CURR_ENC_SLOT */ void boot_enc_zeroize(struct enc_key_data *enc_state); #ifdef __cplusplus diff --git a/boot/bootutil/include/bootutil/image.h b/boot/bootutil/include/bootutil/image.h index 3d103f8da..63dbac696 100644 --- a/boot/bootutil/include/bootutil/image.h +++ b/boot/bootutil/include/bootutil/image.h @@ -134,6 +134,7 @@ extern "C" { #define IMAGE_TLV_COMP_DEC_SIZE 0x73 /* Compressed decrypted image size */ #define IMAGE_TLV_UUID_VID 0x74 /* Vendor unique identifier */ #define IMAGE_TLV_UUID_CID 0x75 /* Device class unique identifier */ +#define IMAGE_TLV_MANIFEST 0x76 /* Transaction manifest */ /* * vendor reserved TLVs at xxA0-xxFF, * where xx denotes the upper byte diff --git a/boot/bootutil/include/bootutil/mcuboot_manifest.h b/boot/bootutil/include/bootutil/mcuboot_manifest.h new file mode 100644 index 000000000..923df39b8 --- /dev/null +++ b/boot/bootutil/include/bootutil/mcuboot_manifest.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __MCUBOOT_MANIFEST_H__ +#define __MCUBOOT_MANIFEST_H__ + +/** + * @file mcuboot_manifest.h + * + * @note This file is only used when MCUBOOT_MANIFEST_UPDATES is enabled. + */ + +#include +#include "bootutil/bootutil.h" +#ifdef CONFIG_MCUBOOT +#include "bootutil/crypto/sha.h" +#elif defined(CONFIG_MCUBOOT_BOOTLOADER_USES_SHA512) + #define IMAGE_HASH_SIZE (64) +#else + #define IMAGE_HASH_SIZE (32) +#endif + +#ifndef MCUBOOT_MANIFEST_IMAGE_NUMBER +#ifdef CONFIG_NCS_MCUBOOT_MANIFEST_IMAGE_NUMBER +#define MCUBOOT_MANIFEST_IMAGE_NUMBER CONFIG_NCS_MCUBOOT_MANIFEST_IMAGE_NUMBER +#else +#error "MCUBOOT_MANIFEST_IMAGE_NUMBER must be defined when MCUBOOT_MANIFEST_UPDATES is enabled" +#endif +#endif + +#ifndef MCUBOOT_IMAGE_NUMBER +#ifdef CONFIG_UPDATEABLE_IMAGE_NUMBER +#define MCUBOOT_IMAGE_NUMBER CONFIG_UPDATEABLE_IMAGE_NUMBER +#else +#error "MCUBOOT_IMAGE_NUMBER must be defined when MCUBOOT_MANIFEST_UPDATES is enabled" +#endif +#endif + +#ifndef __packed +#define __packed __attribute__((__packed__)) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** Manifest structure for image updates. */ +struct mcuboot_manifest { + uint32_t format; + uint32_t image_count; + /* Skip a digest of the MCUBOOT_MANIFEST_IMAGE_NUMBER image. */ + uint8_t image_hash[MCUBOOT_IMAGE_NUMBER - 1][IMAGE_HASH_SIZE]; +} __packed; + +/** + * @brief Check if the specified manifest has the correct format. + * + * @param[in] manifest The reference to the manifest structure. + * + * @return true on success. + */ +static inline bool bootutil_verify_manifest(const struct mcuboot_manifest *manifest) +{ + if (manifest == NULL) { + return false; + } + + /* Currently only the simplest manifest format is supported */ + if (manifest->format != 0x1) { + return false; + } + + if (manifest->image_count != MCUBOOT_IMAGE_NUMBER - 1) { + return false; + } + + return true; +} + +/** + * @brief Get the image hash from the manifest. + * + * @param[in] manifest The reference to the manifest structure. + * @param[in] image_index The index of the image to get the hash for. + * Must be in range <0, MCUBOOT_IMAGE_NUMBER - 1>, but + * must not be equal to MCUBOOT_MANIFEST_IMAGE_NUMBER. + * + * @return true if hash matches with the manifest, false otherwise. + */ +static inline bool bootutil_verify_manifest_image_hash(const struct mcuboot_manifest *manifest, + const uint8_t *exp_hash, uint32_t image_index) +{ + if (!bootutil_verify_manifest(manifest)) { + return false; + } + + if (image_index >= MCUBOOT_IMAGE_NUMBER) { + return false; + } + + if (image_index < MCUBOOT_MANIFEST_IMAGE_NUMBER) { + if (memcmp(exp_hash, manifest->image_hash[image_index], IMAGE_HASH_SIZE) == 0) { + return true; + } + } else if (image_index > MCUBOOT_MANIFEST_IMAGE_NUMBER) { + if (memcmp(exp_hash, manifest->image_hash[image_index - 1], IMAGE_HASH_SIZE) == 0) { + return true; + } + } + + return false; +} + +#ifdef __cplusplus +} +#endif + +#endif /* __MCUBOOT_MANIFEST_H__ */ diff --git a/boot/bootutil/src/bootutil_area.c b/boot/bootutil/src/bootutil_area.c new file mode 100644 index 000000000..302d3f897 --- /dev/null +++ b/boot/bootutil/src/bootutil_area.c @@ -0,0 +1,400 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017-2019 Linaro LTD + * Copyright (c) 2016-2019 JUUL Labs + * Copyright (c) 2019-2020 Arm Limited + * Copyright (c) 2025 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. + */ + +#include "bootutil_area.h" +#include "bootutil/image.h" +#include "bootutil/bootutil_public.h" +#ifdef MCUBOOT_ENC_IMAGES +#include "bootutil/enc_key.h" +#endif +#include "bootutil/bootutil_log.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +#if !defined(MCUBOOT_OVERWRITE_ONLY) && \ + !defined(MCUBOOT_SWAP_USING_MOVE) && \ + !defined(MCUBOOT_SWAP_USING_OFFSET) && \ + !defined(MCUBOOT_DIRECT_XIP) && \ + !defined(MCUBOOT_RAM_LOAD) && \ + !defined(MCUBOOT_SINGLE_APPLICATION_SLOT) && \ + !defined(MCUBOOT_FIRMWARE_LOADER) +#define MCUBOOT_SWAP_USING_SCRATCH 1 +#endif + +/** + * Amount of space used to save information required when doing a swap, + * or while a swap is under progress, but not the status of sector swap + * progress itself. + */ +static inline uint32_t +boot_trailer_info_sz(void) +{ + return ( +#ifdef MCUBOOT_ENC_IMAGES + /* encryption keys */ +# if MCUBOOT_SWAP_SAVE_ENCTLV + BOOT_ENC_TLV_ALIGN_SIZE * 2 + +# else + BOOT_ENC_KEY_ALIGN_SIZE * 2 + +# endif +#endif + /* swap_type + copy_done + image_ok + swap_size */ + BOOT_MAX_ALIGN * 4 + +#ifdef MCUBOOT_SWAP_USING_OFFSET + /* TLV size for both slots */ + BOOT_MAX_ALIGN + +#endif + BOOT_MAGIC_ALIGN_SIZE + ); +} + +/** + * Amount of space used to maintain progress information for a single swap + * operation. + */ +static inline uint32_t +boot_status_entry_sz(uint32_t min_write_sz) +{ +#if defined(MCUBOOT_SINGLE_APPLICATION_SLOT) || \ + defined(MCUBOOT_FIRMWARE_LOADER) || \ + defined(MCUBOOT_SINGLE_APPLICATION_SLOT_RAM_LOAD) + /* Single image MCUboot modes do not have a swap status fields */ + return 0; +#else + return BOOT_STATUS_STATE_COUNT * min_write_sz; +#endif +} + +uint32_t +boot_status_sz(uint32_t min_write_sz) +{ + return BOOT_STATUS_MAX_ENTRIES * boot_status_entry_sz(min_write_sz); +} + +uint32_t +boot_trailer_sz(uint32_t min_write_sz) +{ + return boot_status_sz(min_write_sz) + boot_trailer_info_sz(); +} + +#if MCUBOOT_SWAP_USING_SCRATCH +uint32_t boot_scratch_trailer_sz(uint32_t min_write_sz) +{ + return boot_status_entry_sz(min_write_sz) + boot_trailer_info_sz(); +} +#endif + +#if defined(MCUBOOT_MINIMAL_SCRAMBLE) +/** + * Get size of header aligned to device erase unit or write block, depending on whether device has + * erase or not. + * + * @param fa The flash_area containing the header. + * @param slot The slot the header belongs to. + * @param off Pointer to variable to store the offset of the header. + * @param size Pointer to variable to store the size of the header. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_header_scramble_off_sz(const struct flash_area *fa, int slot, size_t *off, size_t *size) +{ + int ret = 0; + const size_t write_block = flash_area_align(fa); + size_t loff = 0; + struct flash_sector sector; + + BOOT_LOG_DBG("boot_header_scramble_off_sz: slot %d", slot); + + (void)slot; +#if defined(MCUBOOT_SWAP_USING_OFFSET) + /* In case of swap offset, header of secondary slot image is positioned + * in second sector of slot. + */ + if (slot == BOOT_SLOT_SECONDARY) { + ret = flash_area_get_sector(fa, 0, §or); + if (ret < 0) { + return ret; + } + loff = flash_sector_get_off(§or); + } +#endif + + if (device_requires_erase(fa)) { + /* For device requiring erase align to erase unit */ + ret = flash_area_get_sector(fa, loff, §or); + if (ret < 0) { + return ret; + } + + *size = flash_sector_get_size(§or); + } else { + /* For device not requiring erase align to write block */ + *size = ALIGN_UP(sizeof(((struct image_header *)0)->ih_magic), write_block); + } + *off = loff; + + BOOT_LOG_DBG("boot_header_scramble_off_sz: size %u", (unsigned int)*size); + + return ret; +} +#endif /* MCUBOOT_MINIMAL_SCRAMBLE */ + +int +boot_trailer_scramble_offset(const struct flash_area *fa, size_t alignment, size_t *off) +{ + int ret = 0; + + BOOT_LOG_DBG("boot_trailer_scramble_offset: flash_area %p, alignment %u", + fa, (unsigned int)alignment); + + /* Not allowed to enforce alignment smaller than device allows */ + if (alignment < flash_area_align(fa)) { + alignment = flash_area_align(fa); + } + + if (device_requires_erase(fa)) { + /* For device requiring erase align to erase unit */ + struct flash_sector sector; + + ret = flash_area_get_sector(fa, flash_area_get_size(fa) - boot_trailer_sz(alignment), + §or); + if (ret < 0) { + return ret; + } + + *off = flash_sector_get_off(§or); + } else { + /* For device not requiring erase align to write block */ + *off = flash_area_get_size(fa) - ALIGN_DOWN(boot_trailer_sz(alignment), alignment); + } + + BOOT_LOG_DBG("boot_trailer_scramble_offset: final alignment %u, offset %u", + (unsigned int)alignment, (unsigned int)*off); + + return ret; +} + +int +boot_erase_region(const struct flash_area *fa, uint32_t off, uint32_t size, bool backwards) +{ + int rc = 0; + + BOOT_LOG_DBG("boot_erase_region: flash_area %p, offset %d, size %d, backwards == %d", + fa, off, size, (int)backwards); + + if (off >= flash_area_get_size(fa) || (flash_area_get_size(fa) - off) < size) { + rc = -1; + goto end; + } else if (device_requires_erase(fa)) { + uint32_t end_offset = 0; + struct flash_sector sector; + + BOOT_LOG_DBG("boot_erase_region: device with erase"); + + if (backwards) { + /* Get the lowest page offset first */ + rc = flash_area_get_sector(fa, off, §or); + + if (rc < 0) { + goto end; + } + + end_offset = flash_sector_get_off(§or); + + /* Set boundary condition, the highest probable offset to erase, within + * last sector to erase + */ + off += size - 1; + } else { + /* Get the highest page offset first */ + rc = flash_area_get_sector(fa, (off + size - 1), §or); + + if (rc < 0) { + goto end; + } + + end_offset = flash_sector_get_off(§or); + } + + while (true) { + /* Size to read in this iteration */ + size_t csize; + + /* Get current sector and, also, correct offset */ + rc = flash_area_get_sector(fa, off, §or); + + if (rc < 0) { + goto end; + } + + /* Corrected offset and size of current sector to erase */ + off = flash_sector_get_off(§or); + csize = flash_sector_get_size(§or); + + rc = flash_area_erase(fa, off, csize); + + if (rc < 0) { + goto end; + } + + MCUBOOT_WATCHDOG_FEED(); + + if (backwards) { + if (end_offset >= off) { + /* Reached the first offset in range and already erased it */ + break; + } + + /* Move down to previous sector, the flash_area_get_sector will + * correct the value to real page offset + */ + off -= 1; + } else { + /* Move up to next sector */ + off += csize; + + if (off > end_offset) { + /* Reached the end offset in range and already erased it */ + break; + } + + /* Workaround for flash_sector_get_off() being broken in mynewt, hangs with + * infinite loop if this is not present, should be removed if bug is fixed. + */ + off += 1; + } + } + } else { + BOOT_LOG_DBG("boot_erase_region: device without erase"); + } + +end: + return rc; +} + +int +boot_scramble_region(const struct flash_area *fa, uint32_t off, uint32_t size, bool backwards) +{ + int rc = 0; + + BOOT_LOG_DBG("boot_scramble_region: %p %d %d %d", fa, off, size, (int)backwards); + + if (size == 0) { + goto done; + } + + if (device_requires_erase(fa)) { + rc = boot_erase_region(fa, off, size, backwards); + } else if (off >= flash_area_get_size(fa) || (flash_area_get_size(fa) - off) < size) { + rc = -1; + goto done; + } else { + uint8_t buf[BOOT_MAX_ALIGN]; + const size_t write_block = flash_area_align(fa); + uint32_t end_offset; + + BOOT_LOG_DBG("boot_scramble_region: device without erase, overwriting"); + memset(buf, flash_area_erased_val(fa), sizeof(buf)); + + if (backwards) { + end_offset = ALIGN_DOWN(off, write_block); + /* Starting at the last write block in range */ + off += size - write_block; + } else { + end_offset = ALIGN_DOWN((off + size), write_block); + } + BOOT_LOG_DBG("boot_scramble_region: start offset %u, end offset %u", off, end_offset); + + while (off != end_offset) { + /* Write over the area to scramble data that is there */ + rc = flash_area_write(fa, off, buf, write_block); + if (rc != 0) { + BOOT_LOG_DBG("boot_scramble_region: error %d for %p %d %u", + rc, fa, off, (unsigned int)write_block); + break; + } + + MCUBOOT_WATCHDOG_FEED(); + + if (backwards) { + if (end_offset >= off) { + /* Reached the first offset in range and already scrambled it */ + break; + } + + off -= write_block; + } else { + off += write_block; + + if (end_offset <= off) { + /* Reached the end offset in range and already scrambled it */ + break; + } + } + } + } + +done: + return rc; +} + +int +boot_scramble_slot(const struct flash_area *fa, int slot) +{ + size_t size; + int ret = 0; + + (void)slot; + + /* Without minimal entire area needs to be scrambled */ +#if !defined(MCUBOOT_MINIMAL_SCRAMBLE) + size = flash_area_get_size(fa); + ret = boot_scramble_region(fa, 0, size, false); +#else + size_t off = 0; + + ret = boot_header_scramble_off_sz(fa, slot, &off, &size); + if (ret < 0) { + return ret; + } + + ret = boot_scramble_region(fa, off, size, false); + if (ret < 0) { + return ret; + } + + ret = boot_trailer_scramble_offset(fa, 0, &off); + if (ret < 0) { + return ret; + } + + ret = boot_scramble_region(fa, off, (flash_area_get_size(fa) - off), true); +#endif + return ret; +} diff --git a/boot/bootutil/src/bootutil_area.h b/boot/bootutil/src/bootutil_area.h new file mode 100644 index 000000000..5189e9034 --- /dev/null +++ b/boot/bootutil/src/bootutil_area.h @@ -0,0 +1,163 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017-2019 Linaro LTD + * Copyright (c) 2016-2019 JUUL Labs + * Copyright (c) 2019-2020 Arm Limited + * Copyright (c) 2025 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. + */ + +#ifndef H_BOOTUTIL_AREA_ +#define H_BOOTUTIL_AREA_ + +#include +#include +#include +#include +#include + +#if MCUBOOT_SWAP_USING_MOVE +#define BOOT_STATUS_MOVE_STATE_COUNT 1 +#define BOOT_STATUS_SWAP_STATE_COUNT 2 +#define BOOT_STATUS_STATE_COUNT (BOOT_STATUS_MOVE_STATE_COUNT + BOOT_STATUS_SWAP_STATE_COUNT) +#elif MCUBOOT_SWAP_USING_OFFSET +#define BOOT_STATUS_SWAP_STATE_COUNT 2 +#define BOOT_STATUS_STATE_COUNT BOOT_STATUS_SWAP_STATE_COUNT +#else +#define BOOT_STATUS_STATE_COUNT 3 +#endif + +/** Maximum number of image sectors supported by the bootloader. */ +#define BOOT_MAX_IMG_SECTORS MCUBOOT_MAX_IMG_SECTORS +#define BOOT_STATUS_MAX_ENTRIES BOOT_MAX_IMG_SECTORS + +#define BOOT_STATUS_SOURCE_NONE 0 +#define BOOT_STATUS_SOURCE_SCRATCH 1 +#define BOOT_STATUS_SOURCE_PRIMARY_SLOT 2 + +/* Helper macro to avoid compile errors with systems that do not + * provide function to check device type. + * Note: it used to be inline, but somehow compiler would not + * optimize out branches that were impossible when this evaluated to + * just "true". + */ +#if defined(MCUBOOT_SUPPORT_DEV_WITHOUT_ERASE) && defined(MCUBOOT_SUPPORT_DEV_WITH_ERASE) +#define device_requires_erase(fa) (flash_area_erase_required(fa)) +#elif defined(MCUBOOT_SUPPORT_DEV_WITHOUT_ERASE) +#define device_requires_erase(fa) (false) +#else +#define device_requires_erase(fa) (true) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Amount of space used to maintain progress information for all swap + * operations. + */ +uint32_t boot_status_sz(uint32_t min_write_sz); + +/** + * Amount of space used to maintain progress information for all swap + * operations as well as to save information required when doing a swap, + * or while a swap is under progress. + */ +uint32_t boot_trailer_sz(uint32_t min_write_sz); + +/* + * Similar to `boot_trailer_sz` but this function returns the space used to + * store status in the scratch partition. The scratch partition only stores + * status during the swap of the last sector from primary/secondary (which + * is the first swap operation) and thus only requires space for one swap. + */ +uint32_t boot_scratch_trailer_sz(uint32_t min_write_sz); + +/** + * Get offset of trailer aligned to either device erase unit or alignment depending on whether + * device has erase or not. + * + * @param fa The flash_area containing the trailer. + * @param alignment The required alignment for the trailer. + * @param off Pointer to variable to store the offset of the trailer. + * + * @return 0 on success; nonzero on failure. + */ +int boot_trailer_scramble_offset(const struct flash_area *fa, size_t alignment, size_t *off); + +/** + * Erases a region of device that requires erase prior to write; does + * nothing on devices without erase. + * + * @param fa The flash_area containing the region to erase. + * @param off The offset within the flash area to start the erase. + * @param size The number of bytes to erase. + * @param backwards If set to true will erase from end to start addresses, otherwise erases from + * start to end addresses. + * + * @return 0 on success; nonzero on failure. + */ +int boot_erase_region(const struct flash_area *fap, uint32_t off, uint32_t sz, bool backwards); + +/** + * Removes data from specified region either by writing erase value in place of data or by doing + * erase, if device has such hardware requirement. + * + * @note This function will fail if off or size are not aligned to device write block size or erase + * block size. + * + * @param fa The flash_area containing the region to erase. + * @param off The offset within the flash area to start the erase. + * @param size The number of bytes to erase. + * @param backwards If set to true will erase from end to start addresses, otherwise erases from + * start to end addresses. + * + * @return 0 on success; nonzero on failure. + */ +int boot_scramble_region(const struct flash_area *fap, uint32_t off, uint32_t sz, bool backwards); + +/** + * Removes enough data from slot to mark it as unused. + * + * This function either scrambles header magic, header sector or entire slot, depending on + * configuration. + * + * @warning This function assumes that header and trailer are not overlapping on write block or + * erase block, if device has erase requirement. + * + * @note This function is intended for removing data not preparing device for write. + * @note Slot is passed here because at this point there is no function matching flash_area object + * to slot. + * + * @param fa Pointer to flash area object for slot + * @param slot Slot the @p fa represents + * + * @return 0 on success; nonzero on failure. + */ +int boot_scramble_slot(const struct flash_area *fap, int slot); + +#ifdef __cplusplus +} +#endif + +#endif /* H_BOOTUTIL_AREA_ */ diff --git a/boot/bootutil/src/bootutil_img_hash.c b/boot/bootutil/src/bootutil_img_hash.c index ff8340d70..ae52ffb91 100644 --- a/boot/bootutil/src/bootutil_img_hash.c +++ b/boot/bootutil/src/bootutil_img_hash.c @@ -65,7 +65,6 @@ bootutil_img_hash(struct boot_loader_state *state, int fa_ret; #endif #if defined(MCUBOOT_ENC_IMAGES) - struct enc_key_data *enc_state; int image_index; #endif #if defined(MCUBOOT_SWAP_USING_OFFSET) @@ -91,16 +90,14 @@ bootutil_img_hash(struct boot_loader_state *state, #ifdef MCUBOOT_ENC_IMAGES if (state == NULL) { - enc_state = NULL; image_index = 0; } else { - enc_state = BOOT_CURR_ENC(state); image_index = BOOT_CURR_IMG(state); } /* Encrypted images only exist in the secondary slot */ if (MUST_DECRYPT(fap, image_index, hdr) && - !boot_enc_valid(enc_state, 1)) { + !boot_enc_valid(BOOT_CURR_ENC_SLOT(state, BOOT_SLOT_SECONDARY))) { BOOT_LOG_DBG("bootutil_img_hash: error encrypted image found in primary slot"); return -1; } @@ -182,7 +179,7 @@ bootutil_img_hash(struct boot_loader_state *state, if (off >= hdr_size && off < tlv_off) { blk_off = (off - hdr_size) & 0xf; - boot_enc_decrypt(enc_state, slot, off - hdr_size, + boot_enc_decrypt(BOOT_CURR_ENC_SLOT(state, slot), off - hdr_size, blk_sz, blk_off, tmp_buf); } } diff --git a/boot/bootutil/src/bootutil_loader.c b/boot/bootutil/src/bootutil_loader.c new file mode 100644 index 000000000..7a0e3a1f2 --- /dev/null +++ b/boot/bootutil/src/bootutil_loader.c @@ -0,0 +1,436 @@ +/* + * 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-2025 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 manifest-based boot loader. + * Functions defined in this file should only be called while the boot loader is + * running. + */ + +#include "bootutil_loader.h" +#include "bootutil/boot_record.h" +#include "bootutil/boot_hooks.h" +#ifdef MCUBOOT_ENC_IMAGES +#include "bootutil/enc_key.h" +#endif +#ifdef MCUBOOT_HW_ROLLBACK_PROT +#include "bootutil/security_cnt.h" +#endif +#if defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_OFFSET) || \ + defined(MCUBOOT_SWAP_USING_SCRATCH) +#include "swap_priv.h" +#endif +#include "bootutil/bootutil_log.h" + +#if defined(CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS) +#include +#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS */ + +#if defined(MCUBOOT_DECOMPRESS_IMAGES) +#include +#include +#endif + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +bool +boot_check_header_erased(struct boot_loader_state *state, int slot) +{ + const struct flash_area *fap = NULL; + struct image_header *hdr; + + fap = BOOT_IMG_AREA(state, slot); + assert(fap != NULL); + + hdr = boot_img_hdr(state, slot); + if (bootutil_buffer_is_erased(fap, &hdr->ih_magic, sizeof(hdr->ih_magic))) { + return true; + } + + return false; +} + +bool +boot_check_header_valid(struct boot_loader_state *state, int slot) +{ + const struct flash_area *fap = NULL; + struct image_header *hdr; + uint32_t size; + + fap = BOOT_IMG_AREA(state, slot); + assert(fap != NULL); + + hdr = boot_img_hdr(state, slot); + 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; +} + +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. + */ +#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) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER && i == 0) { + continue; + } +#endif /* CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 */ + if (i > 0 && !require_all) { + return 0; + } else { + return rc; + } + } + } + + return 0; +} + +fih_ret +boot_check_image(struct boot_loader_state *state, struct boot_status *bs, int slot) +{ + TARGET_STATIC uint8_t tmpbuf[BOOT_TMPBUF_SZ]; + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + const struct flash_area *fap = NULL; + struct image_header *hdr; + + fap = BOOT_IMG_AREA(state, slot); + assert(fap != NULL); + + hdr = boot_img_hdr(state, slot); + + (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(state, BOOT_SLOT_SECONDARY, hdr, fap, bs); + if (rc < 0) { + FIH_RET(fih_rc); + } + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC_SLOT(state, BOOT_SLOT_SECONDARY), + bs->enckey[BOOT_SLOT_SECONDARY])) { + FIH_RET(fih_rc); + } + } +#endif + + for (int i = 1; i <= CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT; i++ ) { +#if CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 + BOOT_LOG_DBG("Image validation attempt %d/%d", i, CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT); +#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ + + FIH_CALL(bootutil_img_validate, fih_rc, state, hdr, fap, tmpbuf, BOOT_TMPBUF_SZ, + NULL, 0, NULL); + if (FIH_EQ(fih_rc, FIH_SUCCESS)) { +#if CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 + BOOT_LOG_DBG("Image validation attempt %d/%d success", i, CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT); +#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ + break; + } else { +#if CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 + BOOT_LOG_WRN("Image validation attempt %d/%d failure: %d", + i, + CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT, fih_rc); +#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ + + if (i < CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT) { +#if defined(CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS) +#if CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 + BOOT_LOG_DBG("Waiting %d ms before next attempt", + CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS); +#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ + k_busy_wait(CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS * 1000); +#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS */ + } + } + } + + FIH_RET(fih_rc); +} + +int +boot_compare_version(const struct image_version *ver1, const struct image_version *ver2) +{ +#if !defined(MCUBOOT_VERSION_CMP_USE_BUILD_NUMBER) + BOOT_LOG_DBG("boot_version_cmp: ver1 %u.%u.%u vs ver2 %u.%u.%u", + (unsigned)ver1->iv_major, (unsigned)ver1->iv_minor, + (unsigned)ver1->iv_revision, (unsigned)ver2->iv_major, + (unsigned)ver2->iv_minor, (unsigned)ver2->iv_revision); +#else + BOOT_LOG_DBG("boot_version_cmp: ver1 %u.%u.%u.%u vs ver2 %u.%u.%u.%u", + (unsigned)ver1->iv_major, (unsigned)ver1->iv_minor, + (unsigned)ver1->iv_revision, (unsigned)ver1->iv_build_num, + (unsigned)ver2->iv_major, (unsigned)ver2->iv_minor, + (unsigned)ver2->iv_revision, (unsigned)ver2->iv_build_num); +#endif + + 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; +} + +#ifdef MCUBOOT_HW_ROLLBACK_PROT +int +boot_update_security_counter(struct boot_loader_state *state, int slot, int hdr_slot_idx) +{ + const struct flash_area *fap = NULL; + uint32_t img_security_cnt; + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + FIH_CALL(boot_nv_image_should_have_security_counter, fih_rc, BOOT_CURR_IMG(state)); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + /* No security counter update needed. */ + return 0; + } + + fap = BOOT_IMG_AREA(state, slot); + assert(fap != NULL); + + rc = bootutil_get_img_security_cnt(state, hdr_slot_idx, fap, &img_security_cnt); + if (rc != 0) { + goto done; + } + + rc = boot_nv_security_counter_update(BOOT_CURR_IMG(state), img_security_cnt); + if (rc != 0) { + goto done; + } + +done: + return rc; +} +#endif /* MCUBOOT_HW_ROLLBACK_PROT */ + +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, boot_get_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 +} + +#if !defined(MCUBOOT_SINGLE_APPLICATION_SLOT_RAM_LOAD) && !defined(MCUBOOT_SINGLE_APPLICATION_SLOT) +int +boot_open_all_flash_areas(struct boot_loader_state *state) +{ + size_t slot; + int rc = 0; + int fa_id; + int image_index; + + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif + image_index = BOOT_CURR_IMG(state); + + 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 %zu): %d", + fa_id, image_index, slot, rc); + goto out; + } + } + } + +#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", rc); + goto out; + } +#endif + +out: + if (rc != 0) { + boot_close_all_flash_areas(state); + } + + return rc; +} + +void +boot_close_all_flash_areas(struct boot_loader_state *state) +{ + uint32_t slot; + +#if MCUBOOT_SWAP_USING_SCRATCH + if (BOOT_SCRATCH_AREA(state) != NULL) { + flash_area_close(BOOT_SCRATCH_AREA(state)); + } +#endif + + IMAGES_ITER(BOOT_CURR_IMG(state)) { +#if BOOT_IMAGE_NUMBER > 1 + if (state->img_mask[BOOT_CURR_IMG(state)]) { + continue; + } +#endif + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + if (BOOT_IMG_AREA(state, BOOT_NUM_SLOTS - 1 - slot) != NULL) { + flash_area_close(BOOT_IMG_AREA(state, BOOT_NUM_SLOTS - 1 - slot)); + } + } + } +} +#endif /* !MCUBOOT_SINGLE_APPLICATION_SLOT_RAM_LOAD && !MCUBOOT_SINGLE_APPLICATION_SLOT */ diff --git a/boot/bootutil/src/bootutil_loader.h b/boot/bootutil/src/bootutil_loader.h new file mode 100644 index 000000000..530f09131 --- /dev/null +++ b/boot/bootutil/src/bootutil_loader.h @@ -0,0 +1,185 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2017-2020 Linaro LTD + * Copyright (c) 2017-2019 JUUL Labs + * Copyright (c) 2019-2021 Arm Limited + * Copyright (c) 2024-2025 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. + */ + +#ifndef H_BOOTUTIL_LOADER_ +#define H_BOOTUTIL_LOADER_ + +#include +#include + +#include "bootutil/image.h" +#include "bootutil/fault_injection_hardening.h" +#include "bootutil/bootutil.h" +#include "bootutil_priv.h" + +/* + * 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 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Check whether the header in the given slot is erased. + * + * @param state Boot loader state where the current image's slot will be checked. + * @param slot Slot number. + * + * @return true if the header is erased, false otherwise. + */ +bool boot_check_header_erased(struct boot_loader_state *state, int slot); + +/** + * Check whether the header is valid. + * + * 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. + * + * @param state Boot loader state where the current image's slot will be checked. + * @param slot Slot number. + * + * @return true if the header is valid, false otherwise. + */ +bool boot_check_header_valid(struct boot_loader_state *state, int slot); + +/** + * Reads image headers from all slots for the current image. + * + * This function uses boot_read_image_header() to read each slot's image header. + * In all update scenarios, this function checks if an image contains the valid magic tag. + * In swap type updates, it also configures/restores the bootloader state as well as status to + * continue any interrupted swap operations. + * + * @param state Boot loader status information. + * @param require_all If true, all image headers must be read successfully; + * if false, reading at least the first slot's header is sufficient. + * @param bs Pointer to boot status structure; may be NULL. + * + * @return 0 on success; nonzero on failure. + */ +int boot_read_image_headers(struct boot_loader_state *state, bool require_all, + struct boot_status *bs); + +/** + * Validate image hash/signature and optionally the security counter in a slot. + * + * @note This function uses bootutil_img_validate() to perform the actual validation. + * + * @param state Boot loader state where the current image's slot will be checked. + * @param bs Pointer to the boot status structure. Used only when encrypted images are validated. + * @param slot Slot number. + * + * @return FIH_SUCCESS if the image is valid, FIH_FAILURE otherwise. + */ +fih_ret boot_check_image(struct boot_loader_state *state, struct boot_status *bs, int slot); + +/** + * 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. + */ +int boot_compare_version(const struct image_version *ver1, const struct image_version *ver2); + +#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 state Boot state where the current image's security counter will be updated. + * @param slot Slot number of the image. + * @param hdr_slot_idx Index of the header in the state current image variable containing the + * pointer to the image header structure of the image that is currently stored + * in the given slot. + * + * @return 0 on success; nonzero on failure. + */ +int boot_update_security_counter(struct boot_loader_state *state, int slot, int hdr_slot_idx); +#endif /* MCUBOOT_HW_ROLLBACK_PROT */ + +/** + * Saves boot status and shared data for current image. + * + * @note This function is a helper routine, that uses boot_save_boot_status() and + * boot_add_shared_data() to append the respective information, depending on the + * configuration. + * + * @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. + */ +int boot_add_shared_data(struct boot_loader_state *state, uint8_t active_slot); + +/** + * Opens the flash areas of all images. + * + * @note This function opens all areas for all images, including scratch area if + * MCUBOOT_SWAP_USING_SCRATCH is defined. + * + * @param state Bootloader state. + * + * @return 0 on success; nonzero on failure. + */ +int boot_open_all_flash_areas(struct boot_loader_state *state); + +/** + * Closes the flash areas of all images. + * + * @note This function closes all areas for all images, including scratch area if + * MCUBOOT_SWAP_USING_SCRATCH is defined. + * + * @param state Bootloader state. + */ +void boot_close_all_flash_areas(struct boot_loader_state *state); + +#ifdef __cplusplus +} +#endif + +#endif /* H_BOOTUTIL_LOADER_ */ diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c index ec61f6f9e..648432381 100644 --- a/boot/bootutil/src/bootutil_misc.c +++ b/boot/bootutil/src/bootutil_misc.c @@ -108,149 +108,6 @@ fih_ret boot_fih_memequal(const void *s1, const void *s2, size_t n) } #endif -/* - * Amount of space used to save information required when doing a swap, - * or while a swap is under progress, but not the status of sector swap - * progress itself. - */ -static inline uint32_t -boot_trailer_info_sz(void) -{ - return ( -#ifdef MCUBOOT_ENC_IMAGES - /* encryption keys */ -# if MCUBOOT_SWAP_SAVE_ENCTLV - BOOT_ENC_TLV_ALIGN_SIZE * 2 + -# else - BOOT_ENC_KEY_ALIGN_SIZE * 2 + -# endif -#endif - /* swap_type + copy_done + image_ok + swap_size */ - BOOT_MAX_ALIGN * 4 + - BOOT_MAGIC_ALIGN_SIZE - ); -} - -/* - * Amount of space used to maintain progress information for a single swap - * operation. - */ -static inline uint32_t -boot_status_entry_sz(uint32_t min_write_sz) -{ -#if defined(MCUBOOT_SINGLE_APPLICATION_SLOT) || \ - defined(MCUBOOT_FIRMWARE_LOADER) || \ - defined(MCUBOOT_SINGLE_APPLICATION_SLOT_RAM_LOAD) - /* Single image MCUboot modes do not have a swap status fields */ - return 0; -#else - return BOOT_STATUS_STATE_COUNT * min_write_sz; -#endif -} - -uint32_t -boot_status_sz(uint32_t min_write_sz) -{ - return BOOT_STATUS_MAX_ENTRIES * boot_status_entry_sz(min_write_sz); -} - -uint32_t -boot_trailer_sz(uint32_t min_write_sz) -{ - return boot_status_sz(min_write_sz) + boot_trailer_info_sz(); -} - -int boot_trailer_scramble_offset(const struct flash_area *fa, size_t alignment, - size_t *off) -{ - int ret = 0; - - BOOT_LOG_DBG("boot_trailer_scramble_offset: flash_area %p, alignment %u", - fa, (unsigned int)alignment); - - /* Not allowed to enforce alignment smaller than device allows */ - if (alignment < flash_area_align(fa)) { - alignment = flash_area_align(fa); - } - - if (device_requires_erase(fa)) { - /* For device requiring erase align to erase unit */ - struct flash_sector sector; - - ret = flash_area_get_sector(fa, flash_area_get_size(fa) - boot_trailer_sz(alignment), - §or); - if (ret < 0) { - return ret; - } - - *off = flash_sector_get_off(§or); - } else { - /* For device not requiring erase align to write block */ - *off = flash_area_get_size(fa) - ALIGN_DOWN(boot_trailer_sz(alignment), alignment); - } - - BOOT_LOG_DBG("boot_trailer_scramble_offset: final alignment %u, offset %u", - (unsigned int)alignment, (unsigned int)*off); - - return ret; -} - -int boot_header_scramble_off_sz(const struct flash_area *fa, int slot, size_t *off, - size_t *size) -{ - int ret = 0; - const size_t write_block = flash_area_align(fa); - size_t loff = 0; - struct flash_sector sector; - - BOOT_LOG_DBG("boot_header_scramble_off_sz: slot %d", slot); - - (void)slot; -#if defined(MCUBOOT_SWAP_USING_OFFSET) - /* In case of swap offset, header of secondary slot image is positioned - * in second sector of slot. - */ - if (slot == BOOT_SLOT_SECONDARY) { - ret = flash_area_get_sector(fa, 0, §or); - if (ret < 0) { - return ret; - } - loff = flash_sector_get_off(§or); - } -#endif - - if (device_requires_erase(fa)) { - /* For device requiring erase align to erase unit */ - ret = flash_area_get_sector(fa, loff, §or); - if (ret < 0) { - return ret; - } - - *size = flash_sector_get_size(§or); - } else { - /* For device not requiring erase align to write block */ - *size = ALIGN_UP(sizeof(((struct image_header *)0)->ih_magic), write_block); - } - *off = loff; - - BOOT_LOG_DBG("boot_header_scramble_off_sz: size %u", (unsigned int)*size); - - return ret; -} - -#if MCUBOOT_SWAP_USING_SCRATCH -/* - * Similar to `boot_trailer_sz` but this function returns the space used to - * store status in the scratch partition. The scratch partition only stores - * status during the swap of the last sector from primary/secondary (which - * is the first swap operation) and thus only requires space for one swap. - */ -uint32_t boot_scratch_trailer_sz(uint32_t min_write_sz) -{ - return boot_status_entry_sz(min_write_sz) + boot_trailer_info_sz(); -} -#endif - int boot_status_entries(int image_index, const struct flash_area *fap) { @@ -362,6 +219,27 @@ boot_read_swap_size(const struct flash_area *fap, uint32_t *swap_size) return rc; } +#ifdef MCUBOOT_SWAP_USING_OFFSET +int +boot_read_unprotected_tlv_sizes(const struct flash_area *fap, uint16_t *tlv_size_primary, + uint16_t *tlv_size_secondary) +{ + uint32_t off; + uint32_t combined_tlv_sizes = 0; + int rc; + + off = boot_unprotected_tlv_sizes_off(fap); + rc = flash_area_read(fap, off, &combined_tlv_sizes, sizeof(combined_tlv_sizes)); + + if (rc == 0) { + *tlv_size_primary = (uint16_t)(combined_tlv_sizes & 0xffff); + *tlv_size_secondary = (uint16_t)((combined_tlv_sizes & 0xffff0000) >> 16); + } + + return rc; +} +#endif + #ifdef MCUBOOT_ENC_IMAGES int boot_read_enc_key(const struct flash_area *fap, uint8_t slot, struct boot_status *bs) @@ -406,25 +284,46 @@ boot_write_swap_size(const struct flash_area *fap, uint32_t swap_size) return boot_write_trailer(fap, off, (const uint8_t *) &swap_size, 4); } -#ifdef MCUBOOT_ENC_IMAGES +#if defined(MCUBOOT_SWAP_USING_OFFSET) int -boot_write_enc_key(const struct flash_area *fap, uint8_t slot, - const struct boot_status *bs) +boot_write_unprotected_tlv_sizes(const struct flash_area *fap, uint16_t tlv_size_primary, + uint16_t tlv_size_secondary) { uint32_t off; - int rc; + uint32_t tlv_sizes_combined; - off = boot_enc_key_off(fap, slot); - BOOT_LOG_DBG("writing enc_key; fa_id=%d off=0x%lx (0x%lx)", + off = boot_unprotected_tlv_sizes_off(fap); + tlv_sizes_combined = ((uint32_t)tlv_size_secondary << 16) | (uint32_t)tlv_size_primary; + BOOT_LOG_DBG("writing unprotected_tlv_sizes; fa_id=%d off=0x%lx (0x%lx) vals=0x%x,0x%x (0x%lx)", flash_area_get_id(fap), (unsigned long)off, - (unsigned long)flash_area_get_off(fap) + off); + ((unsigned long)flash_area_get_off(fap) + off), tlv_size_primary, + tlv_size_secondary, (unsigned long)tlv_sizes_combined); + return boot_write_trailer(fap, off, (const uint8_t *)&tlv_sizes_combined, + sizeof(tlv_sizes_combined)); +} +#endif + +#ifdef MCUBOOT_ENC_IMAGES +int +boot_write_enc_keys(const struct flash_area *fap, const struct boot_status *bs) +{ + int slot; + + for (slot = 0; slot < BOOT_NUM_SLOTS; ++slot) { + uint32_t off = boot_enc_key_off(fap, slot); + int rc; + + BOOT_LOG_DBG("writing enc_key; fa_id=%d off=0x%lx (0x%lx)", + flash_area_get_id(fap), (unsigned long)off, + (unsigned long)flash_area_get_off(fap) + off); #if MCUBOOT_SWAP_SAVE_ENCTLV - rc = flash_area_write(fap, off, bs->enctlv[slot], BOOT_ENC_TLV_ALIGN_SIZE); + rc = flash_area_write(fap, off, bs->enctlv[slot], BOOT_ENC_TLV_ALIGN_SIZE); #else - rc = flash_area_write(fap, off, bs->enckey[slot], BOOT_ENC_KEY_ALIGN_SIZE); + rc = flash_area_write(fap, off, bs->enckey[slot], BOOT_ENC_KEY_ALIGN_SIZE); #endif - if (rc != 0) { - return BOOT_EFLASH; + if (rc != 0) { + return BOOT_EFLASH; + } } return 0; @@ -565,118 +464,6 @@ boot_read_image_size(struct boot_loader_state *state, int slot, uint32_t *size) } #endif /* !MCUBOOT_OVERWRITE_ONLY */ -/** - * Erases a region of device that requires erase prior to write; does - * nothing on devices without erase. - * - * @param fa The flash_area containing the region to erase. - * @param off The offset within the flash area to start the - * erase. - * @param size The number of bytes to erase. - * @param backwards If set to true will erase from end to start - * addresses, otherwise erases from start to end - * addresses. - * - * @return 0 on success; nonzero on failure. - */ -int -boot_erase_region(const struct flash_area *fa, uint32_t off, uint32_t size, bool backwards) -{ - int rc = 0; - - BOOT_LOG_DBG("boot_erase_region: flash_area %p, offset %d, size %d, backwards == %d", - fa, off, size, (int)backwards); - - if (off >= flash_area_get_size(fa) || (flash_area_get_size(fa) - off) < size) { - rc = -1; - goto end; - } else if (device_requires_erase(fa)) { - uint32_t end_offset = 0; - struct flash_sector sector; - - BOOT_LOG_DBG("boot_erase_region: device with erase"); - - if (backwards) { - /* Get the lowest page offset first */ - rc = flash_area_get_sector(fa, off, §or); - - if (rc < 0) { - goto end; - } - - end_offset = flash_sector_get_off(§or); - - /* Set boundary condition, the highest probable offset to erase, within - * last sector to erase - */ - off += size - 1; - } else { - /* Get the highest page offset first */ - rc = flash_area_get_sector(fa, (off + size - 1), §or); - - if (rc < 0) { - goto end; - } - - end_offset = flash_sector_get_off(§or); - } - - while (true) { - /* Size to read in this iteration */ - size_t csize; - - /* Get current sector and, also, correct offset */ - rc = flash_area_get_sector(fa, off, §or); - - if (rc < 0) { - goto end; - } - - /* Corrected offset and size of current sector to erase */ - off = flash_sector_get_off(§or); - csize = flash_sector_get_size(§or); - - rc = flash_area_erase(fa, off, csize); - - if (rc < 0) { - goto end; - } - - MCUBOOT_WATCHDOG_FEED(); - - if (backwards) { - if (end_offset >= off) { - /* Reached the first offset in range and already erased it */ - break; - } - - /* Move down to previous sector, the flash_area_get_sector will - * correct the value to real page offset - */ - off -= 1; - } else { - /* Move up to next sector */ - off += csize; - - if (off > end_offset) { - /* Reached the end offset in range and already erased it */ - break; - } - - /* Workaround for flash_sector_get_off() being broken in mynewt, hangs with - * infinite loop if this is not present, should be removed if bug is fixed. - */ - off += 1; - } - } - } else { - BOOT_LOG_DBG("boot_erase_region: device without erase"); - } - -end: - return rc; -} - #if (!defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)) || \ defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) int @@ -861,6 +648,31 @@ void boot_fetch_slot_state_sizes(void) memset(state, 0, sizeof(struct boot_loader_state)); } #endif +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) +const struct image_max_size *boot_get_max_app_size(void) +{ + const struct image_max_size *image_max_sizes = boot_get_image_max_sizes(); + +#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 /** * Clears the boot state, so that previous operations have no effect on new diff --git a/boot/bootutil/src/bootutil_misc.h b/boot/bootutil/src/bootutil_misc.h index 1fcaabdfc..9c3a2d2f8 100644 --- a/boot/bootutil/src/bootutil_misc.h +++ b/boot/bootutil/src/bootutil_misc.h @@ -43,10 +43,24 @@ boot_copy_done_off(const struct flash_area *fap) return boot_image_ok_off(fap) - BOOT_MAX_ALIGN; } +#if defined(MCUBOOT_SWAP_USING_OFFSET) +static inline uint32_t +boot_unprotected_tlv_sizes_off(const struct flash_area *fap) +{ + return boot_swap_info_off(fap) - BOOT_MAX_ALIGN; +} + +static inline uint32_t +boot_swap_size_off(const struct flash_area *fap) +{ + return boot_unprotected_tlv_sizes_off(fap) - BOOT_MAX_ALIGN; +} +#else static inline uint32_t boot_swap_size_off(const struct flash_area *fap) { return boot_swap_info_off(fap) - BOOT_MAX_ALIGN; } +#endif #endif /* H_BOOTUTIL_MISC_ */ diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h index 784baf7e7..3b442c99a 100644 --- a/boot/bootutil/src/bootutil_priv.h +++ b/boot/bootutil/src/bootutil_priv.h @@ -37,12 +37,17 @@ #include "bootutil/bootutil.h" #include "bootutil/image.h" #include "bootutil/fault_injection_hardening.h" +#include "bootutil_area.h" #include "mcuboot_config/mcuboot_config.h" #ifdef MCUBOOT_ENC_IMAGES #include "bootutil/enc_key.h" #endif +#ifdef MCUBOOT_MANIFEST_UPDATES +#include "bootutil/mcuboot_manifest.h" +#endif /* MCUBOOT_MANIFEST_UPDATES */ + #ifdef __cplusplus extern "C" { #endif @@ -196,8 +201,6 @@ _Static_assert(sizeof(boot_img_magic) == BOOT_MAGIC_SZ, "Invalid size for image #endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_ENC_IMAGES */ #endif /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */ -#define BOOT_MAX_IMG_SECTORS MCUBOOT_MAX_IMG_SECTORS - #define BOOT_LOG_IMAGE_INFO(slot, hdr) \ BOOT_LOG_INF("%-9s slot: version=%u.%u.%u+%u", \ ((slot) == BOOT_SLOT_PRIMARY) ? "Primary" : "Secondary", \ @@ -206,24 +209,6 @@ _Static_assert(sizeof(boot_img_magic) == BOOT_MAGIC_SZ, "Invalid size for image (hdr)->ih_ver.iv_revision, \ (hdr)->ih_ver.iv_build_num) -#if MCUBOOT_SWAP_USING_MOVE -#define BOOT_STATUS_MOVE_STATE_COUNT 1 -#define BOOT_STATUS_SWAP_STATE_COUNT 2 -#define BOOT_STATUS_STATE_COUNT (BOOT_STATUS_MOVE_STATE_COUNT + BOOT_STATUS_SWAP_STATE_COUNT) -#elif MCUBOOT_SWAP_USING_OFFSET -#define BOOT_STATUS_SWAP_STATE_COUNT 2 -#define BOOT_STATUS_STATE_COUNT BOOT_STATUS_SWAP_STATE_COUNT -#else -#define BOOT_STATUS_STATE_COUNT 3 -#endif - -/** Maximum number of image sectors supported by the bootloader. */ -#define BOOT_STATUS_MAX_ENTRIES BOOT_MAX_IMG_SECTORS - -#define BOOT_STATUS_SOURCE_NONE 0 -#define BOOT_STATUS_SOURCE_SCRATCH 1 -#define BOOT_STATUS_SOURCE_PRIMARY_SLOT 2 - /** * Compatibility shim for flash sector type. * @@ -242,6 +227,9 @@ struct boot_loader_state { const struct flash_area *area; boot_sector_t *sectors; uint32_t num_sectors; +#if defined(MCUBOOT_SWAP_USING_OFFSET) + uint16_t unprotected_tlv_size; +#endif } imgs[BOOT_IMAGE_NUMBER][BOOT_NUM_SLOTS]; #if MCUBOOT_SWAP_USING_SCRATCH @@ -287,6 +275,14 @@ struct boot_loader_state { #endif } slot_usage[BOOT_IMAGE_NUMBER]; #endif /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */ + +#if defined(MCUBOOT_MANIFEST_UPDATES) + struct mcuboot_manifest manifest[BOOT_NUM_SLOTS]; + bool manifest_valid[BOOT_NUM_SLOTS]; +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_OFFSET) + enum boot_slot matching_manifest[BOOT_IMAGE_NUMBER][BOOT_NUM_SLOTS]; +#endif +#endif }; struct boot_sector_buffer { @@ -314,18 +310,6 @@ fih_ret boot_fih_memequal(const void *s1, const void *s2, size_t n); const struct flash_area *boot_find_status(const struct boot_loader_state *state, int image_index); int boot_magic_compatible_check(uint8_t tbl_val, uint8_t val); -uint32_t boot_status_sz(uint32_t min_write_sz); -uint32_t boot_trailer_sz(uint32_t min_write_sz); -/* Get offset of trailer aligned to either device erase unit or alignment - * depending on whether device has erase or not. - */ -int boot_trailer_scramble_offset(const struct flash_area *fa, size_t alignment, - size_t *off); -/* Get size of header aligned to device erase unit or write block, - * depending on whether device has erase or not. - */ -int boot_header_scramble_off_sz(const struct flash_area *fa, int slot, size_t *off, - size_t *size); int boot_status_entries(int image_index, const struct flash_area *fap); uint32_t boot_status_off(const struct flash_area *fap); int boot_read_swap_state(const struct flash_area *fap, @@ -342,6 +326,12 @@ int boot_write_trailer(const struct flash_area *fap, uint32_t off, int boot_write_trailer_flag(const struct flash_area *fap, uint32_t off, uint8_t flag_val); int boot_read_swap_size(const struct flash_area *fap, uint32_t *swap_size); +#if defined(MCUBOOT_SWAP_USING_OFFSET) +int boot_write_unprotected_tlv_sizes(const struct flash_area *fap, uint16_t tlv_size_primary, + uint16_t tlv_size_secondary); +int boot_read_unprotected_tlv_sizes(const struct flash_area *fap, uint16_t *tlv_size_primary, + uint16_t *tlv_size_secondary); +#endif int boot_slots_compatible(struct boot_loader_state *state); uint32_t boot_status_internal_off(const struct boot_status *bs, int elem_sz); int boot_read_image_header(struct boot_loader_state *state, int slot, @@ -357,36 +347,14 @@ int boot_copy_region(struct boot_loader_state *state, const struct flash_area *fap_dst, uint32_t off_src, uint32_t off_dst, uint32_t sz); #endif -/* Prepare for write device that requires erase prior to write. This will - * do nothing on devices without erase requirement. - */ -int boot_erase_region(const struct flash_area *fap, uint32_t off, uint32_t sz, bool backwards); -/* Similar to boot_erase_region but will always remove data */ -int boot_scramble_region(const struct flash_area *fap, uint32_t off, uint32_t sz, bool backwards); -/* Makes slot unbootable, either by scrambling header magic, header sector - * or entire slot, depending on settings. - * Note: slot is passed here becuase at this point there is no function - * matching flash_area object to slot */ -int boot_scramble_slot(const struct flash_area *fap, int slot); bool boot_status_is_reset(const struct boot_status *bs); #ifdef MCUBOOT_ENC_IMAGES -int boot_write_enc_key(const struct flash_area *fap, uint8_t slot, - const struct boot_status *bs); +int boot_write_enc_keys(const struct flash_area *fap, const struct boot_status *bs); int boot_read_enc_key(const struct flash_area *fap, uint8_t slot, struct boot_status *bs); #endif -#if MCUBOOT_SWAP_USING_SCRATCH -/* - * Similar to `boot_trailer_sz` but this function returns the space used to - * store status in the scratch partition. The scratch partition only stores - * status during the swap of the last sector from primary/secondary (which - * is the first swap operation) and thus only requires space for one swap. - */ -uint32_t boot_scratch_trailer_sz(uint32_t min_write_sz); -#endif - /** * Checks that a buffer is erased according to what the erase value for the * flash device provided in `flash_area` is. @@ -475,11 +443,14 @@ static inline bool boot_u16_safe_add(uint16_t *dest, uint16_t a, uint16_t b) #endif #ifdef MCUBOOT_ENC_IMAGES #define BOOT_CURR_ENC(state) ((state)->enc[BOOT_CURR_IMG(state)]) +#define BOOT_CURR_ENC_SLOT(state, slot) (&((state)->enc[BOOT_CURR_IMG(state)][slot])) #else #define BOOT_CURR_ENC(state) NULL +#define BOOT_CURR_ENC_SLOT(state, slot) NULL #endif #define BOOT_IMG(state, slot) ((state)->imgs[BOOT_CURR_IMG(state)][(slot)]) #define BOOT_IMG_AREA(state, slot) (BOOT_IMG(state, slot).area) +#define BOOT_IMG_UNPROTECTED_TLV_SIZE(state, slot) (BOOT_IMG(state, slot).unprotected_tlv_size) #define BOOT_WRITE_SZ(state) ((state)->write_sz[BOOT_CURR_IMG(state)]) #define BOOT_SWAP_TYPE(state) ((state)->swap_type[BOOT_CURR_IMG(state)]) #define BOOT_TLV_OFF(hdr) ((hdr)->ih_hdr_size + (hdr)->ih_img_size) @@ -590,20 +561,6 @@ uint32_t bootutil_max_image_size(struct boot_loader_state *state, const struct f int boot_read_image_size(struct boot_loader_state *state, int slot, uint32_t *size); -/* Helper macro to avoid compile errors with systems that do not - * provide function to check device type. - * Note: it used to be inline, but somehow compiler would not - * optimize out branches that were impossible when this evaluated to - * just "true". - */ -#if defined(MCUBOOT_SUPPORT_DEV_WITHOUT_ERASE) && defined(MCUBOOT_SUPPORT_DEV_WITH_ERASE) -#define device_requires_erase(fa) (flash_area_erase_required(fa)) -#elif defined(MCUBOOT_SUPPORT_DEV_WITHOUT_ERASE) -#define device_requires_erase(fa) (false) -#else -#define device_requires_erase(fa) (true) -#endif - #ifdef __cplusplus } #endif diff --git a/boot/bootutil/src/bootutil_public.c b/boot/bootutil/src/bootutil_public.c index 7365eac5b..40755a347 100644 --- a/boot/bootutil/src/bootutil_public.c +++ b/boot/bootutil/src/bootutil_public.c @@ -56,6 +56,10 @@ #define SEND_BOOT_REQUEST #endif /* CONFIG_NRF_MCUBOOT_BOOT_REQUEST && !CONFIG_MCUBOOT */ +#ifdef CONFIG_NCS_MCUBOOT_MANIFEST_UPDATES +#include +#endif /* CONFIG_NCS_MCUBOOT_MANIFEST_UPDATES */ + #ifdef CONFIG_MCUBOOT BOOT_LOG_MODULE_DECLARE(mcuboot); #else @@ -243,6 +247,45 @@ boot_read_copy_done(const struct flash_area *fap, uint8_t *copy_done) return boot_read_flag(fap, copy_done, boot_copy_done_off(fap)); } +#if defined(SEND_BOOT_REQUEST) || (!defined(MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP)) || \ + defined(CONFIG_NCS_MCUBOOT_MANIFEST_UPDATES) +static int flash_area_to_image_slot(const struct flash_area *fa, uint32_t *slot) +{ + int id = flash_area_get_id(fa); +#if BOOT_IMAGE_NUMBER > 1 + uint8_t i = 0; + + for (i = 0; i < BOOT_IMAGE_NUMBER; i++) { + if (FLASH_AREA_IMAGE_PRIMARY(i) == id) { + if (slot != NULL) { + *slot = 0; + } + return i; + } else if (FLASH_AREA_IMAGE_SECONDARY(i) == id) { + if (slot != NULL) { + *slot = 1; + } + return i; + } + } + + /* Image not found */ + *slot = UINT32_MAX; +#else + (void)fa; + if (slot != NULL) { + if (FLASH_AREA_IMAGE_PRIMARY(0) == id) { + *slot = 0; + } else if (FLASH_AREA_IMAGE_SECONDARY(0) == id) { + *slot = 1; + } else { + *slot = UINT32_MAX; + } + } +#endif + return 0; +} +#endif /* SEND_BOOT_REQUEST || !MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP || CONFIG_NCS_MCUBOOT_MANIFEST_UPDATES */ int boot_read_swap_state(const struct flash_area *fap, @@ -251,7 +294,23 @@ boot_read_swap_state(const struct flash_area *fap, uint8_t magic[BOOT_MAGIC_SZ]; uint32_t off; uint8_t swap_info; - int rc; + int rc = -1; +#ifdef CONFIG_NCS_MCUBOOT_MANIFEST_UPDATES + enum boot_slot slot_id = BOOT_SLOT_NONE; + int image_id = flash_area_to_image_slot(fap, &slot_id); + + /* If manifest-based updates are used, only the manifest image is considered. */ + image_id = CONFIG_NCS_MCUBOOT_MANIFEST_IMAGE_NUMBER; + if (slot_id == BOOT_SLOT_PRIMARY) { + rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_id), &fap); + } else if (slot_id == BOOT_SLOT_SECONDARY) { + rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_id), &fap); + } + + if (rc != 0) { + return rc; + } +#endif /* CONFIG_NCS_MCUBOOT_MANIFEST_UPDATES */ off = boot_magic_off(fap); rc = flash_area_read(fap, off, magic, BOOT_MAGIC_SZ); @@ -398,45 +457,6 @@ boot_write_image_ok(const struct flash_area *fap) return boot_write_trailer_flag(fap, off, BOOT_FLAG_SET); } -#if defined(SEND_BOOT_REQUEST) || (!defined(MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP)) -static int flash_area_to_image_slot(const struct flash_area *fa, uint32_t *slot) -{ - int id = flash_area_get_id(fa); -#if BOOT_IMAGE_NUMBER > 1 - uint8_t i = 0; - - for (i = 0; i < BOOT_IMAGE_NUMBER; i++) { - if (FLASH_AREA_IMAGE_PRIMARY(i) == id) { - if (slot != NULL) { - *slot = 0; - } - return i; - } else if (FLASH_AREA_IMAGE_SECONDARY(i) == id) { - if (slot != NULL) { - *slot = 1; - } - return i; - } - } - - /* Image not found */ - *slot = UINT32_MAX; -#else - (void)fa; - if (slot != NULL) { - if (FLASH_AREA_IMAGE_PRIMARY(0) == id) { - *slot = 0; - } else if (FLASH_AREA_IMAGE_SECONDARY(0) == id) { - *slot = 1; - } else { - *slot = UINT32_MAX; - } - } -#endif - return 0; -} -#endif /* SEND_BOOT_REQUEST || !MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP */ - int boot_read_image_ok(const struct flash_area *fap, uint8_t *image_ok) { diff --git a/boot/bootutil/src/encrypted.c b/boot/bootutil/src/encrypted.c index 97791994f..9f86bb457 100644 --- a/boot/bootutil/src/encrypted.c +++ b/boot/bootutil/src/encrypted.c @@ -573,7 +573,7 @@ boot_enc_load(struct boot_loader_state *state, int slot, const struct image_header *hdr, const struct flash_area *fap, struct boot_status *bs) { - struct enc_key_data *enc_state = BOOT_CURR_ENC(state); + struct enc_key_data *enc_state = BOOT_CURR_ENC_SLOT(state, slot); uint32_t off; uint16_t len; struct image_tlv_iter it; @@ -587,13 +587,13 @@ boot_enc_load(struct boot_loader_state *state, int slot, BOOT_LOG_DBG("boot_enc_load: slot %d", slot); /* Already loaded... */ - if (enc_state[slot].valid) { + if (boot_enc_valid(enc_state)) { BOOT_LOG_DBG("boot_enc_load: already loaded"); return 1; } /* Initialize the AES context */ - boot_enc_init(enc_state, slot); + boot_enc_init(enc_state); #if defined(MCUBOOT_SWAP_USING_OFFSET) it.start_off = boot_get_state_secondary_offset(state, fap); @@ -627,48 +627,46 @@ boot_enc_load(struct boot_loader_state *state, int slot, } int -boot_enc_init(struct enc_key_data *enc_state, uint8_t slot) +boot_enc_init(struct enc_key_data *enc_state) { - bootutil_aes_ctr_init(&enc_state[slot].aes_ctr); + bootutil_aes_ctr_init(&enc_state->aes_ctr); return 0; } int -boot_enc_drop(struct enc_key_data *enc_state, uint8_t slot) +boot_enc_drop(struct enc_key_data *enc_state) { - bootutil_aes_ctr_drop(&enc_state[slot].aes_ctr); - enc_state[slot].valid = 0; + bootutil_aes_ctr_drop(&enc_state->aes_ctr); + enc_state->valid = 0; return 0; } int -boot_enc_set_key(struct enc_key_data *enc_state, uint8_t slot, - const struct boot_status *bs) +boot_enc_set_key(struct enc_key_data *enc_state, const uint8_t *key) { int rc; - rc = bootutil_aes_ctr_set_key(&enc_state[slot].aes_ctr, bs->enckey[slot]); + rc = bootutil_aes_ctr_set_key(&enc_state->aes_ctr, key); if (rc != 0) { - boot_enc_drop(enc_state, slot); + boot_enc_drop(enc_state); return -1; } - enc_state[slot].valid = 1; + enc_state->valid = 1; return 0; } bool -boot_enc_valid(struct enc_key_data *enc_state, int slot) +boot_enc_valid(const struct enc_key_data *enc_state) { - return enc_state[slot].valid; + return enc_state->valid; } void -boot_enc_encrypt(struct enc_key_data *enc_state, int slot, uint32_t off, +boot_enc_encrypt(struct enc_key_data *enc, uint32_t off, uint32_t sz, uint32_t blk_off, uint8_t *buf) { - struct enc_key_data *enc = &enc_state[slot]; uint8_t nonce[16]; /* Nothing to do with size == 0 */ @@ -688,10 +686,9 @@ boot_enc_encrypt(struct enc_key_data *enc_state, int slot, uint32_t off, } void -boot_enc_decrypt(struct enc_key_data *enc_state, int slot, uint32_t off, +boot_enc_decrypt(struct enc_key_data *enc, uint32_t off, uint32_t sz, uint32_t blk_off, uint8_t *buf) { - struct enc_key_data *enc = &enc_state[slot]; uint8_t nonce[16]; /* Nothing to do with size == 0 */ @@ -718,7 +715,7 @@ boot_enc_zeroize(struct enc_key_data *enc_state) { uint8_t slot; for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { - (void)boot_enc_drop(enc_state, slot); + (void)boot_enc_drop(&enc_state[slot]); } memset(enc_state, 0, sizeof(struct enc_key_data) * BOOT_NUM_SLOTS); } diff --git a/boot/bootutil/src/image_validate.c b/boot/bootutil/src/image_validate.c index 16ff9c09c..954b233af 100644 --- a/boot/bootutil/src/image_validate.c +++ b/boot/bootutil/src/image_validate.c @@ -50,6 +50,10 @@ BOOT_LOG_MODULE_DECLARE(mcuboot); #include "bootutil/mcuboot_uuid.h" #endif /* MCUBOOT_UUID_VID || MCUBOOT_UUID_CID */ +#ifdef MCUBOOT_MANIFEST_UPDATES +#include "bootutil/mcuboot_manifest.h" +#endif /* MCUBOOT_MANIFEST_UPDATES */ + #if defined(MCUBOOT_DECOMPRESS_IMAGES) #include #include @@ -210,9 +214,10 @@ bootutil_img_validate(struct boot_loader_state *state, int seed_len, uint8_t *out_hash ) { -#if (defined(EXPECTED_KEY_TLV) && defined(MCUBOOT_HW_KEY)) || defined(MCUBOOT_HW_ROLLBACK_PROT) \ - || defined(MCUBOOT_UUID_VID) || defined(MCUBOOT_UUID_CID) || defined(MCUBOOT_DECOMPRESS_IMAGES) \ - || defined(MCUBOOT_BUILTIN_KEY) +#if (defined(EXPECTED_KEY_TLV) && defined(MCUBOOT_HW_KEY)) || \ + (defined(EXPECTED_SIG_TLV) && defined(MCUBOOT_BUILTIN_KEY)) || \ + defined(MCUBOOT_HW_ROLLBACK_PROT) || defined(MCUBOOT_MANIFEST_UPDATES) || \ + defined(MCUBOOT_UUID_VID) || defined(MCUBOOT_UUID_CID) || defined(MCUBOOT_DECOMPRESS_IMAGES) int image_index = (state == NULL ? 0 : BOOT_CURR_IMG(state)); #endif uint32_t off; @@ -258,6 +263,11 @@ bootutil_img_validate(struct boot_loader_state *state, goto out; } #endif +#ifdef MCUBOOT_MANIFEST_UPDATES + bool manifest_found = false; + bool manifest_valid = false; + uint8_t slot = (flash_area_get_id(fap) == FLASH_AREA_IMAGE_SECONDARY(image_index) ? 1 : 0); +#endif #ifdef MCUBOOT_UUID_VID struct image_uuid img_uuid_vid = {0x00}; FIH_DECLARE(uuid_vid_valid, FIH_FAILURE); @@ -431,6 +441,69 @@ bootutil_img_validate(struct boot_loader_state *state, goto out; } +#ifdef MCUBOOT_MANIFEST_UPDATES + if (image_index == MCUBOOT_MANIFEST_IMAGE_NUMBER) { + if (!state->manifest_valid[slot]) { + /* Manifest TLV must be processed before any of the image's hash TLV. */ + BOOT_LOG_ERR("bootutil_img_validate: image rejected, manifest not found before " + "image %d hash", image_index); + rc = -1; + goto out; + } + /* Manifest image does not have hash in the manifest. */ + image_hash_valid = 1; + break; + } +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) || \ + defined(MCUBOOT_SWAP_USING_OFFSET) + state->matching_manifest[image_index][slot] = BOOT_SLOT_NONE; + /* Try to match with the primary manifest first. */ + if (state->manifest_valid[BOOT_SLOT_PRIMARY]) { + if (bootutil_verify_manifest_image_hash(&state->manifest[BOOT_SLOT_PRIMARY], hash, + image_index)) { + state->matching_manifest[image_index][slot] = BOOT_SLOT_PRIMARY; + } + } + + /* Try to match with the secondary manifest if not matched with the primary. */ + if(state->matching_manifest[image_index][slot] == BOOT_SLOT_NONE && + state->manifest_valid[BOOT_SLOT_SECONDARY]) { + if (bootutil_verify_manifest_image_hash(&state->manifest[BOOT_SLOT_SECONDARY], hash, + image_index)) { + state->matching_manifest[image_index][slot] = BOOT_SLOT_SECONDARY; + } + } + + /* No matching manifest found. */ + if (state->matching_manifest[image_index][slot] == BOOT_SLOT_NONE) { + BOOT_LOG_ERR( + "bootutil_img_validate: image rejected, no valid manifest for image %d slot %d", + image_index, slot); + rc = -1; + goto out; + } else { + BOOT_LOG_INF("bootutil_img_validate: image %d slot %d matches manifest in slot %d", + image_index, slot, state->matching_manifest[image_index][slot]); + } +#else /* MCUBOOT_SWAP_USING_SCRATCH || MCUBOOT_SWAP_USING_MOVE || MCUBOOT_SWAP_USING_OFFSET */ + /* Manifest image for a given slot must precede any of other images. */ + if (!state->manifest_valid[slot]) { + /* Manifest TLV must be processed before any of the image's hash TLV. */ + BOOT_LOG_ERR("bootutil_img_validate: image rejected, no valid manifest for slot %d", + slot); + rc = -1; + goto out; + } + + /* Any image, not described by the manifest is considered as invalid. */ + if (!bootutil_verify_manifest_image_hash(&state->manifest[slot], hash, image_index)) { + BOOT_LOG_ERR( + "bootutil_img_validate: image rejected, hash does not match manifest contents"); + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } +#endif /* MCUBOOT_SWAP_USING_SCRATCH || MCUBOOT_SWAP_USING_MOVE || MCUBOOT_SWAP_USING_OFFSET */ +#endif /* MCUBOOT_MANIFEST_UPDATES */ image_hash_valid = 1; break; } @@ -568,6 +641,43 @@ bootutil_img_validate(struct boot_loader_state *state, break; } #endif /* MCUBOOT_HW_ROLLBACK_PROT */ +#ifdef MCUBOOT_MANIFEST_UPDATES + case IMAGE_TLV_MANIFEST: + { + /* There can be only one manifest and must be a part of image with specific index. */ + if (manifest_found || image_index != MCUBOOT_MANIFEST_IMAGE_NUMBER || + len != sizeof(struct mcuboot_manifest)) { + BOOT_LOG_ERR( + "bootutil_img_validate: image %d slot %d rejected, unexpected manifest TLV", + image_index, slot); + rc = -1; + goto out; + } + + manifest_found = true; + + rc = LOAD_IMAGE_DATA(hdr, fap, off, &state->manifest[slot], + sizeof(struct mcuboot_manifest)); + if (rc) { + BOOT_LOG_ERR("bootutil_img_validate: slot %d rejected, unable to load manifest", + slot); + goto out; + } + + manifest_valid = bootutil_verify_manifest(&state->manifest[slot]); + if (!manifest_valid) { + BOOT_LOG_ERR("bootutil_img_validate: slot %d rejected, invalid manifest contents", + slot); + rc = -1; + goto out; + } + + /* The image's manifest has been successfully verified. */ + state->manifest_valid[slot] = true; + BOOT_LOG_INF("bootutil_img_validate: slot %d manifest verified", slot); + break; + } +#endif #ifdef MCUBOOT_UUID_VID case IMAGE_TLV_UUID_VID: { @@ -654,6 +764,13 @@ bootutil_img_validate(struct boot_loader_state *state, skip_security_counter_check: #endif +#ifdef MCUBOOT_MANIFEST_UPDATES + if (image_index == MCUBOOT_MANIFEST_IMAGE_NUMBER && (!manifest_found || !manifest_valid)) { + BOOT_LOG_ERR("bootutil_img_validate: slot %d rejected, manifest missing or invalid", slot); + rc = -1; + goto out; + } +#endif #ifdef MCUBOOT_UUID_VID if (FIH_NOT_EQ(uuid_vid_valid, FIH_SUCCESS)) { rc = -1; diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c index faffab03a..7feafbbbe 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -49,6 +49,9 @@ #include "bootutil/ramload.h" #include "bootutil/boot_hooks.h" #include "bootutil/mcuboot_status.h" +#include "bootutil_loader.h" + +#ifndef MCUBOOT_MANIFEST_UPDATES #if defined(MCUBOOT_DECOMPRESS_IMAGES) #include @@ -57,9 +60,6 @@ #ifdef __ZEPHYR__ #include -#if defined(CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS) -#include -#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS */ #endif #if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) && defined(CONFIG_PCD_APP) @@ -99,6 +99,10 @@ static bool owner_nsib[BOOT_IMAGE_NUMBER] = {false}; static struct image_max_size image_max_sizes[BOOT_IMAGE_NUMBER] = {0}; #endif +#if defined(MCUBOOT_VERIFY_IMG_ADDRESS) && defined(MCUBOOT_CHECK_HEADER_LOAD_ADDRESS) +#warning MCUBOOT_CHECK_HEADER_LOAD_ADDRESS takes precedence over MCUBOOT_VERIFY_IMG_ADDRESS +#endif + #if (!defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)) || \ defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) #if !defined(__BOOTSIM__) @@ -127,24 +131,8 @@ static const struct image_version mcuboot_s0_s1_image_version = { }; #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 +/* Valid only for ARM Cortext M */ +#define RESET_OFFSET (2 * sizeof(uint32_t)) #if BOOT_MAX_ALIGN > 1024 #define BUF_SZ BOOT_MAX_ALIGN @@ -165,100 +153,13 @@ struct boot_loader_state *boot_get_loader_state(void) return &boot_data; } -#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) struct image_max_size *boot_get_image_max_sizes(void) { return image_max_sizes; } #endif - -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. - */ -#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) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER && i == 0) { - continue; - } -#endif /* CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 */ - 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. * @@ -295,75 +196,6 @@ fill_rsp(struct boot_loader_state *state, struct boot_rsp *rsp) rsp->br_hdr = boot_img_hdr(state, active_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 !defined(MCUBOOT_VERSION_CMP_USE_BUILD_NUMBER) - BOOT_LOG_DBG("boot_version_cmp: ver1 %u.%u.%u vs ver2 %u.%u.%u", - (unsigned)ver1->iv_major, (unsigned)ver1->iv_minor, - (unsigned)ver1->iv_revision, (unsigned)ver2->iv_major, - (unsigned)ver2->iv_minor, (unsigned)ver2->iv_revision); -#else - BOOT_LOG_DBG("boot_version_cmp: ver1 %u.%u.%u.%u vs ver2 %u.%u.%u.%u", - (unsigned)ver1->iv_major, (unsigned)ver1->iv_minor, - (unsigned)ver1->iv_revision, (unsigned)ver1->iv_build_num, - (unsigned)ver2->iv_major, (unsigned)ver2->iv_minor, - (unsigned)ver2->iv_revision, (unsigned)ver2->iv_build_num); -#endif - - 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 (BOOT_IMAGE_NUMBER > 1) static int @@ -397,7 +229,7 @@ boot_verify_slot_dependency(struct boot_loader_state *state, dep_version = &state->imgs[dep->image_id][dep_slot].hdr.ih_ver; - rc = boot_version_cmp(dep_version, &dep->image_min_version); + rc = boot_compare_version(dep_version, &dep->image_min_version); #if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) if (rc < 0) { /* Dependency not satisfied. @@ -688,73 +520,6 @@ boot_write_status(const struct boot_loader_state *state, struct boot_status *bs) #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(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 - - for (int i = 1; i <= CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT; i++ ) { -#if CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 - BOOT_LOG_DBG("Image validation attempt %d/%d", i, CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT); -#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ - - FIH_CALL(bootutil_img_validate, fih_rc, state, hdr, fap, tmpbuf, BOOT_TMPBUF_SZ, - NULL, 0, NULL); - - if (FIH_EQ(fih_rc, FIH_SUCCESS)) { -#if CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 - BOOT_LOG_DBG("Image validation attempt %d/%d success", i, CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT); -#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ - break; - } else { -#if CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 - BOOT_LOG_WRN("Image validation attempt %d/%d failure: %d", - i, - CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT, fih_rc); -#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ - - if (i < CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT) { -#if defined(CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS) -#if CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 - BOOT_LOG_DBG("Waiting %d ms before next attempt", - CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS); -#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ - k_busy_wait(CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS * 1000); -#endif /* CONFIG_NCS_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS */ - } - } - } - FIH_RET(fih_rc); -} - #if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) static fih_ret split_image_check(struct image_header *app_hdr, @@ -788,107 +553,6 @@ split_image_check(struct image_header *app_hdr, } #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 = NULL; - struct image_header *hdr; - uint8_t erased_val; - - fap = BOOT_IMG_AREA(state, slot); - assert(fap != NULL); - - erased_val = flash_area_erased_val(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 @@ -953,8 +617,7 @@ boot_validate_slot(struct boot_loader_state *state, int slot, assert(fap != NULL); hdr = boot_img_hdr(state, slot); - if (boot_check_header_erased(state, slot) == 0 || - (hdr->ih_flags & IMAGE_F_NON_BOOTABLE)) { + if (boot_check_header_erased(state, slot) || (hdr->ih_flags & IMAGE_F_NON_BOOTABLE)) { #if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_OFFSET) /* * This fixes an issue where an image might be erased, but a trailer @@ -1021,10 +684,9 @@ boot_validate_slot(struct boot_loader_state *state, int slot, if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { rc = pcd_version_cmp_net(fap, boot_img_hdr(state, BOOT_SLOT_SECONDARY)); } else { - rc = boot_version_cmp( - &boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, - &boot_img_hdr(state, BOOT_SLOT_PRIMARY)->ih_ver); - + rc = boot_compare_version( + &boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, + &boot_img_hdr(state, BOOT_SLOT_PRIMARY)->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 @@ -1032,8 +694,9 @@ boot_validate_slot(struct boot_loader_state *state, int slot, */ int version_check; - version_check = boot_version_cmp(&boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, - &mcuboot_s0_s1_image_version); + version_check = boot_compare_version( + &boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, + &mcuboot_s0_s1_image_version); /* Only update rc if the currently running version is newer */ if (version_check < rc) { @@ -1043,10 +706,9 @@ boot_validate_slot(struct boot_loader_state *state, int slot, #endif } #else - rc = boot_version_cmp( - &boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, - &boot_img_hdr(state, BOOT_SLOT_PRIMARY)->ih_ver); - + rc = boot_compare_version( + &boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, + &boot_img_hdr(state, BOOT_SLOT_PRIMARY)->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 @@ -1054,8 +716,9 @@ boot_validate_slot(struct boot_loader_state *state, int slot, */ int version_check; - version_check = boot_version_cmp(&boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, - &mcuboot_s0_s1_image_version); + version_check = boot_compare_version( + &boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, + &mcuboot_s0_s1_image_version); /* Only update rc if the currently running version is newer */ if (version_check < rc) { @@ -1064,7 +727,7 @@ boot_validate_slot(struct boot_loader_state *state, int slot, } #endif #endif - if (rc < 0 && boot_check_header_erased(state, BOOT_SLOT_PRIMARY)) { + if (rc < 0 && !boot_check_header_erased(state, BOOT_SLOT_PRIMARY)) { BOOT_LOG_ERR("insufficient version in secondary slot"); boot_scramble_slot(fap, slot); /* Image in the secondary slot does not satisfy version requirement. @@ -1075,13 +738,13 @@ boot_validate_slot(struct boot_loader_state *state, int slot, } } #endif - if (!boot_is_header_valid(hdr, fap, state)) { + if (!boot_check_header_valid(state, slot)) { 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); + FIH_CALL(boot_check_image, fih_rc, state, bs, slot); } } #if defined(MCUBOOT_SWAP_USING_OFFSET) @@ -1103,7 +766,8 @@ boot_validate_slot(struct boot_loader_state *state, int slot, goto out; } -#if MCUBOOT_IMAGE_NUMBER > 1 && !defined(MCUBOOT_ENC_IMAGES) && defined(MCUBOOT_VERIFY_IMG_ADDRESS) +#if defined(MCUBOOT_VERIFY_IMG_ADDRESS) && !defined(MCUBOOT_ENC_IMAGES) || \ + defined(MCUBOOT_CHECK_HEADER_LOAD_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. @@ -1120,18 +784,12 @@ boot_validate_slot(struct boot_loader_state *state, int slot, } #endif if (fap == BOOT_IMG_AREA(state, BOOT_SLOT_SECONDARY)) { - const struct flash_area *pri_fa = BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY); 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; + uint32_t internal_img_addr = 0; /* either the reset handler addres or the image beginning addres */ + uint32_t min_addr; + uint32_t max_addr; bool check_addresses = false; - if (flash_area_read(fap, reset_addr, &reset_value, sizeof(reset_value)) != 0) { - fih_rc = FIH_NO_BOOTABLE_IMAGE; - goto out; - } - #ifdef PM_CPUNET_APP_ADDRESS /* The primary slot for the network core is emulated in RAM. * Its flash_area hasn't got relevant boundaries. @@ -1156,23 +814,43 @@ boot_validate_slot(struct boot_loader_state *state, int slot, } else #endif if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { + min_addr = flash_area_get_off(BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY)); + max_addr = flash_area_get_size(BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY)) + min_addr; + #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)); + min_addr = MIN(min_addr, PM_S0_ADDRESS); + max_addr = MAX(max_addr, (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)); + min_addr = MIN(min_addr, PM_S1_ADDRESS); + max_addr = MAX(max_addr, (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 (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"); +/* MCUBOOT_CHECK_HEADER_LOAD_ADDRESS takes priority over MCUBOOT_VERIFY_IMG_ADDRESS */ +#ifdef MCUBOOT_CHECK_HEADER_LOAD_ADDRESS + internal_img_addr = secondary_hdr->ih_load_addr; +#else + /* This is platform specific code that should not be here */ + const uint32_t offset = secondary_hdr->ih_hdr_size + RESET_OFFSET; + BOOT_LOG_DBG("Getting image %d internal addr from offset %u", + BOOT_CURR_IMG(state), offset); + if (flash_area_read(fap, offset, &internal_img_addr, sizeof(internal_img_addr)) != 0) { + BOOT_LOG_ERR("Failed to read image %d load address", BOOT_CURR_IMG(state)); + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } +#endif + + BOOT_LOG_DBG("Image %d expected load address 0x%x", BOOT_CURR_IMG(state), internal_img_addr); + BOOT_LOG_DBG("Check 0x%x is within [min_addr, max_addr] = [0x%x, 0x%x)", + internal_img_addr, min_addr, max_addr); + + if (check_addresses == true && (internal_img_addr < min_addr || internal_img_addr >= max_addr)) { + BOOT_LOG_ERR("Binary in secondary slot of image %d is not designated for the primary slot", + BOOT_CURR_IMG(state)); BOOT_LOG_ERR("Erasing image from secondary slot"); /* The vector table in the image located in the secondary @@ -1224,64 +902,6 @@ fih_ret boot_nv_image_should_have_security_counter(uint32_t image_index) return FIH_SUCCESS; } - -/** - * 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 state Boot state where the current image's security counter will - * be updated. - * @param slot Slot number of the image. - * @param hdr_slot_idx Index of the header in the state current image variable - * containing the 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(struct boot_loader_state *state, int slot, int hdr_slot_idx) -{ - const struct flash_area *fap = NULL; - uint32_t img_security_cnt; - int rc; - -#if defined(PM_S1_ADDRESS) - if (owner_nsib[BOOT_CURR_IMG(state)]) { - /* - * Downgrade prevention on S0/S1 image is managed by NSIB which is a software (not - * hardware) check - */ - return 0; - } -#endif - -#if defined(CONFIG_SOC_NRF5340_CPUAPP) && CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER != -1 - if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { - /* - * Downgrade prevention on network core image is managed by NSIB which is a software (not - * hardware) check - */ - return 0; - } -#endif - - fap = BOOT_IMG_AREA(state, slot); - assert(fap != NULL); - - rc = bootutil_get_img_security_cnt(state, hdr_slot_idx, fap, &img_security_cnt); - if (rc != 0) { - goto done; - } - - rc = boot_nv_security_counter_update(BOOT_CURR_IMG(state), img_security_cnt); - if (rc != 0) { - goto done; - } - -done: - return rc; -} #endif /* MCUBOOT_HW_ROLLBACK_PROT */ #if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) @@ -1361,14 +981,9 @@ static void sec_slot_cleanup_if_unusable(void) &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 image %d", idx); - } } - if (rc) { - BOOT_LOG_ERR("Failed to clean-up secondary slot of image %d: %d", idx, rc); - } + BOOT_LOG_ERR("Erase secondary: img %d: %d", idx, rc); } } } @@ -1408,8 +1023,7 @@ boot_validated_swap_type(struct boot_loader_state *state, #endif #if defined(PM_S1_ADDRESS) || defined(PM_CPUNET_B0N_ADDRESS) - const struct flash_area *secondary_fa = - BOOT_IMG_AREA(state, BOOT_SLOT_SECONDARY); + const struct flash_area *secondary_fa = BOOT_IMG_AREA(state, BOOT_SLOT_SECONDARY); struct image_header *hdr = boot_img_hdr(state, BOOT_SLOT_SECONDARY); uint32_t reset_addr = 0; int rc = 0; @@ -1438,12 +1052,12 @@ boot_validated_swap_type(struct boot_loader_state *state, { const struct flash_area *primary_fa; rc = flash_area_open(flash_area_id_from_multi_image_slot( - BOOT_CURR_IMG(state), BOOT_SLOT_PRIMARY), - &primary_fa); + BOOT_CURR_IMG(state), + BOOT_SLOT_PRIMARY), + &primary_fa); if (rc != 0) { return BOOT_SWAP_TYPE_FAIL; } - /* Check start and end of primary slot for current image */ #if (CONFIG_NCS_IS_VARIANT_IMAGE) if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) { @@ -1524,163 +1138,19 @@ boot_validated_swap_type(struct boot_loader_state *state, * method is used. There is no need to erase sectors, because * the image cannot be reverted. */ - rc = swap_erase_trailer_sectors(state, - secondary_fa); + 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 */ + !CONFIG_NRF53_MULTI_IMAGE_UPDATE && CONFIG_PCD_APP */ } return swap_type; } #endif -/** - * Removes data from specified region either by writing erase value in place of - * data or by doing erase, if device has such hardware requirement. - * Note that function will fail if off or size are not aligned to device - * write block size or erase block size. - * - * @param fa The flash_area containing the region to erase. - * @param off The offset within the flash area to start the - * erase. - * @param size The number of bytes to erase. - * @param backwards If set to true will erase from end to start - * addresses, otherwise erases from start to end - * addresses. - * - * @return 0 on success; nonzero on failure. - */ -int -boot_scramble_region(const struct flash_area *fa, uint32_t off, uint32_t size, bool backwards) -{ - int rc = 0; - - BOOT_LOG_DBG("boot_scramble_region: %p %d %d %d", fa, off, size, (int)backwards); - - if (size == 0) { - goto done; - } - - if (device_requires_erase(fa)) { - rc = boot_erase_region(fa, off, size, backwards); - } else if (off >= flash_area_get_size(fa) || (flash_area_get_size(fa) - off) < size) { - rc = -1; - goto done; - } else { - uint8_t buf[BOOT_MAX_ALIGN]; - const size_t write_block = flash_area_align(fa); - uint32_t end_offset; - - BOOT_LOG_DBG("boot_scramble_region: device without erase, overwriting"); - memset(buf, flash_area_erased_val(fa), sizeof(buf)); - - if (backwards) { - end_offset = ALIGN_DOWN(off, write_block); - /* Starting at the last write block in range */ - off += size - write_block; - } else { - end_offset = ALIGN_DOWN((off + size), write_block); - } - BOOT_LOG_DBG("boot_scramble_region: start offset %u, end offset %u", off, end_offset); - - while (off != end_offset) { - /* Write over the area to scramble data that is there */ - rc = flash_area_write(fa, off, buf, write_block); - if (rc != 0) { - BOOT_LOG_DBG("boot_scramble_region: error %d for %p %d %u", - rc, fa, off, (unsigned int)write_block); - break; - } - - MCUBOOT_WATCHDOG_FEED(); - - if (backwards) { - if (end_offset >= off) { - /* Reached the first offset in range and already scrambled it */ - break; - } - - off -= write_block; - } else { - off += write_block; - - if (end_offset <= off) { - /* Reached the end offset in range and already scrambled it */ - break; - } - } - } - } - -done: - return rc; -} - -/** - * Removes data from specified region backwards either by writing erase_value - * in place of data or by doing erase, if device has such hardware requirement. - * Note that function will fail if off or size are not aligned to device - * write block size or erase block size. - * - * @param fa The flash_area containing the region to erase. - * @param off The offset within the flash area to start the - * erase. - * @param size The number of bytes to erase. - * - * @return 0 on success; nonzero on failure. - */ - -/** - * Remove enough data from slot to mark is as unused - * Assumption: header and trailer are not overlapping on write block or - * erase block, if device has erase requirement. - * Note that this function is intended for removing data not preparing device - * for write. - * - * @param fa Pointer to flash area object for slot - * @param slot Slot the @p fa represents - * - * @return 0 on success; nonzero on failure. - */ -int -boot_scramble_slot(const struct flash_area *fa, int slot) -{ - size_t size; - int ret = 0; - - (void)slot; - - /* Without minimal entire area needs to be scrambled */ -#if !defined(MCUBOOT_MINIMAL_SCRAMBLE) - size = flash_area_get_size(fa); - ret = boot_scramble_region(fa, 0, size, false); -#else - size_t off = 0; - - ret = boot_header_scramble_off_sz(fa, slot, &off, &size); - if (ret < 0) { - return ret; - } - - ret = boot_scramble_region(fa, off, size, false); - if (ret < 0) { - return ret; - } - - ret = boot_trailer_scramble_offset(fa, 0, &off); - if (ret < 0) { - return ret; - } - - ret = boot_scramble_region(fa, off, (flash_area_get_size(fa) - off), true); -#endif - return ret; -} - #if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) #if defined(MCUBOOT_ENC_IMAGES) || defined(MCUBOOT_SWAP_SAVE_ENCTLV) @@ -1840,11 +1310,11 @@ boot_copy_region(struct boot_loader_state *state, } } if (source_slot == 0) { - boot_enc_encrypt(BOOT_CURR_ENC(state), source_slot, + boot_enc_encrypt(BOOT_CURR_ENC_SLOT(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, + boot_enc_decrypt(BOOT_CURR_ENC_SLOT(state, source_slot), (abs_off + idx) - hdr->ih_hdr_size, blk_sz, blk_off, &buf[idx]); } @@ -1958,7 +1428,7 @@ boot_copy_image(struct boot_loader_state *state, struct boot_status *bs) if (rc < 0) { return BOOT_EBADIMAGE; } - if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs)) { + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC_SLOT(state, BOOT_SLOT_SECONDARY), bs->enckey[BOOT_SLOT_SECONDARY])) { return BOOT_EBADIMAGE; } } @@ -2076,17 +1546,17 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) #ifdef MCUBOOT_ENC_IMAGES if (IS_ENCRYPTED(hdr)) { fap = BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY); - rc = boot_enc_load(state, 0, hdr, fap, bs); + rc = boot_enc_load(state, BOOT_SLOT_PRIMARY, hdr, fap, bs); assert(rc >= 0); if (rc == 0) { - rc = boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs); + rc = boot_enc_set_key(BOOT_CURR_ENC_SLOT(state, BOOT_SLOT_PRIMARY), bs->enckey[BOOT_SLOT_PRIMARY]); assert(rc == 0); } else { rc = 0; } } else { - memset(bs->enckey[0], 0xff, BOOT_ENC_KEY_ALIGN_SIZE); + memset(bs->enckey[BOOT_SLOT_PRIMARY], 0xff, BOOT_ENC_KEY_ALIGN_SIZE); } #endif @@ -2100,17 +1570,17 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) hdr = boot_img_hdr(state, BOOT_SLOT_SECONDARY); if (IS_ENCRYPTED(hdr)) { fap = BOOT_IMG_AREA(state, BOOT_SLOT_SECONDARY); - rc = boot_enc_load(state, 1, hdr, fap, bs); + rc = boot_enc_load(state, BOOT_SLOT_SECONDARY, hdr, fap, bs); assert(rc >= 0); if (rc == 0) { - rc = boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs); + rc = boot_enc_set_key(BOOT_CURR_ENC_SLOT(state, BOOT_SLOT_SECONDARY), bs->enckey[BOOT_SLOT_SECONDARY]); assert(rc == 0); } else { rc = 0; } } else { - memset(bs->enckey[1], 0xff, BOOT_ENC_KEY_ALIGN_SIZE); + memset(bs->enckey[BOOT_SLOT_SECONDARY], 0xff, BOOT_ENC_KEY_ALIGN_SIZE); } #endif @@ -2134,6 +1604,9 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) #ifdef MCUBOOT_ENC_IMAGES for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + + boot_enc_init(BOOT_CURR_ENC_SLOT(state, slot)); + rc = boot_read_enc_key(fap, slot, bs); assert(rc == 0); @@ -2143,10 +1616,8 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) } } - boot_enc_init(BOOT_CURR_ENC(state), slot); - if (i != BOOT_ENC_KEY_SIZE) { - boot_enc_set_key(BOOT_CURR_ENC(state), slot, bs); + boot_enc_set_key(BOOT_CURR_ENC_SLOT(state, slot), bs->enckey[slot]); } } #endif @@ -2208,9 +1679,8 @@ boot_perform_update(struct boot_loader_state *state, struct boot_status *bs) * already been checked). */ FIH_DECLARE(fih_rc, FIH_FAILURE); - rc = boot_check_header_erased(state, BOOT_SLOT_PRIMARY); FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_SLOT_PRIMARY, bs, 0); - if (rc == 0 || FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + if (boot_check_header_erased(state, BOOT_SLOT_PRIMARY) || FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { rc = boot_copy_image(state, bs); } else { rc = boot_swap_image(state, bs); @@ -2440,7 +1910,7 @@ boot_prepare_image_for_update(struct boot_loader_state *state, 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_check_header_erased(state, BOOT_SLOT_PRIMARY) != 0) { + if (rc != 0 && !boot_check_header_erased(state, BOOT_SLOT_PRIMARY)) { #else if (rc != 0) { #endif @@ -2519,11 +1989,10 @@ boot_prepare_image_for_update(struct boot_loader_state *state, * magic, so also run validation on the primary slot to be * sure it's not OK. */ - rc = boot_check_header_erased(state, BOOT_SLOT_PRIMARY); FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_SLOT_PRIMARY, bs, 0); - - if (rc == 0 || FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + if (boot_check_header_erased(state, BOOT_SLOT_PRIMARY) || + FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { rc = (boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_magic == IMAGE_MAGIC) ? 1: 0; FIH_CALL(boot_validate_slot, fih_rc, @@ -2652,8 +2121,9 @@ check_downgrade_prevention(struct boot_loader_state *state) } } else { - rc = boot_version_cmp(&boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, - &boot_img_hdr(state, BOOT_SLOT_PRIMARY)->ih_ver); + rc = boot_compare_version( + &boot_img_hdr(state, BOOT_SLOT_SECONDARY)->ih_ver, + &boot_img_hdr(state, BOOT_SLOT_PRIMARY)->ih_ver); } if (rc < 0) { /* Image in slot 0 prevents downgrade, delete image in slot 1 */ @@ -2929,11 +2399,11 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) 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_update_hw_rollback_protection(state); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } } rc = boot_add_shared_data(state, BOOT_SLOT_PRIMARY); @@ -3077,7 +2547,7 @@ boot_get_slot_usage(struct boot_loader_state *state) 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)) { + if (boot_check_header_valid(state, slot)) { state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = true; BOOT_LOG_IMAGE_INFO(slot, hdr); } else { @@ -3116,7 +2586,7 @@ find_slot_with_highest_version(struct boot_loader_state *state) if (candidate_slot == BOOT_SLOT_NONE) { candidate_slot = slot; } else { - rc = boot_version_cmp( + rc = boot_compare_version( &boot_img_hdr(state, slot)->ih_ver, &boot_img_hdr(state, candidate_slot)->ih_ver); if (rc == 1) { @@ -3506,105 +2976,6 @@ boot_go_for_image_id(struct boot_rsp *rsp, uint32_t image_id) FIH_RET(fih_rc); } -int -boot_open_all_flash_areas(struct boot_loader_state *state) -{ - size_t slot; - int rc = 0; - int fa_id; - int image_index; - - IMAGES_ITER(BOOT_CURR_IMG(state)) { -#if BOOT_IMAGE_NUMBER > 1 - if (state->img_mask[BOOT_CURR_IMG(state)]) { - continue; - } -#endif - image_index = BOOT_CURR_IMG(state); - - 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 %zu): %d", - fa_id, image_index, slot, rc); - goto out; - } - } - } - -#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", rc); - goto out; - } -#endif - -out: - if (rc != 0) { - boot_close_all_flash_areas(state); - } - - return rc; -} - -void -boot_close_all_flash_areas(struct boot_loader_state *state) -{ - uint32_t slot; - -#if MCUBOOT_SWAP_USING_SCRATCH - if (BOOT_SCRATCH_AREA(state) != NULL) { - flash_area_close(BOOT_SCRATCH_AREA(state)); - } -#endif - - IMAGES_ITER(BOOT_CURR_IMG(state)) { -#if BOOT_IMAGE_NUMBER > 1 - if (state->img_mask[BOOT_CURR_IMG(state)]) { - continue; - } -#endif - for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { - if (BOOT_IMG_AREA(state, BOOT_NUM_SLOTS - 1 - slot) != NULL) { - flash_area_close(BOOT_IMG_AREA(state, BOOT_NUM_SLOTS - 1 - slot)); - } - } - } -} - -#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 - #if defined(MCUBOOT_SWAP_USING_OFFSET) uint32_t boot_get_state_secondary_offset(struct boot_loader_state *state, const struct flash_area *fap) @@ -3616,3 +2987,5 @@ uint32_t boot_get_state_secondary_offset(struct boot_loader_state *state, return 0; } #endif + +#endif /* !MCUBOOT_MANIFEST_UPDATES */ diff --git a/boot/bootutil/src/loader_manifest_xip.c b/boot/bootutil/src/loader_manifest_xip.c new file mode 100644 index 000000000..858fdb82b --- /dev/null +++ b/boot/bootutil/src/loader_manifest_xip.c @@ -0,0 +1,630 @@ +/* + * 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-2025 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 manifest-based boot loader. + * Functions defined in this file should only be called while the boot loader is + * running. + */ + +#include +#include +#include +#include +#include +#include "flash_map_backend/flash_map_backend.h" +#include "mcuboot_config/mcuboot_config.h" +#include "bootutil/bootutil.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/image.h" +#include "bootutil_priv.h" +#include "bootutil/bootutil_log.h" +#include "bootutil/security_cnt.h" +#include "bootutil/fault_injection_hardening.h" +#include "bootutil/boot_hooks.h" +#include "bootutil_loader.h" + +#if defined(MCUBOOT_MANIFEST_UPDATES) && defined(MCUBOOT_DIRECT_XIP) +#include "bootutil/mcuboot_manifest.h" + +#if defined(MCUBOOT_DIRECT_XIP) && defined(MCUBOOT_DECOMPRESS_IMAGES) +#error "Image decompression is not supported when MCUBOOT_DIRECT_XIP is selected." +#endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_DECOMPRESS_IMAGES */ + +#ifdef MCUBOOT_ENC_IMAGES +#include "bootutil/enc_key.h" +#endif + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +static struct boot_loader_state boot_data; + +#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 BOOT_MAX_ALIGN > 1024 +#define BUF_SZ BOOT_MAX_ALIGN +#else +#define BUF_SZ 1024 +#endif + +struct boot_loader_state *boot_get_loader_state(void) +{ + return &boot_data; +} + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) +struct image_max_size *boot_get_image_max_sizes(void) +{ + return image_max_sizes; +} +#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; + + /* Always boot from the first image. */ + BOOT_CURR_IMG(state) = 0; + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + + 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); +} + +#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); + + /* 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, int expected_swap_type) +{ + const struct flash_area *fap; + struct image_header *hdr; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + BOOT_LOG_DBG("boot_validate_slot: slot %d, expected_swap_type %d", + slot, expected_swap_type); + (void)expected_swap_type; + + fap = BOOT_IMG_AREA(state, slot); + assert(fap != NULL); + + hdr = boot_img_hdr(state, slot); + if (boot_check_header_erased(state, slot) || (hdr->ih_flags & IMAGE_F_NON_BOOTABLE)) { + /* No bootable image in slot; continue booting from the primary slot. */ + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + + if (!boot_check_header_valid(state, slot)) { + 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_check_image, fih_rc, state, bs, slot); + } + } + + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + if ((slot != BOOT_SLOT_PRIMARY) || ARE_SLOTS_EQUIVALENT()) { + boot_scramble_slot(fap, slot); + /* 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_SLOT_PRIMARY) ? "primary" : "secondary"); +#endif + fih_rc = FIH_NO_BOOTABLE_IMAGE; + goto out; + } + +out: + FIH_RET(fih_rc); +} + +/** + * 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 rc; + + IMAGES_ITER(BOOT_CURR_IMG(state)) { + /* 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++) { + if (boot_check_header_valid(state, slot)) { + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = true; + BOOT_LOG_IMAGE_INFO(slot, boot_img_hdr(state, slot)); + } 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_SLOT_PRIMARY) + ? "Primary" : "Secondary"); + } + } + + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = BOOT_SLOT_NONE; + } + + return 0; +} + +/** + * Finds the slot containing the image with the highest version number for the + * current image. + * + * @param state Boot loader status information. + * + * @return BOOT_SLOT_NONE 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 = BOOT_SLOT_NONE; + int rc; + + for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) { + if (state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot]) { + if (candidate_slot == BOOT_SLOT_NONE) { + candidate_slot = slot; + } else { + rc = boot_compare_version( + &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; + + IMAGES_ITER(BOOT_CURR_IMG(state)) { + 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_SLOT_PRIMARY) ? + "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 = NULL; + int rc; + uint32_t active_slot; + struct boot_swap_state* active_swap_state; + + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + + fap = BOOT_IMG_AREA(state, active_slot); + assert(fap != NULL); + + 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) { + /* Image was not selected for test. Skip slot. */ + return -1; + } + + if (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_SLOT_PRIMARY) ? "primary" : "secondary"); + rc = boot_scramble_region(fap, 0, flash_area_get_size(fap), false); + assert(rc == 0); + 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_SLOT_PRIMARY) ? + "primary" : "secondary"); + rc = 0; + } + } + } + + return rc; +} +#endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_DIRECT_XIP_REVERT */ + +/** + * Tries to load and validate a single slot. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static fih_ret +boot_load_and_validate_current_image(struct boot_loader_state *state) +{ + int rc; + fih_ret fih_rc; + uint32_t active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + + if (active_slot == BOOT_SLOT_NONE) { + FIH_RET(FIH_FAILURE); + } + + BOOT_LOG_INF("Loading image %d from slot %d", BOOT_CURR_IMG(state), active_slot); + +#ifdef MCUBOOT_DIRECT_XIP + rc = boot_rom_address_check(state); + if (rc != 0) { + FIH_RET(FIH_FAILURE); + } +#endif /* MCUBOOT_DIRECT_XIP */ + +#if defined(MCUBOOT_DIRECT_XIP_REVERT) + /* The manifest binds images together. The act of validating the manifest + * image implies that the other images are also validated. + * Skip this step and ignore those flags for other images, so a sudden power + * loss after confirming some of the images does not result in partially + * confirmed state. + */ + if (BOOT_CURR_IMG(state) == MCUBOOT_MANIFEST_IMAGE_NUMBER) { + rc = boot_select_or_erase(state); + if (rc != 0) { + FIH_RET(FIH_FAILURE); + } + } +#endif /* MCUBOOT_DIRECT_XIP_REVERT */ + + FIH_CALL(boot_validate_slot, fih_rc, state, active_slot, NULL, 0); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + /* Image is invalid. */ + FIH_RET(FIH_FAILURE); + } + + FIH_RET(FIH_SUCCESS); +} + +/** + * 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; + + while (true) { +#if (BOOT_IMAGE_NUMBER > 1) + BOOT_CURR_IMG(state) = MCUBOOT_MANIFEST_IMAGE_NUMBER; +#endif + rc = BOOT_HOOK_FIND_SLOT_CALL(boot_find_next_slot_hook, BOOT_HOOK_REGULAR, + state, BOOT_CURR_IMG(state), &active_slot); + if (rc == BOOT_HOOK_REGULAR) { + active_slot = find_slot_with_highest_version(state); + } + if (active_slot == BOOT_SLOT_NONE) { + BOOT_LOG_ERR("No more manifest slots available"); + FIH_RET(FIH_FAILURE); + } + + /* Save the number of the active manifest slot. */ + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = active_slot; + + FIH_CALL(boot_load_and_validate_current_image, fih_rc, state); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + state->slot_usage[MCUBOOT_MANIFEST_IMAGE_NUMBER].slot_available[active_slot] = false; + state->slot_usage[MCUBOOT_MANIFEST_IMAGE_NUMBER].active_slot = BOOT_SLOT_NONE; + BOOT_LOG_INF("No valid manifest in slot %d", active_slot); + continue; + } + + BOOT_LOG_INF("Try to validate images using manifest in slot %d", active_slot); + +#if BOOT_IMAGE_NUMBER > 1 + /* Go over all other images and try to load one */ + IMAGES_ITER(BOOT_CURR_IMG(state)) { + /* Skip the image with manifest - it's been already verified. */ + if (BOOT_CURR_IMG(state) == MCUBOOT_MANIFEST_IMAGE_NUMBER) { + continue; + } + + /* Check if there is a matching slot available. */ + if (!state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot]) { + /* Invalidate manifest */ + FIH_SET(fih_rc, FIH_FAILURE); + break; + } + + /* Save the number of the active slot. */ + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = active_slot; + + FIH_CALL(boot_load_and_validate_current_image, fih_rc, state); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + state->slot_usage[BOOT_CURR_IMG(state)].slot_available[active_slot] = false; + state->slot_usage[BOOT_CURR_IMG(state)].active_slot = BOOT_SLOT_NONE; + /* Invalidate manifest */ + break; + } + } +#endif + + if (FIH_EQ(fih_rc, FIH_SUCCESS)) { + /* All images have been loaded and validated successfully. */ + break; + } + + BOOT_LOG_DBG("Manifest in slot %d is invalid", active_slot); + + /* Invalidate manifest */ + state->slot_usage[MCUBOOT_MANIFEST_IMAGE_NUMBER].slot_available[active_slot] = false; + state->slot_usage[MCUBOOT_MANIFEST_IMAGE_NUMBER].active_slot = BOOT_SLOT_NONE; + } + + 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 or RAM load 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(state, + state->slot_usage[BOOT_CURR_IMG(state)].active_slot, + 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_open_all_flash_areas(state); + if (rc != 0) { + goto out; + } + + rc = boot_get_slot_usage(state); + if (rc != 0) { + goto close; + } + + 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 close; + } + + IMAGES_ITER(BOOT_CURR_IMG(state)) { + rc = boot_update_hw_rollback_protection(state); + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + goto close; + } + + 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 close; + } + } + + /* All image loaded successfully. */ +#ifdef MCUBOOT_HAVE_LOGGING + print_loaded_images(state); +#endif + + fill_rsp(state, rsp); + +close: + boot_close_all_flash_areas(state); + +out: + if (rc != 0) { + FIH_SET(fih_rc, FIH_FAILURE); + } + + FIH_RET(fih_rc); +} + +/** + * 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); +} + +#endif /* MCUBOOT_MANIFEST_UPDATES && MCUBOOT_DIRECT_XIP */ diff --git a/boot/bootutil/src/ram_load.c b/boot/bootutil/src/ram_load.c index cfbee60ca..692db0b34 100644 --- a/boot/bootutil/src/ram_load.c +++ b/boot/bootutil/src/ram_load.c @@ -155,7 +155,7 @@ boot_decrypt_and_copy_image_to_sram(struct boot_loader_state *state, } /* if rc > 0 then the key has already been loaded */ - if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), slot, &bs)) { + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC_SLOT(state, slot), bs.enckey[slot])) { goto done; } @@ -176,7 +176,7 @@ boot_decrypt_and_copy_image_to_sram(struct boot_loader_state *state, * Part of the chunk is encrypted payload */ blk_sz = tlv_off - (bytes_copied); } - boot_enc_decrypt(BOOT_CURR_ENC(state), slot, + boot_enc_decrypt(BOOT_CURR_ENC_SLOT(state, slot), (bytes_copied + idx) - hdr->ih_hdr_size, blk_sz, blk_off, cur_dst); bytes_copied += chunk_sz; diff --git a/boot/bootutil/src/swap_misc.c b/boot/bootutil/src/swap_misc.c index 99aac2b02..c00f281a7 100644 --- a/boot/bootutil/src/swap_misc.c +++ b/boot/bootutil/src/swap_misc.c @@ -2,6 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * * Copyright (c) 2019 JUUL Labs + * Copyright (c) 2025 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. @@ -136,12 +137,15 @@ swap_status_init(const struct boot_loader_state *state, rc = boot_write_swap_size(fap, bs->swap_size); assert(rc == 0); -#ifdef MCUBOOT_ENC_IMAGES - rc = boot_write_enc_key(fap, 0, bs); +#ifdef MCUBOOT_SWAP_USING_OFFSET + rc = boot_write_unprotected_tlv_sizes(fap, + BOOT_IMG_UNPROTECTED_TLV_SIZE(state, BOOT_SLOT_PRIMARY), + BOOT_IMG_UNPROTECTED_TLV_SIZE(state, BOOT_SLOT_SECONDARY)); assert(rc == 0); +#endif - rc = boot_write_enc_key(fap, 1, bs); - assert(rc == 0); +#ifdef MCUBOOT_ENC_IMAGES + rc = boot_write_enc_keys(fap, bs); #endif rc = boot_write_magic(fap); @@ -247,5 +251,4 @@ swap_set_image_ok(uint8_t image_index) return rc; } - #endif /* defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_OFFSET) */ diff --git a/boot/bootutil/src/swap_offset.c b/boot/bootutil/src/swap_offset.c index 27862ea00..01cb29f93 100644 --- a/boot/bootutil/src/swap_offset.c +++ b/boot/bootutil/src/swap_offset.c @@ -615,12 +615,20 @@ void swap_run(struct boot_loader_state *state, struct boot_status *bs, const struct flash_area *fap_pri = NULL; const struct flash_area *fap_sec = NULL; int rc; + uint16_t unprotected_tlv_size_pri; + uint16_t unprotected_tlv_size_sec; BOOT_LOG_INF("Starting swap using offset algorithm."); last_idx = find_last_idx(state, copy_size); sector_sz = boot_img_sector_size(state, BOOT_SLOT_PRIMARY, 0); + fap_pri = BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY); + assert(fap_pri != NULL); + + fap_sec = BOOT_IMG_AREA(state, BOOT_SLOT_SECONDARY); + assert(fap_sec != NULL); + /* When starting a new swap upgrade, check that there is enough space */ if (boot_status_is_reset(bs)) { sz = 0; @@ -645,12 +653,6 @@ void swap_run(struct boot_loader_state *state, struct boot_status *bs, } } - fap_pri = BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY); - assert(fap_pri != NULL); - - fap_sec = BOOT_IMG_AREA(state, BOOT_SLOT_SECONDARY); - assert(fap_sec != NULL); - fixup_revert(state, bs, fap_sec); /* Init areas for storing swap status */ @@ -669,14 +671,24 @@ void swap_run(struct boot_loader_state *state, struct boot_status *bs, assert(rc == 0); } + /* Read the unprotected TLV sizes from the boot swap status area, this information might get + * jangled if rebooted during an update so it needs to be stored in this area for safe + * retrieval + */ + boot_read_unprotected_tlv_sizes(fap_pri, &unprotected_tlv_size_pri, &unprotected_tlv_size_sec); + BOOT_LOG_DBG("Unprotected TLV sizes image=%d: pri=%d, sec=%d", BOOT_CURR_IMG(state), + unprotected_tlv_size_pri, unprotected_tlv_size_sec); + bs->op = BOOT_STATUS_OP_SWAP; idx = 0; used_sectors_pri = ((state->imgs[BOOT_CURR_IMG(state)][BOOT_SLOT_PRIMARY].hdr.ih_hdr_size + + unprotected_tlv_size_pri + state->imgs[BOOT_CURR_IMG(state)][BOOT_SLOT_PRIMARY].hdr.ih_protect_tlv_size + state->imgs[BOOT_CURR_IMG(state)][BOOT_SLOT_PRIMARY].hdr.ih_img_size) + sector_sz - 1) / sector_sz; used_sectors_sec = ((state->imgs[BOOT_CURR_IMG(state)][BOOT_SLOT_SECONDARY].hdr.ih_hdr_size + state->imgs[BOOT_CURR_IMG(state)][BOOT_SLOT_SECONDARY].hdr.ih_protect_tlv_size + + unprotected_tlv_size_sec + state->imgs[BOOT_CURR_IMG(state)][BOOT_SLOT_SECONDARY].hdr.ih_img_size) + sector_sz - 1) / sector_sz; @@ -792,6 +804,11 @@ int boot_read_image_size(struct boot_loader_state *state, int slot, uint32_t *si goto done; } + /* This is needed as unprotected TLV size cannot be calculated once a swap has been started, + * it is only able to be properly calculated when images are in pristine states + */ + BOOT_IMG_UNPROTECTED_TLV_SIZE(state, slot) = info.it_tlv_tot; + *size = off + protect_tlv_size + info.it_tlv_tot; rc = 0; diff --git a/boot/bootutil/src/swap_scratch.c b/boot/bootutil/src/swap_scratch.c index 35fc8bf0c..2b0b20cf4 100644 --- a/boot/bootutil/src/swap_scratch.c +++ b/boot/bootutil/src/swap_scratch.c @@ -906,11 +906,7 @@ boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state, assert(rc == 0); #ifdef MCUBOOT_ENC_IMAGES - rc = boot_write_enc_key(fap_primary_slot, 0, bs); - assert(rc == 0); - - rc = boot_write_enc_key(fap_primary_slot, 1, bs); - assert(rc == 0); + rc = boot_write_enc_keys(fap_primary_slot, bs); #endif rc = boot_write_magic(fap_primary_slot); assert(rc == 0); diff --git a/boot/espressif/CMakeLists.txt b/boot/espressif/CMakeLists.txt index 0fa2759ea..d7dbd7284 100644 --- a/boot/espressif/CMakeLists.txt +++ b/boot/espressif/CMakeLists.txt @@ -240,6 +240,8 @@ set(bootutil_srcs ${BOOTUTIL_DIR}/src/bootutil_img_hash.c ${BOOTUTIL_DIR}/src/bootutil_img_security_cnt.c ${BOOTUTIL_DIR}/src/bootutil_misc.c + ${BOOTUTIL_DIR}/src/bootutil_area.c + ${BOOTUTIL_DIR}/src/bootutil_loader.c ${BOOTUTIL_DIR}/src/bootutil_public.c ${BOOTUTIL_DIR}/src/caps.c ${BOOTUTIL_DIR}/src/encrypted.c diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index b34824def..1b10d65f9 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -120,6 +120,8 @@ zephyr_library_sources( ${BOOT_DIR}/bootutil/src/image_ecdsa.c ${BOOT_DIR}/bootutil/src/image_ed25519.c ${BOOT_DIR}/bootutil/src/bootutil_misc.c + ${BOOT_DIR}/bootutil/src/bootutil_area.c + ${BOOT_DIR}/bootutil/src/bootutil_loader.c ${BOOT_DIR}/bootutil/src/fault_injection_hardening.c ) @@ -172,6 +174,26 @@ elseif(CONFIG_BOOT_FIRMWARE_LOADER) ) endif() zephyr_library_include_directories(${BOOT_DIR}/bootutil/src) +elseif(CONFIG_MCUBOOT_MANIFEST_UPDATES) + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/loader_manifest_xip.c + ${BOOT_DIR}/bootutil/src/swap_misc.c + ${BOOT_DIR}/bootutil/src/caps.c + ) + + if(CONFIG_BOOT_SWAP_USING_MOVE) + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/swap_move.c + ) + elseif(CONFIG_BOOT_SWAP_USING_OFFSET) + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/swap_offset.c + ) + else() + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/swap_scratch.c + ) + endif() else() zephyr_library_sources( ${BOOT_DIR}/bootutil/src/loader.c diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index 222a724a5..6cd1fbb08 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -1156,6 +1156,31 @@ config MCUBOOT_HW_DOWNGRADE_PREVENTION_COUNTER_LIMITED endchoice +config MCUBOOT_MANIFEST_UPDATES + bool "Enable transactional updates" + select EXPERIMENTAL + help + If y, enables support for transactional updates using manifests. + This allows multiple images to be updated atomically. The manifest + is a separate TLV which contains a list of images to update and + their expected hash values. The manifest TLV is a part of an image + that is signed to prevent tampering. + The manifest must be transferred as part of the image with index 0. + It can be a dedicated image, or part of an existing image. + If the second option is selected, all updates must contain an update + for image 0. + +if MCUBOOT_MANIFEST_UPDATES + +config MCUBOOT_MANIFEST_IMAGE_NUMBER + int "Number of image that must include manifest" + default 0 + range 0 UPDATEABLE_IMAGE_NUMBER + help + Specifies the index of the image that must include the manifest. + +endif # MCUBOOT_MANIFEST_UPDATES + config MCUBOOT_UUID_VID bool "Expect vendor unique identifier in image's TLV" help @@ -1402,17 +1427,28 @@ config USB_DEVICE_PRODUCT config MCUBOOT_BOOTUTIL_LIB_OWN_LOG bool +config MCUBOOT_CHECK_HEADER_LOAD_ADDRESS + bool "Use load address to verify application is in proper slot" + help + The bootloader will use the load address, from the image header, + to verify that binary is in slot designated for its execution. + When not selected reset vector, read from image, is used for + the same purpose. + config NRF_MCUBOOT_BOOT_REQUEST bool imply FIND_NEXT_SLOT_HOOKS if BOOT_DIRECT_XIP || BOOT_RAM_LOAD config MCUBOOT_VERIFY_IMG_ADDRESS - bool "Verify reset address of image in secondary slot" + bool "Verify reset address of image in secondary slot [DEPRECATED]" + select DEPRECATED depends on UPDATEABLE_IMAGE_NUMBER > 1 depends on !BOOT_ENCRYPT_IMAGE depends on ARM default y if BOOT_UPGRADE_ONLY help + This option is deprecated, please use MCUBOOT_CHECK_HEADER_LOAD_ADDRESS + instead. Verify that the reset address in the image located in the secondary slot is contained within the corresponding primary slot. This is recommended if swapping is not used (that is, BOOT_UPGRADE_ONLY is set). If a user diff --git a/boot/zephyr/decompression.c b/boot/zephyr/decompression.c index c8b279a75..5b161feb1 100644 --- a/boot/zephyr/decompression.c +++ b/boot/zephyr/decompression.c @@ -115,12 +115,12 @@ bool boot_is_compressed_header_valid(const struct image_header *hdr, const struc static bool is_compression_object_valid(struct nrf_compress_implementation *compression) { - if (compression == NULL || compression->init == NULL || compression->deinit == NULL || - compression->decompress_bytes_needed == NULL || compression->decompress == NULL) { - return false; - } + if (compression == NULL || compression->init == NULL || compression->deinit == NULL || + compression->decompress_bytes_needed == NULL || compression->decompress == NULL) { + return false; + } - return true; + return true; } #ifdef MCUBOOT_ENC_IMAGES @@ -206,13 +206,12 @@ int bootutil_img_hash_decompress(struct boot_loader_state *state, struct image_h enc_state = NULL; image_index = 0; } else { - enc_state = BOOT_CURR_ENC(state); + enc_state = BOOT_CURR_ENC_SLOT(state, BOOT_SLOT_SECONDARY); image_index = BOOT_CURR_IMG(state); } /* Encrypted images only exist in the secondary slot */ - if (MUST_DECRYPT(fap, image_index, hdr) && - !boot_enc_valid(enc_state, 1)) { + if (MUST_DECRYPT(fap, image_index, hdr) && !boot_enc_valid(enc_state)) { return -1; } @@ -249,7 +248,7 @@ int bootutil_img_hash_decompress(struct boot_loader_state *state, struct image_h compression_arm_thumb = nrf_compress_implementation_find(NRF_COMPRESS_TYPE_ARM_THUMB); if (!is_compression_object_valid(compression_lzma) || - !is_compression_object_valid(compression_arm_thumb)) { + !is_compression_object_valid(compression_arm_thumb)) { /* Compression library missing or missing required function pointer */ BOOT_LOG_ERR("Decompression library fatal error"); rc = BOOT_EBADSTATUS; @@ -359,7 +358,7 @@ int bootutil_img_hash_decompress(struct boot_loader_state *state, struct image_h memset(&tmp_buf[copy_size], 0x00, dummy_bytes); } - boot_enc_decrypt(enc_state, 1, read_pos, (copy_size + dummy_bytes), (read_pos & 0xf), + boot_enc_decrypt(enc_state, read_pos, (copy_size + dummy_bytes), (read_pos & 0xf), tmp_buf); } #endif @@ -389,7 +388,7 @@ int bootutil_img_hash_decompress(struct boot_loader_state *state, struct image_h } rc = compression_lzma->decompress(NULL, &tmp_buf[tmp_off], chunk_size, last_packet, - &offset, &output, &output_size); + &offset, &output, &output_size); if (rc) { BOOT_LOG_ERR("Decompression error: %d", rc); @@ -1138,7 +1137,7 @@ int boot_copy_region_decompress(struct boot_loader_state *state, const struct fl compression_arm_thumb = nrf_compress_implementation_find(NRF_COMPRESS_TYPE_ARM_THUMB); if (!is_compression_object_valid(compression_lzma) || - !is_compression_object_valid(compression_arm_thumb)) { + !is_compression_object_valid(compression_arm_thumb)) { /* Compression library missing or missing required function pointer */ BOOT_LOG_ERR("Decompression library fatal error"); rc = BOOT_EBADSTATUS; @@ -1239,7 +1238,8 @@ int boot_copy_region_decompress(struct boot_loader_state *state, const struct fl memset(&buf[copy_size], 0x00, dummy_bytes); } - boot_enc_decrypt(BOOT_CURR_ENC(state), 1, pos, (copy_size + dummy_bytes), (pos & 0xf), buf); + boot_enc_decrypt(BOOT_CURR_ENC_SLOT(state, BOOT_SLOT_SECONDARY), pos, + (copy_size + dummy_bytes), (pos & 0xf), buf); } #endif diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h index 2d759217b..c7e380937 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -239,6 +239,14 @@ #define MCUBOOT_HW_ROLLBACK_PROT_COUNTER_LIMITED #endif +#ifdef CONFIG_MCUBOOT_MANIFEST_UPDATES +#define MCUBOOT_MANIFEST_UPDATES + +#ifdef CONFIG_MCUBOOT_MANIFEST_IMAGE_NUMBER +#define MCUBOOT_MANIFEST_IMAGE_NUMBER CONFIG_MCUBOOT_MANIFEST_IMAGE_NUMBER +#endif /* CONFIG_MCUBOOT_MANIFEST_IMAGE_NUMBER */ +#endif /* CONFIG_MCUBOOT_MANIFEST_UPDATES */ + #ifdef CONFIG_MCUBOOT_UUID_VID #define MCUBOOT_UUID_VID #endif @@ -309,6 +317,10 @@ #define MCUBOOT_FIND_NEXT_SLOT_HOOKS #endif +#ifdef CONFIG_MCUBOOT_CHECK_HEADER_LOAD_ADDRESS +#define MCUBOOT_CHECK_HEADER_LOAD_ADDRESS +#endif + #ifdef CONFIG_MCUBOOT_VERIFY_IMG_ADDRESS #define MCUBOOT_VERIFY_IMG_ADDRESS #endif diff --git a/docs/design.md b/docs/design.md index 595c6a401..d278dbeaf 100755 --- a/docs/design.md +++ b/docs/design.md @@ -4,6 +4,7 @@ - Copyright (c) 2017-2020 Linaro LTD - Copyright (c) 2017-2019 JUUL Labs - Copyright (c) 2019-2024 Arm Limited + - Copyright (c) 2025 Nordic Semiconductor ASA - Original license: @@ -150,8 +151,9 @@ struct image_tlv { * ... * 0xffa0 - 0xfffe */ -#define IMAGE_TLV_UUID_VID 0x80 /* Vendor unique identifier */ -#define IMAGE_TLV_UUID_CID 0x81 /* Device class unique identifier */ +#define IMAGE_TLV_UUID_VID 0x74 /* Vendor unique identifier */ +#define IMAGE_TLV_UUID_CID 0x75 /* Device class unique identifier */ +#define IMAGE_TLV_MANIFEST 0x76 /* Transaction manifest */ ``` Optional type-length-value records (TLVs) containing image metadata are placed @@ -571,6 +573,12 @@ image trailer. An image trailer has the following structure: | 0xff padding as needed | | (BOOT_MAX_ALIGN minus 4 octets from Swap size) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unprotected TLV size [*2] | Unprotected TLV size [*2] | + | Secondary slot (2 octets) | Primary slot (2 octets) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0xff padding as needed [*2] | + | (BOOT_MAX_ALIGN minus 4 octets from unprotected TLV sizes) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Swap info | 0xff padding (BOOT_MAX_ALIGN minus 1 octet) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Copy done | 0xff padding (BOOT_MAX_ALIGN minus 1 octet) | @@ -586,6 +594,7 @@ image trailer. An image trailer has the following structure: ``` [*]: Only present if the encryption option is enabled (`MCUBOOT_ENC_IMAGES`). +[*2]: Only present if swap using offset mode is used (`MCUBOOT_SWAP_USING_OFFSET`). The offset immediately following such a record represents the start of the next flash area. @@ -979,6 +988,123 @@ strategy but there is no need for Scratch area. + Boot the loaded slot of image 0. +### [Multiple image boot using manifest](#multiple-image-boot-using-manifest) + + +Deployments that use multiple images typically require strict control over +versions of firmware components. +There is a dependency TLV that can be used to specify dependencies between +semantic versions of multiple components. However, since these only describe +a minimally compatible version of a counterpart component, there is no mechanism +to enforce a specific revision of the other image. +Therefore, the publisher must ensure that all combinations that satisfy the +dependencies are compatible with each other and are tested before deploying +a new version of the firmware bundle. +One way to simplify the process is to add dependencies to all parts that +enforce the latest revision of all other parts of the bundle. +This effectively enforces equality between the revisions of all parts. +This solution becomes even more problematic when using Direct-XIP mode - since +the bootloader typically only chain-loads the next stage, the next stage must +select and boot the correct slot of the next part of the firmware. +Although the dependencies ensure that the next part with a specific +version is present on the device, there is no guarantee in which slot it +was validated. +Again, the publisher may use different version numbers for the same firmware +created for different slots, but this raises the question of whether there is +a better way to manage dependencies between images. + +The bootloader manifest is an additional, protected TLV that serves as the sole +source of information about the compatibility and bootability of the multipart +firmware. The basic rules for all types of manifests are: + + * The manifest must be protected (directly or indirectly) by a cryptographic + signature. + * There must be exactly one selected image that can provide the manifest TLV. + * There must be only one manifest per slot. + * Processing of the manifest must validate the full functional set of firmware + components. + * If the manifest does not describe a part of the firmware, it must be + considered invalid. + * Each update candidate must provide a new manifest. + + +``` + +-------------------+ + +----------------->| Mainfest | + | +===================+ + | |+-----------------+| + | || format || + | |+-----------------+| + | || image count || + | |+-----------------+| + | +------------|| digest(Image 1) || + | | |+-----------------+| + | | || digest(Image 2) ||-------------+ + | | |+-----------------+| | + | | +-------------------+ | + | | | + +-----------------+ | | +-----------------+ +-----------------+ | + | Manifest image | | | | Image 1 | | Image 2 | | + +=================+ | | +=================+ +=================+ | + |+---------------+| | | |+---------------+| |+---------------+| | + || header || | | || header || || header || | + |+---------------+| | | |+---------------+| |+---------------+| | + || manifest TLV ||-+ | || firmware || || firmware || | + |+---------------+| | |+---------------+| |+---------------+| | + || digest TLV || +->|| digest TLV || || digest TLV ||<-+ + |+---------------+| |+---------------+| |+---------------+| + || signature TLV || || signature TLV || || signature TLV || + |+---------------+| |+---------------+| |+---------------+| + +-----------------+ +-----------------+ +-----------------+ +``` + +The manifest TLV has a format field that allows for the development of complex +boot logic in the future. The default manifest is structured as a list of +digests of firmware parts. + +The manifest is transferred as a protected TLV in a dedicated "Manifest image." +This image is updated using the same mechanisms as regular images. + +The manifest image may contain firmware - if so, this part of the firmware must +be updated with every firmware update. + +The bootloader's behavior changes once manifest-based updates +and booting are enabled. + +Boot process for Direct-XIP modes: + ++ Loop 1. Until all images are loaded and validated against the active manifest + 1. Subloop 1. Iterate over the manifest image slots + + Does any of the slots contain a manifest? + + Yes: + + Select the newer manifest. + + Copy it to the bootloader state. + + Validate the manifest image (integrity and security check). + + If validation fails mark the active manifest slot as + unavailable and try the other slot. + + No: Return with an error. + + 2. Subloop 2. Iterate over all images except manifest image + + Does the current image contain a valid header in the same slot + as the selected (active) manifest? + + Yes: Is the image valid (integrity and security check) and its + digest matches the manifest? + + Yes: Skip to the next image. + + No: + + Mark the active manifest slot as unavailable. + + Restart main loop. + + No: + + Mark the active manifest slot as unavailable. + + Restart main loop. + ++ Loop 2. Iterate over all images + + Increase the security counter if needed. + + Do the measured boot and the data sharing if needed. + ++ Boot the loaded slot of image 0. + +Manifest-based updates and booting for other modes are not yet implemented. + ## [Image swapping](#image-swapping) The bootloader swaps the contents of the two image slots for two reasons: diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py index 017307bab..4476b687e 100755 --- a/scripts/imgtool/image.py +++ b/scripts/imgtool/image.py @@ -39,6 +39,7 @@ from cryptography.hazmat.primitives.kdf.hkdf import HKDF from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat from intelhex import IntelHex +from yaml import safe_load as yaml_safe_load from . import keys from . import version as versmod @@ -93,6 +94,7 @@ 'COMP_DEC_SIZE' : 0x73, 'UUID_VID': 0x74, 'UUID_CID': 0x75, + 'MANIFEST': 0x76, } TLV_SIZE = 4 @@ -271,6 +273,73 @@ def parse_uuid(namespace, value): return uuid_bytes +class Manifest: + def __init__(self, endian, path): + self.path = path + self.format = 1 + self.data = None + self.config = None + self.endian = endian + self.load() + + def load(self): + try: + with open(self.path) as f: + self.config = yaml_safe_load(f) + format = self.config.get('format', 0) + if isinstance(format, str) and format.isdigit(): + format = int(format) + if format != self.format: + raise click.UsageError(f"Unsupported manifest format: {format}") + + # Encode manifest format + e = STRUCT_ENDIAN_DICT[self.endian] + self.data = struct.pack(e + 'I', format) + + # Encode number of images/hashes + n_images = len(self.config.get('images', [])) + self.data += struct.pack(e + 'I', n_images) + + # Encode each image hash + exp_hash_len = None + for image in self.config.get('images', []): + if 'path' not in image and 'hash' not in image: + raise click.UsageError( + "Manifest image entry must contain either 'path' or 'hash'") + + # Encode hash, based on the signed image path + if 'path' in image: + (result, version, digest, _) = Image.verify(image['path'], None) + if result != VerifyResult.OK: + raise click.UsageError(f"Failed to verify image: {image['path']}") + + if exp_hash_len is None: + exp_hash_len = len(digest) + elif exp_hash_len != len(digest): + raise click.UsageError("All image hashes must have the same length") + self.data += struct.pack(e + f'{exp_hash_len}s', digest) + + # Encode RAW image hash + if 'hash' in image: + if exp_hash_len is None: + exp_hash_len = len(bytes.fromhex(image['hash'])) + elif exp_hash_len != len(bytes.fromhex(image['hash'])): + raise click.UsageError("All image hashes must have the same length") + self.data += struct.pack(e + f'{exp_hash_len}s', bytes.fromhex(image['hash'])) + + except FileNotFoundError: + raise click.UsageError(f"Manifest file {self.path} not found") from None + + def encode(self): + if self.data is None: + raise click.UsageError("Manifest data is empty") + return self.data + + def __len__(self): + return len(self.data) if self.data is not None else 0 + + def __repr__(self): + return f"" class Image: @@ -280,7 +349,7 @@ def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE, overwrite_only=False, endian="little", load_addr=0, rom_fixed=None, erased_val=None, save_enctlv=False, security_counter=None, max_align=None, - non_bootable=False, vid=None, cid=None): + non_bootable=False, vid=None, cid=None, manifest=None): if load_addr and rom_fixed: raise click.UsageError("Can not set rom_fixed and load_addr at the same time") @@ -311,6 +380,7 @@ def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE, self.non_bootable = non_bootable self.vid = vid self.cid = cid + self.manifest = Manifest(endian=endian, path=manifest) if manifest is not None else None if self.max_align == DEFAULT_MAX_ALIGN: self.boot_magic = bytes([ @@ -340,7 +410,7 @@ def __repr__(self): return "".format( + payloadlen=0x{:x}, vid={}, cid={}, manifest={}>".format( self.version, self.header_size, self.security_counter, @@ -354,7 +424,8 @@ def __repr__(self): self.__class__.__name__, len(self.payload), self.vid, - self.cid) + self.cid, + self.manifest) def load(self, path): """Load an image from a given file""" @@ -539,6 +610,11 @@ def create(self, key, public_key_format, enckey, dependencies=None, # = 4 + 16 = 20 Bytes protected_tlv_size += TLV_SIZE + 16 + if self.manifest is not None: + # Size of the MANIFEST TLV: header ('HH') + payload (len(manifest)) + # = 4 + len(manifest) Bytes + protected_tlv_size += TLV_SIZE + len(self.manifest.encode()) + if sw_type is not None: if len(sw_type) > MAX_SW_TYPE_LENGTH: msg = f"'{sw_type}' is too long ({len(sw_type)} characters) for sw_type. Its " \ @@ -654,6 +730,10 @@ def create(self, key, public_key_format, enckey, dependencies=None, payload = struct.pack(e + '16s', cid) prot_tlv.add('UUID_CID', payload) + if self.manifest is not None: + payload = self.manifest.encode() + prot_tlv.add('MANIFEST', payload) + if custom_tlvs is not None: for tag, value in custom_tlvs.items(): prot_tlv.add(tag, value) diff --git a/scripts/imgtool/main.py b/scripts/imgtool/main.py index c4abf06f1..a7567bad4 100755 --- a/scripts/imgtool/main.py +++ b/scripts/imgtool/main.py @@ -449,13 +449,15 @@ def convert(self, value, param, ctx): help='Unique vendor identifier, format: (|') @click.option('--cid', default=None, required=False, help='Unique image class identifier, format: (|)') +@click.option('--manifest', default=None, required=False, + help='Path to the update manifest file') def sign(key, public_key_format, align, version, pad_sig, header_size, pad_header, slot_size, pad, confirm, max_sectors, overwrite_only, endian, encrypt_keylen, encrypt, compression, infile, outfile, dependencies, load_addr, hex_addr, erased_val, save_enctlv, security_counter, boot_record, custom_tlv, rom_fixed, max_align, clear, fix_sig, fix_sig_pubkey, sig_out, user_sha, hmac_sha, is_pure, - vector_to_sign, non_bootable, vid, cid): + vector_to_sign, non_bootable, vid, cid, manifest): if confirm: # Confirmed but non-padded images don't make much sense, because @@ -468,7 +470,8 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, endian=endian, load_addr=load_addr, rom_fixed=rom_fixed, erased_val=erased_val, save_enctlv=save_enctlv, security_counter=security_counter, max_align=max_align, - non_bootable=non_bootable, vid=vid, cid=cid) + non_bootable=non_bootable, vid=vid, cid=cid, + manifest=manifest) compression_tlvs = {} img.load(infile) key = load_key(key) if key else None @@ -539,7 +542,7 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, load_addr=load_addr, rom_fixed=rom_fixed, erased_val=erased_val, save_enctlv=save_enctlv, security_counter=security_counter, max_align=max_align, - vid=vid, cid=cid) + vid=vid, cid=cid, manifest=manifest) compression_filters = [ {"id": lzma.FILTER_LZMA2, "preset": comp_default_preset, "dict_size": comp_default_dictsize, "lp": comp_default_lp, diff --git a/sim/Cargo.toml b/sim/Cargo.toml index 6d2262d90..f7d3505a5 100644 --- a/sim/Cargo.toml +++ b/sim/Cargo.toml @@ -34,6 +34,7 @@ direct-xip = ["mcuboot-sys/direct-xip"] downgrade-prevention = ["mcuboot-sys/downgrade-prevention"] max-align-32 = ["mcuboot-sys/max-align-32"] hw-rollback-protection = ["mcuboot-sys/hw-rollback-protection"] +check-load-addr = ["mcuboot-sys/check-load-addr"] [dependencies] byteorder = "1.4" diff --git a/sim/mcuboot-sys/Cargo.toml b/sim/mcuboot-sys/Cargo.toml index f2eb70634..b3e46082f 100644 --- a/sim/mcuboot-sys/Cargo.toml +++ b/sim/mcuboot-sys/Cargo.toml @@ -99,6 +99,9 @@ hw-rollback-protection = [] # Enable the PSA Crypto APIs where supported for cryptography related operations. psa-crypto-api = [] +# Test for ih_load_addr in upgrade/next boot slot +check-load-addr = [] + [build-dependencies] cc = "1.0.25" diff --git a/sim/mcuboot-sys/build.rs b/sim/mcuboot-sys/build.rs index b2595689b..5276fbeb7 100644 --- a/sim/mcuboot-sys/build.rs +++ b/sim/mcuboot-sys/build.rs @@ -39,6 +39,7 @@ fn main() { let direct_xip = env::var("CARGO_FEATURE_DIRECT_XIP").is_ok(); let max_align_32 = env::var("CARGO_FEATURE_MAX_ALIGN_32").is_ok(); let hw_rollback_protection = env::var("CARGO_FEATURE_HW_ROLLBACK_PROTECTION").is_ok(); + let check_load_addr = env::var("CARGO_FEATURE_CHECK_LOAD_ADDR").is_ok(); let mut conf = CachedBuild::new(); conf.conf.define("__BOOTSIM__", None); @@ -64,6 +65,10 @@ fn main() { conf.conf.define("MCUBOOT_OVERWRITE_ONLY_FAST", None); } + if check_load_addr { + conf.conf.define("MCUBOOT_CHECK_HEADER_LOAD_ADDRESS", None); + } + if validate_primary_slot { conf.conf.define("MCUBOOT_VALIDATE_PRIMARY_SLOT", None); } @@ -487,6 +492,8 @@ fn main() { conf.file("../../boot/bootutil/src/swap_offset.c"); conf.file("../../boot/bootutil/src/caps.c"); conf.file("../../boot/bootutil/src/bootutil_misc.c"); + conf.file("../../boot/bootutil/src/bootutil_area.c"); + conf.file("../../boot/bootutil/src/bootutil_loader.c"); conf.file("../../boot/bootutil/src/bootutil_public.c"); conf.file("../../boot/bootutil/src/tlv.c"); conf.file("../../boot/bootutil/src/fault_injection_hardening.c"); diff --git a/sim/src/image.rs b/sim/src/image.rs index 9703714b3..227617d7d 100644 --- a/sim/src/image.rs +++ b/sim/src/image.rs @@ -297,7 +297,7 @@ impl ImagesBuilder { images } - pub fn make_bad_secondary_slot_image(self) -> Images { + pub fn make_bad_secondary_slot_image(self, img_manipulation : ImageManipulation) -> Images { let mut bad_flash = self.flash; let ram = self.ram.clone(); // TODO: Avoid this clone. let images = self.slots.into_iter().enumerate().map(|(image_num, slots)| { @@ -305,7 +305,7 @@ impl ImagesBuilder { let primaries = install_image(&mut bad_flash, &self.areadesc, &slots, 0, maximal(32784), &ram, &dep, ImageManipulation::None, Some(0)); let upgrades = install_image(&mut bad_flash, &self.areadesc, &slots, 1, - maximal(41928), &ram, &dep, ImageManipulation::BadSignature, Some(0)); + maximal(41928), &ram, &dep, img_manipulation, Some(0)); OneImage { slots, primaries, @@ -899,8 +899,8 @@ impl Images { fails > 0 } - // Test taht too big upgrade image will be rejected - pub fn run_oversizefail_upgrade(&self) -> bool { + // Test expecting failed upgrade and primary slot left untouched + pub fn run_fail_upgrade_primary_intact(&self) -> bool { let mut flash = self.flash.clone(); let mut fails = 0; @@ -940,7 +940,7 @@ impl Images { } if fails > 0 { - error!("Expected an upgrade failure when image has to big size"); + error!("Expected an upgrade failure and primary slot left untouched"); } fails > 0 @@ -1930,7 +1930,21 @@ fn install_image(flash: &mut SimMultiFlash, areadesc: &AreaDesc, slots: &[SlotIn _ => place.offset } } else { - 0 + if cfg!(feature = "check-load-addr") { + let wrong_off = match img_manipulation { + ImageManipulation::WrongOffset => true, + _ => false + }; + if wrong_off { + u32::MAX + } else if cfg!(feature = "direct-xip") { + slots[slot_ind].base_off as u32 + } else { + slots[0].base_off as u32 + } + } else { + 0 + } }; let len = match len { diff --git a/sim/src/lib.rs b/sim/src/lib.rs index 8c648ca63..a496db9db 100644 --- a/sim/src/lib.rs +++ b/sim/src/lib.rs @@ -202,7 +202,7 @@ impl RunStatus { // Creates a badly signed image in the secondary slot to check that // it is not upgraded to - let bad_secondary_slot_image = run.clone().make_bad_secondary_slot_image(); + let bad_secondary_slot_image = run.clone().make_bad_secondary_slot_image(ImageManipulation::BadSignature); failed |= bad_secondary_slot_image.run_signfail_upgrade(); diff --git a/sim/tests/core.rs b/sim/tests/core.rs index 69745ab26..b7be7530d 100644 --- a/sim/tests/core.rs +++ b/sim/tests/core.rs @@ -49,7 +49,7 @@ macro_rules! sim_test { }; } -sim_test!(bad_secondary_slot, make_bad_secondary_slot_image(), run_signfail_upgrade()); +sim_test!(bad_secondary_slot, make_bad_secondary_slot_image(ImageManipulation::BadSignature), run_signfail_upgrade()); sim_test!(secondary_trailer_leftover, make_erased_secondary_image(), run_secondary_leftover_trailer()); sim_test!(bootstrap, make_bootstrap_image(), run_bootstrap()); sim_test!(oversized_bootstrap, make_oversized_bootstrap_image(), run_oversized_bootstrap()); @@ -59,23 +59,33 @@ sim_test!(revert_with_fails, make_image(&NO_DEPS, false), run_revert_with_fails( sim_test!(perm_with_fails, make_image(&NO_DEPS, true), run_perm_with_fails()); sim_test!(perm_with_random_fails, make_image(&NO_DEPS, true), run_perm_with_random_fails(5)); sim_test!(norevert, make_image(&NO_DEPS, true), run_norevert()); -sim_test!(oversized_secondary_slot, make_oversized_secondary_slot_image(), run_oversizefail_upgrade()); +sim_test!(oversized_secondary_slot, make_oversized_secondary_slot_image(), run_fail_upgrade_primary_intact()); +#[cfg(feature = "check-load-addr")] +sim_test!(wrong_load_addr, make_bad_secondary_slot_image(ImageManipulation::WrongOffset), run_fail_upgrade_primary_intact()); sim_test!(status_write_fails_complete, make_image(&NO_DEPS, true), run_with_status_fails_complete()); sim_test!(status_write_fails_with_reset, make_image(&NO_DEPS, true), run_with_status_fails_with_reset()); sim_test!(downgrade_prevention, make_image(&REV_DEPS, true), run_nodowngrade()); sim_test!(direct_xip_first, make_no_upgrade_image(&NO_DEPS, ImageManipulation::None), run_direct_xip()); +#[cfg(not(feature = "check-load-addr"))] sim_test!(ram_load_first, make_no_upgrade_image(&NO_DEPS, ImageManipulation::None), run_ram_load()); +#[cfg(not(feature = "check-load-addr"))] sim_test!(ram_load_split, make_no_upgrade_image(&NO_DEPS, ImageManipulation::None), run_split_ram_load()); +#[cfg(not(feature = "check-load-addr"))] sim_test!(ram_load_from_flash, make_no_upgrade_image(&NO_DEPS, ImageManipulation::None), run_ram_load_from_flash()); -sim_test!(hw_prot_failed_security_cnt_check, make_image_with_security_counter(Some(0)), run_hw_rollback_prot()); -sim_test!(hw_prot_missing_security_cnt, make_image_with_security_counter(None), run_hw_rollback_prot()); +#[cfg(not(feature = "check-load-addr"))] sim_test!(ram_load_out_of_bounds, make_no_upgrade_image(&NO_DEPS, ImageManipulation::WrongOffset), run_ram_load_boot_with_result(false)); +#[cfg(not(feature = "check-load-addr"))] sim_test!(ram_load_missing_header_flag, make_no_upgrade_image(&NO_DEPS, ImageManipulation::IgnoreRamLoadFlag), run_ram_load_boot_with_result(false)); +#[cfg(not(feature = "check-load-addr"))] sim_test!(ram_load_failed_validation, make_no_upgrade_image(&NO_DEPS, ImageManipulation::BadSignature), run_ram_load_boot_with_result(false)); +#[cfg(not(feature = "check-load-addr"))] sim_test!(ram_load_corrupt_higher_version_image, make_no_upgrade_image(&NO_DEPS, ImageManipulation::CorruptHigherVersionImage), run_ram_load_boot_with_result(true)); +sim_test!(hw_prot_missing_security_cnt, make_image_with_security_counter(None), run_hw_rollback_prot()); +sim_test!(hw_prot_failed_security_cnt_check, make_image_with_security_counter(Some(0)), run_hw_rollback_prot()); + #[cfg(feature = "multiimage")] sim_test!(ram_load_overlapping_images_same_base, make_no_upgrade_image(&NO_DEPS, ImageManipulation::OverlapImages(true)), run_ram_load_boot_with_result(false)); #[cfg(feature = "multiimage")]