Skip to content

Commit 89d1d85

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 9f20910 commit 89d1d85

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) {
@@ -2673,6 +2724,119 @@ boot_select_or_erase(struct boot_loader_state *state)
26732724
}
26742725
#endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_DIRECT_XIP_REVERT */
26752726

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

27712935
FIH_RET(FIH_SUCCESS);
27722936
}
2937+
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
27732938

27742939
/**
27752940
* 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
@@ -898,6 +898,15 @@ config BOOT_VERSION_CMP_USE_BUILD_NUMBER
898898
minor and revision. Enable this option to take into account the build
899899
number as well.
900900

901+
config BOOT_VERSION_CMP_USE_SLOT_NUMBER
902+
bool "Use slot number while comparing image version"
903+
depends on (UPDATEABLE_IMAGE_NUMBER > 1) || BOOT_DIRECT_XIP || \
904+
BOOT_RAM_LOAD || MCUBOOT_DOWNGRADE_PREVENTION
905+
help
906+
By default, the image slot comparison relies only on active slot.
907+
Enable this option to take into account the specified slot number
908+
instead.
909+
901910
choice BOOT_DOWNGRADE_PREVENTION_CHOICE
902911
prompt "Downgrade prevention"
903912
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

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 spacified 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
@@ -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)