From 4772da1506272c5c13fe0b1dc1b6e026a4793541 Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Mon, 23 Jun 2025 19:07:38 +0200 Subject: [PATCH 1/2] bootutil: Support for logical sectors The commit adds support for logical/software sectors. User can select size of sector by which image will be moved using configuration identifier MCUBOOT_LOGICAL_SECTOR_SIZE. Non-0 value set to MCUBOOT_LOGICAL_SECTOR_SIZE will be used as sector size for image swap algorithms. Note that the value provided here should be aligned to hardware erase page of device(s) used and may not be smaller than of such a device. There is also additional option provided, MCUBOOT_LOGICAL_SECTOR_VALIDATE, that allows to enable validation of selected logical sector against true layout of a device. Signed-off-by: Dominik Ermel --- boot/bootutil/src/bootutil_misc.c | 140 +++++++++++++++++++++++++++++- boot/bootutil/src/bootutil_priv.h | 24 +++++ boot/bootutil/src/loader.c | 6 ++ 3 files changed, 168 insertions(+), 2 deletions(-) diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c index 3f774b664..e45603bbd 100644 --- a/boot/bootutil/src/bootutil_misc.c +++ b/boot/bootutil/src/bootutil_misc.c @@ -56,6 +56,7 @@ BOOT_LOG_MODULE_DECLARE(mcuboot); /* Currently only used by imgmgr */ int boot_current_slot; +#if !defined(MCUBOOT_LOGICAL_SECTOR_SIZE) || MCUBOOT_LOGICAL_SECTOR_SIZE == 0 #if (!defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)) || \ defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) /* Used for holding static buffers in multiple functions to work around issues @@ -63,6 +64,7 @@ defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) */ static struct boot_sector_buffer sector_buffers; #endif +#endif /* !defined(MCUBOOT_LOGICAL_SECTOR_SIZE) || MCUBOOT_LOGICAL_SECTOR_SIZE == 0 */ /** * @brief Determine if the data at two memory addresses is equal @@ -625,6 +627,7 @@ boot_erase_region(const struct flash_area *fa, uint32_t off, uint32_t size, bool #if (!defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)) || \ defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +#if !defined(MCUBOOT_LOGICAL_SECTOR_SIZE) || MCUBOOT_LOGICAL_SECTOR_SIZE == 0 int boot_initialize_area(struct boot_loader_state *state, int flash_area) { @@ -665,6 +668,112 @@ boot_initialize_area(struct boot_loader_state *state, int flash_area) return 0; } +#else /* defined(MCUBOOT_LOGICAL_SECTOR_SIZE) && MCUBOOT_LOGICAL_SECTOR_SIZE != 0 */ +#if defined(MCUBOOT_LOGICAL_SECTOR_VALIDATION) +/* Validation can only run once all flash areas are open and pointers to + * flash area objects are stored in state. + */ +static int +boot_validate_logical_sectors(const struct boot_loader_state *state, int faid, const struct flash_area *fa) +{ + uint32_t num_sectors = BOOT_MAX_IMG_SECTORS; + size_t slot_size; + size_t slot_off; + size_t sect_off = 0; + int rc; + int final_rc = 0; + + assert(fa != NULL); + assert(faid != 0); + + slot_off = flash_area_get_off(fa); + slot_size = flash_area_get_size(fa); + + + /* Go till all validations are complete or we face issue that does not allow + * to proceede with further tests. + */ + BOOT_LOG_INF("boot_validate_logical_sectors: validating flash area %p", fa); + BOOT_LOG_INF("boot_validate_logical_sectors: MCUBOOT_LOGICAL_SECTOR_SIZE == 0x%x", + MCUBOOT_LOGICAL_SECTOR_SIZE); + BOOT_LOG_INF("boot_validate_logical_sectors: slot offset == 0x%x", slot_off); + if (slot_size != 0) { + BOOT_LOG_INF("boot_validate_logical_sectors: slot size == 0x%x", slot_size); + } else { + BOOT_LOG_ERR("boot_validate_logical_sectors: 0 size slot"); + return BOOT_EFLASH; + } + + BOOT_LOG_INF("boot_validate_logical_sectors: max %d logical sectors", + slot_size / MCUBOOT_LOGICAL_SECTOR_SIZE); + + if (slot_off % MCUBOOT_LOGICAL_SECTOR_SIZE) { + BOOT_LOG_ERR("boot_validate_logical_sectors: area offset not aligned"); + final_rc = BOOT_EFLASH; + } + + if (slot_size % MCUBOOT_LOGICAL_SECTOR_SIZE) { + BOOT_LOG_ERR("boot_validate_logical_sectors: area size not aligned"); + final_rc = BOOT_EFLASH; + } + + /* Check all hardware specific pages against erase pages of a device */ + for (size_t i = 0; i < num_sectors; i++) { + struct flash_sector fas; + + MCUBOOT_WATCHDOG_FEED(); + + BOOT_LOG_INF("boot_validate_logical_sectors: page 0x%x:0x%x ", slot_off, sect_off); + rc = flash_area_get_sector(fa, sect_off, &fas); + if (rc < 0) { + BOOT_LOG_ERR("boot_validate_logical_sectors: query err %d", rc); + final_rc = BOOT_EFLASH; + continue; + } + + + if (flash_sector_get_off(&fas) % MCUBOOT_LOGICAL_SECTOR_SIZE) { + BOOT_LOG_ERR("boot_validate_logical_sectors: misaligned offset"); + final_rc = BOOT_EFLASH; + } + + sect_off += flash_sector_get_size(&fas); + } + + BOOT_LOG_INF("boot_validate_logical_sectors: done %d", final_rc); + + return final_rc; +} +#endif /* MCUBOOT_LOGICAL_SECTOR_VALIDATION */ + +static int +boot_initialize_area(struct boot_loader_state *state, int flash_area) +{ + size_t area_size; + uint32_t *out_num_sectors; + + if (flash_area == FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state))) { + area_size = flash_area_get_size(BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)); + out_num_sectors = &BOOT_IMG(state, BOOT_PRIMARY_SLOT).num_sectors; + } else if (flash_area == FLASH_AREA_IMAGE_SECONDARY(BOOT_CURR_IMG(state))) { + area_size = flash_area_get_size(BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT)); + out_num_sectors = &BOOT_IMG(state, BOOT_SECONDARY_SLOT).num_sectors; +#if MCUBOOT_SWAP_USING_SCRATCH + } else if (flash_area == FLASH_AREA_IMAGE_SCRATCH) { + area_size = flash_area_get_size(state->scratch.area); + out_num_sectors = &state->scratch.num_sectors; +#endif + } else { + return BOOT_EFLASH; + } + + *out_num_sectors = area_size / MCUBOOT_LOGICAL_SECTOR_SIZE; + + return 0; +} + +#endif /* defined(MCUBOOT_LOGICAL_SECTOR_SIZE) && MCUBOOT_LOGICAL_SECTOR_SIZE != 0 */ + static uint32_t boot_write_sz(struct boot_loader_state *state) { @@ -694,12 +803,13 @@ boot_read_sectors(struct boot_loader_state *state, struct boot_sector_buffer *se uint8_t image_index; int rc; + image_index = BOOT_CURR_IMG(state); + +#if !defined(MCUBOOT_LOGICAL_SECTOR_SIZE) || MCUBOOT_LOGICAL_SECTOR_SIZE == 0 if (sectors == NULL) { sectors = §or_buffers; } - image_index = BOOT_CURR_IMG(state); - BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors = sectors->primary[image_index]; #if BOOT_NUM_SLOTS > 1 @@ -709,6 +819,9 @@ boot_read_sectors(struct boot_loader_state *state, struct boot_sector_buffer *se state->scratch.sectors = sectors->scratch; #endif #endif +#else + (void)sectors; +#endif /* !defined(MCUBOOT_LOGICAL_SECTOR_SIZE) || MCUBOOT_LOGICAL_SECTOR_SIZE == 0 */ rc = boot_initialize_area(state, FLASH_AREA_IMAGE_PRIMARY(image_index)); if (rc != 0) { @@ -732,6 +845,29 @@ boot_read_sectors(struct boot_loader_state *state, struct boot_sector_buffer *se BOOT_WRITE_SZ(state) = boot_write_sz(state); +#if defined(MCUBOOT_LOGICAL_SECTOR_VALIDATION) + BOOT_LOG_INF("boot_read_sectors: validate image %d slots", image_index); + BOOT_LOG_INF("boot_read_sectors: BOOT_PRIMARY_SLOT"); + if (boot_validate_logical_sectors(state, FLASH_AREA_IMAGE_PRIMARY(image_index), + BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)) != 0) { + rc = BOOT_EFLASH; + } + + BOOT_LOG_INF("boot_read_sectors: BOOT_SECONDARY_SLOT"); + if(boot_validate_logical_sectors(state, FLASH_AREA_IMAGE_SECONDARY(image_index), + BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT)) != 0) { + rc = BOOT_EFLASH_SEC; + } + +#if MCUBOOT_SWAP_USING_SCRATCH + BOOT_LOG_INF("boot_read_sectors: SCRATCH"); + if(boot_validate_logical_sectors(state, FLASH_AREA_IMAGE_SCRATCH, + state->scratch.area) != 0) { + rc = BOOT_EFLASH; + } +#endif /* MCUBOOT_SWAP_USING_SCRATCH */ +#endif /* defined(MCUBOOT_LOGICAL_SECTOR_VALIDATION) */ + return 0; } #endif diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h index c0a285bbe..f60ef6ccc 100644 --- a/boot/bootutil/src/bootutil_priv.h +++ b/boot/bootutil/src/bootutil_priv.h @@ -245,14 +245,18 @@ struct boot_loader_state { struct { struct image_header hdr; const struct flash_area *area; +#if !defined(MCUBOOT_LOGICAL_SECTOR_SIZE) || MCUBOOT_LOGICAL_SECTOR_SIZE == 0 boot_sector_t *sectors; +#endif uint32_t num_sectors; } imgs[BOOT_IMAGE_NUMBER][BOOT_NUM_SLOTS]; #if MCUBOOT_SWAP_USING_SCRATCH struct { const struct flash_area *area; +#if !defined(MCUBOOT_LOGICAL_SECTOR_SIZE) || MCUBOOT_LOGICAL_SECTOR_SIZE == 0 boot_sector_t *sectors; +#endif uint32_t num_sectors; } scratch; #endif @@ -515,6 +519,7 @@ boot_img_slot_off(struct boot_loader_state *state, size_t slot) return flash_area_get_off(BOOT_IMG_AREA(state, slot)); } +#if !defined(MCUBOOT_LOGICAL_SECTOR_SIZE) || MCUBOOT_LOGICAL_SECTOR_SIZE == 0 #ifndef MCUBOOT_USE_FLASH_AREA_GET_SECTORS static inline size_t @@ -554,6 +559,25 @@ boot_img_sector_off(const struct boot_loader_state *state, size_t slot, } #endif /* !defined(MCUBOOT_USE_FLASH_AREA_GET_SECTORS) */ +#else +static inline size_t +boot_img_sector_size(const struct boot_loader_state *state, + size_t slot, size_t sector) +{ + return MCUBOOT_LOGICAL_SECTOR_SIZE; +} + +/* + * Offset of the sector from the beginning of the image, NOT the flash + * device. + */ +static inline uint32_t +boot_img_sector_off(const struct boot_loader_state *state, size_t slot, + size_t sector) +{ + return MCUBOOT_LOGICAL_SECTOR_SIZE * sector; +} +#endif #ifdef MCUBOOT_RAM_LOAD # ifdef __BOOTSIM__ diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c index 37adc5faf..8a28ee9f7 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -651,6 +651,7 @@ boot_image_check(struct boot_loader_state *state, struct image_header *hdr, } #if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) +#if !defined(MCUBOOT_LOGICAL_SECTOR_SIZE) || MCUBOOT_LOGICAL_SECTOR_SIZE == 0 static fih_ret split_image_check(struct image_header *app_hdr, const struct flash_area *app_fap, @@ -681,6 +682,7 @@ split_image_check(struct image_header *app_hdr, out: FIH_RET(fih_rc); } +#endif /* !defined(MCUBOOT_LOGICAL_SECTOR_SIZE) || MCUBOOT_LOGICAL_SECTOR_SIZE == 0 */ #endif /* !MCUBOOT_DIRECT_XIP && !MCUBOOT_RAM_LOAD */ /* @@ -2159,10 +2161,12 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) BOOT_LOG_DBG("context_boot_go"); +#if !defined(MCUBOOT_LOGICAL_SECTOR_SIZE) || MCUBOOT_LOGICAL_SECTOR_SIZE == 0 #if defined(__BOOTSIM__) struct boot_sector_buffer sector_buf; sectors = §or_buf; #endif +#endif /* !defined(MCUBOOT_LOGICAL_SECTOR_SIZE) || MCUBOOT_LOGICAL_SECTOR_SIZE == 0 */ has_upgrade = false; @@ -2422,6 +2426,7 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) FIH_RET(fih_rc); } +#if !defined(MCUBOOT_LOGICAL_SECTOR_SIZE) || MCUBOOT_LOGICAL_SECTOR_SIZE == 0 fih_ret split_go(int loader_slot, int split_slot, void **entry) { @@ -2487,6 +2492,7 @@ split_go(int loader_slot, int split_slot, void **entry) FIH_RET(fih_rc); } +#endif /* !defined(MCUBOOT_LOGICAL_SECTOR_SIZE) || MCUBOOT_LOGICAL_SECTOR_SIZE == 0 */ #else /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */ From 06c777aeb02d99aaaf4c5c9aaa94ed64023f29a6 Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Mon, 23 Jun 2025 19:20:13 +0200 Subject: [PATCH 2/2] zephyr: Allow enabling logical sectors Add Kconfigs: - CONFIG_MCUBOOT_LOGICAL_SECTOR_SIZE - CONFIG_MCUBOOT_LOGICAL_SECTOR_VALIDATION Signed-off-by: Dominik Ermel --- boot/zephyr/Kconfig | 23 +++++++- boot/zephyr/flash_map_extended.c | 55 ++++++++++++++----- .../include/mcuboot_config/mcuboot_config.h | 21 +++++++ 3 files changed, 83 insertions(+), 16 deletions(-) diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index 7c18a0329..d75cceb3b 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -1112,6 +1112,26 @@ config BOOT_FIH_PROFILE_DEFAULT_HIGH endmenu +config MCUBOOT_LOGICAL_SECTOR_SIZE + int "Size of a logical sector" + default 0 + help + Set to 0 to use hardware sectors. Any other value here should be + aligned to hardware sectors in size and alignment. + +config MCUBOOT_LOGICAL_SECTOR_SIZE_SET + bool + default y if MCUBOOT_LOGICAL_SECTOR_SIZE != 0 + +config MCUBOOT_LOGICAL_SECTOR_VALIDATION + bool "Validate logical sector layout" + default true if MCUBOOT_LOGICAL_SECTOR_SIZE != 0 + depends on MCUBOOT_LOGICAL_SECTOR_SIZE_SET + help + Validation of logical sector size against hardware constrains. + Should be used to validate compile-time configuration against run-time + system. + config MCUBOOT_DEVICE_SETTINGS # Hidden selector for device-specific settings bool @@ -1119,7 +1139,8 @@ config MCUBOOT_DEVICE_SETTINGS # CPU options select MCUBOOT_DEVICE_CPU_CORTEX_M0 if CPU_CORTEX_M0 # Enable flash page layout if available - select FLASH_PAGE_LAYOUT if FLASH_HAS_PAGE_LAYOUT + select FLASH_PAGE_LAYOUT if FLASH_HAS_PAGE_LAYOUT && !MCUBOOT_LOGICAL_SECTOR_SIZE_SET + select FLASH_PAGE_LAYOUT if FLASH_HAS_PAGE_LAYOUT && MCUBOOT_LOGICAL_SECTOR_VALIDATION # Enable flash_map module as flash I/O back-end select FLASH_MAP diff --git a/boot/zephyr/flash_map_extended.c b/boot/zephyr/flash_map_extended.c index c5afb632b..9d456465b 100644 --- a/boot/zephyr/flash_map_extended.c +++ b/boot/zephyr/flash_map_extended.c @@ -65,8 +65,6 @@ BOOT_LOG_MODULE_DECLARE(mcuboot); #error "FLASH_DEVICE_ID could not be determined" #endif -static const struct device *flash_dev = DEVICE_DT_GET(FLASH_DEVICE_NODE); - int flash_device_base(uint8_t fd_id, uintptr_t *ret) { if (fd_id != FLASH_DEVICE_ID) { @@ -156,10 +154,26 @@ int flash_area_id_from_direct_image(int image_id) } #endif +uint8_t flash_area_get_device_id(const struct flash_area *fa) +{ + (void)fa; + return FLASH_DEVICE_ID; +} + +#define ERASED_VAL 0xff +__weak uint8_t flash_area_erased_val(const struct flash_area *fap) +{ + (void)fap; + return ERASED_VAL; +} + +#if (defined(CONFIG_MCUBOOT_LOGICAL_SECTOR_SIZE) && CONFIG_MCUBOOT_LOGICAL_SECTOR_SIZE == 0) || \ + defined(CONFIG_MCUBOOT_LOGICAL_SECTOR_VALIDATION) int flash_area_sector_from_off(off_t off, struct flash_sector *sector) { int rc; struct flash_pages_info page; + static const struct device *flash_dev = DEVICE_DT_GET(FLASH_DEVICE_NODE); rc = flash_get_page_info_by_offs(flash_dev, off, &page); if (rc) { @@ -172,19 +186,6 @@ int flash_area_sector_from_off(off_t off, struct flash_sector *sector) return rc; } -uint8_t flash_area_get_device_id(const struct flash_area *fa) -{ - (void)fa; - return FLASH_DEVICE_ID; -} - -#define ERASED_VAL 0xff -__weak uint8_t flash_area_erased_val(const struct flash_area *fap) -{ - (void)fap; - return ERASED_VAL; -} - int flash_area_get_sector(const struct flash_area *fap, off_t off, struct flash_sector *fsp) { @@ -205,3 +206,27 @@ int flash_area_get_sector(const struct flash_area *fap, off_t off, return rc; } +#else +int flash_area_sector_from_off(off_t off, struct flash_sector *sector) +{ + sector->fs_off = off & ~(CONFIG_MCUBOOT_LOGICAL_SECTOR_SIZE - 1); + sector->fs_size = CONFIG_MCUBOOT_LOGICAL_SECTOR_SIZE; + + return 0; +} + +int flash_area_get_sector(const struct flash_area *fap, off_t off, + struct flash_sector *fsp) +{ + if (off < 0 || (size_t) off >= flash_area_get_size(fap)) { + BOOT_LOG_ERR("flash_area_get_sector: off %ld out of area %p", + (long)off, fap); + return -ERANGE; + } + + fsp->fs_off = off & ~(CONFIG_MCUBOOT_LOGICAL_SECTOR_SIZE - 1); + fsp->fs_size = CONFIG_MCUBOOT_LOGICAL_SECTOR_SIZE; + + return 0; +} +#endif diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h index 095620846..518a4c8b9 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -357,6 +357,27 @@ # endif #endif +/* If set to non-0 it will use logical sectors rather than querying + * device for sector sizes. This slightly reduces code and RAM usage. + * Note that the logical sector size has to be multiple of erase + * sector size that is biggest for of all devices in the system. + */ +#if defined(CONFIG_MCUBOOT_LOGICAL_SECTOR_SIZE) +#define MCUBOOT_LOGICAL_SECTOR_SIZE CONFIG_MCUBOOT_LOGICAL_SECTOR_SIZE +#endif + +/* Enable to validate compile time logical sector setup vs the real device layout. + * This increases the size of bootloader but is useful to check whether + * selected logical sector size can be used with provided partitions + * and devices they are placed on. + * Once layout is tested, this option should be disabled, for production + * devices, as it is pointless to re-validate non-changing setup on + * every MCUboot run. + */ +#if defined(CONFIG_MCUBOOT_LOGICAL_SECTOR_VALIDATION) +#define MCUBOOT_LOGICAL_SECTOR_VALIDATION 1 +#endif + #if defined(CONFIG_BOOT_MAX_IMG_SECTORS_AUTO) && defined(MIN_SECTOR_COUNT) #define MCUBOOT_MAX_IMG_SECTORS MIN_SECTOR_COUNT