Skip to content

Commit fc8ea2a

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 461e060 commit fc8ea2a

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
@@ -317,6 +317,24 @@ boot_verify_slot_dependency(struct boot_loader_state *state,
317317
uint8_t swap_type = state->swap_type[dep->image_id];
318318
dep_slot = BOOT_IS_UPGRADE(swap_type) ? BOOT_SECONDARY_SLOT
319319
: BOOT_PRIMARY_SLOT;
320+
#elif defined(MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER)
321+
switch(dep->slot) {
322+
case VERSION_DEP_SLOT_ACTIVE:
323+
dep_slot = state->slot_usage[dep->image_id].active_slot;
324+
break;
325+
case VERSION_DEP_SLOT_PRIMARY:
326+
dep_slot = BOOT_PRIMARY_SLOT;
327+
break;
328+
case VERSION_DEP_SLOT_SECONDARY:
329+
dep_slot = BOOT_SECONDARY_SLOT;
330+
break;
331+
default:
332+
return -1;
333+
}
334+
335+
if (!state->slot_usage[dep->image_id].slot_available[dep_slot]) {
336+
return -1;
337+
}
320338
#else
321339
dep_slot = state->slot_usage[dep->image_id].active_slot;
322340
#endif
@@ -354,7 +372,27 @@ boot_verify_slot_dependency(struct boot_loader_state *state,
354372
}
355373
#endif
356374

357-
return rc;
375+
#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
376+
if (rc == 0) {
377+
switch(dep->slot) {
378+
case VERSION_DEP_SLOT_PRIMARY:
379+
state->slot_usage[dep->image_id].slot_available[BOOT_PRIMARY_SLOT] = true;
380+
state->slot_usage[dep->image_id].slot_available[BOOT_SECONDARY_SLOT] = false;
381+
state->slot_usage[dep->image_id].active_slot = BOOT_PRIMARY_SLOT;
382+
break;
383+
case VERSION_DEP_SLOT_SECONDARY:
384+
state->slot_usage[dep->image_id].slot_available[BOOT_PRIMARY_SLOT] = false;
385+
state->slot_usage[dep->image_id].slot_available[BOOT_SECONDARY_SLOT] = true;
386+
state->slot_usage[dep->image_id].active_slot = BOOT_SECONDARY_SLOT;
387+
break;
388+
case VERSION_DEP_SLOT_ACTIVE:
389+
default:
390+
break;
391+
}
392+
}
393+
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
394+
395+
return rc;
358396
}
359397

360398
#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
@@ -499,6 +537,19 @@ boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot)
499537
goto done;
500538
}
501539

540+
#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
541+
/* Validate against possible dependency slot values. */
542+
switch(dep->slot) {
543+
case VERSION_DEP_SLOT_ACTIVE:
544+
case VERSION_DEP_SLOT_PRIMARY:
545+
case VERSION_DEP_SLOT_SECONDARY:
546+
break;
547+
default:
548+
rc = BOOT_EBADARGS;
549+
goto done;
550+
}
551+
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
552+
502553
/* Verify dependency and modify the swap type if not satisfied. */
503554
rc = boot_verify_slot_dependency(state, &dep);
504555
if (rc != 0) {
@@ -2674,6 +2725,119 @@ boot_select_or_erase(struct boot_loader_state *state)
26742725
}
26752726
#endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_DIRECT_XIP_REVERT */
26762727

2728+
#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
2729+
/**
2730+
* Tries to load a slot for all the images with validation.
2731+
*
2732+
* @param state Boot loader status information.
2733+
*
2734+
* @return 0 on success; nonzero on failure.
2735+
*/
2736+
fih_ret
2737+
boot_load_and_validate_images(struct boot_loader_state *state)
2738+
{
2739+
uint32_t active_slot;
2740+
int rc;
2741+
fih_ret fih_rc;
2742+
uint32_t slot;
2743+
2744+
/* Go over all the images and all slots and validate them */
2745+
IMAGES_ITER(BOOT_CURR_IMG(state)) {
2746+
for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
2747+
#if BOOT_IMAGE_NUMBER > 1
2748+
if (state->img_mask[BOOT_CURR_IMG(state)]) {
2749+
continue;
2750+
}
2751+
#endif
2752+
2753+
/* Save the number of the active slot. */
2754+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = slot;
2755+
2756+
#ifdef MCUBOOT_DIRECT_XIP
2757+
rc = boot_rom_address_check(state);
2758+
if (rc != 0) {
2759+
/* The image is placed in an unsuitable slot. */
2760+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
2761+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
2762+
continue;
2763+
}
2764+
2765+
#ifdef MCUBOOT_DIRECT_XIP_REVERT
2766+
rc = boot_select_or_erase(state);
2767+
if (rc != 0) {
2768+
/* The selected image slot has been erased. */
2769+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
2770+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
2771+
continue;
2772+
}
2773+
#endif /* MCUBOOT_DIRECT_XIP_REVERT */
2774+
#endif /* MCUBOOT_DIRECT_XIP */
2775+
2776+
#ifdef MCUBOOT_RAM_LOAD
2777+
/* Image is first loaded to RAM and authenticated there in order to
2778+
* prevent TOCTOU attack during image copy. This could be applied
2779+
* when loading images from external (untrusted) flash to internal
2780+
* (trusted) RAM and image is authenticated before copying.
2781+
*/
2782+
rc = boot_load_image_to_sram(state);
2783+
if (rc != 0 ) {
2784+
/* Image cannot be ramloaded. */
2785+
boot_remove_image_from_flash(state, slot);
2786+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
2787+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
2788+
continue;
2789+
}
2790+
#endif /* MCUBOOT_RAM_LOAD */
2791+
2792+
FIH_CALL(boot_validate_slot, fih_rc, state, slot, NULL, 0);
2793+
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
2794+
/* Image is invalid. */
2795+
#ifdef MCUBOOT_RAM_LOAD
2796+
boot_remove_image_from_sram(state);
2797+
#endif /* MCUBOOT_RAM_LOAD */
2798+
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
2799+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
2800+
continue;
2801+
}
2802+
2803+
/* Valid image loaded from a slot, go to the next slot. */
2804+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
2805+
}
2806+
}
2807+
2808+
/* Go over all the images and all slots and validate them */
2809+
IMAGES_ITER(BOOT_CURR_IMG(state)) {
2810+
/* All slots tried until a valid image found. Breaking from this loop
2811+
* means that a valid image found or already loaded. If no slot is
2812+
* found the function returns with error code. */
2813+
while (true) {
2814+
/* Go over all the slots and try to load one */
2815+
active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot;
2816+
if (active_slot != NO_ACTIVE_SLOT){
2817+
/* A slot is already active, go to next image. */
2818+
break;
2819+
}
2820+
2821+
active_slot = find_slot_with_highest_version(state);
2822+
if (active_slot == NO_ACTIVE_SLOT) {
2823+
BOOT_LOG_INF("No slot to load for image %d",
2824+
BOOT_CURR_IMG(state));
2825+
FIH_RET(FIH_FAILURE);
2826+
}
2827+
2828+
/* Save the number of the active slot. */
2829+
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = active_slot;
2830+
2831+
/* Valid image loaded from a slot, go to the next image. */
2832+
break;
2833+
}
2834+
}
2835+
2836+
FIH_RET(FIH_SUCCESS);
2837+
}
2838+
2839+
#else /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
2840+
26772841
/**
26782842
* Tries to load a slot for all the images with validation.
26792843
*
@@ -2771,6 +2935,7 @@ boot_load_and_validate_images(struct boot_loader_state *state)
27712935

27722936
FIH_RET(FIH_SUCCESS);
27732937
}
2938+
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
27742939

27752940
/**
27762941
* 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
@@ -969,6 +969,15 @@ config BOOT_VERSION_CMP_USE_BUILD_NUMBER
969969
minor and revision. Enable this option to take into account the build
970970
number as well.
971971

972+
config BOOT_VERSION_CMP_USE_SLOT_NUMBER
973+
bool "Use slot number while comparing image version"
974+
depends on (UPDATEABLE_IMAGE_NUMBER > 1) || BOOT_DIRECT_XIP || \
975+
BOOT_RAM_LOAD || MCUBOOT_DOWNGRADE_PREVENTION
976+
help
977+
By default, the image slot comparison relies only on active slot.
978+
Enable this option to take into account the specified slot number
979+
instead.
980+
972981
choice BOOT_DOWNGRADE_PREVENTION_CHOICE
973982
prompt "Downgrade prevention"
974983
optional

boot/zephyr/include/mcuboot_config/mcuboot_config.h

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

127+
#ifdef CONFIG_BOOT_VERSION_CMP_USE_SLOT_NUMBER
128+
#define MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
129+
#endif
130+
127131
#ifdef CONFIG_BOOT_SWAP_SAVE_ENCTLV
128132
#define MCUBOOT_SWAP_SAVE_ENCTLV 1
129133
#endif

docs/design.md

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

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

903920
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
@@ -590,8 +590,9 @@ def create(self, key, public_key_format, enckey, dependencies=None,
590590
if dependencies is not None:
591591
for i in range(dependencies_num):
592592
payload = struct.pack(
593-
e + 'B3x' + 'BBHI',
593+
e + 'BB2x' + 'BBHI',
594594
int(dependencies[DEP_IMAGES_KEY][i]),
595+
dependencies[DEP_VERSIONS_KEY][i].slot,
595596
dependencies[DEP_VERSIONS_KEY][i].major,
596597
dependencies[DEP_VERSIONS_KEY][i].minor,
597598
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)