From 373872d7d6e197e6384b940313176ab45840ee41 Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Mon, 11 Nov 2024 14:31:57 +0100 Subject: [PATCH] bootutil: Add support for devices without erase add boot_scramble_slot function and modify boot_erase_region to check if device requires erase. The change reduces wear on devices that do not require erase and improves swap times. Signed-off-by: Dominik Ermel --- boot/boot_serial/src/boot_serial.c | 4 +- boot/boot_serial/src/boot_serial_encryption.c | 2 +- boot/bootutil/src/bootutil_priv.h | 3 + boot/bootutil/src/loader.c | 60 +++++++++++++++++-- .../flash_map_backend/flash_map_backend.h | 2 + .../flash_map_backend/flash_map_backend.h | 2 + .../flash_map_backend/flash_map_backend.h | 2 + .../flash_map_backend/flash_map_backend.h | 2 + .../flash_map_backend/flash_map_backend.h | 15 +++++ sim/mcuboot-sys/csupport/storage/flash_map.h | 2 + 10 files changed, 85 insertions(+), 9 deletions(-) diff --git a/boot/boot_serial/src/boot_serial.c b/boot/boot_serial/src/boot_serial.c index ff1fa8a02..4573840db 100644 --- a/boot/boot_serial/src/boot_serial.c +++ b/boot/boot_serial/src/boot_serial.c @@ -759,7 +759,7 @@ static off_t erase_range(const struct flash_area *fap, off_t start, off_t end) BOOT_LOG_DBG("Erasing range 0x%jx:0x%jx", (intmax_t)start, (intmax_t)(start + size - 1)); - rc = flash_area_erase(fap, start, size); + rc = boot_erase_region(fap, start, size); if (rc != 0) { BOOT_LOG_ERR("Error %d while erasing range", rc); return -EINVAL; @@ -895,7 +895,7 @@ bs_upload(char *buf, int len) /* Non-progressive erase erases entire image slot when first chunk of * an image is received. */ - rc = flash_area_erase(fap, 0, area_size); + rc = boot_erase_region(fap, 0, area_size); if (rc) { goto out_invalid_data; } diff --git a/boot/boot_serial/src/boot_serial_encryption.c b/boot/boot_serial/src/boot_serial_encryption.c index c4bd7d87b..b75e4560a 100644 --- a/boot/boot_serial/src/boot_serial_encryption.c +++ b/boot/boot_serial/src/boot_serial_encryption.c @@ -175,7 +175,7 @@ decrypt_region_inplace(struct boot_loader_state *state, (off + bytes_copied + idx) - hdr->ih_hdr_size, blk_sz, blk_off, &buf[idx]); } - rc = flash_area_erase(fap, off + bytes_copied, chunk_sz); + rc = boot_erase_region(fap, off + bytes_copied, chunk_sz); if (rc != 0) { return BOOT_EFLASH; } diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h index 208d189b9..29c1f33bb 100644 --- a/boot/bootutil/src/bootutil_priv.h +++ b/boot/bootutil/src/bootutil_priv.h @@ -301,6 +301,9 @@ int boot_copy_region(struct boot_loader_state *state, const struct flash_area *fap_dst, uint32_t off_src, uint32_t off_dst, uint32_t sz); int boot_erase_region(const struct flash_area *fap, uint32_t off, uint32_t sz); +/* Makes slot unbootable, either by scrambling header magic, header sector + * or entire slot, depending on settings */ +int boot_scramble_slot(const struct flash_area *fap); bool boot_status_is_reset(const struct boot_status *bs); #ifdef MCUBOOT_ENC_IMAGES diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c index 12d4abf40..171247c83 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -1089,7 +1089,7 @@ boot_validate_slot(struct boot_loader_state *state, int slot, &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); if (rc < 0 && boot_check_header_erased(state, BOOT_PRIMARY_SLOT)) { BOOT_LOG_ERR("insufficient version in secondary slot"); - flash_area_erase(fap, 0, flash_area_get_size(fap)); + boot_scramble_slot(fap); /* Image in the secondary slot does not satisfy version requirement. * Erase the image and continue booting from the primary slot. */ @@ -1109,7 +1109,7 @@ boot_validate_slot(struct boot_loader_state *state, int slot, } if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { if ((slot != BOOT_PRIMARY_SLOT) || ARE_SLOTS_EQUIVALENT()) { - flash_area_erase(fap, 0, flash_area_get_size(fap)); + boot_scramble_slot(fap); /* Image is invalid, erase it to prevent further unnecessary * attempts to validate and boot it. */ @@ -1150,7 +1150,7 @@ boot_validate_slot(struct boot_loader_state *state, int slot, * * Erase the image and continue booting from the primary slot. */ - flash_area_erase(fap, 0, fap->fa_size); + boot_scramble_slot(fap); fih_rc = FIH_NO_BOOTABLE_IMAGE; goto out; } @@ -1256,7 +1256,54 @@ boot_validated_swap_type(struct boot_loader_state *state, int boot_erase_region(const struct flash_area *fap, uint32_t off, uint32_t sz) { - return flash_area_erase(fap, off, sz); + if (flash_area_erase_required(fap)) { + return flash_area_erase(fap, off, sz); + } + return 0; +} + +int +boot_scramble_slot(const struct flash_area *fap) +{ + int ret = 0; + size_t size; + +#if !defined(MCUBOOT_MINIMAL_SCRAMBLE) + size = flash_area_get_size(fap); +#endif + + if (flash_area_erase_required(fap)) { +#if defined(MCUBOOT_MINIMAL_SCRAMBLE) + struct flash_sector header; + size_t size; + + ret = flash_area_get_sector(fap, 0, &header); + size = header.size; + + if (ret != 0) { + return ret; + } +#endif + return flash_area_erase(fap, 0, size); + } else { + uint8_t buf[BOOT_MAX_ALIGN]; + size_t size_done = 0; + +#if defined(MCUBOOT_MINIMAL_SCRAMBLE) + size_t size = MAX(image_header.magic, BOOT_MAX_ALIGN); + size = (size + BOOT_MAX_ALIGN - 1) & ~(BOOT_MAX_ALIGN - 1); +#endif + memset(buf, 0x00, sizeof(buf)); + + while (size_done < size) { + ret = flash_area_write(fap, size_done, buf, sizeof(buf)); + if (ret != 0) { + break; + } + size_done += sizeof(buf); + } + } + return ret; } #if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) @@ -2181,8 +2228,8 @@ check_downgrade_prevention(struct boot_loader_state *state) if (rc < 0) { /* Image in slot 0 prevents downgrade, delete image in slot 1 */ BOOT_LOG_INF("Image %d in slot 1 erased due to downgrade prevention", BOOT_CURR_IMG(state)); - flash_area_erase(BOOT_IMG(state, 1).area, 0, - flash_area_get_size(BOOT_IMG(state, 1).area)); + /* TODO: removal of header should be enough */ + boot_scramble_slot(fap); } else { rc = 0; } @@ -2732,6 +2779,7 @@ boot_select_or_erase(struct boot_loader_state *state) */ BOOT_LOG_DBG("Erasing faulty image in the %s slot.", (active_slot == BOOT_PRIMARY_SLOT) ? "primary" : "secondary"); + /* TODO: Scramble header should be enough */ rc = flash_area_erase(fap, 0, flash_area_get_size(fap)); assert(rc == 0); diff --git a/boot/cypress/cy_flash_pal/include/flash_map_backend/flash_map_backend.h b/boot/cypress/cy_flash_pal/include/flash_map_backend/flash_map_backend.h index 6330934eb..03f98b641 100644 --- a/boot/cypress/cy_flash_pal/include/flash_map_backend/flash_map_backend.h +++ b/boot/cypress/cy_flash_pal/include/flash_map_backend/flash_map_backend.h @@ -192,4 +192,6 @@ uint8_t flash_area_erased_val(const struct flash_area *fap); int flash_area_read_is_empty(const struct flash_area *fa, uint32_t off, void *dst, uint32_t len); +#define flash_area_erase_required(fa) (true) + #endif /* __FLASH_MAP_BACKEND_H__ */ diff --git a/boot/espressif/include/flash_map_backend/flash_map_backend.h b/boot/espressif/include/flash_map_backend/flash_map_backend.h index 4bd3d2eed..17ecd43c6 100644 --- a/boot/espressif/include/flash_map_backend/flash_map_backend.h +++ b/boot/espressif/include/flash_map_backend/flash_map_backend.h @@ -91,3 +91,5 @@ int flash_area_get_sector(const struct flash_area *area, uint32_t off, int flash_area_id_from_multi_image_slot(int image_index, int slot); int flash_area_id_from_image_slot(int slot); int flash_area_to_sectors(int idx, int *cnt, struct flash_area *fa); + +#define flash_area_erase_required(fa) (true) diff --git a/boot/mbed/include/flash_map_backend/flash_map_backend.h b/boot/mbed/include/flash_map_backend/flash_map_backend.h index d526c5cfa..3a00f5a47 100644 --- a/boot/mbed/include/flash_map_backend/flash_map_backend.h +++ b/boot/mbed/include/flash_map_backend/flash_map_backend.h @@ -170,6 +170,8 @@ int flash_area_id_from_multi_image_slot(int image_index, int slot); */ int flash_area_id_to_multi_image_slot(int image_index, int area_id); +#define flash_area_erase_required(fa) (true) + #ifdef __cplusplus } #endif diff --git a/boot/nuttx/include/flash_map_backend/flash_map_backend.h b/boot/nuttx/include/flash_map_backend/flash_map_backend.h index fb29f4068..1e687ca46 100644 --- a/boot/nuttx/include/flash_map_backend/flash_map_backend.h +++ b/boot/nuttx/include/flash_map_backend/flash_map_backend.h @@ -442,6 +442,8 @@ int flash_area_id_to_multi_image_slot(int image_index, int area_id); int flash_area_id_from_image_offset(uint32_t offset); +#define flash_area_erase_required(fa) (true) + #ifdef __cplusplus } #endif diff --git a/boot/zephyr/include/flash_map_backend/flash_map_backend.h b/boot/zephyr/include/flash_map_backend/flash_map_backend.h index e5408a18a..952943035 100644 --- a/boot/zephyr/include/flash_map_backend/flash_map_backend.h +++ b/boot/zephyr/include/flash_map_backend/flash_map_backend.h @@ -106,6 +106,21 @@ static inline uint32_t flash_sector_get_size(const struct flash_sector *fs) int flash_area_get_sector(const struct flash_area *fa, off_t off, struct flash_sector *fs); +static inline bool flash_area_erase_required(const struct flash_area *fa) +{ +#if defined(CONFIG_FLASH_HAS_EXPLICIT_ERASE) && defined(CONFIG_FLASH_HAS_NO_EXPLICIT_ERASE) + const struct flash_parameters *fp = flash_get_parameters(flash_area_get_device(fap)); + + return flash_params_get_erase_cap(flash_get_parameters(flash_area_get_device(fap))) & FLASH_ERASE_C_EXPLICIT; +#else +#if defined(CONFIG_FLASH_HAS_EXPLICIT_ERASE) + return true; +#else + return false; +#endif +#endif + +} #ifdef __cplusplus } #endif diff --git a/sim/mcuboot-sys/csupport/storage/flash_map.h b/sim/mcuboot-sys/csupport/storage/flash_map.h index 1c54597c4..02ef856f8 100644 --- a/sim/mcuboot-sys/csupport/storage/flash_map.h +++ b/sim/mcuboot-sys/csupport/storage/flash_map.h @@ -164,6 +164,8 @@ int flash_area_id_from_image_slot(int slot); int flash_area_id_from_multi_image_slot(int image_index, int slot); int flash_area_id_to_multi_image_slot(int image_index, int area_id); +#define flash_area_erase_required (true) + #ifdef __cplusplus } #endif