Skip to content

Commit 81f694c

Browse files
tomchyrlubos
authored andcommitted
[nrf fromtree] 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 <[email protected]> (cherry picked from commit dce784a)
1 parent 23a2592 commit 81f694c

File tree

8 files changed

+247
-6
lines changed

8 files changed

+247
-6
lines changed

boot/bootutil/include/bootutil/image.h

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

147+
#define VERSION_DEP_SLOT_ACTIVE 0x00 /* Check dependency against active slot. */
148+
#define VERSION_DEP_SLOT_PRIMARY 0x01 /* Check dependency against primary slot. */
149+
#define VERSION_DEP_SLOT_SECONDARY 0x02 /* Check dependency against secondary slot. */
150+
147151
STRUCT_PACKED image_version {
148152
uint8_t iv_major;
149153
uint8_t iv_minor;
@@ -153,7 +157,11 @@ STRUCT_PACKED image_version {
153157

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

boot/bootutil/src/loader.c

Lines changed: 166 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,24 @@ boot_verify_slot_dependency(struct boot_loader_state *state,
471471
uint8_t swap_type = state->swap_type[dep->image_id];
472472
dep_slot = BOOT_IS_UPGRADE(swap_type) ? BOOT_SECONDARY_SLOT
473473
: BOOT_PRIMARY_SLOT;
474+
#elif defined(MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER)
475+
switch(dep->slot) {
476+
case VERSION_DEP_SLOT_ACTIVE:
477+
dep_slot = state->slot_usage[dep->image_id].active_slot;
478+
break;
479+
case VERSION_DEP_SLOT_PRIMARY:
480+
dep_slot = BOOT_PRIMARY_SLOT;
481+
break;
482+
case VERSION_DEP_SLOT_SECONDARY:
483+
dep_slot = BOOT_SECONDARY_SLOT;
484+
break;
485+
default:
486+
return -1;
487+
}
488+
489+
if (!state->slot_usage[dep->image_id].slot_available[dep_slot]) {
490+
return -1;
491+
}
474492
#else
475493
dep_slot = state->slot_usage[dep->image_id].active_slot;
476494
#endif
@@ -508,7 +526,27 @@ boot_verify_slot_dependency(struct boot_loader_state *state,
508526
}
509527
#endif
510528

511-
return rc;
529+
#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
530+
if (rc == 0) {
531+
switch(dep->slot) {
532+
case VERSION_DEP_SLOT_PRIMARY:
533+
state->slot_usage[dep->image_id].slot_available[BOOT_PRIMARY_SLOT] = true;
534+
state->slot_usage[dep->image_id].slot_available[BOOT_SECONDARY_SLOT] = false;
535+
state->slot_usage[dep->image_id].active_slot = BOOT_PRIMARY_SLOT;
536+
break;
537+
case VERSION_DEP_SLOT_SECONDARY:
538+
state->slot_usage[dep->image_id].slot_available[BOOT_PRIMARY_SLOT] = false;
539+
state->slot_usage[dep->image_id].slot_available[BOOT_SECONDARY_SLOT] = true;
540+
state->slot_usage[dep->image_id].active_slot = BOOT_SECONDARY_SLOT;
541+
break;
542+
case VERSION_DEP_SLOT_ACTIVE:
543+
default:
544+
break;
545+
}
546+
}
547+
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
548+
549+
return rc;
512550
}
513551

514552
#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
@@ -656,6 +694,19 @@ boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot)
656694
goto done;
657695
}
658696

697+
#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
698+
/* Validate against possible dependency slot values. */
699+
switch(dep->slot) {
700+
case VERSION_DEP_SLOT_ACTIVE:
701+
case VERSION_DEP_SLOT_PRIMARY:
702+
case VERSION_DEP_SLOT_SECONDARY:
703+
break;
704+
default:
705+
rc = BOOT_EBADARGS;
706+
goto done;
707+
}
708+
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
709+
659710
/* Verify dependency and modify the swap type if not satisfied. */
660711
rc = boot_verify_slot_dependency(state, &dep);
661712
if (rc != 0) {
@@ -3461,6 +3512,119 @@ boot_select_or_erase(struct boot_loader_state *state)
34613512
}
34623513
#endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_DIRECT_XIP_REVERT */
34633514

3515+
#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
3516+
/**
3517+
* Tries to load a slot for all the images with validation.
3518+
*
3519+
* @param state Boot loader status information.
3520+
*
3521+
* @return 0 on success; nonzero on failure.
3522+
*/
3523+
fih_ret
3524+
boot_load_and_validate_images(struct boot_loader_state *state)
3525+
{
3526+
uint32_t active_slot;
3527+
int rc;
3528+
fih_ret fih_rc;
3529+
uint32_t slot;
3530+
3531+
/* Go over all the images and all slots and validate them */
3532+
IMAGES_ITER(BOOT_CURR_IMG(state)) {
3533+
for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
3534+
#if BOOT_IMAGE_NUMBER > 1
3535+
if (state->img_mask[BOOT_CURR_IMG(state)]) {
3536+
continue;
3537+
}
3538+
#endif
3539+
3540+
/* Save the number of the active slot. */
3541+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = slot;
3542+
3543+
#ifdef MCUBOOT_DIRECT_XIP
3544+
rc = boot_rom_address_check(state);
3545+
if (rc != 0) {
3546+
/* The image is placed in an unsuitable slot. */
3547+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
3548+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
3549+
continue;
3550+
}
3551+
3552+
#ifdef MCUBOOT_DIRECT_XIP_REVERT
3553+
rc = boot_select_or_erase(state);
3554+
if (rc != 0) {
3555+
/* The selected image slot has been erased. */
3556+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
3557+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
3558+
continue;
3559+
}
3560+
#endif /* MCUBOOT_DIRECT_XIP_REVERT */
3561+
#endif /* MCUBOOT_DIRECT_XIP */
3562+
3563+
#ifdef MCUBOOT_RAM_LOAD
3564+
/* Image is first loaded to RAM and authenticated there in order to
3565+
* prevent TOCTOU attack during image copy. This could be applied
3566+
* when loading images from external (untrusted) flash to internal
3567+
* (trusted) RAM and image is authenticated before copying.
3568+
*/
3569+
rc = boot_load_image_to_sram(state);
3570+
if (rc != 0 ) {
3571+
/* Image cannot be ramloaded. */
3572+
boot_remove_image_from_flash(state, slot);
3573+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
3574+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
3575+
continue;
3576+
}
3577+
#endif /* MCUBOOT_RAM_LOAD */
3578+
3579+
FIH_CALL(boot_validate_slot, fih_rc, state, slot, NULL, 0);
3580+
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
3581+
/* Image is invalid. */
3582+
#ifdef MCUBOOT_RAM_LOAD
3583+
boot_remove_image_from_sram(state);
3584+
#endif /* MCUBOOT_RAM_LOAD */
3585+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
3586+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
3587+
continue;
3588+
}
3589+
3590+
/* Valid image loaded from a slot, go to the next slot. */
3591+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
3592+
}
3593+
}
3594+
3595+
/* Go over all the images and all slots and validate them */
3596+
IMAGES_ITER(BOOT_CURR_IMG(state)) {
3597+
/* All slots tried until a valid image found. Breaking from this loop
3598+
* means that a valid image found or already loaded. If no slot is
3599+
* found the function returns with error code. */
3600+
while (true) {
3601+
/* Go over all the slots and try to load one */
3602+
active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot;
3603+
if (active_slot != NO_ACTIVE_SLOT){
3604+
/* A slot is already active, go to next image. */
3605+
break;
3606+
}
3607+
3608+
active_slot = find_slot_with_highest_version(state);
3609+
if (active_slot == NO_ACTIVE_SLOT) {
3610+
BOOT_LOG_INF("No slot to load for image %d",
3611+
BOOT_CURR_IMG(state));
3612+
FIH_RET(FIH_FAILURE);
3613+
}
3614+
3615+
/* Save the number of the active slot. */
3616+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = active_slot;
3617+
3618+
/* Valid image loaded from a slot, go to the next image. */
3619+
break;
3620+
}
3621+
}
3622+
3623+
FIH_RET(FIH_SUCCESS);
3624+
}
3625+
3626+
#else /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
3627+
34643628
/**
34653629
* Tries to load a slot for all the images with validation.
34663630
*
@@ -3558,6 +3722,7 @@ boot_load_and_validate_images(struct boot_loader_state *state)
35583722

35593723
FIH_RET(FIH_SUCCESS);
35603724
}
3725+
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
35613726

35623727
/**
35633728
* 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
@@ -1006,6 +1006,15 @@ config BOOT_VERSION_CMP_USE_BUILD_NUMBER
10061006
minor and revision. Enable this option to take into account the build
10071007
number as well.
10081008

1009+
config BOOT_VERSION_CMP_USE_SLOT_NUMBER
1010+
bool "Use slot number while comparing image version"
1011+
depends on (UPDATEABLE_IMAGE_NUMBER > 1) || BOOT_DIRECT_XIP || \
1012+
BOOT_RAM_LOAD || MCUBOOT_DOWNGRADE_PREVENTION
1013+
help
1014+
By default, the image slot comparison relies only on active slot.
1015+
Enable this option to take into account the specified slot number
1016+
instead.
1017+
10091018
choice BOOT_DOWNGRADE_PREVENTION_CHOICE
10101019
prompt "Downgrade prevention"
10111020
optional

boot/zephyr/include/mcuboot_config/mcuboot_config.h

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

125+
#ifdef CONFIG_BOOT_VERSION_CMP_USE_SLOT_NUMBER
126+
#define MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
127+
#endif
128+
125129
#ifdef CONFIG_BOOT_SWAP_SAVE_ENCTLV
126130
#define MCUBOOT_SWAP_SAVE_ENCTLV 1
127131
#endif

docs/design.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,23 @@ process is presented below.
890890
+ Boot into image in the primary slot of the 0th image position\
891891
(other image in the boot chain is started by another image).
892892

893+
By enabling the `MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER` configuration option,
894+
the dependency check may be extended to match for a specified slot of a specific
895+
image. This functionality is useful in a multi-core system when Direct XIP mode
896+
is used.
897+
In this case, the main image can be started from one of the two (primary or
898+
secondary) slots.
899+
If there is a fixed connection between the slots of two different images,
900+
e.g. if the main image always chainloads a companion image from the same slot,
901+
the check must take this into account and only consider a matching slot when
902+
resolving dependencies.
903+
904+
There are three values that can be passed when specifying dependencies:
905+
906+
1. ``active``: the dependency should be checked against either primary or secondary slot.
907+
2. ``primary``: the dependency should be checked only against primary slot.
908+
3. ``secondary``: the dependency should be checked only against secondary slot.
909+
893910
### [Multiple image boot for RAM loading and direct-xip](#multiple-image-boot-for-ram-loading-and-direct-xip)
894911

895912
The operation of the bootloader is different when the ram-load or the

docs/imgtool.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ primary slot and adds a header and trailer that the bootloader is expecting:
9191
the `auto` keyword to automatically generate
9292
it from the image version.
9393
-d, --dependencies TEXT Add dependence on another image, format:
94-
"(<image_ID>,<image_version>), ... "
94+
"(<image_ID>,[<slot:active|primary|secondary>,]
95+
<image_version>), ... "
9596
--pad-sig Add 0-2 bytes of padding to ECDSA signature
9697
(for mcuboot <1.5)
9798
-H, --header-size INTEGER [required]
@@ -182,6 +183,16 @@ which the current image depends on. The `image_version` is the minimum version
182183
of that image to satisfy compliance. For example `-d "(1, 1.2.3+0)"` means this
183184
image depends on Image 1 which version has to be at least 1.2.3+0.
184185

186+
In addition, a dependency can specify the slot as follows:
187+
`-d "(image_id, slot, image_version)"`. The `image_id` is the number of the
188+
image on which the current image depends.
189+
The slot specifies which slots of the image are to be taken into account
190+
(`active`: primary or secondary, `primary`: only primary `secondary`: only
191+
secondary slot). The `image_version` is the minimum version of that image to
192+
fulfill the requirements.
193+
For example `-d "(1, primary, 1.2.3+0)"` means that this image depends on the
194+
primary slot of the Image 1, whose version must be at least 1.2.3+0.
195+
185196
The `--public-key-format` argument can be used to distinguish where the public
186197
key is stored for image authentication. The `hash` option is used by default, in
187198
which case only the hash of the public key is added to the TLV area (the full

scripts/imgtool/image.py

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

scripts/imgtool/main.py

Lines changed: 29 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,14 @@
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'])
51+
52+
DEPENDENCY_SLOT_VALUES = {
53+
'active': 0x00,
54+
'primary': 0x01,
55+
'secondary': 0x02
56+
}
4857

4958
def gen_rsa2048(keyfile, passwd):
5059
keys.RSA.generate().export_private(path=keyfile, passwd=passwd)
@@ -301,16 +310,33 @@ def get_dependencies(ctx, param, value):
301310
if len(images) == 0:
302311
raise click.BadParameter(
303312
"Image dependency format is invalid: {}".format(value))
304-
raw_versions = re.findall(r",\s*([0-9.+]+)\)", value)
313+
raw_versions = re.findall(r",\s*((active|primary|secondary)\s*,)?\s*([0-9.+]+)\)", value)
305314
if len(images) != len(raw_versions):
306315
raise click.BadParameter(
307316
'''There's a mismatch between the number of dependency images
308317
and versions in: {}'''.format(value))
309318
for raw_version in raw_versions:
310319
try:
311-
versions.append(decode_version(raw_version))
320+
decoded_version = decode_version(raw_version[2])
321+
if len(raw_version[1]) > 0:
322+
slotted_version = SlottedSemiSemVersion(
323+
decoded_version.major,
324+
decoded_version.minor,
325+
decoded_version.revision,
326+
decoded_version.build,
327+
DEPENDENCY_SLOT_VALUES[raw_version[1]]
328+
)
329+
else:
330+
slotted_version = SlottedSemiSemVersion(
331+
decoded_version.major,
332+
decoded_version.minor,
333+
decoded_version.revision,
334+
decoded_version.build,
335+
0
336+
)
312337
except ValueError as e:
313338
raise click.BadParameter("{}".format(e))
339+
versions.append(slotted_version)
314340
dependencies = dict()
315341
dependencies[image.DEP_IMAGES_KEY] = images
316342
dependencies[image.DEP_VERSIONS_KEY] = versions
@@ -405,7 +431,7 @@ def convert(self, value, param, ctx):
405431
'(for mcuboot <1.5)')
406432
@click.option('-d', '--dependencies', callback=get_dependencies,
407433
required=False, help='''Add dependence on another image, format:
408-
"(<image_ID>,<image_version>), ... "''')
434+
"(<image_ID>,[<slot:active|primary|secondary>,]<image_version>), ... "''')
409435
@click.option('-s', '--security-counter', callback=validate_security_counter,
410436
help='Specify the value of security counter. Use the `auto` '
411437
'keyword to automatically generate it from the image version.')

0 commit comments

Comments
 (0)