Commit dce784a2 authored by Tomasz Chyrowicz's avatar Tomasz Chyrowicz Committed by Dominik Ermel
Browse files

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: default avatarTomasz Chyrowicz <tomasz.chyrowicz@nordicsemi.no>
parent 5ee96f5b
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -144,6 +144,10 @@ extern "C" {
                                             */
#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;
@@ -153,7 +157,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
+166 −1
Original line number Diff line number Diff line
@@ -317,6 +317,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
@@ -354,6 +372,26 @@ boot_verify_slot_dependency(struct boot_loader_state *state,
    }
#endif

#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;
}

@@ -499,6 +537,19 @@ boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot)
            goto done;
        }

#ifdef MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER
        /* Validate against possible dependency slot values. */
        switch(dep->slot) {
            case VERSION_DEP_SLOT_ACTIVE:
            case VERSION_DEP_SLOT_PRIMARY:
            case VERSION_DEP_SLOT_SECONDARY:
                break;
            default:
                rc = BOOT_EBADARGS;
                goto done;
        }
#endif /* MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER */

        /* Verify dependency and modify the swap type if not satisfied. */
        rc = boot_verify_slot_dependency(state, &dep);
        if (rc != 0) {
@@ -2674,6 +2725,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.
 *
@@ -2771,6 +2935,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.
+9 −0
Original line number Diff line number Diff line
@@ -987,6 +987,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
+4 −0
Original line number Diff line number Diff line
@@ -124,6 +124,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
+17 −0
Original line number Diff line number Diff line
@@ -898,6 +898,23 @@ process is presented below.
+ Boot into image in the primary slot of the 0th image position\
  (other image in the boot chain is started by another image).

By enabling the `MCUBOOT_VERSION_CMP_USE_SLOT_NUMBER` configuration option,
the dependency check may be extended to match for a specified slot of a specific
image. This functionality is useful in a multi-core system when Direct XIP mode
is used.
In this case, the main image can be started from one of the two (primary or
secondary) slots.
If there is a fixed connection between the slots of two different images,
e.g. if the main image always chainloads a companion image from the same slot,
the check must take this into account and only consider a matching slot when
resolving dependencies.

There are three values that can be passed when specifying dependencies:

1. ``active``: the dependency should be checked against either primary or secondary slot.
2. ``primary``: the dependency should be checked only against primary slot.
3. ``secondary``: the dependency should be checked only against secondary slot.

### [Multiple image boot for RAM loading and direct-xip](#multiple-image-boot-for-ram-loading-and-direct-xip)

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