Skip to content

Commit a79cd55

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 68a4c96 commit a79cd55

File tree

6 files changed

+199
-5
lines changed

6 files changed

+199
-5
lines changed

boot/bootutil/include/bootutil/image.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ extern "C" {
141141
*/
142142
#define IMAGE_TLV_ANY 0xffff /* Used to iterate over all TLV */
143143

144+
#define VERSION_DEP_SLOT_ACTIVE 0x00 /* Check dependency against active slot. */
145+
#define VERSION_DEP_SLOT_PRIMARY 0x01 /* Check dependency against primary slot. */
146+
#define VERSION_DEP_SLOT_SECONDARY 0x02 /* Check dependency against secondary slot. */
147+
144148
STRUCT_PACKED image_version {
145149
uint8_t iv_major;
146150
uint8_t iv_minor;
@@ -150,7 +154,11 @@ STRUCT_PACKED image_version {
150154

151155
struct image_dependency {
152156
uint8_t image_id; /* Image index (from 0) */
157+
#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
158+
uint8_t slot; /* Image slot */
159+
#else
153160
uint8_t _pad1;
161+
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
154162
uint16_t _pad2;
155163
struct image_version image_min_version; /* Indicates at minimum which
156164
* 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,27 @@ 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+
case VERSION_DEP_SLOT_PRIMARY:
473+
state->slot_usage[dep->image_id].slot_available[BOOT_PRIMARY_SLOT] = true;
474+
state->slot_usage[dep->image_id].slot_available[BOOT_SECONDARY_SLOT] = false;
475+
state->slot_usage[dep->image_id].active_slot = BOOT_PRIMARY_SLOT;
476+
break;
477+
case VERSION_DEP_SLOT_SECONDARY:
478+
state->slot_usage[dep->image_id].slot_available[BOOT_PRIMARY_SLOT] = false;
479+
state->slot_usage[dep->image_id].slot_available[BOOT_SECONDARY_SLOT] = true;
480+
state->slot_usage[dep->image_id].active_slot = BOOT_SECONDARY_SLOT;
481+
break;
482+
case VERSION_DEP_SLOT_ACTIVE:
483+
default:
484+
break;
485+
}
486+
}
487+
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
488+
489+
return rc;
452490
}
453491

454492
#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
@@ -2904,6 +2942,119 @@ boot_select_or_erase(struct boot_loader_state *state)
29042942
}
29052943
#endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_DIRECT_XIP_REVERT */
29062944

2945+
#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
2946+
/**
2947+
* Tries to load a slot for all the images with validation.
2948+
*
2949+
* @param state Boot loader status information.
2950+
*
2951+
* @return 0 on success; nonzero on failure.
2952+
*/
2953+
fih_ret
2954+
boot_load_and_validate_images(struct boot_loader_state *state)
2955+
{
2956+
uint32_t active_slot;
2957+
int rc;
2958+
fih_ret fih_rc;
2959+
uint32_t slot;
2960+
2961+
/* Go over all the images and all slots and validate them */
2962+
IMAGES_ITER(BOOT_CURR_IMG(state)) {
2963+
for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
2964+
#if BOOT_IMAGE_NUMBER > 1
2965+
if (state->img_mask[BOOT_CURR_IMG(state)]) {
2966+
continue;
2967+
}
2968+
#endif
2969+
2970+
/* Save the number of the active slot. */
2971+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = slot;
2972+
2973+
#ifdef MCUBOOT_DIRECT_XIP
2974+
rc = boot_rom_address_check(state);
2975+
if (rc != 0) {
2976+
/* The image is placed in an unsuitable slot. */
2977+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
2978+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
2979+
continue;
2980+
}
2981+
2982+
#ifdef MCUBOOT_DIRECT_XIP_REVERT
2983+
rc = boot_select_or_erase(state);
2984+
if (rc != 0) {
2985+
/* The selected image slot has been erased. */
2986+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
2987+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
2988+
continue;
2989+
}
2990+
#endif /* MCUBOOT_DIRECT_XIP_REVERT */
2991+
#endif /* MCUBOOT_DIRECT_XIP */
2992+
2993+
#ifdef MCUBOOT_RAM_LOAD
2994+
/* Image is first loaded to RAM and authenticated there in order to
2995+
* prevent TOCTOU attack during image copy. This could be applied
2996+
* when loading images from external (untrusted) flash to internal
2997+
* (trusted) RAM and image is authenticated before copying.
2998+
*/
2999+
rc = boot_load_image_to_sram(state);
3000+
if (rc != 0 ) {
3001+
/* Image cannot be ramloaded. */
3002+
boot_remove_image_from_flash(state, slot);
3003+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
3004+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
3005+
continue;
3006+
}
3007+
#endif /* MCUBOOT_RAM_LOAD */
3008+
3009+
FIH_CALL(boot_validate_slot, fih_rc, state, slot, NULL, 0);
3010+
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
3011+
/* Image is invalid. */
3012+
#ifdef MCUBOOT_RAM_LOAD
3013+
boot_remove_image_from_sram(state);
3014+
#endif /* MCUBOOT_RAM_LOAD */
3015+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
3016+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
3017+
continue;
3018+
}
3019+
3020+
/* Valid image loaded from a slot, go to the next slot. */
3021+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_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
@@ -588,8 +588,9 @@ def create(self, key, public_key_format, enckey, dependencies=None,
588588
if dependencies is not None:
589589
for i in range(dependencies_num):
590590
payload = struct.pack(
591-
e + 'B3x' + 'BBHI',
591+
e + 'BB2x' + 'BBHI',
592592
int(dependencies[DEP_IMAGES_KEY][i]),
593+
dependencies[DEP_VERSIONS_KEY][i].slot,
593594
dependencies[DEP_VERSIONS_KEY][i].major,
594595
dependencies[DEP_VERSIONS_KEY][i].minor,
595596
dependencies[DEP_VERSIONS_KEY][i].revision,

scripts/imgtool/main.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import lzma
2828
import hashlib
2929
import base64
30+
from collections import namedtuple
3031
from imgtool import image, imgtool_version
3132
from imgtool.version import decode_version
3233
from imgtool.dumpinfo import dump_imginfo
@@ -45,6 +46,8 @@
4546
sys.exit("Python %s.%s or newer is required by imgtool."
4647
% MIN_PYTHON_VERSION)
4748

49+
SlottedSemiSemVersion = namedtuple('SemiSemVersion', ['major', 'minor', 'revision',
50+
'build', 'slot'])
4851

4952
def gen_rsa2048(keyfile, passwd):
5053
keys.RSA.generate().export_private(path=keyfile, passwd=passwd)
@@ -300,16 +303,33 @@ def get_dependencies(ctx, param, value):
300303
if len(images) == 0:
301304
raise click.BadParameter(
302305
"Image dependency format is invalid: {}".format(value))
303-
raw_versions = re.findall(r",\s*([0-9.+]+)\)", value)
306+
raw_versions = re.findall(r",\s*(([0-9]+)\s*,)?\s*([0-9.+]+)\)", value)
304307
if len(images) != len(raw_versions):
305308
raise click.BadParameter(
306309
'''There's a mismatch between the number of dependency images
307310
and versions in: {}'''.format(value))
308311
for raw_version in raw_versions:
309312
try:
310-
versions.append(decode_version(raw_version))
313+
decoded_version = decode_version(raw_version[2])
314+
if len(raw_version[1]) > 0:
315+
slotted_version = SlottedSemiSemVersion(
316+
decoded_version.major,
317+
decoded_version.minor,
318+
decoded_version.revision,
319+
decoded_version.build,
320+
int(raw_version[1])
321+
)
322+
else:
323+
slotted_version = SlottedSemiSemVersion(
324+
decoded_version.major,
325+
decoded_version.minor,
326+
decoded_version.revision,
327+
decoded_version.build,
328+
0
329+
)
311330
except ValueError as e:
312331
raise click.BadParameter("{}".format(e))
332+
versions.append(slotted_version)
313333
dependencies = dict()
314334
dependencies[image.DEP_IMAGES_KEY] = images
315335
dependencies[image.DEP_VERSIONS_KEY] = versions
@@ -404,7 +424,7 @@ def convert(self, value, param, ctx):
404424
'(for mcuboot <1.5)')
405425
@click.option('-d', '--dependencies', callback=get_dependencies,
406426
required=False, help='''Add dependence on another image, format:
407-
"(<image_ID>,<image_version>), ... "''')
427+
"(<image_ID>,[<slot_ID>],<image_version>), ... "''')
408428
@click.option('-s', '--security-counter', callback=validate_security_counter,
409429
help='Specify the value of security counter. Use the `auto` '
410430
'keyword to automatically generate it from the image version.')

0 commit comments

Comments
 (0)