Skip to content

Commit 0e8cc3e

Browse files
nordicjmkartben
authored andcommitted
dfu: Add support for new MCUboot swap using offset mode
Allows using this newly introduced MCUboot algorithm Signed-off-by: Jamie McCrae <[email protected]>
1 parent 55dba48 commit 0e8cc3e

File tree

7 files changed

+224
-22
lines changed

7 files changed

+224
-22
lines changed

include/zephyr/dfu/mcuboot.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ extern "C" {
8181

8282
#define BOOT_IMG_VER_STRLEN_MAX 25 /* 255.255.65535.4294967295\0 */
8383

84+
/** Sector at which firmware update should be placed by application in swap using offset mode */
85+
#define SWAP_USING_OFFSET_SECTOR_UPDATE_BEGIN 1
8486

8587
/**
8688
* @brief MCUboot image header representation for image version
@@ -287,6 +289,20 @@ ssize_t boot_get_area_trailer_status_offset(uint8_t area_id);
287289
*/
288290
ssize_t boot_get_trailer_status_offset(size_t area_size);
289291

292+
#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET) || defined(__DOXYGEN__)
293+
/**
294+
* @brief Get the offset of the image header, this should be used in swap using offset mode to
295+
* account for the secondary slot data starting in the first or second sector, depending
296+
* upon the current state
297+
*
298+
* @param area_id flash_area ID of image bank to get the status offset
299+
* @return offset of the image header
300+
*/
301+
size_t boot_get_image_start_offset(uint8_t area_id);
302+
#else
303+
#define boot_get_image_start_offset(...) 0
304+
#endif
305+
290306
#ifdef __cplusplus
291307
}
292308
#endif

modules/Kconfig.mcuboot

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,15 @@ config MCUBOOT_BOOTLOADER_MODE_SINGLE_APP
149149
to DFU its own update to secondary slot and all updates need to
150150
be performed using MCUboot serial recovery.
151151

152+
config MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET
153+
bool "MCUboot has been configured for swap using offset operation"
154+
select MCUBOOT_BOOTLOADER_MODE_HAS_NO_DOWNGRADE
155+
help
156+
MCUboot expects slot0_partition and slot1_partition to be present
157+
in DT and application will boot from slot0_partition.
158+
MCUBOOT_BOOTLOADER_NO_DOWNGRADE should also be selected
159+
if MCUboot has been built with MCUBOOT_DOWNGRADE_PREVENTION.
160+
152161
config MCUBOOT_BOOTLOADER_MODE_SWAP_USING_MOVE
153162
bool "MCUboot has been configured for swap using move operation"
154163
select MCUBOOT_BOOTLOADER_MODE_HAS_NO_DOWNGRADE

subsys/dfu/boot/mcuboot.c

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ LOG_MODULE_REGISTER(mcuboot_dfu, LOG_LEVEL_DBG);
4242
#define BOOT_HEADER_MAGIC_V1 0x96f3b83d
4343
#define BOOT_HEADER_SIZE_V1 32
4444

45+
enum IMAGE_INDEXES {
46+
IMAGE_INDEX_INVALID = -1,
47+
IMAGE_INDEX_0,
48+
IMAGE_INDEX_1,
49+
IMAGE_INDEX_2
50+
};
51+
4552
#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
4653
/* For RAM LOAD mode, the active image must be fetched from the bootloader */
4754
#define ACTIVE_SLOT_FLASH_AREA_ID boot_fetch_active_slot()
@@ -99,39 +106,89 @@ uint8_t boot_fetch_active_slot(void)
99106
}
100107
#endif /* CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD */
101108

109+
#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET)
110+
size_t boot_get_image_start_offset(uint8_t area_id)
111+
{
112+
size_t off = 0;
113+
int image = IMAGE_INDEX_INVALID;
114+
115+
if (area_id == FIXED_PARTITION_ID(slot1_partition)) {
116+
image = IMAGE_INDEX_0;
117+
#if FIXED_PARTITION_EXISTS(slot3_partition)
118+
} else if (area_id == FIXED_PARTITION_ID(slot3_partition)) {
119+
image = IMAGE_INDEX_1;
120+
#endif
121+
#if FIXED_PARTITION_EXISTS(slot5_partition)
122+
} else if (area_id == FIXED_PARTITION_ID(slot5_partition)) {
123+
image = IMAGE_INDEX_2;
124+
#endif
125+
}
126+
127+
if (image != IMAGE_INDEX_INVALID) {
128+
/* Need to check status from primary slot to get correct offset for secondary
129+
* slot image header
130+
*/
131+
const struct flash_area *fa;
132+
uint32_t num_sectors = SWAP_USING_OFFSET_SECTOR_UPDATE_BEGIN;
133+
struct flash_sector sector_data;
134+
int rc;
135+
136+
rc = flash_area_open(area_id, &fa);
137+
if (rc) {
138+
LOG_ERR("Flash open area %u failed: %d", area_id, rc);
139+
goto done;
140+
}
141+
142+
if (mcuboot_swap_type_multi(image) != BOOT_SWAP_TYPE_REVERT) {
143+
/* For swap using offset mode, the image starts in the second sector of
144+
* the upgrade slot, so apply the offset when this is needed, do this by
145+
* getting information on first sector only, this is expected to return an
146+
* error as there are more slots, so allow the not enough memory error
147+
*/
148+
rc = flash_area_get_sectors(area_id, &num_sectors, &sector_data);
149+
if ((rc != 0 && rc != -ENOMEM) ||
150+
num_sectors != SWAP_USING_OFFSET_SECTOR_UPDATE_BEGIN) {
151+
LOG_ERR("Failed to get sector details: %d", rc);
152+
} else {
153+
off = sector_data.fs_size;
154+
}
155+
}
156+
157+
flash_area_close(fa);
158+
}
159+
160+
done:
161+
LOG_DBG("Start offset for area %u: 0x%x", area_id, off);
162+
return off;
163+
}
164+
#endif /* CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET */
165+
102166
static int boot_read_v1_header(uint8_t area_id,
103167
struct mcuboot_v1_raw_header *v1_raw)
104168
{
105169
const struct flash_area *fa;
106170
int rc;
171+
size_t off = boot_get_image_start_offset(area_id);
107172

108173
rc = flash_area_open(area_id, &fa);
109174
if (rc) {
110175
return rc;
111176
}
112177

113178
/*
114-
* Read and sanity-check the raw header.
179+
* Read and validty-check the raw header.
115180
*/
116-
rc = flash_area_read(fa, 0, v1_raw, sizeof(*v1_raw));
181+
rc = flash_area_read(fa, off, v1_raw, sizeof(*v1_raw));
117182
flash_area_close(fa);
118183
if (rc) {
119184
return rc;
120185
}
121186

122187
v1_raw->header_magic = sys_le32_to_cpu(v1_raw->header_magic);
123-
v1_raw->image_load_address =
124-
sys_le32_to_cpu(v1_raw->image_load_address);
125188
v1_raw->header_size = sys_le16_to_cpu(v1_raw->header_size);
126-
v1_raw->image_size = sys_le32_to_cpu(v1_raw->image_size);
127-
v1_raw->image_flags = sys_le32_to_cpu(v1_raw->image_flags);
128-
v1_raw->version.revision =
129-
sys_le16_to_cpu(v1_raw->version.revision);
130-
v1_raw->version.build_num =
131-
sys_le32_to_cpu(v1_raw->version.build_num);
132189

133190
/*
134-
* Sanity checks.
191+
* Validity checks.
135192
*
136193
* Larger values in header_size than BOOT_HEADER_SIZE_V1 are
137194
* possible, e.g. if Zephyr was linked with
@@ -142,6 +199,16 @@ static int boot_read_v1_header(uint8_t area_id,
142199
return -EIO;
143200
}
144201

202+
v1_raw->image_load_address =
203+
sys_le32_to_cpu(v1_raw->image_load_address);
204+
v1_raw->header_size = sys_le16_to_cpu(v1_raw->header_size);
205+
v1_raw->image_size = sys_le32_to_cpu(v1_raw->image_size);
206+
v1_raw->image_flags = sys_le32_to_cpu(v1_raw->image_flags);
207+
v1_raw->version.revision =
208+
sys_le16_to_cpu(v1_raw->version.revision);
209+
v1_raw->version.build_num =
210+
sys_le32_to_cpu(v1_raw->version.build_num);
211+
145212
return 0;
146213
}
147214

subsys/dfu/img_util/flash_img.c

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
/*
2-
* Copyright (c) 2017, 2020 Nordic Semiconductor ASA
2+
* Copyright (c) 2017-2025 Nordic Semiconductor ASA
33
* Copyright (c) 2017 Linaro Limited
44
* Copyright (c) 2020 Gerson Fernando Budke <[email protected]>
55
*
66
* SPDX-License-Identifier: Apache-2.0
77
*/
88
#include <zephyr/types.h>
9+
#include <assert.h>
910
#include <stddef.h>
1011
#include <stdio.h>
1112
#include <string.h>
13+
#include <zephyr/logging/log.h>
14+
#include <zephyr/dfu/mcuboot.h>
1215
#include <zephyr/dfu/flash_img.h>
1316
#include <zephyr/dfu/mcuboot.h>
1417
#include <zephyr/storage/flash_map.h>
1518
#include <zephyr/storage/stream_flash.h>
1619

20+
LOG_MODULE_REGISTER(flash_img, CONFIG_IMG_MANAGER_LOG_LEVEL);
21+
1722
#ifdef CONFIG_IMG_ERASE_PROGRESSIVELY
1823
#include <bootutil/bootutil_public.h>
1924
#endif
@@ -48,6 +53,9 @@ BUILD_ASSERT((CONFIG_IMG_BLOCK_BUF_SIZE % FLASH_WRITE_BLOCK_SIZE == 0),
4853
"FLASH_WRITE_BLOCK_SIZE");
4954
#endif
5055

56+
#define FLASH_CHECK_ERASED_BUFFER_SIZE 16
57+
#define ERASED_VAL_32(x) (((x) << 24) | ((x) << 16) | ((x) << 8) | (x))
58+
5159
static int scramble_mcuboot_trailer(struct flash_img_context *ctx)
5260
{
5361
int rc = 0;
@@ -132,10 +140,64 @@ size_t flash_img_bytes_written(struct flash_img_context *ctx)
132140
return stream_flash_bytes_written(&ctx->stream);
133141
}
134142

143+
#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET)
144+
/**
145+
* Determines if the specified area of flash is completely unwritten.
146+
*
147+
* @param fa pointer to flash area to scan
148+
*
149+
* @return 0 when not empty, 1 when empty, negative errno code on error.
150+
*/
151+
static int flash_check_erased(const struct flash_area *fa)
152+
{
153+
uint32_t data[FLASH_CHECK_ERASED_BUFFER_SIZE];
154+
off_t addr;
155+
off_t end;
156+
int bytes_to_read;
157+
int rc;
158+
int i;
159+
uint8_t erased_val;
160+
uint32_t erased_val_32;
161+
162+
assert(fa->fa_size % sizeof(erased_val_32) == 0);
163+
164+
erased_val = flash_area_erased_val(fa);
165+
erased_val_32 = ERASED_VAL_32(erased_val);
166+
167+
end = fa->fa_size;
168+
for (addr = 0; addr < end; addr += sizeof(data)) {
169+
if (end - addr < sizeof(data)) {
170+
bytes_to_read = end - addr;
171+
} else {
172+
bytes_to_read = sizeof(data);
173+
}
174+
175+
rc = flash_area_read(fa, addr, data, bytes_to_read);
176+
177+
if (rc < 0) {
178+
LOG_ERR("Failed to read data from flash area: %d", rc);
179+
return rc;
180+
}
181+
182+
for (i = 0; i < bytes_to_read / sizeof(erased_val_32); i++) {
183+
if (data[i] != erased_val_32) {
184+
return 0;
185+
}
186+
}
187+
}
188+
189+
return 1;
190+
}
191+
#endif
192+
135193
int flash_img_init_id(struct flash_img_context *ctx, uint8_t area_id)
136194
{
137195
int rc;
138196
const struct device *flash_dev;
197+
#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET)
198+
uint32_t sector_count = SWAP_USING_OFFSET_SECTOR_UPDATE_BEGIN;
199+
struct flash_sector sector_data;
200+
#endif
139201

140202
rc = flash_area_open(area_id,
141203
(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)
145207

146208
flash_dev = flash_area_get_device(ctx->flash_area);
147209

210+
#if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET)
211+
/* Query size of first sector in flash for upgrade slot, so it can be erased, and begin
212+
* upload started at the second sector
213+
*/
214+
rc = flash_area_sectors((const struct flash_area *)ctx->flash_area, &sector_count,
215+
&sector_data);
216+
217+
if (rc && rc != -ENOMEM) {
218+
flash_area_close(ctx->flash_area);
219+
ctx->flash_area = NULL;
220+
return rc;
221+
} else if (sector_count != SWAP_USING_OFFSET_SECTOR_UPDATE_BEGIN) {
222+
flash_area_close(ctx->flash_area);
223+
ctx->flash_area = NULL;
224+
return -ENOENT;
225+
}
226+
227+
if (!flash_check_erased((const struct flash_area *)ctx->flash_area)) {
228+
/* Flash is not empty, therefore flatten/erase the area to prevent issues when
229+
* the firmware update process begins
230+
*/
231+
rc = flash_area_flatten((const struct flash_area *)ctx->flash_area, 0,
232+
sector_data.fs_size);
233+
234+
if (rc) {
235+
flash_area_close(ctx->flash_area);
236+
ctx->flash_area = NULL;
237+
return rc;
238+
}
239+
}
240+
241+
return stream_flash_init(&ctx->stream, flash_dev, ctx->buf, CONFIG_IMG_BLOCK_BUF_SIZE,
242+
(ctx->flash_area->fa_off + sector_data.fs_size),
243+
(ctx->flash_area->fa_size - sector_data.fs_size), NULL);
244+
#else
148245
return stream_flash_init(&ctx->stream, flash_dev, ctx->buf,
149246
CONFIG_IMG_BLOCK_BUF_SIZE, ctx->flash_area->fa_off,
150247
ctx->flash_area->fa_size, NULL);
248+
#endif
151249
}
152250

153251
#ifdef CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD
@@ -194,7 +292,7 @@ int flash_img_check(struct flash_img_context *ctx,
194292

195293
fac.match = fic->match;
196294
fac.clen = fic->clen;
197-
fac.off = 0;
295+
fac.off = boot_get_image_start_offset(area_id);
198296
fac.rbuf = ctx->buf;
199297
fac.rblen = sizeof(ctx->buf);
200298

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <zephyr/toolchain.h>
1414
#include <zephyr/logging/log.h>
1515
#include <zephyr/storage/flash_map.h>
16+
#include <zephyr/dfu/mcuboot.h>
1617

1718
#include <zcbor_common.h>
1819
#include <zcbor_decode.h>
@@ -262,7 +263,10 @@ int img_mgmt_read_info(int image_slot, struct image_version *ver, uint8_t *hash,
262263
return IMG_MGMT_ERR_FLASH_CONFIG_QUERY_FAIL;
263264
}
264265

265-
rc = img_mgmt_read(image_slot, 0, &hdr, sizeof(hdr));
266+
rc = img_mgmt_read(image_slot,
267+
boot_get_image_start_offset(img_mgmt_flash_area_id(image_slot)),
268+
&hdr, sizeof(hdr));
269+
266270
if (rc != 0) {
267271
return rc;
268272
}
@@ -290,7 +294,8 @@ int img_mgmt_read_info(int image_slot, struct image_version *ver, uint8_t *hash,
290294
* TLV. All images are required to have a hash TLV. If the hash is missing, the image
291295
* is considered invalid.
292296
*/
293-
data_off = hdr.ih_hdr_size + hdr.ih_img_size;
297+
data_off = hdr.ih_hdr_size + hdr.ih_img_size +
298+
boot_get_image_start_offset(img_mgmt_flash_area_id(image_slot));
294299

295300
rc = img_mgmt_find_tlvs(image_slot, &data_off, &data_end, IMAGE_TLV_PROT_INFO_MAGIC);
296301
if (!rc) {

0 commit comments

Comments
 (0)