Skip to content

Commit ab92081

Browse files
committed
loader: Allow to specify slot number in version
Allow to depend on a specific slot while specifying the version number. This functionality is useful when the Direct XIP mode is used and the booting process of other images is done by the next stage, not the MCUboot itself. Signed-off-by: Tomasz Chyrowicz <tomasz.chyrowicz@nordicsemi.no>
1 parent 9e0bebc commit ab92081

File tree

6 files changed

+184
-5
lines changed

6 files changed

+184
-5
lines changed

boot/bootutil/include/bootutil/image.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ struct flash_area;
143143
*/
144144
#define IMAGE_TLV_ANY 0xffff /* Used to iterate over all TLV */
145145

146+
#define VERSION_DEP_SLOT_ACTIVE 0x00 /* Check dependency against active slot. */
147+
#define VERSION_DEP_SLOT_PRIMARY 0x01 /* Check dependency against primary slot. */
148+
#define VERSION_DEP_SLOT_SECONDARY 0x02 /* Check dependency against secondary slot. */
149+
146150
STRUCT_PACKED image_version {
147151
uint8_t iv_major;
148152
uint8_t iv_minor;
@@ -152,7 +156,11 @@ STRUCT_PACKED image_version {
152156

153157
struct image_dependency {
154158
uint8_t image_id; /* Image index (from 0) */
159+
#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
160+
uint8_t slot; /* Image slot */
161+
#else
155162
uint8_t _pad1;
163+
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
156164
uint16_t _pad2;
157165
struct image_version image_min_version; /* Indicates at minimum which
158166
* version of firmware must be

boot/bootutil/src/loader.c

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,24 @@ boot_verify_slot_dependency(struct boot_loader_state *state,
411411
uint8_t swap_type = state->swap_type[dep->image_id];
412412
dep_slot = BOOT_IS_UPGRADE(swap_type) ? BOOT_SECONDARY_SLOT
413413
: BOOT_PRIMARY_SLOT;
414+
#elif defined(MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER)
415+
switch(dep->slot) {
416+
case VERSION_DEP_SLOT_ACTIVE:
417+
dep_slot = state->slot_usage[dep->image_id].active_slot;
418+
break;
419+
case VERSION_DEP_SLOT_PRIMARY:
420+
dep_slot = BOOT_PRIMARY_SLOT;
421+
break;
422+
case VERSION_DEP_SLOT_SECONDARY:
423+
dep_slot = BOOT_SECONDARY_SLOT;
424+
break;
425+
default:
426+
return -1;
427+
}
428+
429+
if (!state->slot_usage[dep->image_id].slot_available[dep_slot]) {
430+
return -1;
431+
}
414432
#else
415433
dep_slot = state->slot_usage[dep->image_id].active_slot;
416434
#endif
@@ -448,7 +466,28 @@ boot_verify_slot_dependency(struct boot_loader_state *state,
448466
}
449467
#endif
450468

451-
return rc;
469+
#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
470+
if (rc == 0) {
471+
switch(dep->slot) {
472+
break;
473+
case VERSION_DEP_SLOT_PRIMARY:
474+
state->slot_usage[dep->image_id].slot_available[BOOT_PRIMARY_SLOT] = true;
475+
state->slot_usage[dep->image_id].slot_available[BOOT_SECONDARY_SLOT] = false;
476+
state->slot_usage[dep->image_id].active_slot = BOOT_PRIMARY_SLOT;
477+
break;
478+
case VERSION_DEP_SLOT_SECONDARY:
479+
state->slot_usage[dep->image_id].slot_available[BOOT_PRIMARY_SLOT] = false;
480+
state->slot_usage[dep->image_id].slot_available[BOOT_SECONDARY_SLOT] = true;
481+
state->slot_usage[dep->image_id].active_slot = BOOT_SECONDARY_SLOT;
482+
break;
483+
case VERSION_DEP_SLOT_ACTIVE:
484+
default:
485+
break;
486+
}
487+
}
488+
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
489+
490+
return rc;
452491
}
453492

454493
#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
@@ -2904,6 +2943,118 @@ boot_select_or_erase(struct boot_loader_state *state)
29042943
}
29052944
#endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_DIRECT_XIP_REVERT */
29062945

2946+
#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
2947+
/**
2948+
* Tries to load a slot for all the images with validation.
2949+
*
2950+
* @param state Boot loader status information.
2951+
*
2952+
* @return 0 on success; nonzero on failure.
2953+
*/
2954+
fih_ret
2955+
boot_load_and_validate_images(struct boot_loader_state *state)
2956+
{
2957+
uint32_t active_slot;
2958+
int rc;
2959+
fih_ret fih_rc;
2960+
uint32_t slot;
2961+
2962+
/* Go over all the images and all slots and validate them */
2963+
IMAGES_ITER(BOOT_CURR_IMG(state)) {
2964+
for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
2965+
/* Save the number of the active slot. */
2966+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = slot;
2967+
2968+
#if BOOT_IMAGE_NUMBER > 1
2969+
if (state->img_mask[BOOT_CURR_IMG(state)]) {
2970+
continue;
2971+
}
2972+
#endif
2973+
2974+
#ifdef MCUBOOT_DIRECT_XIP
2975+
rc = boot_rom_address_check(state);
2976+
if (rc != 0) {
2977+
/* The image is placed in an unsuitable slot. */
2978+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
2979+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
2980+
continue;
2981+
}
2982+
2983+
#ifdef MCUBOOT_DIRECT_XIP_REVERT
2984+
rc = boot_select_or_erase(state);
2985+
if (rc != 0) {
2986+
/* The selected image slot has been erased. */
2987+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
2988+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
2989+
continue;
2990+
}
2991+
#endif /* MCUBOOT_DIRECT_XIP_REVERT */
2992+
#endif /* MCUBOOT_DIRECT_XIP */
2993+
2994+
#ifdef MCUBOOT_RAM_LOAD
2995+
/* Image is first loaded to RAM and authenticated there in order to
2996+
* prevent TOCTOU attack during image copy. This could be applied
2997+
* when loading images from external (untrusted) flash to internal
2998+
* (trusted) RAM and image is authenticated before copying.
2999+
*/
3000+
rc = boot_load_image_to_sram(state);
3001+
if (rc != 0 ) {
3002+
/* Image cannot be ramloaded. */
3003+
boot_remove_image_from_flash(state, slot);
3004+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
3005+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
3006+
continue;
3007+
}
3008+
#endif /* MCUBOOT_RAM_LOAD */
3009+
3010+
FIH_CALL(boot_validate_slot, fih_rc, state, slot, NULL, 0);
3011+
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
3012+
/* Image is invalid. */
3013+
#ifdef MCUBOOT_RAM_LOAD
3014+
boot_remove_image_from_sram(state);
3015+
#endif /* MCUBOOT_RAM_LOAD */
3016+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
3017+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
3018+
continue;
3019+
}
3020+
3021+
/* Valid image loaded from a slot, go to the next slot. */
3022+
}
3023+
}
3024+
3025+
/* Go over all the images and all slots and validate them */
3026+
IMAGES_ITER(BOOT_CURR_IMG(state)) {
3027+
/* All slots tried until a valid image found. Breaking from this loop
3028+
* means that a valid image found or already loaded. If no slot is
3029+
* found the function returns with error code. */
3030+
while (true) {
3031+
/* Go over all the slots and try to load one */
3032+
active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot;
3033+
if (active_slot != NO_ACTIVE_SLOT){
3034+
/* A slot is already active, go to next image. */
3035+
break;
3036+
}
3037+
3038+
active_slot = find_slot_with_highest_version(state);
3039+
if (active_slot == NO_ACTIVE_SLOT) {
3040+
BOOT_LOG_INF("No slot to load for image %d",
3041+
BOOT_CURR_IMG(state));
3042+
FIH_RET(FIH_FAILURE);
3043+
}
3044+
3045+
/* Save the number of the active slot. */
3046+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = active_slot;
3047+
3048+
/* Valid image loaded from a slot, go to the next image. */
3049+
break;
3050+
}
3051+
}
3052+
3053+
FIH_RET(FIH_SUCCESS);
3054+
}
3055+
3056+
#else /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
3057+
29073058
/**
29083059
* Tries to load a slot for all the images with validation.
29093060
*
@@ -3001,6 +3152,7 @@ boot_load_and_validate_images(struct boot_loader_state *state)
30013152

30023153
FIH_RET(FIH_SUCCESS);
30033154
}
3155+
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
30043156

30053157
/**
30063158
* Updates the security counter for the current image.

boot/zephyr/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,15 @@ config BOOT_VERSION_CMP_USE_BUILD_NUMBER
881881
minor and revision. Enable this option to take into account the build
882882
number as well.
883883

884+
config BOOT_VERSION_CMP_USE_SLOT_NUMBER
885+
bool "Use slot number while comparing image version"
886+
depends on (UPDATEABLE_IMAGE_NUMBER > 1) || BOOT_DIRECT_XIP || \
887+
BOOT_RAM_LOAD || MCUBOOT_DOWNGRADE_PREVENTION
888+
help
889+
By default, the image slot comparison relies only on active slot.
890+
Enable this option to take into account the specified slot number
891+
instead.
892+
884893
choice BOOT_DOWNGRADE_PREVENTION_CHOICE
885894
prompt "Downgrade prevention"
886895
optional

boot/zephyr/include/mcuboot_config/mcuboot_config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@
120120
#define MCUBOOT_VERSION_CMP_USE_BUILD_NUMBER
121121
#endif
122122

123+
#ifdef CONFIG_BOOT_VERSION_CMP_USE_SLOT_NUMBER
124+
#define MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
125+
#endif
126+
123127
#ifdef CONFIG_BOOT_SWAP_SAVE_ENCTLV
124128
#define MCUBOOT_SWAP_SAVE_ENCTLV 1
125129
#endif

scripts/imgtool/image.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,8 +587,9 @@ def create(self, key, public_key_format, enckey, dependencies=None,
587587
if dependencies is not None:
588588
for i in range(dependencies_num):
589589
payload = struct.pack(
590-
e + 'B3x' + 'BBHI',
590+
e + 'BB2x' + 'BBHI',
591591
int(dependencies[DEP_IMAGES_KEY][i]),
592+
dependencies[DEP_VERSIONS_KEY][i].slot,
592593
dependencies[DEP_VERSIONS_KEY][i].major,
593594
dependencies[DEP_VERSIONS_KEY][i].minor,
594595
dependencies[DEP_VERSIONS_KEY][i].revision,

scripts/imgtool/main.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,16 +300,21 @@ def get_dependencies(ctx, param, value):
300300
if len(images) == 0:
301301
raise click.BadParameter(
302302
"Image dependency format is invalid: {}".format(value))
303-
raw_versions = re.findall(r",\s*([0-9.+]+)\)", value)
303+
raw_versions = re.findall(r",\s*(([0-9]+)\s*,)?\s*([0-9.+]+)\)", inp)
304304
if len(images) != len(raw_versions):
305305
raise click.BadParameter(
306306
'''There's a mismatch between the number of dependency images
307307
and versions in: {}'''.format(value))
308308
for raw_version in raw_versions:
309309
try:
310-
versions.append(decode_version(raw_version))
310+
decoded_version = decode_version(raw_version[2])
311+
if len(raw_version[1]) > 0:
312+
decoded_version.slot = int(raw_version[1])
313+
else:
314+
decoded_version.slot = 0
311315
except ValueError as e:
312316
raise click.BadParameter("{}".format(e))
317+
versions.append(decoded_version)
313318
dependencies = dict()
314319
dependencies[image.DEP_IMAGES_KEY] = images
315320
dependencies[image.DEP_VERSIONS_KEY] = versions
@@ -404,7 +409,7 @@ def convert(self, value, param, ctx):
404409
'(for mcuboot <1.5)')
405410
@click.option('-d', '--dependencies', callback=get_dependencies,
406411
required=False, help='''Add dependence on another image, format:
407-
"(<image_ID>,<image_version>), ... "''')
412+
"(<image_ID>,[<slot_ID>],<image_version>), ... "''')
408413
@click.option('-s', '--security-counter', callback=validate_security_counter,
409414
help='Specify the value of security counter. Use the `auto` '
410415
'keyword to automatically generate it from the image version.')

0 commit comments

Comments
 (0)