From 38ec4bef7c04f349a4d26eae5c7c69ed2b82443f Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Fri, 19 Jul 2024 14:11:33 -0700 Subject: [PATCH 1/4] boot/bootutil: Split RAM load code to its own file RAM loading code is currently under bootutil/loader.c, and it's not accessible for different loaders, such as the single loaders. Future patches will make use of the RAM loading code outside the bootutil/loader.c context, and this patch prepares for that by making it standalone on boot/bootutil/src/ram_load.c Signed-off-by: Ederson de Souza --- boot/bootutil/CMakeLists.txt | 6 + boot/bootutil/src/bootutil_misc.c | 65 +++ boot/bootutil/src/bootutil_priv.h | 11 + boot/bootutil/src/loader.c | 470 ------------------ boot/bootutil/src/ram_load.c | 441 ++++++++++++++++ boot/espressif/CMakeLists.txt | 6 + .../include/mcuboot_config/mcuboot_config.h | 6 +- boot/zephyr/CMakeLists.txt | 6 + sim/mcuboot-sys/build.rs | 3 + 9 files changed, 542 insertions(+), 472 deletions(-) create mode 100644 boot/bootutil/src/ram_load.c 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/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c index 46202135c..09fbe8948 100644 --- a/boot/bootutil/src/bootutil_misc.c +++ b/boot/bootutil/src/bootutil_misc.c @@ -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 32f996e78..2afde79d3 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 @@ -461,15 +463,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 80be91fd3..acd5f3a0c 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -88,8 +88,6 @@ static struct boot_loader_state boot_data; #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) @@ -400,11 +398,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 @@ -518,68 +511,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 @@ -2646,407 +2577,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/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index 45548e0c3..2c1d8b9f5 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -147,6 +147,12 @@ zephyr_library_sources( ) endif() +if(CONFIG_BOOT_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 diff --git a/sim/mcuboot-sys/build.rs b/sim/mcuboot-sys/build.rs index ea17d8014..35d2759e6 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/loader_ram.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"); From f39123f9bc8287557a2df5f362527f8a4aec4e44 Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Fri, 19 Jul 2024 14:22:41 -0700 Subject: [PATCH 2/4] boot/zephyr: Move RAM load Kconfigs outside !SINGLE_APPLICATION_SLOT Now that RAM load code is not dependent on bootutil/loader.c, it can be used independently of !SINGLE_APPLICATION_SLOT configurations. Move the related Kconfigs accordingly, so that applications configured to use single loader can do RAM loading. Signed-off-by: Ederson de Souza --- boot/zephyr/Kconfig | 44 +++++++++---------- .../include/mcuboot_config/mcuboot_config.h | 12 ++--- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index effedfb4f..b44e34c12 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -248,15 +248,6 @@ config BOOT_DIRECT_XIP images must be linked to be executed from the given image slot. Using this mode results in a simpler code path and smaller code size. -config BOOT_RAM_LOAD - bool "RAM load" - help - If y, mcuboot selects the newest valid image based on the image version - numbers, thereafter the selected image is copied 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. - config BOOT_FIRMWARE_LOADER bool "Firmware loader" help @@ -271,19 +262,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 +298,28 @@ config BOOT_SWAP_SAVE_ENCTLV endif # !SINGLE_APPLICATION_SLOT +config BOOT_RAM_LOAD + bool "RAM load" + help + If y, mcuboot selects the newest valid image based on the image version + numbers, thereafter the selected image is copied 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 +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_ENCRYPTION_SUPPORT bool help diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h index 8f5d17bf5..90b91a27a 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -82,12 +82,6 @@ #define MCUBOOT_DIRECT_XIP_REVERT #endif -#ifdef CONFIG_BOOT_RAM_LOAD -#define MCUBOOT_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_BOOT_FIRMWARE_LOADER #define MCUBOOT_FIRMWARE_LOADER #endif @@ -108,6 +102,12 @@ #endif /* CONFIG_SINGLE_APPLICATION_SLOT */ +#ifdef CONFIG_BOOT_RAM_LOAD +#define MCUBOOT_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 From 74f74bc7d4681f139bcd4068282b354cd292da0d Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Fri, 19 Jul 2024 15:36:43 -0700 Subject: [PATCH 3/4] boot/zephyr: Support image sources other than flash MCUboot assumes that the images being booted live on a flash (or will be loaded to it, in case of serial support). However, some devices may end up loading an image from a different source, such as an I2C/eSPI storage, using some appropriate protocol. In these cases, usually the image is then loaded directly to RAM. This patch adds support for such scenarios, currently only on Zephyr port. It expects Zephyr CONFIG_FLASH_MAP_CUSTOM_BACKEND is used to provide Flash Map API to access the image in the non-flash device (but doesn't mandate it). This allows for an integration that is less intrusive on MCUboot code. It uses single loader to load an image from a set of "sources". The single loader loops through available sources and the first one to succeed signature/validation boots. To access the images, weak functions flash_map_id_get_next() and flash_map_id_get_current() are used. Default implementation keeps current behaviour for single loader, i.e. just loads from FLASH_AREA_IMAGE_PRIMARY(0). It is expected applications will reimplement these functions, allowing them to define a priority of different sources. As these different storage media may be ready only, MCUboot won't attempt to update them to record last source to succeed or so, it's application responsibility to define the correct priority of sources on every boot. Signed-off-by: Ederson de Souza --- boot/bootutil/src/bootutil_misc.c | 4 +- boot/zephyr/CMakeLists.txt | 2 +- boot/zephyr/Kconfig | 12 +++ boot/zephyr/flash_map_extended.c | 21 +++++ .../zephyr/include/flash_map_custom_sources.h | 46 ++++++++++ .../include/mcuboot_config/mcuboot_config.h | 4 + boot/zephyr/include/sysflash/sysflash.h | 9 ++ boot/zephyr/include/target.h | 5 +- boot/zephyr/single_loader.c | 85 ++++++++++++++----- 9 files changed, 165 insertions(+), 23 deletions(-) create mode 100644 boot/zephyr/include/flash_map_custom_sources.h diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c index 09fbe8948..88acb9749 100644 --- a/boot/bootutil/src/bootutil_misc.c +++ b/boot/bootutil/src/bootutil_misc.c @@ -333,7 +333,9 @@ 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) || \ +#if defined(MCUBOOT_FLASH_MAP_CUSTOM_SOURCES) + return flash_area_get_size(fap); +#elif defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SINGLE_APPLICATION_SLOT) || \ defined(MCUBOOT_FIRMWARE_LOADER) return boot_status_off(fap); #elif defined(MCUBOOT_SWAP_USING_MOVE) diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index 2c1d8b9f5..faad2b1de 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -80,7 +80,7 @@ if(DEFINED CONFIG_ENABLE_MGMT_PERUSER) endif() endif() -if(NOT DEFINED CONFIG_FLASH_PAGE_LAYOUT) +if(NOT DEFINED CONFIG_FLASH_PAGE_LAYOUT AND NOT DEFINED CONFIG_FLASH_MAP_CUSTOM_SOURCES) zephyr_library_sources( flash_map_legacy.c ) diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index b44e34c12..0b3c0b594 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -320,6 +320,18 @@ config BOOT_IMAGE_EXECUTABLE_RAM_SIZE default $(dt_chosen_reg_size_int,$(DT_CHOSEN_Z_SRAM),0) endif +config FLASH_MAP_CUSTOM_SOURCES + bool "Flash map custom sources" + select BOOT_RAM_LOAD + select SINGLE_APPLICATION_SLOT + help + Instead of using normal slots on flash to store the images, get images + from custom "sources", like I2C or eSPI. The application must provide + `flash_map_id_get_next` and `flash_map_id_get_next` functions tell + mcuboot where to find the images. The application must also have + a custom flash backend that can read the images from the custom sources. + Note that such images are always ready directly to RAM. + config BOOT_ENCRYPTION_SUPPORT bool help diff --git a/boot/zephyr/flash_map_extended.c b/boot/zephyr/flash_map_extended.c index 4631da75b..a641d026c 100644 --- a/boot/zephyr/flash_map_extended.c +++ b/boot/zephyr/flash_map_extended.c @@ -14,6 +14,10 @@ #include #include +#ifdef CONFIG_FLASH_MAP_CUSTOM_SOURCES +#include +#endif + #include "bootutil/bootutil_log.h" BOOT_LOG_MODULE_DECLARE(mcuboot); @@ -34,11 +38,20 @@ BOOT_LOG_MODULE_DECLARE(mcuboot); #define FLASH_DEVICE_BASE 0 #define FLASH_DEVICE_NODE DT_CHOSEN(zephyr_flash_controller) +#elif defined(CONFIG_FLASH_MAP_CUSTOM_SOURCES) + +#define FLASH_DEVICE_ID 0 +#define FLASH_DEVICE_BASE 0 + #else #error "FLASH_DEVICE_ID could not be determined" #endif +#if !defined(CONFIG_FLASH_MAP_CUSTOM_SOURCES) static const struct device *flash_dev = DEVICE_DT_GET(FLASH_DEVICE_NODE); +#else +static const struct device *flash_dev = NULL; +#endif int flash_device_base(uint8_t fd_id, uintptr_t *ret) { @@ -58,6 +71,14 @@ int flash_device_base(uint8_t fd_id, uintptr_t *ret) */ int flash_area_id_from_multi_image_slot(int image_index, int slot) { +#ifdef CONFIG_FLASH_MAP_CUSTOM_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) diff --git a/boot/zephyr/include/flash_map_custom_sources.h b/boot/zephyr/include/flash_map_custom_sources.h new file mode 100644 index 000000000..e07b0ab71 --- /dev/null +++ b/boot/zephyr/include/flash_map_custom_sources.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __FLASH_MAP_CUSTOM_SOURCES_H__ +#define __FLASH_MAP_CUSTOM_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_MAP_CUSTOM_SOURCES_H__ */ diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h index 90b91a27a..3ebc95833 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -307,6 +307,10 @@ #define MCUBOOT_BOOTUTIL_LIB_FOR_DIRECT_XIP 1 #endif +#ifdef CONFIG_FLASH_MAP_CUSTOM_SOURCES +#define MCUBOOT_FLASH_MAP_CUSTOM_SOURCES +#endif + #if CONFIG_BOOT_WATCHDOG_FEED #if CONFIG_NRFX_WDT #include diff --git a/boot/zephyr/include/sysflash/sysflash.h b/boot/zephyr/include/sysflash/sysflash.h index 1952950b9..f0caee043 100644 --- a/boot/zephyr/include/sysflash/sysflash.h +++ b/boot/zephyr/include/sysflash/sysflash.h @@ -60,9 +60,18 @@ static inline uint32_t __flash_area_ids_for_slot(int img, int slot) #else /* !CONFIG_SINGLE_APPLICATION_SLOT && !CONFIG_MCUBOOT_BOOTLOADER_MODE_SINGLE_APP */ +#if defined(CONFIG_FLASH_MAP_CUSTOM_SOURCES) + +#define FLASH_AREA_IMAGE_PRIMARY(x) 0 +#define FLASH_AREA_IMAGE_SECONDARY(x) 0 + +#else /* CONFIG_FLASH_MAP_CUSTOM_SOURCES */ + #define FLASH_AREA_IMAGE_PRIMARY(x) FIXED_PARTITION_ID(slot0_partition) #define FLASH_AREA_IMAGE_SECONDARY(x) FIXED_PARTITION_ID(slot0_partition) +#endif /* CONFIG_FLASH_MAP_CUSTOM_SOURCES */ + #endif /* CONFIG_SINGLE_APPLICATION_SLOT */ #endif /* __SYSFLASH_H__ */ diff --git a/boot/zephyr/include/target.h b/boot/zephyr/include/target.h index 9bbfd4b19..4da82775a 100644 --- a/boot/zephyr/include/target.h +++ b/boot/zephyr/include/target.h @@ -30,13 +30,14 @@ /* * Sanity check the target support. */ -#if (!defined(CONFIG_XTENSA) && !DT_HAS_CHOSEN(zephyr_flash_controller)) || \ +#if (!defined(CONFIG_FLASH_MAP_CUSTOM_SOURCES)) && \ + ((!defined(CONFIG_XTENSA) && !DT_HAS_CHOSEN(zephyr_flash_controller)) || \ (defined(CONFIG_XTENSA) && !DT_NODE_EXISTS(DT_INST(0, jedec_spi_nor)) && \ !defined(CONFIG_SOC_FAMILY_ESPRESSIF_ESP32)) || \ !defined(FLASH_ALIGN) || \ !(FIXED_PARTITION_EXISTS(slot0_partition)) || \ !(FIXED_PARTITION_EXISTS(slot1_partition) || CONFIG_SINGLE_APPLICATION_SLOT) || \ - (defined(CONFIG_BOOT_SWAP_USING_SCRATCH) && !FIXED_PARTITION_EXISTS(scratch_partition)) + (defined(CONFIG_BOOT_SWAP_USING_SCRATCH) && !FIXED_PARTITION_EXISTS(scratch_partition))) #error "Target support is incomplete; cannot build mcuboot." #endif diff --git a/boot/zephyr/single_loader.c b/boot/zephyr/single_loader.c index 75374d2db..44e93fe6f 100644 --- a/boot/zephyr/single_loader.c +++ b/boot/zephyr/single_loader.c @@ -20,6 +20,26 @@ 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) { + return false; + } + + *id = FLASH_AREA_IMAGE_PRIMARY(0); + + return true; +} + +__weak bool +flash_map_id_get_current(uint8_t *id) +{ + *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 +122,62 @@ 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 = boot_image_load_header(_fa_p, &_hdr); - if (rc != 0) - goto out; + rc = flash_area_open(flash_id, &_fa_p); + if (rc != 0) { + continue; + } + + rc = boot_image_load_header(_fa_p, &_hdr); + if (rc != 0) { + flash_area_close(_fa_p); + continue; + } + +#ifdef MCUBOOT_FLASH_MAP_CUSTOM_SOURCES + static struct boot_loader_state state; + state.imgs[0][0].hdr = _hdr; + + rc = boot_load_image_to_sram(&state); +#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_FLASH_MAP_CUSTOM_SOURCES + 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_FLASH_MAP_CUSTOM_SOURCES + 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); -out: - flash_area_close(_fa_p); + break; + } FIH_RET(fih_rc); } From 9645ebd2d7725cb1d01abf8068c1f70935a1f137 Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Wed, 7 Aug 2024 08:34:26 -0700 Subject: [PATCH 4/4] samples: Add a sample for non-flash-source A sample for non-flash-source on MEC17 EVB. It provides implementation for Zephyr flash_area_open_custom(), so the right flash map implementation is used, and MCUboot flash_map_id_get_next() and flash_map_id_get_current() to prioritize sources. It should show what is expected from an application to be able to use non-flash sources for images. For the I2C device simulation, an Aardvark I2C host adapter is needed, and a script is provided to make the image loadable via I2C. An implementation for an "Aardvark driver" on top of I2C is also provided. Finally, a sample application to be loaded is also available. For more details on how to build and test the samples, check the provided README.md. Signed-off-by: Ederson de Souza --- boot/zephyr/CMakeLists.txt | 10 ++ boot/zephyr/Kconfig | 11 ++ samples/non-flash-source/zephyr/README.md | 66 +++++++ .../zephyr/aardvark_i2c_flash.c | 109 ++++++++++++ .../zephyr/app/CMakeLists.txt | 14 ++ .../app/boards/mec172xevb_assy6906.overlay | 24 +++ samples/non-flash-source/zephyr/app/prj.conf | 5 + .../zephyr/app/scripts/aardvark_i2c_image.py | 161 ++++++++++++++++++ .../non-flash-source/zephyr/app/src/main.c | 14 ++ .../zephyr/boards/mec172xevb_assy6906.conf | 5 + .../zephyr/boards/mec172xevb_assy6906.overlay | 26 +++ .../zephyr/flash_map_dispatcher.c | 60 +++++++ .../zephyr/include/aardvark_i2c_flash.h | 12 ++ samples/non-flash-source/zephyr/sample.conf | 3 + 14 files changed, 520 insertions(+) create mode 100644 samples/non-flash-source/zephyr/README.md create mode 100644 samples/non-flash-source/zephyr/aardvark_i2c_flash.c create mode 100644 samples/non-flash-source/zephyr/app/CMakeLists.txt create mode 100644 samples/non-flash-source/zephyr/app/boards/mec172xevb_assy6906.overlay create mode 100644 samples/non-flash-source/zephyr/app/prj.conf create mode 100644 samples/non-flash-source/zephyr/app/scripts/aardvark_i2c_image.py create mode 100644 samples/non-flash-source/zephyr/app/src/main.c create mode 100644 samples/non-flash-source/zephyr/boards/mec172xevb_assy6906.conf create mode 100644 samples/non-flash-source/zephyr/boards/mec172xevb_assy6906.overlay create mode 100644 samples/non-flash-source/zephyr/flash_map_dispatcher.c create mode 100644 samples/non-flash-source/zephyr/include/aardvark_i2c_flash.h create mode 100644 samples/non-flash-source/zephyr/sample.conf diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index faad2b1de..9c0c2af0d 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -369,6 +369,16 @@ if(CONFIG_MCUBOOT_BOOT_BANNER) zephyr_sources(kernel/banner.c) endif() +if(CONFIG_AARDVARK_I2C_FLASH) + zephyr_library_sources( + ${MCUBOOT_DIR}/samples/non-flash-source/zephyr/aardvark_i2c_flash.c + ${MCUBOOT_DIR}/samples/non-flash-source/zephyr/flash_map_dispatcher.c + ) + zephyr_library_include_directories( + ${MCUBOOT_DIR}/samples/non-flash-source/zephyr/include + ) +endif() + if(SYSBUILD) function(align_up num align result) math(EXPR out "(((${num}) + ((${align}) - 1)) & ~((${align}) - 1))") diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index 0b3c0b594..7299d55e0 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -332,6 +332,17 @@ config FLASH_MAP_CUSTOM_SOURCES a custom flash backend that can read the images from the custom sources. Note that such images are always ready directly to RAM. +if FLASH_MAP_CUSTOM_SOURCES +config AARDVARK_I2C_FLASH + bool "Use aardvark to emulate an I2C device from where a firmware image is read" + default n + depends on I2C + help + If you have an I2C device that acts as a flash memory, you can enable this + option to read the firmware image from it. + +endif + config BOOT_ENCRYPTION_SUPPORT bool help diff --git a/samples/non-flash-source/zephyr/README.md b/samples/non-flash-source/zephyr/README.md new file mode 100644 index 000000000..53f40ee13 --- /dev/null +++ b/samples/non-flash-source/zephyr/README.md @@ -0,0 +1,66 @@ +# Non-flash storage sample application + +This sample demonstrates how to use a non flash storage to retrieve the image +being booted. It was tested on an MEC17 EVB. The image is provided via I2C and +an Aardvark I2C/SPI Host Adapter. A script, using Aardvark API, is used to +provide the image. + +## Set up + +Set up the MEC17 EVB. The instructions here expect that MEC17 serial, dediprog +and Aardvark are connected to the same host, in which things are built. Adjust +accordingly to different topologies. Refer to +https://docs.zephyrproject.org/latest/boards/microchip/mec172xevb_assy6906/doc/index.html#programming-and-debugging to have the board set up, with serial output. + +Connect the Aardvark to the MEC17 EVB I2C pins. The Aardvark should be +connected to the host machine. + +To use the Aardvark script, you need to have the Aardvark API installed. Refer +to https://www.totalphase.com/products/aardvark-i2cspi/ for details. The library +(aardvark.so) and Python bindings (aardvark.py) should be on the available (usually, +copied to the scripts directory, alongside the aardvark_i2c_image.py). + +## Build + +Build mcuboot. First, ensure ZEPHYR_SDK_INSTALL_DIR is defined, as well that +MEC17 image generator is on path. Refer to https://docs.zephyrproject.org/latest/boards/microchip/mec172xevb_assy6906/doc/index.html#setup for details. From the mcuboot +directory, run the following commands: + +``` + source /zephyr-env.sh + + west build -p -b mec172xevb_assy6906 boot/zephyr/ \ + -- -DEXTRA_DTC_OVERLAY_FILE=../../samples/non-flash-source/zephyr/boards/mec172xevb_assy6906.overlay \ + -DEXTRA_CONF_FILE="../../samples/non-flash-source/zephyr/boards/mec172xevb_assy6906.conf;../../samples/non-flash-source/zephyr/sample.conf" + + west build -t flash +``` + +Then, build the sample application to be loaded via I2C. From the sample +app directory (mcuboot/samples/non-flash-source/zephyr/app), run: + +``` + west build -p -b mec172xevb_assy6906 . +``` + +## Run + +Run the script to provide the image. From the sample app directory, run: + +``` + python3 scripts/aardvark_i2c_image.py 0x20 build/zephyr/zephyr.signed.bin +``` + +Now, when you reset the MEC17 EVB, the image will be loaded via I2C. You shall +see something like the following in the serial output: + +``` + *** Booting MCUboot v2.1.0-67-g9c2b470ca027 *** + *** Using Zephyr OS build v3.7.0-715-geeedb29f7e1a *** + I: Starting bootloader + I: Image 0 RAM loading to 0xd0000 is succeeded. + I: Bootloader chainload address offset: 0xd0000 + I: Jumping to the first image slot + *** Booting Zephyr OS build v3.7.0-715-geeedb29f7e1a *** + Hello World from Zephyr on mec172xevb_assy6906! +``` diff --git a/samples/non-flash-source/zephyr/aardvark_i2c_flash.c b/samples/non-flash-source/zephyr/aardvark_i2c_flash.c new file mode 100644 index 000000000..4662b3727 --- /dev/null +++ b/samples/non-flash-source/zephyr/aardvark_i2c_flash.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#define I2C_ADDR 0x20 + +#define I2C_WAIT_USEC 1000 * 5 + +static const struct device *const i2c_bus = DEVICE_DT_GET(DT_ALIAS(i2c0)); +static struct flash_area area; + +static uint8_t data[64]; +static uint8_t write_data[16]; + +static int aardvark_read(const struct device *dev, uint8_t *buf, size_t buf_len, uint32_t addr, uint8_t len) +{ + int ret; + + if (len > 64 || buf_len < len) { + return -EINVAL; + } + + write_data[0] = 0x02; + write_data[1] = (addr >> 24) & 0xFF; + write_data[2] = (addr >> 16) & 0xFF; + write_data[3] = (addr >> 8) & 0xFF; + write_data[4] = addr & 0xFF; + + write_data[5] = len; + + i2c_write(i2c_bus, (uint8_t *)&write_data, 6, I2C_ADDR); + + k_busy_wait(I2C_WAIT_USEC); + ret = i2c_read(i2c_bus, data, len, I2C_ADDR); + + /* On esp32s3, I can't just i2c_read directly into buf if it's + * in IRAM, as IRAM access needs 4-byte alignment. Hence, the memcpy + * below. */ + memcpy(buf, data, len); + + return 0; +} + +static void aardvark_get_size(const struct device *dev, uint32_t *size) +{ + write_data[0] = 0x01; + i2c_write(i2c_bus, (uint8_t *)&write_data, 1, I2C_ADDR); + k_busy_wait(I2C_WAIT_USEC); + i2c_read(i2c_bus, (uint8_t *)&data, 4, I2C_ADDR); + *size = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; +} + +static int aardvark_flash_area_read(const struct flash_area *fa, off_t off, void *dst_v, size_t len) +{ + size_t to_read; + uint8_t *dst = dst_v; + + if (off < 0 || (off + len > fa->fa_size)) { + return -EINVAL; + } + + while (len > 0) { + to_read = MIN(len, 64); + aardvark_read(i2c_bus, dst, len, off, to_read); + off += to_read; + len -= to_read; + dst += to_read; + k_busy_wait(I2C_WAIT_USEC); + } + + return 0; +} + +static void aardvark_flash_area_close(const struct flash_area *fa) +{ +} + +static uint32_t aardvark_flash_area_align(const struct flash_area *fa) +{ + return 1; +} + +static const struct flash_map_backend_api aardvark_flash_map_backend_api = { + .read = aardvark_flash_area_read, + .align = aardvark_flash_area_align, + .close = aardvark_flash_area_close, +}; + +int aardvark_flash_area_open(uint8_t id, const struct flash_area **fa) +{ + if (!device_is_ready(i2c_bus)) + return -ENODEV; + + aardvark_get_size(i2c_bus, &area.fa_size); + + area.api = &aardvark_flash_map_backend_api; + area.fa_id = id; + *fa = &area; + + + return 0; +} diff --git a/samples/non-flash-source/zephyr/app/CMakeLists.txt b/samples/non-flash-source/zephyr/app/CMakeLists.txt new file mode 100644 index 000000000..bb60128f3 --- /dev/null +++ b/samples/non-flash-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/non-flash-source/zephyr/app/boards/mec172xevb_assy6906.overlay b/samples/non-flash-source/zephyr/app/boards/mec172xevb_assy6906.overlay new file mode 100644 index 000000000..b90171ec2 --- /dev/null +++ b/samples/non-flash-source/zephyr/app/boards/mec172xevb_assy6906.overlay @@ -0,0 +1,24 @@ +/ { + chosen { + zephyr,code-partition = &slot0_partition; + }; +}; + +&flash0 { + compatible = "soc-nv-flash"; + write-block-size = <1>; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "boot"; + reg = <0x0 0x10000>; + }; + slot0_partition: partition@10000 { + label = "slot0"; + reg = <0x10000 0x48000>; + }; + }; +}; diff --git a/samples/non-flash-source/zephyr/app/prj.conf b/samples/non-flash-source/zephyr/app/prj.conf new file mode 100644 index 000000000..c3812adc7 --- /dev/null +++ b/samples/non-flash-source/zephyr/app/prj.conf @@ -0,0 +1,5 @@ +CONFIG_BOOTLOADER_MCUBOOT=y +CONFIG_MCUBOOT_IMGTOOL_LOAD_ADDRESS=0xd0000 +CONFIG_ROM_START_OFFSET=0x400 + +CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="./bootloader/mcuboot/root-rsa-2048.pem" diff --git a/samples/non-flash-source/zephyr/app/scripts/aardvark_i2c_image.py b/samples/non-flash-source/zephyr/app/scripts/aardvark_i2c_image.py new file mode 100644 index 000000000..0c75f554c --- /dev/null +++ b/samples/non-flash-source/zephyr/app/scripts/aardvark_i2c_image.py @@ -0,0 +1,161 @@ +# SPDX-License-Identifier: Apache-2.0 + +import argparse +import signal +import sys +import time + +from aardvark_py import * + +done = False +contents = None + + +def parse_args(): + global args + parser = argparse.ArgumentParser(description='Aardvark I2C "Storage Device"') + parser.add_argument("-p", "--port", default=0, help="Aardvark port number") + parser.add_argument("device_addr", type=str, help="I2C device address") + parser.add_argument("file", type=str, help="File to provide") + parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output") + args = parser.parse_args() + + +def signal_handler(sig, frame): + global done + global contents + + if sig == signal.SIGINT: + done = True + elif sig == signal.SIGQUIT: + print("Reloading file") + with open(args.file, "rb") as f: + contents = f.read() + + +def watch(handle): + print("Watching I2C data (Use ^\\ to reload file) ...") + + # Loop until aa_async_poll times out + while True: + result = aa_async_poll(handle, 100) + # Read the I2C message. + if (result & AA_ASYNC_I2C_READ) == AA_ASYNC_I2C_READ: + # Get data written by master + (num_bytes, addr, data_in) = aa_i2c_slave_read(handle, 6) + + if num_bytes < 0: + print("slave_read error: %s" % aa_status_string(num_bytes)) + return + + if num_bytes == 0: + continue + + if data_in[0] == 0x1: + if args.verbose: + print( + f"Got 0x1, asking size of file. Responding with {len(contents)}" + ) + ret = aa_i2c_slave_set_response( + handle, + array( + "B", + [ + (len(contents) >> 24) & 0xFF, + (len(contents) >> 16) & 0xFF, + (len(contents) >> 8) & 0xFF, + len(contents) & 0xFF, + ], + ), + ) + elif data_in[0] == 0x2: + addr = ( + data_in[1] << 24 | data_in[2] << 16 | data_in[3] << 8 | data_in[4] + ) + size = data_in[5] + + if args.verbose: + print(f"Got 0x2, asking for data, at {addr} with size {size}.") + + if addr < 0 or addr + size > len(contents): + print("Requested data is out of bounds, responding with 0x0") + ret = aa_i2c_slave_set_response(handle, array("B", [0x0])) + else: + ret = aa_i2c_slave_set_response( + handle, array("B", contents[addr : addr + size]) + ) + else: + print("Got unknown data, responding with 0x0") + ret = aa_i2c_slave_set_response(handle, array("B", [0x0])) + + elif (result & AA_ASYNC_I2C_WRITE) == AA_ASYNC_I2C_WRITE: + # Get number of bytes written to master + num_bytes = aa_i2c_slave_write_stats(handle) + + if num_bytes < 0: + print("slave_write_stats error: %s" % aa_status_string(num_bytes)) + return + + # Print status information to the screen + if args.verbose: + print(f"Number of bytes written to master: {num_bytes:04}\n") + + elif result == AA_ASYNC_NO_DATA: + if done: + break + else: + print("error: non-I2C asynchronous message is pending", result) + return + + +def main(): + global contents + + parse_args() + + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGQUIT, signal_handler) + + port = args.port + addr = int(args.device_addr, 0) + + # Open the device + handle = aa_open(port) + if handle <= 0: + print("Unable to open Aardvark device on port %d" % port) + print("Error code = %d" % handle) + sys.exit() + + # Ensure that the I2C subsystem is enabled + aa_configure(handle, AA_CONFIG_SPI_I2C) + + # Disable the I2C bus pullup resistors (2.2k resistors). + # This command is only effective on v2.0 hardware or greater. + # The pullup resistors on the v1.02 hardware are enabled by default. + aa_i2c_pullup(handle, AA_I2C_PULLUP_NONE) + + # Power the EEPROM using the Aardvark adapter's power supply. + # This command is only effective on v2.0 hardware or greater. + # The power pins on the v1.02 hardware are not enabled by default. + aa_target_power(handle, AA_TARGET_POWER_BOTH) + + # Set default response + aa_i2c_slave_set_response(handle, array("B", [0])) + + # Enabled the device + aa_i2c_slave_enable(handle, addr, 64, 6) + + # Read the file + with open(args.file, "rb") as f: + contents = f.read() + + # Watch the I2C port + watch(handle) + + # Disable and close the device + aa_i2c_slave_disable(handle) + aa_close(handle) + + +if __name__ == "__main__": + main() diff --git a/samples/non-flash-source/zephyr/app/src/main.c b/samples/non-flash-source/zephyr/app/src/main.c new file mode 100644 index 000000000..fdaf32cf0 --- /dev/null +++ b/samples/non-flash-source/zephyr/app/src/main.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2017 Linaro, Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +int main(void) +{ + printk("Hello World from %s on %s!\n", + MCUBOOT_HELLO_WORLD_FROM, CONFIG_BOARD); +} diff --git a/samples/non-flash-source/zephyr/boards/mec172xevb_assy6906.conf b/samples/non-flash-source/zephyr/boards/mec172xevb_assy6906.conf new file mode 100644 index 000000000..6b4488a7f --- /dev/null +++ b/samples/non-flash-source/zephyr/boards/mec172xevb_assy6906.conf @@ -0,0 +1,5 @@ +CONFIG_BOOT_IMAGE_EXECUTABLE_RAM_START=0x000C8000 +CONFIG_BOOT_IMAGE_EXECUTABLE_RAM_SIZE=327680 + +CONFIG_AARDVARK_I2C_FLASH=y +CONFIG_I2C=y diff --git a/samples/non-flash-source/zephyr/boards/mec172xevb_assy6906.overlay b/samples/non-flash-source/zephyr/boards/mec172xevb_assy6906.overlay new file mode 100644 index 000000000..e62913459 --- /dev/null +++ b/samples/non-flash-source/zephyr/boards/mec172xevb_assy6906.overlay @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + zephyr,code-partition = &boot_partition; + }; +}; + +&flash0 { + compatible = "soc-nv-flash"; + write-block-size = <1>; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "boot"; + reg = <0x0 0x10000>; + }; + }; +}; diff --git a/samples/non-flash-source/zephyr/flash_map_dispatcher.c b/samples/non-flash-source/zephyr/flash_map_dispatcher.c new file mode 100644 index 000000000..d8fce595f --- /dev/null +++ b/samples/non-flash-source/zephyr/flash_map_dispatcher.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "aardvark_i2c_flash.h" + +static int curr_idx = -1; + +static uint8_t known_ids[] = { + -1, /* Just to show a "failing" image. Next one should work */ + AARDVARK_FLASH_AREA_ID +}; + +bool +flash_map_id_get_next(uint8_t *id, bool reset) +{ + if (reset) { + curr_idx = 0; + } 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; +} + +int +flash_area_open_custom(uint8_t id, const struct flash_area **fap) +{ + switch (id) { + case 0: + return flash_area_open(id, fap); + case AARDVARK_FLASH_AREA_ID: + return aardvark_flash_area_open(id, fap); + default: + return -1; + } +} diff --git a/samples/non-flash-source/zephyr/include/aardvark_i2c_flash.h b/samples/non-flash-source/zephyr/include/aardvark_i2c_flash.h new file mode 100644 index 000000000..dc187ff15 --- /dev/null +++ b/samples/non-flash-source/zephyr/include/aardvark_i2c_flash.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define AARDVARK_FLASH_AREA_ID (0 | FLASH_MAP_CUSTOM_BACKEND_MASK) + +int aardvark_flash_area_open(uint8_t id, const struct flash_area **fap); diff --git a/samples/non-flash-source/zephyr/sample.conf b/samples/non-flash-source/zephyr/sample.conf new file mode 100644 index 000000000..a3e519b61 --- /dev/null +++ b/samples/non-flash-source/zephyr/sample.conf @@ -0,0 +1,3 @@ +CONFIG_FLASH_MAP_CUSTOM_SOURCES=y +CONFIG_BOOT_SIGNATURE_TYPE_RSA=y +CONFIG_FLASH_MAP_CUSTOM_BACKEND=y