From dffcfd8c79552c7ca48f0ec4f32f06b183d5cd87 Mon Sep 17 00:00:00 2001 From: Andrzej Puzdrowski Date: Fri, 15 Mar 2019 17:17:16 +0100 Subject: [PATCH] dfu: introduce progressive erasing Patch adds option for progressive erase of firmware image. When using this, flash is erased as necessary when receiving new firmware, instead of erasing the whole image slot at once. This is useful on some hardware (like nRF52840) that has long erase times, to prevent long wait times at the beginning of the DFU process. Signed-off-by: Andrzej Puzdrowski --- include/dfu/flash_img.h | 3 ++ include/dfu/mcuboot.h | 7 +++ subsys/dfu/Kconfig | 10 +++++ subsys/dfu/boot/mcuboot.c | 2 - subsys/dfu/img_util/flash_img.c | 75 +++++++++++++++++++++++++++++++++ 5 files changed, 95 insertions(+), 2 deletions(-) diff --git a/include/dfu/flash_img.h b/include/dfu/flash_img.h index 104e94d8162..46bb51cba8b 100644 --- a/include/dfu/flash_img.h +++ b/include/dfu/flash_img.h @@ -19,6 +19,9 @@ struct flash_img_context { const struct flash_area *flash_area; size_t bytes_written; u16_t buf_bytes; +#ifdef CONFIG_IMG_ERASE_PROGRESSIVELY + off_t off_last; +#endif }; /** diff --git a/include/dfu/mcuboot.h b/include/dfu/mcuboot.h index 7bc003d20c3..a153d6fd9f1 100644 --- a/include/dfu/mcuboot.h +++ b/include/dfu/mcuboot.h @@ -30,6 +30,13 @@ #define BOOT_IMG_VER_STRLEN_MAX 25 /* 255.255.65535.4294967295\0 */ +/* Trailer: */ +#define BOOT_MAX_ALIGN 8 +#define BOOT_MAGIC_SZ 16 + +#define BOOT_TRAILER_IMG_STATUS_OFFS(bank_area) ((bank_area)->fa_size -\ + BOOT_MAGIC_SZ -\ + BOOT_MAX_ALIGN * 2) /** * @brief MCUboot image header representation for image version * diff --git a/subsys/dfu/Kconfig b/subsys/dfu/Kconfig index a8dfd6d7d16..abfdda68850 100644 --- a/subsys/dfu/Kconfig +++ b/subsys/dfu/Kconfig @@ -41,6 +41,16 @@ config IMG_BLOCK_BUF_SIZE Size (in Bytes) of buffer for image writer. Must be a multiple of the access alignment required by used flash driver. +config IMG_ERASE_PROGRESSIVELY + bool "Erase flash progressively when receiving new firmware" + depends on MCUBOOT_IMG_MANAGER + select FLASH_PAGE_LAYOUT + help + If enabled, flash is erased as necessary when receiving new firmware, + instead of erasing the whole image slot at once. This is necessary + on some hardware that has long erase times, to prevent long wait + times at the beginning of the DFU process. + module = IMG_MANAGER module-str = image manager source "subsys/logging/Kconfig.template.log_config" diff --git a/subsys/dfu/boot/mcuboot.c b/subsys/dfu/boot/mcuboot.c index a905331e1ee..fbb9c23aa09 100644 --- a/subsys/dfu/boot/mcuboot.c +++ b/subsys/dfu/boot/mcuboot.c @@ -32,8 +32,6 @@ #define BOOT_HEADER_SIZE_V1 32 /* Trailer: */ -#define BOOT_MAX_ALIGN 8 -#define BOOT_MAGIC_SZ 16 #define BOOT_FLAG_SET 0x01 #define BOOT_FLAG_UNSET 0xff diff --git a/subsys/dfu/img_util/flash_img.c b/subsys/dfu/img_util/flash_img.c index 055801e9c4a..50e9e28ad09 100644 --- a/subsys/dfu/img_util/flash_img.c +++ b/subsys/dfu/img_util/flash_img.c @@ -18,6 +18,11 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME); #include #include +#ifdef CONFIG_IMG_ERASE_PROGRESSIVELY +#include +#include +#endif + BUILD_ASSERT_MSG((CONFIG_IMG_BLOCK_BUF_SIZE % DT_FLASH_WRITE_BLOCK_SIZE == 0), "CONFIG_IMG_BLOCK_BUF_SIZE is not a multiple of " "DT_FLASH_WRITE_BLOCK_SIZE"); @@ -52,6 +57,63 @@ static bool flash_verify(const struct flash_area *fa, off_t offset, return (len == 0) ? true : false; } +#ifdef CONFIG_IMG_ERASE_PROGRESSIVELY + +static int flash_sector_from_off(struct flash_area const *fap, off_t off, + struct flash_sector *sector) +{ + struct flash_pages_info page; + struct device *flash_dev; + int rc = -ENODEV; + + flash_dev = flash_area_get_device(fap); + if (flash_dev) { + rc = flash_get_page_info_by_offs(flash_dev, off, &page); + if (rc == 0) { + sector->fs_off = page.start_offset; + sector->fs_size = page.size; + } + } + + return rc; +} + +/** + * Erase the image slot progressively + * + * This function erases a flash page to which offset belongs if only this page + * wasn't erased before. + * + * @param[in] ctx context of the image collection process. + * @param[in] off offset from the beginning of the image flash area beginning + * + * @return 0 on success, negative errno code on fail. + */ +static int flash_progressive_erase(struct flash_img_context *ctx, off_t off) +{ + struct flash_sector sector; + int rc; + + rc = flash_sector_from_off(ctx->flash_area, off, §or); + if (rc) { + LOG_ERR("Unable to determine flash sector size"); + } else { + if (ctx->off_last != sector.fs_off) { + ctx->off_last = sector.fs_off; + LOG_INF("Erasing sector at offset 0x%x", sector.fs_off); + rc = flash_area_erase(ctx->flash_area, sector.fs_off, + sector.fs_size); + if (rc) { + LOG_ERR("Error %d while erasing sector", rc); + } + } + } + + return rc; +} + +#endif /* CONFIG_IMG_ERASE_PROGRESSIVELY */ + static int flash_sync(struct flash_img_context *ctx) { int rc = 0; @@ -61,6 +123,11 @@ static int flash_sync(struct flash_img_context *ctx) CONFIG_IMG_BLOCK_BUF_SIZE - ctx->buf_bytes); } +#ifdef CONFIG_IMG_ERASE_PROGRESSIVELY + flash_progressive_erase(ctx, ctx->bytes_written + + CONFIG_IMG_BLOCK_BUF_SIZE); +#endif + rc = flash_area_write(ctx->flash_area, ctx->bytes_written, ctx->buf, CONFIG_IMG_BLOCK_BUF_SIZE); if (rc) { @@ -121,6 +188,11 @@ int flash_img_buffered_write(struct flash_img_context *ctx, u8_t *data, return rc; } } +#ifdef CONFIG_IMG_ERASE_PROGRESSIVELY + /* erase the image trailer area if it was not erased */ + flash_progressive_erase(ctx, + BOOT_TRAILER_IMG_STATUS_OFFS(ctx->flash_area)); +#endif flash_area_close(ctx->flash_area); ctx->flash_area = NULL; @@ -137,6 +209,9 @@ int flash_img_init(struct flash_img_context *ctx) { ctx->bytes_written = 0; ctx->buf_bytes = 0U; +#ifdef CONFIG_IMG_ERASE_PROGRESSIVELY + ctx->off_last = -1; +#endif return flash_area_open(DT_FLASH_AREA_IMAGE_1_ID, (const struct flash_area **)&(ctx->flash_area)); }