Skip to content

loader: Allow to specify slot number in version #456

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions boot/bootutil/include/bootutil/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ struct flash_area;
*/
#define IMAGE_TLV_ANY 0xffff /* Used to iterate over all TLV */

#define VERSION_DEP_SLOT_ACTIVE 0x00 /* Check dependency against active slot. */
#define VERSION_DEP_SLOT_PRIMARY 0x01 /* Check dependency against primary slot. */
#define VERSION_DEP_SLOT_SECONDARY 0x02 /* Check dependency against secondary slot. */

STRUCT_PACKED image_version {
uint8_t iv_major;
uint8_t iv_minor;
Expand All @@ -152,7 +156,11 @@ STRUCT_PACKED image_version {

struct image_dependency {
uint8_t image_id; /* Image index (from 0) */
#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
uint8_t slot; /* Image slot */
#else
uint8_t _pad1;
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */
uint16_t _pad2;
struct image_version image_min_version; /* Indicates at minimum which
* version of firmware must be
Expand Down
154 changes: 153 additions & 1 deletion boot/bootutil/src/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,24 @@ boot_verify_slot_dependency(struct boot_loader_state *state,
uint8_t swap_type = state->swap_type[dep->image_id];
dep_slot = BOOT_IS_UPGRADE(swap_type) ? BOOT_SECONDARY_SLOT
: BOOT_PRIMARY_SLOT;
#elif defined(MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER)
switch(dep->slot) {
case VERSION_DEP_SLOT_ACTIVE:
dep_slot = state->slot_usage[dep->image_id].active_slot;
break;
case VERSION_DEP_SLOT_PRIMARY:
dep_slot = BOOT_PRIMARY_SLOT;
break;
case VERSION_DEP_SLOT_SECONDARY:
dep_slot = BOOT_SECONDARY_SLOT;
break;
default:
return -1;
}

if (!state->slot_usage[dep->image_id].slot_available[dep_slot]) {
return -1;
}
#else
dep_slot = state->slot_usage[dep->image_id].active_slot;
#endif
Expand Down Expand Up @@ -492,7 +510,27 @@ boot_verify_slot_dependency(struct boot_loader_state *state,
}
#endif

return rc;
#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
if (rc == 0) {
switch(dep->slot) {
case VERSION_DEP_SLOT_PRIMARY:
state->slot_usage[dep->image_id].slot_available[BOOT_PRIMARY_SLOT] = true;
state->slot_usage[dep->image_id].slot_available[BOOT_SECONDARY_SLOT] = false;
state->slot_usage[dep->image_id].active_slot = BOOT_PRIMARY_SLOT;
break;
case VERSION_DEP_SLOT_SECONDARY:
state->slot_usage[dep->image_id].slot_available[BOOT_PRIMARY_SLOT] = false;
state->slot_usage[dep->image_id].slot_available[BOOT_SECONDARY_SLOT] = true;
state->slot_usage[dep->image_id].active_slot = BOOT_SECONDARY_SLOT;
break;
case VERSION_DEP_SLOT_ACTIVE:
default:
break;
}
}
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */

return rc;
}

#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
Expand Down Expand Up @@ -3399,6 +3437,119 @@ boot_select_or_erase(struct boot_loader_state *state)
}
#endif /* MCUBOOT_DIRECT_XIP && MCUBOOT_DIRECT_XIP_REVERT */

#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
/**
* Tries to load a slot for all the images with validation.
*
* @param state Boot loader status information.
*
* @return 0 on success; nonzero on failure.
*/
fih_ret
boot_load_and_validate_images(struct boot_loader_state *state)
{
uint32_t active_slot;
int rc;
fih_ret fih_rc;
uint32_t slot;

/* Go over all the images and all slots and validate them */
IMAGES_ITER(BOOT_CURR_IMG(state)) {
for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
#if BOOT_IMAGE_NUMBER > 1
if (state->img_mask[BOOT_CURR_IMG(state)]) {
continue;
}
#endif

/* Save the number of the active slot. */
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = slot;

#ifdef MCUBOOT_DIRECT_XIP
rc = boot_rom_address_check(state);
if (rc != 0) {
/* The image is placed in an unsuitable slot. */
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
continue;
}

#ifdef MCUBOOT_DIRECT_XIP_REVERT
rc = boot_select_or_erase(state);
if (rc != 0) {
/* The selected image slot has been erased. */
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
continue;
}
#endif /* MCUBOOT_DIRECT_XIP_REVERT */
#endif /* MCUBOOT_DIRECT_XIP */

#ifdef MCUBOOT_RAM_LOAD
/* Image is first loaded to RAM and authenticated there in order to
* prevent TOCTOU attack during image copy. This could be applied
* when loading images from external (untrusted) flash to internal
* (trusted) RAM and image is authenticated before copying.
*/
rc = boot_load_image_to_sram(state);
if (rc != 0 ) {
/* Image cannot be ramloaded. */
boot_remove_image_from_flash(state, slot);
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
continue;
}
#endif /* MCUBOOT_RAM_LOAD */

FIH_CALL(boot_validate_slot, fih_rc, state, slot, NULL, 0);
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
/* Image is invalid. */
#ifdef MCUBOOT_RAM_LOAD
boot_remove_image_from_sram(state);
#endif /* MCUBOOT_RAM_LOAD */
state->slot_usage[BOOT_CURR_IMG(state)].slot_available[slot] = false;
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
continue;
}

/* Valid image loaded from a slot, go to the next slot. */
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = NO_ACTIVE_SLOT;
}
}

/* Go over all the images and all slots and validate them */
IMAGES_ITER(BOOT_CURR_IMG(state)) {
/* All slots tried until a valid image found. Breaking from this loop
* means that a valid image found or already loaded. If no slot is
* found the function returns with error code. */
while (true) {
/* Go over all the slots and try to load one */
active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot;
if (active_slot != NO_ACTIVE_SLOT){
/* A slot is already active, go to next image. */
break;
}

active_slot = find_slot_with_highest_version(state);
if (active_slot == NO_ACTIVE_SLOT) {
BOOT_LOG_INF("No slot to load for image %d",
BOOT_CURR_IMG(state));
FIH_RET(FIH_FAILURE);
}

/* Save the number of the active slot. */
state->slot_usage[BOOT_CURR_IMG(state)].active_slot = active_slot;

/* Valid image loaded from a slot, go to the next image. */
break;
}
}

FIH_RET(FIH_SUCCESS);
}

#else /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */

/**
* Tries to load a slot for all the images with validation.
*
Expand Down Expand Up @@ -3496,6 +3647,7 @@ boot_load_and_validate_images(struct boot_loader_state *state)

FIH_RET(FIH_SUCCESS);
}
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */

/**
* Updates the security counter for the current image.
Expand Down
9 changes: 9 additions & 0 deletions boot/zephyr/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,15 @@ config BOOT_VERSION_CMP_USE_BUILD_NUMBER
minor and revision. Enable this option to take into account the build
number as well.

config BOOT_VERSION_CMP_USE_SLOT_NUMBER
bool "Use slot number while comparing image version"
depends on (UPDATEABLE_IMAGE_NUMBER > 1) || BOOT_DIRECT_XIP || \
BOOT_RAM_LOAD || MCUBOOT_DOWNGRADE_PREVENTION
help
By default, the image slot comparison relies only on active slot.
Enable this option to take into account the specified slot number
instead.

choice BOOT_DOWNGRADE_PREVENTION_CHOICE
prompt "Downgrade prevention"
optional
Expand Down
4 changes: 4 additions & 0 deletions boot/zephyr/include/mcuboot_config/mcuboot_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@
#define MCUBOOT_VERSION_CMP_USE_BUILD_NUMBER
#endif

#ifdef CONFIG_BOOT_VERSION_CMP_USE_SLOT_NUMBER
#define MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
#endif

#ifdef CONFIG_BOOT_SWAP_SAVE_ENCTLV
#define MCUBOOT_SWAP_SAVE_ENCTLV 1
#endif
Expand Down
3 changes: 2 additions & 1 deletion scripts/imgtool/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,8 +587,9 @@ def create(self, key, public_key_format, enckey, dependencies=None,
if dependencies is not None:
for i in range(dependencies_num):
payload = struct.pack(
e + 'B3x' + 'BBHI',
e + 'BB2x' + 'BBHI',
int(dependencies[DEP_IMAGES_KEY][i]),
dependencies[DEP_VERSIONS_KEY][i].slot,
dependencies[DEP_VERSIONS_KEY][i].major,
dependencies[DEP_VERSIONS_KEY][i].minor,
dependencies[DEP_VERSIONS_KEY][i].revision,
Expand Down
26 changes: 23 additions & 3 deletions scripts/imgtool/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import lzma
import hashlib
import base64
from collections import namedtuple
from imgtool import image, imgtool_version
from imgtool.version import decode_version
from imgtool.dumpinfo import dump_imginfo
Expand All @@ -45,6 +46,8 @@
sys.exit("Python %s.%s or newer is required by imgtool."
% MIN_PYTHON_VERSION)

SlottedSemiSemVersion = namedtuple('SemiSemVersion', ['major', 'minor', 'revision',
'build', 'slot'])

def gen_rsa2048(keyfile, passwd):
keys.RSA.generate().export_private(path=keyfile, passwd=passwd)
Expand Down Expand Up @@ -300,16 +303,33 @@ def get_dependencies(ctx, param, value):
if len(images) == 0:
raise click.BadParameter(
"Image dependency format is invalid: {}".format(value))
raw_versions = re.findall(r",\s*([0-9.+]+)\)", value)
raw_versions = re.findall(r",\s*(([0-9]+)\s*,)?\s*([0-9.+]+)\)", value)
if len(images) != len(raw_versions):
raise click.BadParameter(
'''There's a mismatch between the number of dependency images
and versions in: {}'''.format(value))
for raw_version in raw_versions:
try:
versions.append(decode_version(raw_version))
decoded_version = decode_version(raw_version[2])
if len(raw_version[1]) > 0:
slotted_version = SlottedSemiSemVersion(
decoded_version.major,
decoded_version.minor,
decoded_version.revision,
decoded_version.build,
int(raw_version[1])
)
else:
slotted_version = SlottedSemiSemVersion(
decoded_version.major,
decoded_version.minor,
decoded_version.revision,
decoded_version.build,
0
)
except ValueError as e:
raise click.BadParameter("{}".format(e))
versions.append(slotted_version)
dependencies = dict()
dependencies[image.DEP_IMAGES_KEY] = images
dependencies[image.DEP_VERSIONS_KEY] = versions
Expand Down Expand Up @@ -404,7 +424,7 @@ def convert(self, value, param, ctx):
'(for mcuboot <1.5)')
@click.option('-d', '--dependencies', callback=get_dependencies,
required=False, help='''Add dependence on another image, format:
"(<image_ID>,<image_version>), ... "''')
"(<image_ID>,[<slot_ID>],<image_version>), ... "''')
@click.option('-s', '--security-counter', callback=validate_security_counter,
help='Specify the value of security counter. Use the `auto` '
'keyword to automatically generate it from the image version.')
Expand Down
Loading