diff --git a/boot/bootutil/CMakeLists.txt b/boot/bootutil/CMakeLists.txt index 90977064f..3d1b32717 100644 --- a/boot/bootutil/CMakeLists.txt +++ b/boot/bootutil/CMakeLists.txt @@ -33,3 +33,9 @@ target_sources(bootutil src/swap_scratch.c src/tlv.c ) +if(CONFIG_BOOT_RAM_LOAD) + target_sources(bootutil + PRIVATE + src/ram_load.c + ) +endif() diff --git a/boot/bootutil/include/bootutil/boot_status.h b/boot/bootutil/include/bootutil/boot_status.h index ea3de5e89..fddcc4bc2 100644 --- a/boot/bootutil/include/bootutil/boot_status.h +++ b/boot/bootutil/include/bootutil/boot_status.h @@ -128,7 +128,8 @@ enum mcuboot_mode { MCUBOOT_MODE_DIRECT_XIP, MCUBOOT_MODE_DIRECT_XIP_WITH_REVERT, MCUBOOT_MODE_RAM_LOAD, - MCUBOOT_MODE_FIRMWARE_LOADER + MCUBOOT_MODE_FIRMWARE_LOADER, + MCUBOOT_MODE_SINGLE_SLOT_RAM_LOAD, }; enum mcuboot_signature_type { diff --git a/boot/bootutil/pkg.yml b/boot/bootutil/pkg.yml index e91978589..ed6f35810 100644 --- a/boot/bootutil/pkg.yml +++ b/boot/bootutil/pkg.yml @@ -45,6 +45,9 @@ pkg.ign_files.BOOTUTIL_SINGLE_APPLICATION_SLOT: - "loader.c" - "swap_scratch.c" +pkg.ign_files: + - "ram_load.c" + pkg.deps.BOOTUTIL_USE_MBED_TLS: - "@apache-mynewt-core/crypto/mbedtls" diff --git a/boot/bootutil/src/boot_record.c b/boot/bootutil/src/boot_record.c index ccd9e6e32..a434c9e50 100644 --- a/boot/bootutil/src/boot_record.c +++ b/boot/bootutil/src/boot_record.c @@ -249,6 +249,8 @@ int boot_save_shared_data(const struct image_header *hdr, const struct flash_are #else uint8_t mode = MCUBOOT_MODE_DIRECT_XIP; #endif +#elif defined(MCUBOOT_SINGLE_APPLICATION_SLOT_RAM_LOAD) + uint8_t mode = MCUBOOT_MODE_SINGLE_SLOT_RAM_LOAD; #elif defined(MCUBOOT_RAM_LOAD) uint8_t mode = MCUBOOT_MODE_RAM_LOAD; #elif defined(MCUBOOT_FIRMWARE_LOADER) diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c index 46202135c..56859d515 100644 --- a/boot/bootutil/src/bootutil_misc.c +++ b/boot/bootutil/src/bootutil_misc.c @@ -334,7 +334,7 @@ boot_write_enc_key(const struct flash_area *fap, uint8_t slot, uint32_t bootutil_max_image_size(const struct flash_area *fap) { #if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SINGLE_APPLICATION_SLOT) || \ - defined(MCUBOOT_FIRMWARE_LOADER) + defined(MCUBOOT_FIRMWARE_LOADER) || defined(MCUBOOT_SINGLE_APPLICATION_SLOT_RAM_LOAD) return boot_status_off(fap); #elif defined(MCUBOOT_SWAP_USING_MOVE) struct flash_sector sector; @@ -355,3 +355,68 @@ uint32_t bootutil_max_image_size(const struct flash_area *fap) return boot_swap_info_off(fap); #endif } + +/* + * Compute the total size of the given image. Includes the size of + * the TLVs. + */ +#if !defined(MCUBOOT_DIRECT_XIP) && \ + (!defined(MCUBOOT_OVERWRITE_ONLY) || \ + defined(MCUBOOT_OVERWRITE_ONLY_FAST)) +int +boot_read_image_size(struct boot_loader_state *state, int slot, uint32_t *size) +{ + const struct flash_area *fap; + struct image_tlv_info info; + uint32_t off; + uint32_t protect_tlv_size; + int area_id; + int rc; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc != 0) { + rc = BOOT_EFLASH; + goto done; + } + + off = BOOT_TLV_OFF(boot_img_hdr(state, slot)); + + if (flash_area_read(fap, off, &info, sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + + protect_tlv_size = boot_img_hdr(state, slot)->ih_protect_tlv_size; + if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) { + if (protect_tlv_size != info.it_tlv_tot) { + rc = BOOT_EBADIMAGE; + goto done; + } + + if (flash_area_read(fap, off + info.it_tlv_tot, &info, sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + } else if (protect_tlv_size != 0) { + rc = BOOT_EBADIMAGE; + goto done; + } + + if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { + rc = BOOT_EBADIMAGE; + goto done; + } + + *size = off + protect_tlv_size + info.it_tlv_tot; + rc = 0; + +done: + flash_area_close(fap); + return rc; +} +#endif /* !MCUBOOT_OVERWRITE_ONLY */ diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h index 208d189b9..345933a5f 100644 --- a/boot/bootutil/src/bootutil_priv.h +++ b/boot/bootutil/src/bootutil_priv.h @@ -51,6 +51,8 @@ struct flash_area; #define BOOT_TMPBUF_SZ 256 +#define NO_ACTIVE_SLOT UINT32_MAX + /** Number of image slots in flash; currently limited to two. */ #define BOOT_NUM_SLOTS 2 @@ -467,15 +469,24 @@ struct bootsim_ram_info *bootsim_get_ram_info(void); #define LOAD_IMAGE_DATA(hdr, fap, start, output, size) \ (memcpy((output),(void*)(IMAGE_RAM_BASE + (hdr)->ih_load_addr + (start)), \ (size)), 0) + +int boot_load_image_to_sram(struct boot_loader_state *state); +int boot_remove_image_from_sram(struct boot_loader_state *state); +int boot_remove_image_from_flash(struct boot_loader_state *state, + uint32_t slot); #else #define IMAGE_RAM_BASE ((uintptr_t)0) #define LOAD_IMAGE_DATA(hdr, fap, start, output, size) \ (flash_area_read((fap), (start), (output), (size))) + #endif /* MCUBOOT_RAM_LOAD */ uint32_t bootutil_max_image_size(const struct flash_area *fap); +int boot_read_image_size(struct boot_loader_state *state, int slot, + uint32_t *size); + #ifdef __cplusplus } #endif diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c index 3f0793388..6b019a2f3 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -105,8 +105,6 @@ struct sector_buffer_t { #define BUF_SZ 1024 #endif -#define NO_ACTIVE_SLOT UINT32_MAX - static int boot_read_image_headers(struct boot_loader_state *state, bool require_all, struct boot_status *bs) @@ -412,11 +410,6 @@ boot_verify_dependencies(struct boot_loader_state *state) } #else -#if defined MCUBOOT_RAM_LOAD -static inline int -boot_remove_image_from_sram(struct boot_loader_state *state); -#endif - /** * Checks the dependency of all the active slots. If an image found with * invalid or not satisfied dependencies the image is removed from SRAM (in @@ -530,68 +523,6 @@ boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot) #endif /* (BOOT_IMAGE_NUMBER > 1) */ #if !defined(MCUBOOT_DIRECT_XIP) -/* - * Compute the total size of the given image. Includes the size of - * the TLVs. - */ -#if !defined(MCUBOOT_OVERWRITE_ONLY) || defined(MCUBOOT_OVERWRITE_ONLY_FAST) -static int -boot_read_image_size(struct boot_loader_state *state, int slot, uint32_t *size) -{ - const struct flash_area *fap; - struct image_tlv_info info; - uint32_t off; - uint32_t protect_tlv_size; - int area_id; - int rc; - -#if (BOOT_IMAGE_NUMBER == 1) - (void)state; -#endif - - area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); - rc = flash_area_open(area_id, &fap); - if (rc != 0) { - rc = BOOT_EFLASH; - goto done; - } - - off = BOOT_TLV_OFF(boot_img_hdr(state, slot)); - - if (flash_area_read(fap, off, &info, sizeof(info))) { - rc = BOOT_EFLASH; - goto done; - } - - protect_tlv_size = boot_img_hdr(state, slot)->ih_protect_tlv_size; - if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) { - if (protect_tlv_size != info.it_tlv_tot) { - rc = BOOT_EBADIMAGE; - goto done; - } - - if (flash_area_read(fap, off + info.it_tlv_tot, &info, sizeof(info))) { - rc = BOOT_EFLASH; - goto done; - } - } else if (protect_tlv_size != 0) { - rc = BOOT_EBADIMAGE; - goto done; - } - - if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { - rc = BOOT_EBADIMAGE; - goto done; - } - - *size = off + protect_tlv_size + info.it_tlv_tot; - rc = 0; - -done: - flash_area_close(fap); - return rc; -} -#endif /* !MCUBOOT_OVERWRITE_ONLY */ #if !defined(MCUBOOT_RAM_LOAD) static uint32_t @@ -2755,407 +2686,6 @@ boot_select_or_erase(struct boot_loader_state *state) } #endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_DIRECT_XIP_REVERT */ -#ifdef MCUBOOT_RAM_LOAD - -#ifndef MULTIPLE_EXECUTABLE_RAM_REGIONS -#if !defined(IMAGE_EXECUTABLE_RAM_START) || !defined(IMAGE_EXECUTABLE_RAM_SIZE) -#error "Platform MUST define executable RAM bounds in case of RAM_LOAD" -#endif -#endif - -/** - * Verifies that the active slot of the current image can be loaded within the - * predefined bounds that are allowed to be used by executable images. - * - * @param state Boot loader status information. - * - * @return 0 on success; nonzero on failure. - */ -static int -boot_verify_ram_load_address(struct boot_loader_state *state) -{ - uint32_t img_dst; - uint32_t img_sz; - uint32_t img_end_addr; - uint32_t exec_ram_start; - uint32_t exec_ram_size; - - (void)state; - -#ifdef MULTIPLE_EXECUTABLE_RAM_REGIONS - int rc; - - rc = boot_get_image_exec_ram_info(BOOT_CURR_IMG(state), &exec_ram_start, - &exec_ram_size); - if (rc != 0) { - return BOOT_EBADSTATUS; - } -#else - exec_ram_start = IMAGE_EXECUTABLE_RAM_START; - exec_ram_size = IMAGE_EXECUTABLE_RAM_SIZE; -#endif - - img_dst = state->slot_usage[BOOT_CURR_IMG(state)].img_dst; - img_sz = state->slot_usage[BOOT_CURR_IMG(state)].img_sz; - - if (img_dst < exec_ram_start) { - return BOOT_EBADIMAGE; - } - - if (!boot_u32_safe_add(&img_end_addr, img_dst, img_sz)) { - return BOOT_EBADIMAGE; - } - - if (img_end_addr > (exec_ram_start + exec_ram_size)) { - return BOOT_EBADIMAGE; - } - - return 0; -} - -#ifdef MCUBOOT_ENC_IMAGES - -/** - * Copies and decrypts an image from a slot in the flash to an SRAM address. - * - * @param state Boot loader status information. - * @param slot The flash slot of the image to be copied to SRAM. - * @param hdr The image header. - * @param src_sz Size of the image. - * @param img_dst Pointer to the address at which the image needs to be - * copied to SRAM. - * - * @return 0 on success; nonzero on failure. - */ -static int -boot_decrypt_and_copy_image_to_sram(struct boot_loader_state *state, - uint32_t slot, struct image_header *hdr, - uint32_t src_sz, uint32_t img_dst) -{ - /* The flow for the decryption and copy of the image is as follows : - * 1. The whole image is copied to the RAM (header + payload + TLV). - * 2. The encryption key is loaded from the TLV in flash. - * 3. The image is then decrypted chunk by chunk in RAM (1 chunk - * is 1024 bytes). Only the payload section is decrypted. - * 4. The image is authenticated in RAM. - */ - const struct flash_area *fap_src = NULL; - struct boot_status bs; - uint32_t blk_off; - uint32_t tlv_off; - uint32_t blk_sz; - uint32_t bytes_copied = hdr->ih_hdr_size; - uint32_t chunk_sz; - uint32_t max_sz = 1024; - uint16_t idx; - uint8_t * cur_dst; - int area_id; - int rc; - uint8_t * ram_dst = (void *)(IMAGE_RAM_BASE + img_dst); - - area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); - rc = flash_area_open(area_id, &fap_src); - if (rc != 0){ - return BOOT_EFLASH; - } - - tlv_off = BOOT_TLV_OFF(hdr); - - /* Copying the whole image in RAM */ - rc = flash_area_read(fap_src, 0, ram_dst, src_sz); - if (rc != 0) { - goto done; - } - - rc = boot_enc_load(BOOT_CURR_ENC(state), slot, hdr, fap_src, &bs); - if (rc < 0) { - goto done; - } - - /* if rc > 0 then the key has already been loaded */ - if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), slot, &bs)) { - goto done; - } - - /* Starting at the end of the header as the header section is not encrypted */ - while (bytes_copied < tlv_off) { /* TLV section copied previously */ - if (src_sz - bytes_copied > max_sz) { - chunk_sz = max_sz; - } else { - chunk_sz = src_sz - bytes_copied; - } - - cur_dst = ram_dst + bytes_copied; - blk_sz = chunk_sz; - idx = 0; - blk_off = ((bytes_copied) - hdr->ih_hdr_size) & 0xf; - if (bytes_copied + chunk_sz > tlv_off) { - /* Going over TLV section - * Part of the chunk is encrypted payload */ - blk_sz = tlv_off - (bytes_copied); - } - boot_encrypt(BOOT_CURR_ENC(state), slot, - (bytes_copied + idx) - hdr->ih_hdr_size, blk_sz, - blk_off, cur_dst); - - bytes_copied += chunk_sz; - } - rc = 0; - -done: - flash_area_close(fap_src); - - return rc; -} - -#endif /* MCUBOOT_ENC_IMAGES */ -/** - * Copies a slot of the current image into SRAM. - * - * @param state Boot loader status information. - * @param slot The flash slot of the image to be copied to SRAM. - * @param img_dst The address at which the image needs to be copied to - * SRAM. - * @param img_sz The size of the image that needs to be copied to SRAM. - * - * @return 0 on success; nonzero on failure. - */ -static int -boot_copy_image_to_sram(struct boot_loader_state *state, int slot, - uint32_t img_dst, uint32_t img_sz) -{ - int rc; - const struct flash_area *fap_src = NULL; - int area_id; - -#if (BOOT_IMAGE_NUMBER == 1) - (void)state; -#endif - - area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); - - rc = flash_area_open(area_id, &fap_src); - if (rc != 0) { - return BOOT_EFLASH; - } - - /* Direct copy from flash to its new location in SRAM. */ - rc = flash_area_read(fap_src, 0, (void *)(IMAGE_RAM_BASE + img_dst), img_sz); - if (rc != 0) { - BOOT_LOG_INF("Error whilst copying image %d from Flash to SRAM: %d", - BOOT_CURR_IMG(state), rc); - } - - flash_area_close(fap_src); - - return rc; -} - -#if (BOOT_IMAGE_NUMBER > 1) -/** - * Checks if two memory regions (A and B) are overlap or not. - * - * @param start_a Start of the A region. - * @param end_a End of the A region. - * @param start_b Start of the B region. - * @param end_b End of the B region. - * - * @return true if there is overlap; false otherwise. - */ -static bool -do_regions_overlap(uint32_t start_a, uint32_t end_a, - uint32_t start_b, uint32_t end_b) -{ - if (start_b > end_a) { - return false; - } else if (start_b >= start_a) { - return true; - } else if (end_b > start_a) { - return true; - } - - return false; -} - -/** - * Checks if the image we want to load to memory overlap with an already - * ramloaded image. - * - * @param state Boot loader status information. - * - * @return 0 if there is no overlap; nonzero otherwise. - */ -static int -boot_check_ram_load_overlapping(struct boot_loader_state *state) -{ - uint32_t i; - - uint32_t start_a; - uint32_t end_a; - uint32_t start_b; - uint32_t end_b; - uint32_t image_id_to_check = BOOT_CURR_IMG(state); - - start_a = state->slot_usage[image_id_to_check].img_dst; - /* Safe to add here, values are already verified in - * boot_verify_ram_load_address() */ - end_a = start_a + state->slot_usage[image_id_to_check].img_sz; - - for (i = 0; i < BOOT_IMAGE_NUMBER; i++) { - if (state->slot_usage[i].active_slot == NO_ACTIVE_SLOT - || i == image_id_to_check) { - continue; - } - - start_b = state->slot_usage[i].img_dst; - /* Safe to add here, values are already verified in - * boot_verify_ram_load_address() */ - end_b = start_b + state->slot_usage[i].img_sz; - - if (do_regions_overlap(start_a, end_a, start_b, end_b)) { - return -1; - } - } - - return 0; -} -#endif - -/** - * Loads the active slot of the current image into SRAM. The load address and - * image size is extracted from the image header. - * - * @param state Boot loader status information. - * - * @return 0 on success; nonzero on failure. - */ -static int -boot_load_image_to_sram(struct boot_loader_state *state) -{ - uint32_t active_slot; - struct image_header *hdr = NULL; - uint32_t img_dst; - uint32_t img_sz; - int rc; - - active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; - hdr = boot_img_hdr(state, active_slot); - - if (hdr->ih_flags & IMAGE_F_RAM_LOAD) { - - img_dst = hdr->ih_load_addr; - - rc = boot_read_image_size(state, active_slot, &img_sz); - if (rc != 0) { - return rc; - } - - state->slot_usage[BOOT_CURR_IMG(state)].img_dst = img_dst; - state->slot_usage[BOOT_CURR_IMG(state)].img_sz = img_sz; - - rc = boot_verify_ram_load_address(state); - if (rc != 0) { - BOOT_LOG_INF("Image %d RAM load address 0x%x is invalid.", BOOT_CURR_IMG(state), img_dst); - return rc; - } - -#if (BOOT_IMAGE_NUMBER > 1) - rc = boot_check_ram_load_overlapping(state); - if (rc != 0) { - BOOT_LOG_INF("Image %d RAM loading to address 0x%x would overlap with\ - another image.", BOOT_CURR_IMG(state), img_dst); - return rc; - } -#endif -#ifdef MCUBOOT_ENC_IMAGES - /* decrypt image if encrypted and copy it to RAM */ - if (IS_ENCRYPTED(hdr)) { - rc = boot_decrypt_and_copy_image_to_sram(state, active_slot, hdr, img_sz, img_dst); - } else { - rc = boot_copy_image_to_sram(state, active_slot, img_dst, img_sz); - } -#else - /* Copy image to the load address from where it currently resides in - * flash. - */ - rc = boot_copy_image_to_sram(state, active_slot, img_dst, img_sz); -#endif - if (rc != 0) { - BOOT_LOG_INF("Image %d RAM loading to 0x%x is failed.", BOOT_CURR_IMG(state), img_dst); - } else { - BOOT_LOG_INF("Image %d RAM loading to 0x%x is succeeded.", BOOT_CURR_IMG(state), img_dst); - } - } else { - /* Only images that support IMAGE_F_RAM_LOAD are allowed if - * MCUBOOT_RAM_LOAD is set. - */ - rc = BOOT_EBADIMAGE; - } - - if (rc != 0) { - state->slot_usage[BOOT_CURR_IMG(state)].img_dst = 0; - state->slot_usage[BOOT_CURR_IMG(state)].img_sz = 0; - } - - return rc; -} - -/** - * Removes an image from SRAM, by overwriting it with zeros. - * - * @param state Boot loader status information. - * - * @return 0 on success; nonzero on failure. - */ -static inline int -boot_remove_image_from_sram(struct boot_loader_state *state) -{ - (void)state; - - BOOT_LOG_INF("Removing image %d from SRAM at address 0x%x", - BOOT_CURR_IMG(state), - state->slot_usage[BOOT_CURR_IMG(state)].img_dst); - - memset((void*)(IMAGE_RAM_BASE + state->slot_usage[BOOT_CURR_IMG(state)].img_dst), - 0, state->slot_usage[BOOT_CURR_IMG(state)].img_sz); - - state->slot_usage[BOOT_CURR_IMG(state)].img_dst = 0; - state->slot_usage[BOOT_CURR_IMG(state)].img_sz = 0; - - return 0; -} - -/** - * Removes an image from flash by erasing the corresponding flash area - * - * @param state Boot loader status information. - * @param slot The flash slot of the image to be erased. - * - * @return 0 on success; nonzero on failure. - */ -static inline int -boot_remove_image_from_flash(struct boot_loader_state *state, uint32_t slot) -{ - int area_id; - int rc; - const struct flash_area *fap; - - (void)state; - - BOOT_LOG_INF("Removing image %d slot %d from flash", BOOT_CURR_IMG(state), - slot); - area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); - rc = flash_area_open(area_id, &fap); - if (rc == 0) { - flash_area_erase(fap, 0, flash_area_get_size(fap)); - flash_area_close(fap); - } - - return rc; -} -#endif /* MCUBOOT_RAM_LOAD */ - - /** * Tries to load a slot for all the images with validation. * diff --git a/boot/bootutil/src/ram_load.c b/boot/bootutil/src/ram_load.c new file mode 100644 index 000000000..822514eaf --- /dev/null +++ b/boot/bootutil/src/ram_load.c @@ -0,0 +1,441 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2016-2020 Linaro LTD + * Copyright (c) 2016-2019 JUUL Labs + * Copyright (c) 2019-2023 Arm Limited + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * Original license: + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "bootutil/bootutil.h" +#include "bootutil/bootutil_log.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/image.h" +#include "bootutil_priv.h" +#include "bootutil/fault_injection_hardening.h" +#include "bootutil/ramload.h" +#include "bootutil/mcuboot_status.h" + +#ifdef MCUBOOT_ENC_IMAGES +#include "bootutil/enc_key.h" +#endif + +#include "mcuboot_config/mcuboot_config.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +#ifndef MULTIPLE_EXECUTABLE_RAM_REGIONS +#if !defined(IMAGE_EXECUTABLE_RAM_START) || !defined(IMAGE_EXECUTABLE_RAM_SIZE) +#error "Platform MUST define executable RAM bounds in case of RAM_LOAD" +#endif +#endif + +/** + * Verifies that the active slot of the current image can be loaded within the + * predefined bounds that are allowed to be used by executable images. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_verify_ram_load_address(struct boot_loader_state *state) +{ + uint32_t img_dst; + uint32_t img_sz; + uint32_t img_end_addr; + uint32_t exec_ram_start; + uint32_t exec_ram_size; + + (void)state; + +#ifdef MULTIPLE_EXECUTABLE_RAM_REGIONS + int rc; + + rc = boot_get_image_exec_ram_info(BOOT_CURR_IMG(state), &exec_ram_start, + &exec_ram_size); + if (rc != 0) { + return BOOT_EBADSTATUS; + } +#else + exec_ram_start = IMAGE_EXECUTABLE_RAM_START; + exec_ram_size = IMAGE_EXECUTABLE_RAM_SIZE; +#endif + + img_dst = state->slot_usage[BOOT_CURR_IMG(state)].img_dst; + img_sz = state->slot_usage[BOOT_CURR_IMG(state)].img_sz; + + if (img_dst < exec_ram_start) { + return BOOT_EBADIMAGE; + } + + if (!boot_u32_safe_add(&img_end_addr, img_dst, img_sz)) { + return BOOT_EBADIMAGE; + } + + if (img_end_addr > (exec_ram_start + exec_ram_size)) { + return BOOT_EBADIMAGE; + } + + return 0; +} + +#ifdef MCUBOOT_ENC_IMAGES + +/** + * Copies and decrypts an image from a slot in the flash to an SRAM address. + * + * @param state Boot loader status information. + * @param slot The flash slot of the image to be copied to SRAM. + * @param hdr The image header. + * @param src_sz Size of the image. + * @param img_dst Pointer to the address at which the image needs to be + * copied to SRAM. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_decrypt_and_copy_image_to_sram(struct boot_loader_state *state, + uint32_t slot, struct image_header *hdr, + uint32_t src_sz, uint32_t img_dst) +{ + /* The flow for the decryption and copy of the image is as follows : + * 1. The whole image is copied to the RAM (header + payload + TLV). + * 2. The encryption key is loaded from the TLV in flash. + * 3. The image is then decrypted chunk by chunk in RAM (1 chunk + * is 1024 bytes). Only the payload section is decrypted. + * 4. The image is authenticated in RAM. + */ + const struct flash_area *fap_src = NULL; + struct boot_status bs; + uint32_t blk_off; + uint32_t tlv_off; + uint32_t blk_sz; + uint32_t bytes_copied = hdr->ih_hdr_size; + uint32_t chunk_sz; + uint32_t max_sz = 1024; + uint16_t idx; + uint8_t * cur_dst; + int area_id; + int rc; + uint8_t * ram_dst = (void *)(IMAGE_RAM_BASE + img_dst); + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap_src); + if (rc != 0){ + return BOOT_EFLASH; + } + + tlv_off = BOOT_TLV_OFF(hdr); + + /* Copying the whole image in RAM */ + rc = flash_area_read(fap_src, 0, ram_dst, src_sz); + if (rc != 0) { + goto done; + } + + rc = boot_enc_load(BOOT_CURR_ENC(state), slot, hdr, fap_src, &bs); + if (rc < 0) { + goto done; + } + + /* if rc > 0 then the key has already been loaded */ + if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), slot, &bs)) { + goto done; + } + + /* Starting at the end of the header as the header section is not encrypted */ + while (bytes_copied < tlv_off) { /* TLV section copied previously */ + if (src_sz - bytes_copied > max_sz) { + chunk_sz = max_sz; + } else { + chunk_sz = src_sz - bytes_copied; + } + + cur_dst = ram_dst + bytes_copied; + blk_sz = chunk_sz; + idx = 0; + blk_off = ((bytes_copied) - hdr->ih_hdr_size) & 0xf; + if (bytes_copied + chunk_sz > tlv_off) { + /* Going over TLV section + * Part of the chunk is encrypted payload */ + blk_sz = tlv_off - (bytes_copied); + } + boot_encrypt(BOOT_CURR_ENC(state), slot, + (bytes_copied + idx) - hdr->ih_hdr_size, blk_sz, + blk_off, cur_dst); + + bytes_copied += chunk_sz; + } + rc = 0; + +done: + flash_area_close(fap_src); + + return rc; +} + +#endif /* MCUBOOT_ENC_IMAGES */ +/** + * Copies a slot of the current image into SRAM. + * + * @param state Boot loader status information. + * @param slot The flash slot of the image to be copied to SRAM. + * @param img_dst The address at which the image needs to be copied to + * SRAM. + * @param img_sz The size of the image that needs to be copied to SRAM. + * + * @return 0 on success; nonzero on failure. + */ +static int +boot_copy_image_to_sram(struct boot_loader_state *state, int slot, + uint32_t img_dst, uint32_t img_sz) +{ + int rc; + const struct flash_area *fap_src = NULL; + int area_id; + +#if (BOOT_IMAGE_NUMBER == 1) + (void)state; +#endif + + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + + rc = flash_area_open(area_id, &fap_src); + if (rc != 0) { + return BOOT_EFLASH; + } + + /* Direct copy from flash to its new location in SRAM. */ + rc = flash_area_read(fap_src, 0, (void *)(IMAGE_RAM_BASE + img_dst), img_sz); + if (rc != 0) { + BOOT_LOG_INF("Error whilst copying image %d from Flash to SRAM: %d", + BOOT_CURR_IMG(state), rc); + } + + flash_area_close(fap_src); + + return rc; +} + +#if (BOOT_IMAGE_NUMBER > 1) +/** + * Checks if two memory regions (A and B) are overlap or not. + * + * @param start_a Start of the A region. + * @param end_a End of the A region. + * @param start_b Start of the B region. + * @param end_b End of the B region. + * + * @return true if there is overlap; false otherwise. + */ +static bool +do_regions_overlap(uint32_t start_a, uint32_t end_a, + uint32_t start_b, uint32_t end_b) +{ + if (start_b > end_a) { + return false; + } else if (start_b >= start_a) { + return true; + } else if (end_b > start_a) { + return true; + } + + return false; +} + +/** + * Checks if the image we want to load to memory overlap with an already + * ramloaded image. + * + * @param state Boot loader status information. + * + * @return 0 if there is no overlap; nonzero otherwise. + */ +static int +boot_check_ram_load_overlapping(struct boot_loader_state *state) +{ + uint32_t i; + + uint32_t start_a; + uint32_t end_a; + uint32_t start_b; + uint32_t end_b; + uint32_t image_id_to_check = BOOT_CURR_IMG(state); + + start_a = state->slot_usage[image_id_to_check].img_dst; + /* Safe to add here, values are already verified in + * boot_verify_ram_load_address() */ + end_a = start_a + state->slot_usage[image_id_to_check].img_sz; + + for (i = 0; i < BOOT_IMAGE_NUMBER; i++) { + if (state->slot_usage[i].active_slot == NO_ACTIVE_SLOT + || i == image_id_to_check) { + continue; + } + + start_b = state->slot_usage[i].img_dst; + /* Safe to add here, values are already verified in + * boot_verify_ram_load_address() */ + end_b = start_b + state->slot_usage[i].img_sz; + + if (do_regions_overlap(start_a, end_a, start_b, end_b)) { + return -1; + } + } + + return 0; +} +#endif + +/** + * Loads the active slot of the current image into SRAM. The load address and + * image size is extracted from the image header. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_load_image_to_sram(struct boot_loader_state *state) +{ + uint32_t active_slot; + struct image_header *hdr = NULL; + uint32_t img_dst; + uint32_t img_sz; + int rc; + + active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot; + hdr = boot_img_hdr(state, active_slot); + + if (hdr->ih_flags & IMAGE_F_RAM_LOAD) { + + img_dst = hdr->ih_load_addr; + + rc = boot_read_image_size(state, active_slot, &img_sz); + if (rc != 0) { + return rc; + } + + state->slot_usage[BOOT_CURR_IMG(state)].img_dst = img_dst; + state->slot_usage[BOOT_CURR_IMG(state)].img_sz = img_sz; + + rc = boot_verify_ram_load_address(state); + if (rc != 0) { + BOOT_LOG_INF("Image %d RAM load address 0x%x is invalid.", BOOT_CURR_IMG(state), img_dst); + return rc; + } + +#if (BOOT_IMAGE_NUMBER > 1) + rc = boot_check_ram_load_overlapping(state); + if (rc != 0) { + BOOT_LOG_INF("Image %d RAM loading to address 0x%x would overlap with\ + another image.", BOOT_CURR_IMG(state), img_dst); + return rc; + } +#endif +#ifdef MCUBOOT_ENC_IMAGES + /* decrypt image if encrypted and copy it to RAM */ + if (IS_ENCRYPTED(hdr)) { + rc = boot_decrypt_and_copy_image_to_sram(state, active_slot, hdr, img_sz, img_dst); + } else { + rc = boot_copy_image_to_sram(state, active_slot, img_dst, img_sz); + } +#else + /* Copy image to the load address from where it currently resides in + * flash. + */ + rc = boot_copy_image_to_sram(state, active_slot, img_dst, img_sz); +#endif + if (rc != 0) { + BOOT_LOG_INF("Image %d RAM loading to 0x%x is failed.", BOOT_CURR_IMG(state), img_dst); + } else { + BOOT_LOG_INF("Image %d RAM loading to 0x%x is succeeded.", BOOT_CURR_IMG(state), img_dst); + } + } else { + /* Only images that support IMAGE_F_RAM_LOAD are allowed if + * MCUBOOT_RAM_LOAD is set. + */ + rc = BOOT_EBADIMAGE; + } + + if (rc != 0) { + state->slot_usage[BOOT_CURR_IMG(state)].img_dst = 0; + state->slot_usage[BOOT_CURR_IMG(state)].img_sz = 0; + } + + return rc; +} + +/** + * Removes an image from SRAM, by overwriting it with zeros. + * + * @param state Boot loader status information. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_remove_image_from_sram(struct boot_loader_state *state) +{ + (void)state; + + BOOT_LOG_INF("Removing image %d from SRAM at address 0x%x", + BOOT_CURR_IMG(state), + state->slot_usage[BOOT_CURR_IMG(state)].img_dst); + + memset((void*)(IMAGE_RAM_BASE + state->slot_usage[BOOT_CURR_IMG(state)].img_dst), + 0, state->slot_usage[BOOT_CURR_IMG(state)].img_sz); + + state->slot_usage[BOOT_CURR_IMG(state)].img_dst = 0; + state->slot_usage[BOOT_CURR_IMG(state)].img_sz = 0; + + return 0; +} + +/** + * Removes an image from flash by erasing the corresponding flash area + * + * @param state Boot loader status information. + * @param slot The flash slot of the image to be erased. + * + * @return 0 on success; nonzero on failure. + */ +int +boot_remove_image_from_flash(struct boot_loader_state *state, uint32_t slot) +{ + int area_id; + int rc; + const struct flash_area *fap; + + (void)state; + + BOOT_LOG_INF("Removing image %d slot %d from flash", BOOT_CURR_IMG(state), + slot); + area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot); + rc = flash_area_open(area_id, &fap); + if (rc == 0) { + flash_area_erase(fap, 0, flash_area_get_size(fap)); + flash_area_close(fap); + } + + return rc; +} diff --git a/boot/espressif/CMakeLists.txt b/boot/espressif/CMakeLists.txt index bc703fc24..d1e18d2da 100644 --- a/boot/espressif/CMakeLists.txt +++ b/boot/espressif/CMakeLists.txt @@ -231,6 +231,12 @@ set(bootutil_srcs ${BOOTUTIL_DIR}/src/swap_scratch.c ${BOOTUTIL_DIR}/src/tlv.c ) + +if(CONFIG_BOOT_RAM_LOAD) + list(APPEND bootutil_srcs + ${BOOTUTIL_DIR}/src/ram_load.c + ) +endif() set(bootutil_paths) set(CFLAGS diff --git a/boot/espressif/hal/include/mcuboot_config/mcuboot_config.h b/boot/espressif/hal/include/mcuboot_config/mcuboot_config.h index a7058e747..a299e3cfc 100644 --- a/boot/espressif/hal/include/mcuboot_config/mcuboot_config.h +++ b/boot/espressif/hal/include/mcuboot_config/mcuboot_config.h @@ -57,8 +57,10 @@ /* Uncomment to enable the direct-xip code path. */ /* #define MCUBOOT_DIRECT_XIP */ -/* Uncomment to enable the ram-load code path. */ -/* #define MCUBOOT_RAM_LOAD */ +/* Define to enable the ram-load code path. */ +#if defined(CONFIG_BOOT_RAM_LOAD) +#define MCUBOOT_RAM_LOAD +#endif /* * Cryptographic settings diff --git a/boot/mynewt/pkg.yml b/boot/mynewt/pkg.yml index e0b313275..9645a5e90 100644 --- a/boot/mynewt/pkg.yml +++ b/boot/mynewt/pkg.yml @@ -39,6 +39,8 @@ pkg.ign_files.!BOOTUTIL_SINGLE_APPLICATION_SLOT: - "single_loader.c" pkg.ign_files.BOOTUTIL_SINGLE_APPLICATION_SLOT: - "swap_scratch.c" +pkg.ign_files: + - "ram_load.c" pkg.deps.BOOTUTIL_NO_LOGGING: - "@apache-mynewt-core/sys/console/stub" diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index d02f93a40..834c4731c 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -127,7 +127,7 @@ zephyr_library_sources( ) endif() -if(CONFIG_SINGLE_APPLICATION_SLOT) +if(CONFIG_SINGLE_APPLICATION_SLOT OR CONFIG_SINGLE_APPLICATION_SLOT_RAM_LOAD) zephyr_library_sources( ${BOOT_DIR}/zephyr/single_loader.c ) @@ -147,6 +147,12 @@ zephyr_library_sources( ) endif() +if(CONFIG_BOOT_RAM_LOAD OR CONFIG_SINGLE_APPLICATION_SLOT_RAM_LOAD) + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/ram_load.c + ) +endif() + if(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256 OR CONFIG_BOOT_ENCRYPT_EC256) zephyr_library_include_directories( ${MBEDTLS_ASN1_DIR}/include @@ -394,7 +400,7 @@ if(CONFIG_BOOT_MAX_IMG_SECTORS_AUTO) math(EXPR slot_min_sectors "${slot0_size} / ${erase_size_slot0}") endif() - if(NOT CONFIG_SINGLE_APPLICATION_SLOT) + if(NOT CONFIG_SINGLE_APPLICATION_SLOT AND NOT CONFIG_SINGLE_APPLICATION_SLOT_RAM_LOAD) dt_nodelabel(slot1_flash NODELABEL "slot1_partition") dt_prop(slot1_size PATH "${slot1_flash}" PROPERTY "reg" INDEX 1) dt_get_parent(slot1_flash) @@ -422,6 +428,12 @@ if(CONFIG_BOOT_MAX_IMG_SECTORS_AUTO) endif() endif() +if(BUILD_RUNTIME_SOURCE_SAMPLE) + zephyr_library_sources( + ${MCUBOOT_DIR}/samples/runtime-source/zephyr/flash_map_dispatcher.c + ) +endif() + if(SYSBUILD) if(CONFIG_SINGLE_APPLICATION_SLOT OR CONFIG_BOOT_FIRMWARE_LOADER OR CONFIG_BOOT_SWAP_USING_SCRATCH OR CONFIG_BOOT_SWAP_USING_MOVE OR CONFIG_BOOT_UPGRADE_ONLY OR CONFIG_BOOT_DIRECT_XIP OR CONFIG_BOOT_RAM_LOAD) # TODO: RAM LOAD support diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index 64e23ac6d..960803ecb 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -271,19 +271,6 @@ config BOOT_FIRMWARE_LOADER endchoice -# Workaround for not being able to have commas in macro arguments -DT_CHOSEN_Z_SRAM := zephyr,sram - -if BOOT_RAM_LOAD -config BOOT_IMAGE_EXECUTABLE_RAM_START - hex "Boot image executable ram start" - default $(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_SRAM)) - -config BOOT_IMAGE_EXECUTABLE_RAM_SIZE - int "Boot image executable base size" - default $(dt_chosen_reg_size_int,$(DT_CHOSEN_Z_SRAM),0) -endif - config BOOT_DIRECT_XIP_REVERT bool "Enable the revert mechanism in direct-xip mode" depends on BOOT_DIRECT_XIP @@ -320,6 +307,39 @@ config BOOT_SWAP_SAVE_ENCTLV endif # !SINGLE_APPLICATION_SLOT +config SINGLE_APPLICATION_SLOT_RAM_LOAD + bool "RAM load for single application slot" + help + If y, the image is loaded to RAM and executed from there. For this reason, + the image has to be linked to be executed from RAM. The address that the + image is copied to is specified using the load-addr argument to the + imgtool.py script which writes it to the image header. + +# Workaround for not being able to have commas in macro arguments +DT_CHOSEN_Z_SRAM := zephyr,sram + +if BOOT_RAM_LOAD || SINGLE_APPLICATION_SLOT_RAM_LOAD +config BOOT_IMAGE_EXECUTABLE_RAM_START + hex "Boot image executable ram start" + default $(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_SRAM)) + +config BOOT_IMAGE_EXECUTABLE_RAM_SIZE + int "Boot image executable base size" + default $(dt_chosen_reg_size_int,$(DT_CHOSEN_Z_SRAM),0) +endif + +config FLASH_RUNTIME_SOURCES + bool "Images are read from flash partitions defined at runtime" + select SINGLE_APPLICATION_SLOT + help + Instead of using information on the flash slots to decide which images + to load/update, the application provides the information from which + flash slot to load in runtime. This is useful when the application + reads the state for hardware straps or other sources to decide which + image to load. The application must provide `flash_map_id_get_next` + and `flash_map_id_get_next` functions to tell mcuboot where to find + the images. + config BOOT_ENCRYPTION_SUPPORT bool help @@ -622,7 +642,7 @@ config BOOT_INTR_VEC_RELOC config UPDATEABLE_IMAGE_NUMBER int "Number of updateable images" default 1 - range 1 1 if SINGLE_APPLICATION_SLOT + range 1 1 if SINGLE_APPLICATION_SLOT || SINGLE_APPLICATION_SLOT_RAM_LOAD help Enables support of multi image update. diff --git a/boot/zephyr/flash_map_extended.c b/boot/zephyr/flash_map_extended.c index 4631da75b..ef11f34cd 100644 --- a/boot/zephyr/flash_map_extended.c +++ b/boot/zephyr/flash_map_extended.c @@ -14,6 +14,10 @@ #include #include +#if defined(CONFIG_FLASH_RUNTIME_SOURCES) +#include +#endif + #include "bootutil/bootutil_log.h" BOOT_LOG_MODULE_DECLARE(mcuboot); @@ -58,6 +62,15 @@ int flash_device_base(uint8_t fd_id, uintptr_t *ret) */ int flash_area_id_from_multi_image_slot(int image_index, int slot) { +#if defined(CONFIG_FLASH_RUNTIME_SOURCES) + uint8_t id; + + if (flash_map_id_get_current(&id)) { + return id; + } + return -1; +#endif + switch (slot) { case 0: return FLASH_AREA_IMAGE_PRIMARY(image_index); #if !defined(CONFIG_SINGLE_APPLICATION_SLOT) @@ -141,7 +154,7 @@ int flash_area_sector_from_off(off_t off, struct flash_sector *sector) uint8_t flash_area_get_device_id(const struct flash_area *fa) { -#if defined(CONFIG_ARM) +#if defined(CONFIG_ARM) || defined(CONFIG_FLASH_RUNTIME_SOURCES) return fa->fa_id; #else (void)fa; diff --git a/boot/zephyr/include/flash_runtime_sources.h b/boot/zephyr/include/flash_runtime_sources.h new file mode 100644 index 000000000..44640b892 --- /dev/null +++ b/boot/zephyr/include/flash_runtime_sources.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __FLASH_RUNTIME_SOURCES_H__ +#define __FLASH_RUNTIME_SOURCES_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Get next flash map id. + * + * Implement this function to get the next flash map id. The function should + * return true if the flash map id was successfully updated. If the reset + * parameter is true, the function should reset the flash map id to the first + * one. + * + * @param id Pointer to the flash map id. + * @param reset If true, the function will reset the flash map id to the first + * one. + * @retval true If the flash map id was successfully updated. + */ +bool flash_map_id_get_next(uint8_t *id, bool reset); + +/* + * Get current flash map id. + * + * Implement this function to get the current flash map id. The function should + * return true if the flash map id was successfully read. + * + * @param id Pointer to the flash map id. + * @retval true If the flash map id was successfully read. + */ +bool flash_map_id_get_current(uint8_t *id); + +#ifdef __cplusplus +} +#endif + +#endif /* __FLASH_RUNTIME_SOURCES_H__ */ diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h index 0891a4b11..f8a6512ab 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -108,6 +108,14 @@ #endif /* CONFIG_SINGLE_APPLICATION_SLOT */ +#ifdef CONFIG_SINGLE_APPLICATION_SLOT_RAM_LOAD +#define MCUBOOT_RAM_LOAD 1 +#define MCUBOOT_IMAGE_NUMBER 1 +#define MCUBOOT_SINGLE_APPLICATION_SLOT_RAM_LOAD 1 +#define IMAGE_EXECUTABLE_RAM_START CONFIG_BOOT_IMAGE_EXECUTABLE_RAM_START +#define IMAGE_EXECUTABLE_RAM_SIZE CONFIG_BOOT_IMAGE_EXECUTABLE_RAM_SIZE +#endif + #ifdef CONFIG_LOG #define MCUBOOT_HAVE_LOGGING 1 #endif diff --git a/boot/zephyr/single_loader.c b/boot/zephyr/single_loader.c index 75374d2db..fb6223312 100644 --- a/boot/zephyr/single_loader.c +++ b/boot/zephyr/single_loader.c @@ -20,6 +20,30 @@ BOOT_LOG_MODULE_DECLARE(mcuboot); static const struct flash_area *_fa_p; static struct image_header _hdr = { 0 }; +__weak bool +flash_map_id_get_next(uint8_t *id, bool reset) +{ + if (!reset || !id) { + return false; + } + + *id = FLASH_AREA_IMAGE_PRIMARY(0); + + return true; +} + +__weak bool +flash_map_id_get_current(uint8_t *id) +{ + if (!id) { + return false; + } + + *id = FLASH_AREA_IMAGE_PRIMARY(0); + + return true; +} + #if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) || defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) /** * Validate hash of a primary boot image. @@ -102,35 +126,65 @@ fih_ret boot_go(struct boot_rsp *rsp) { int rc = -1; + uint8_t flash_id; + bool reset = true; FIH_DECLARE(fih_rc, FIH_FAILURE); - rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &_fa_p); - assert(rc == 0); + while (flash_map_id_get_next(&flash_id, reset)) { + reset = false; + rc = flash_area_open(flash_id, &_fa_p); + if (rc != 0) { + continue; + } - rc = boot_image_load_header(_fa_p, &_hdr); - if (rc != 0) - goto out; + rc = boot_image_load_header(_fa_p, &_hdr); + if (rc != 0) { + flash_area_close(_fa_p); + continue; + } + +#ifdef MCUBOOT_RAM_LOAD + static struct boot_loader_state state; + state.imgs[0][0].hdr = _hdr; + + rc = boot_load_image_to_sram(&state); + if (rc != 0) { + flash_area_close(_fa_p); + continue; + } +#endif #ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT - FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr); - if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { - goto out; - } + FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + flash_area_close(_fa_p); +#ifdef MCUBOOT_RAM_LOAD + boot_remove_image_from_sram(&state); +#endif + continue; + } #elif defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE) - FIH_CALL(boot_image_validate_once, fih_rc, _fa_p, &_hdr); - if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { - goto out; - } + FIH_CALL(boot_image_validate_once, fih_rc, _fa_p, &_hdr); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + flash_area_close(_fa_p); +#ifdef MCUBOOT_RAM_LOAD + boot_remove_image_from_sram(&state); +#endif + continue; + } #else - fih_rc = FIH_SUCCESS; + fih_rc = FIH_SUCCESS; #endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */ - rsp->br_flash_dev_id = flash_area_get_device_id(_fa_p); - rsp->br_image_off = flash_area_get_off(_fa_p); - rsp->br_hdr = &_hdr; + rsp->br_flash_dev_id = flash_area_get_device_id(_fa_p); + rsp->br_image_off = flash_area_get_off(_fa_p); + rsp->br_hdr = &_hdr; + + flash_area_close(_fa_p); + + break; + } -out: - flash_area_close(_fa_p); FIH_RET(fih_rc); } diff --git a/samples/runtime-source/zephyr/README.md b/samples/runtime-source/zephyr/README.md new file mode 100644 index 000000000..825858160 --- /dev/null +++ b/samples/runtime-source/zephyr/README.md @@ -0,0 +1,56 @@ +# Runtime chosen image sample application + +This sample demonstrates how to use a non flash storage to retrieve the image +being booted. It was tested on a FRDM K64F. Both slots are used to store two +different images. The image to be booted is selected based on a button press. + +## Build + +Build mcuboot. First, ensure ZEPHYR_SDK_INSTALL_DIR is defined. From the +mcuboot directory, run the following commands: + +``` + source /zephyr-env.sh + + west build -p -b frdm_k64f boot/zephyr/ -- -DBUILD_RUNTIME_SOURCE_SAMPLE=1 \ + -DEXTRA_CONF_FILE="../../samples/runtime-source/zephyr/sample.conf" + -DEXTRA_DTC_OVERLAY_FILE=../../samples/runtime-source/zephyr/boards/frdm_k64f.overlay + + west build -t flash +``` + +Then, build the sample application to be loaded. We need to build it twice, one +for each slot. From the sample +app directory (mcuboot/samples/non-flash-source/zephyr/app), run: + +``` + west build -p -b frdm_k64f . + west flash +``` + +Then change the overlay file to use the second slot. For instance, open +`boards/frdm_k64f.overlay` and change the line: + +``` + zephyr,code-partition = &slot0_partition; + +``` + +to: + +``` + zephyr,code-partition = &slot1_partition; +``` + +And build and flash again: + +``` + west build -b frdm_k64f . + west flash +``` + +## Run + +Open a serial terminal to see the output and reset the board. It shall boot the +image on slot0 by default. By keeping the SW2 button pressed during reset, the +bootloader will randomly select the image to be booted. diff --git a/samples/runtime-source/zephyr/app/CMakeLists.txt b/samples/runtime-source/zephyr/app/CMakeLists.txt new file mode 100644 index 000000000..bb60128f3 --- /dev/null +++ b/samples/runtime-source/zephyr/app/CMakeLists.txt @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(non_flash_backend_app) + +if (NOT DEFINED FROM_WHO) + set(FROM_WHO Zephyr) +endif() + +target_compile_definitions(app PRIVATE "-DMCUBOOT_HELLO_WORLD_FROM=\"${FROM_WHO}\"") + +target_sources(app PRIVATE src/main.c) diff --git a/samples/runtime-source/zephyr/app/boards/frdm_k64f.overlay b/samples/runtime-source/zephyr/app/boards/frdm_k64f.overlay new file mode 100644 index 000000000..8b642c6b6 --- /dev/null +++ b/samples/runtime-source/zephyr/app/boards/frdm_k64f.overlay @@ -0,0 +1,5 @@ +/ { + chosen { + zephyr,code-partition = &slot0_partition; + }; +}; diff --git a/samples/runtime-source/zephyr/app/prj.conf b/samples/runtime-source/zephyr/app/prj.conf new file mode 100644 index 000000000..bf0ea6a28 --- /dev/null +++ b/samples/runtime-source/zephyr/app/prj.conf @@ -0,0 +1,3 @@ +CONFIG_BOOTLOADER_MCUBOOT=y + +CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="./bootloader/mcuboot/root-rsa-2048.pem" diff --git a/samples/runtime-source/zephyr/app/src/main.c b/samples/runtime-source/zephyr/app/src/main.c new file mode 100644 index 000000000..a3ebbdca3 --- /dev/null +++ b/samples/runtime-source/zephyr/app/src/main.c @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2017 Linaro, Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +int main(void) +{ + printk("Hello World from %s on %s, slot %s!\n", + MCUBOOT_HELLO_WORLD_FROM, CONFIG_BOARD, + DT_PROP(DT_CHOSEN(zephyr_code_partition), label)); +} diff --git a/samples/runtime-source/zephyr/flash_map_dispatcher.c b/samples/runtime-source/zephyr/flash_map_dispatcher.c new file mode 100644 index 000000000..63e1feb88 --- /dev/null +++ b/samples/runtime-source/zephyr/flash_map_dispatcher.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define SW1_NODE DT_ALIAS(sw1) +#if DT_NODE_HAS_STATUS(SW1_NODE, okay) +static struct gpio_dt_spec sw1_spec = GPIO_DT_SPEC_GET(SW1_NODE, gpios); +#endif + +static int curr_idx = -1; + +static uint8_t known_ids[] = { + FIXED_PARTITION_ID(slot0_partition), + FIXED_PARTITION_ID(slot1_partition), +}; + +bool +flash_map_id_get_next(uint8_t *id, bool reset) +{ + if (reset) { + curr_idx = 0; +#if DT_NODE_HAS_STATUS(SW1_NODE, okay) + if (gpio_pin_configure_dt(&sw1_spec, GPIO_INPUT) == 0) { + if (gpio_pin_get_dt(&sw1_spec) == 1) { + curr_idx = sys_rand8_get() % ARRAY_SIZE(known_ids); + printk("Booting from curr_idx = %d\n", curr_idx); + } + } +#endif + } else { + curr_idx++; + } + + if (curr_idx >= ARRAY_SIZE(known_ids)) { + return false; + } + + *id = known_ids[curr_idx]; + + return true; +} + +bool +flash_map_id_get_current(uint8_t *id) +{ + if (curr_idx == -1 || curr_idx >= ARRAY_SIZE(known_ids)) { + return false; + } + + *id = known_ids[curr_idx]; + + return true; +} diff --git a/samples/runtime-source/zephyr/sample.conf b/samples/runtime-source/zephyr/sample.conf new file mode 100644 index 000000000..3a6b9819c --- /dev/null +++ b/samples/runtime-source/zephyr/sample.conf @@ -0,0 +1,4 @@ +CONFIG_FLASH_RUNTIME_SOURCES=y +CONFIG_BOOT_SIGNATURE_TYPE_RSA=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_ENTROPY_GENERATOR=y diff --git a/sim/mcuboot-sys/build.rs b/sim/mcuboot-sys/build.rs index ea17d8014..47ee880c5 100644 --- a/sim/mcuboot-sys/build.rs +++ b/sim/mcuboot-sys/build.rs @@ -455,6 +455,9 @@ fn main() { } conf.file("../../boot/bootutil/src/loader.c"); + if ram_load { + conf.file("../../boot/bootutil/src/ram_load.c"); + } conf.file("../../boot/bootutil/src/swap_misc.c"); conf.file("../../boot/bootutil/src/swap_scratch.c"); conf.file("../../boot/bootutil/src/swap_move.c");