Browse Source

dfu: Add support for new MCUboot swap using offset mode

Allows using this newly introduced MCUboot algorithm

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
pull/85577/head
Jamie McCrae 6 months ago committed by Benjamin Cabé
parent
commit
0e8cc3e52d
  1. 16
      include/zephyr/dfu/mcuboot.h
  2. 9
      modules/Kconfig.mcuboot
  3. 89
      subsys/dfu/boot/mcuboot.c
  4. 102
      subsys/dfu/img_util/flash_img.c
  5. 9
      subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c
  6. 19
      subsys/mgmt/mcumgr/grp/img_mgmt/src/zephyr_img_mgmt.c
  7. 2
      subsys/mgmt/mcumgr/grp/os_mgmt/src/os_mgmt.c

16
include/zephyr/dfu/mcuboot.h

@ -81,6 +81,8 @@ extern "C" { @@ -81,6 +81,8 @@ extern "C" {
#define BOOT_IMG_VER_STRLEN_MAX 25 /* 255.255.65535.4294967295\0 */
/** Sector at which firmware update should be placed by application in swap using offset mode */
#define SWAP_USING_OFFSET_SECTOR_UPDATE_BEGIN 1
/**
* @brief MCUboot image header representation for image version
@ -287,6 +289,20 @@ ssize_t boot_get_area_trailer_status_offset(uint8_t area_id); @@ -287,6 +289,20 @@ ssize_t boot_get_area_trailer_status_offset(uint8_t area_id);
*/
ssize_t boot_get_trailer_status_offset(size_t area_size);
#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET) || defined(__DOXYGEN__)
/**
* @brief Get the offset of the image header, this should be used in swap using offset mode to
* account for the secondary slot data starting in the first or second sector, depending
* upon the current state
*
* @param area_id flash_area ID of image bank to get the status offset
* @return offset of the image header
*/
size_t boot_get_image_start_offset(uint8_t area_id);
#else
#define boot_get_image_start_offset(...) 0
#endif
#ifdef __cplusplus
}
#endif

9
modules/Kconfig.mcuboot

@ -149,6 +149,15 @@ config MCUBOOT_BOOTLOADER_MODE_SINGLE_APP @@ -149,6 +149,15 @@ config MCUBOOT_BOOTLOADER_MODE_SINGLE_APP
to DFU its own update to secondary slot and all updates need to
be performed using MCUboot serial recovery.
config MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET
bool "MCUboot has been configured for swap using offset operation"
select MCUBOOT_BOOTLOADER_MODE_HAS_NO_DOWNGRADE
help
MCUboot expects slot0_partition and slot1_partition to be present
in DT and application will boot from slot0_partition.
MCUBOOT_BOOTLOADER_NO_DOWNGRADE should also be selected
if MCUboot has been built with MCUBOOT_DOWNGRADE_PREVENTION.
config MCUBOOT_BOOTLOADER_MODE_SWAP_USING_MOVE
bool "MCUboot has been configured for swap using move operation"
select MCUBOOT_BOOTLOADER_MODE_HAS_NO_DOWNGRADE

89
subsys/dfu/boot/mcuboot.c

@ -42,6 +42,13 @@ LOG_MODULE_REGISTER(mcuboot_dfu, LOG_LEVEL_DBG); @@ -42,6 +42,13 @@ LOG_MODULE_REGISTER(mcuboot_dfu, LOG_LEVEL_DBG);
#define BOOT_HEADER_MAGIC_V1 0x96f3b83d
#define BOOT_HEADER_SIZE_V1 32
enum IMAGE_INDEXES {
IMAGE_INDEX_INVALID = -1,
IMAGE_INDEX_0,
IMAGE_INDEX_1,
IMAGE_INDEX_2
};
#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
/* For RAM LOAD mode, the active image must be fetched from the bootloader */
#define ACTIVE_SLOT_FLASH_AREA_ID boot_fetch_active_slot()
@ -99,11 +106,69 @@ uint8_t boot_fetch_active_slot(void) @@ -99,11 +106,69 @@ uint8_t boot_fetch_active_slot(void)
}
#endif /* CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD */
#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET)
size_t boot_get_image_start_offset(uint8_t area_id)
{
size_t off = 0;
int image = IMAGE_INDEX_INVALID;
if (area_id == FIXED_PARTITION_ID(slot1_partition)) {
image = IMAGE_INDEX_0;
#if FIXED_PARTITION_EXISTS(slot3_partition)
} else if (area_id == FIXED_PARTITION_ID(slot3_partition)) {
image = IMAGE_INDEX_1;
#endif
#if FIXED_PARTITION_EXISTS(slot5_partition)
} else if (area_id == FIXED_PARTITION_ID(slot5_partition)) {
image = IMAGE_INDEX_2;
#endif
}
if (image != IMAGE_INDEX_INVALID) {
/* Need to check status from primary slot to get correct offset for secondary
* slot image header
*/
const struct flash_area *fa;
uint32_t num_sectors = SWAP_USING_OFFSET_SECTOR_UPDATE_BEGIN;
struct flash_sector sector_data;
int rc;
rc = flash_area_open(area_id, &fa);
if (rc) {
LOG_ERR("Flash open area %u failed: %d", area_id, rc);
goto done;
}
if (mcuboot_swap_type_multi(image) != BOOT_SWAP_TYPE_REVERT) {
/* For swap using offset mode, the image starts in the second sector of
* the upgrade slot, so apply the offset when this is needed, do this by
* getting information on first sector only, this is expected to return an
* error as there are more slots, so allow the not enough memory error
*/
rc = flash_area_get_sectors(area_id, &num_sectors, &sector_data);
if ((rc != 0 && rc != -ENOMEM) ||
num_sectors != SWAP_USING_OFFSET_SECTOR_UPDATE_BEGIN) {
LOG_ERR("Failed to get sector details: %d", rc);
} else {
off = sector_data.fs_size;
}
}
flash_area_close(fa);
}
done:
LOG_DBG("Start offset for area %u: 0x%x", area_id, off);
return off;
}
#endif /* CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET */
static int boot_read_v1_header(uint8_t area_id,
struct mcuboot_v1_raw_header *v1_raw)
{
const struct flash_area *fa;
int rc;
size_t off = boot_get_image_start_offset(area_id);
rc = flash_area_open(area_id, &fa);
if (rc) {
@ -111,27 +176,19 @@ static int boot_read_v1_header(uint8_t area_id, @@ -111,27 +176,19 @@ static int boot_read_v1_header(uint8_t area_id,
}
/*
* Read and sanity-check the raw header.
* Read and validty-check the raw header.
*/
rc = flash_area_read(fa, 0, v1_raw, sizeof(*v1_raw));
rc = flash_area_read(fa, off, v1_raw, sizeof(*v1_raw));
flash_area_close(fa);
if (rc) {
return rc;
}
v1_raw->header_magic = sys_le32_to_cpu(v1_raw->header_magic);
v1_raw->image_load_address =
sys_le32_to_cpu(v1_raw->image_load_address);
v1_raw->header_size = sys_le16_to_cpu(v1_raw->header_size);
v1_raw->image_size = sys_le32_to_cpu(v1_raw->image_size);
v1_raw->image_flags = sys_le32_to_cpu(v1_raw->image_flags);
v1_raw->version.revision =
sys_le16_to_cpu(v1_raw->version.revision);
v1_raw->version.build_num =
sys_le32_to_cpu(v1_raw->version.build_num);
/*
* Sanity checks.
* Validity checks.
*
* Larger values in header_size than BOOT_HEADER_SIZE_V1 are
* possible, e.g. if Zephyr was linked with
@ -142,6 +199,16 @@ static int boot_read_v1_header(uint8_t area_id, @@ -142,6 +199,16 @@ static int boot_read_v1_header(uint8_t area_id,
return -EIO;
}
v1_raw->image_load_address =
sys_le32_to_cpu(v1_raw->image_load_address);
v1_raw->header_size = sys_le16_to_cpu(v1_raw->header_size);
v1_raw->image_size = sys_le32_to_cpu(v1_raw->image_size);
v1_raw->image_flags = sys_le32_to_cpu(v1_raw->image_flags);
v1_raw->version.revision =
sys_le16_to_cpu(v1_raw->version.revision);
v1_raw->version.build_num =
sys_le32_to_cpu(v1_raw->version.build_num);
return 0;
}

102
subsys/dfu/img_util/flash_img.c

@ -1,19 +1,24 @@ @@ -1,19 +1,24 @@
/*
* Copyright (c) 2017, 2020 Nordic Semiconductor ASA
* Copyright (c) 2017-2025 Nordic Semiconductor ASA
* Copyright (c) 2017 Linaro Limited
* Copyright (c) 2020 Gerson Fernando Budke <nandojve@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <zephyr/logging/log.h>
#include <zephyr/dfu/mcuboot.h>
#include <zephyr/dfu/flash_img.h>
#include <zephyr/dfu/mcuboot.h>
#include <zephyr/storage/flash_map.h>
#include <zephyr/storage/stream_flash.h>
LOG_MODULE_REGISTER(flash_img, CONFIG_IMG_MANAGER_LOG_LEVEL);
#ifdef CONFIG_IMG_ERASE_PROGRESSIVELY
#include <bootutil/bootutil_public.h>
#endif
@ -48,6 +53,9 @@ BUILD_ASSERT((CONFIG_IMG_BLOCK_BUF_SIZE % FLASH_WRITE_BLOCK_SIZE == 0), @@ -48,6 +53,9 @@ BUILD_ASSERT((CONFIG_IMG_BLOCK_BUF_SIZE % FLASH_WRITE_BLOCK_SIZE == 0),
"FLASH_WRITE_BLOCK_SIZE");
#endif
#define FLASH_CHECK_ERASED_BUFFER_SIZE 16
#define ERASED_VAL_32(x) (((x) << 24) | ((x) << 16) | ((x) << 8) | (x))
static int scramble_mcuboot_trailer(struct flash_img_context *ctx)
{
int rc = 0;
@ -132,10 +140,64 @@ size_t flash_img_bytes_written(struct flash_img_context *ctx) @@ -132,10 +140,64 @@ size_t flash_img_bytes_written(struct flash_img_context *ctx)
return stream_flash_bytes_written(&ctx->stream);
}
#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET)
/**
* Determines if the specified area of flash is completely unwritten.
*
* @param fa pointer to flash area to scan
*
* @return 0 when not empty, 1 when empty, negative errno code on error.
*/
static int flash_check_erased(const struct flash_area *fa)
{
uint32_t data[FLASH_CHECK_ERASED_BUFFER_SIZE];
off_t addr;
off_t end;
int bytes_to_read;
int rc;
int i;
uint8_t erased_val;
uint32_t erased_val_32;
assert(fa->fa_size % sizeof(erased_val_32) == 0);
erased_val = flash_area_erased_val(fa);
erased_val_32 = ERASED_VAL_32(erased_val);
end = fa->fa_size;
for (addr = 0; addr < end; addr += sizeof(data)) {
if (end - addr < sizeof(data)) {
bytes_to_read = end - addr;
} else {
bytes_to_read = sizeof(data);
}
rc = flash_area_read(fa, addr, data, bytes_to_read);
if (rc < 0) {
LOG_ERR("Failed to read data from flash area: %d", rc);
return rc;
}
for (i = 0; i < bytes_to_read / sizeof(erased_val_32); i++) {
if (data[i] != erased_val_32) {
return 0;
}
}
}
return 1;
}
#endif
int flash_img_init_id(struct flash_img_context *ctx, uint8_t area_id)
{
int rc;
const struct device *flash_dev;
#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET)
uint32_t sector_count = SWAP_USING_OFFSET_SECTOR_UPDATE_BEGIN;
struct flash_sector sector_data;
#endif
rc = flash_area_open(area_id,
(const struct flash_area **)&(ctx->flash_area));
@ -145,9 +207,45 @@ int flash_img_init_id(struct flash_img_context *ctx, uint8_t area_id) @@ -145,9 +207,45 @@ int flash_img_init_id(struct flash_img_context *ctx, uint8_t area_id)
flash_dev = flash_area_get_device(ctx->flash_area);
#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET)
/* Query size of first sector in flash for upgrade slot, so it can be erased, and begin
* upload started at the second sector
*/
rc = flash_area_sectors((const struct flash_area *)ctx->flash_area, &sector_count,
&sector_data);
if (rc && rc != -ENOMEM) {
flash_area_close(ctx->flash_area);
ctx->flash_area = NULL;
return rc;
} else if (sector_count != SWAP_USING_OFFSET_SECTOR_UPDATE_BEGIN) {
flash_area_close(ctx->flash_area);
ctx->flash_area = NULL;
return -ENOENT;
}
if (!flash_check_erased((const struct flash_area *)ctx->flash_area)) {
/* Flash is not empty, therefore flatten/erase the area to prevent issues when
* the firmware update process begins
*/
rc = flash_area_flatten((const struct flash_area *)ctx->flash_area, 0,
sector_data.fs_size);
if (rc) {
flash_area_close(ctx->flash_area);
ctx->flash_area = NULL;
return rc;
}
}
return stream_flash_init(&ctx->stream, flash_dev, ctx->buf, CONFIG_IMG_BLOCK_BUF_SIZE,
(ctx->flash_area->fa_off + sector_data.fs_size),
(ctx->flash_area->fa_size - sector_data.fs_size), NULL);
#else
return stream_flash_init(&ctx->stream, flash_dev, ctx->buf,
CONFIG_IMG_BLOCK_BUF_SIZE, ctx->flash_area->fa_off,
ctx->flash_area->fa_size, NULL);
#endif
}
#ifdef CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD
@ -194,7 +292,7 @@ int flash_img_check(struct flash_img_context *ctx, @@ -194,7 +292,7 @@ int flash_img_check(struct flash_img_context *ctx,
fac.match = fic->match;
fac.clen = fic->clen;
fac.off = 0;
fac.off = boot_get_image_start_offset(area_id);
fac.rbuf = ctx->buf;
fac.rblen = sizeof(ctx->buf);

9
subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c

@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
#include <zephyr/toolchain.h>
#include <zephyr/logging/log.h>
#include <zephyr/storage/flash_map.h>
#include <zephyr/dfu/mcuboot.h>
#include <zcbor_common.h>
#include <zcbor_decode.h>
@ -262,7 +263,10 @@ int img_mgmt_read_info(int image_slot, struct image_version *ver, uint8_t *hash, @@ -262,7 +263,10 @@ int img_mgmt_read_info(int image_slot, struct image_version *ver, uint8_t *hash,
return IMG_MGMT_ERR_FLASH_CONFIG_QUERY_FAIL;
}
rc = img_mgmt_read(image_slot, 0, &hdr, sizeof(hdr));
rc = img_mgmt_read(image_slot,
boot_get_image_start_offset(img_mgmt_flash_area_id(image_slot)),
&hdr, sizeof(hdr));
if (rc != 0) {
return rc;
}
@ -290,7 +294,8 @@ int img_mgmt_read_info(int image_slot, struct image_version *ver, uint8_t *hash, @@ -290,7 +294,8 @@ int img_mgmt_read_info(int image_slot, struct image_version *ver, uint8_t *hash,
* TLV. All images are required to have a hash TLV. If the hash is missing, the image
* is considered invalid.
*/
data_off = hdr.ih_hdr_size + hdr.ih_img_size;
data_off = hdr.ih_hdr_size + hdr.ih_img_size +
boot_get_image_start_offset(img_mgmt_flash_area_id(image_slot));
rc = img_mgmt_find_tlvs(image_slot, &data_off, &data_end, IMAGE_TLV_PROT_INFO_MAGIC);
if (!rc) {

19
subsys/mgmt/mcumgr/grp/img_mgmt/src/zephyr_img_mgmt.c

@ -441,6 +441,10 @@ int img_mgmt_erase_image_data(unsigned int off, unsigned int num_bytes) @@ -441,6 +441,10 @@ int img_mgmt_erase_image_data(unsigned int off, unsigned int num_bytes)
{
const struct flash_area *fa;
int rc;
const struct device *dev;
struct flash_pages_info page;
off_t page_offset;
size_t erase_size;
if (off != 0) {
rc = IMG_MGMT_ERR_INVALID_OFFSET;
@ -454,16 +458,15 @@ int img_mgmt_erase_image_data(unsigned int off, unsigned int num_bytes) @@ -454,16 +458,15 @@ int img_mgmt_erase_image_data(unsigned int off, unsigned int num_bytes)
goto end;
}
/* align requested erase size to the erase-block-size */
const struct device *dev = flash_area_get_device(fa);
/* Align requested erase size to the erase-block-size */
dev = flash_area_get_device(fa);
if (dev == NULL) {
rc = IMG_MGMT_ERR_FLASH_AREA_DEVICE_NULL;
goto end_fa;
}
struct flash_pages_info page;
off_t page_offset = fa->fa_off + num_bytes - 1;
page_offset = fa->fa_off + num_bytes - 1;
rc = flash_get_page_info_by_offs(dev, page_offset, &page);
if (rc != 0) {
LOG_ERR("bad offset (0x%lx)", (long)page_offset);
@ -471,9 +474,9 @@ int img_mgmt_erase_image_data(unsigned int off, unsigned int num_bytes) @@ -471,9 +474,9 @@ int img_mgmt_erase_image_data(unsigned int off, unsigned int num_bytes)
goto end_fa;
}
size_t erase_size = page.start_offset + page.size - fa->fa_off;
rc = flash_area_flatten(fa, 0, erase_size);
erase_size = page.start_offset + page.size - fa->fa_off;
rc = flash_area_flatten(fa, boot_get_image_start_offset(g_img_mgmt_state.area_id),
erase_size);
if (rc != 0) {
LOG_ERR("image slot erase of 0x%zx bytes failed (err %d)", erase_size,
@ -568,6 +571,7 @@ int img_mgmt_upload_inspect(const struct img_mgmt_upload_req *req, @@ -568,6 +571,7 @@ int img_mgmt_upload_inspect(const struct img_mgmt_upload_req *req,
const struct flash_area *fa;
#if defined(CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_SYSBUILD) && \
(defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_WITHOUT_SCRATCH) || \
defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET) || \
defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_MOVE) || \
defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_SCRATCH) || \
defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_OVERWRITE_ONLY) || \
@ -645,6 +649,7 @@ int img_mgmt_upload_inspect(const struct img_mgmt_upload_req *req, @@ -645,6 +649,7 @@ int img_mgmt_upload_inspect(const struct img_mgmt_upload_req *req,
#if defined(CONFIG_MCUMGR_GRP_IMG_TOO_LARGE_SYSBUILD) && \
(defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_WITHOUT_SCRATCH) || \
defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET) || \
defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_MOVE) || \
defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_SCRATCH) || \
defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_OVERWRITE_ONLY) || \

2
subsys/mgmt/mcumgr/grp/os_mgmt/src/os_mgmt.c

@ -430,6 +430,8 @@ os_mgmt_mcumgr_params(struct smp_streamer *ctxt) @@ -430,6 +430,8 @@ os_mgmt_mcumgr_params(struct smp_streamer *ctxt)
#define BOOTLOADER_MODE MCUBOOT_MODE_SWAP_USING_SCRATCH
#elif defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_OVERWRITE_ONLY)
#define BOOTLOADER_MODE MCUBOOT_MODE_UPGRADE_ONLY
#elif defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET)
#define BOOTLOADER_MODE MCUBOOT_MODE_SWAP_USING_OFFSET
#elif defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_MOVE) || \
defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_WITHOUT_SCRATCH)
#define BOOTLOADER_MODE MCUBOOT_MODE_SWAP_USING_MOVE

Loading…
Cancel
Save