Commit 505fba25 authored by David Vincze's avatar David Vincze Committed by Dávid Vincze
Browse files

Boot: Add 'revert' support to direct-xip mode



The 'revert' mechanism in direct-xip is similar to the one in swap mode.
It requires the trailer magic to be added to the signed image. When a
reboot happens without the image being confirmed at runtime (without the
image_ok flag being set), the bootloader considers the image faulty and
erases it. After this it will attempt to boot the previous image
instead. The images can also be made permanent (marked as confirmed in
advance) just like in swap mode.

Change-Id: Ibde9361d4a7302dd8efbb277b691b71eca0ca877
Signed-off-by: default avatarDavid Vincze <david.vincze@linaro.org>
parent f6c69231
Loading
Loading
Loading
Loading
+103 −7
Original line number Diff line number Diff line
@@ -2047,6 +2047,75 @@ boot_get_slot_usage(struct boot_loader_state *state, uint8_t slot_usage[],
    return image_cnt;
}

#ifdef MCUBOOT_DIRECT_XIP_REVERT
/**
 * Checks whether the image in the given slot was previously selected to run.
 * Erases the image if it was selected but its execution failed, otherwise marks
 * it as selected if it has not been before.
 *
 * @param state     Image metadata from the image trailer. This function fills
 *                  this struct with the data read from the image trailer.
 * @param slot      Image slot number.
 *
 * @return          0 on success; nonzero on failure.
 */
static int
boot_select_or_erase(struct boot_swap_state *state, uint32_t slot)
{
    const struct flash_area *fap;
    int fa_id;
    int rc;

    fa_id = flash_area_id_from_image_slot(slot);
    rc = flash_area_open(fa_id, &fap);
    assert(rc == 0);

    memset(state, 0, sizeof(struct boot_swap_state));
    rc = boot_read_swap_state(fap, state);
    assert(rc == 0);

    if (state->magic != BOOT_MAGIC_GOOD ||
        (state->copy_done == BOOT_FLAG_SET &&
         state->image_ok  != BOOT_FLAG_SET)) {
        /*
         * A reboot happened without the image being confirmed at
         * runtime or its trailer is corrupted/invalid. Erase the image
         * to prevent it from being selected again on the next reboot.
         */
        BOOT_LOG_DBG("Erasing faulty image in the %s slot.",
                     (slot == BOOT_PRIMARY_SLOT) ? "primary" : "secondary");
        rc = flash_area_erase(fap, 0, fap->fa_size);
        assert(rc == 0);

        flash_area_close(fap);
        rc = -1;
    } else {
        if (state->copy_done != BOOT_FLAG_SET) {
            if (state->copy_done == BOOT_FLAG_BAD) {
                BOOT_LOG_DBG("The copy_done flag had an unexpected value. Its "
                             "value was neither 'set' nor 'unset', but 'bad'.");
            }
            /*
             * Set the copy_done flag, indicating that the image has been
             * selected to boot. It can be set in advance, before even
             * validating the image, because in case the validation fails, the
             * entire image slot will be erased (including the trailer).
             */
            rc = boot_write_copy_done(fap);
            if (rc != 0) {
                BOOT_LOG_WRN("Failed to set copy_done flag of the image in "
                             "the %s slot.", (slot == BOOT_PRIMARY_SLOT) ?
                             "primary" : "secondary");
                rc = 0;
            }
        }
        flash_area_close(fap);
    }

    return rc;
}
#endif /* MCUBOOT_DIRECT_XIP_REVERT */

#ifdef MCUBOOT_RAM_LOAD

#if !defined(IMAGE_EXECUTABLE_RAM_START) || !defined(IMAGE_EXECUTABLE_RAM_SIZE)
@@ -2207,6 +2276,9 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
    uint32_t img_sz;
    uint32_t img_loaded = 0;
#endif /* MCUBOOT_RAM_LOAD */
#ifdef MCUBOOT_DIRECT_XIP_REVERT
    struct boot_swap_state slot_state;
#endif /* MCUBOOT_DIRECT_XIP_REVERT */
    fih_int fih_rc = FIH_FAILURE;

    memset(state, 0, sizeof(struct boot_loader_state));
@@ -2234,7 +2306,6 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
        /* Select the newest and valid image. */
        for (i = 0; i < img_cnt; i++) {
            selected_slot = 0;
            selected_image_header = NULL;

            /* Iterate over all the slots that are in use (contain an image)
             * and select the one that holds the newest image.
@@ -2257,6 +2328,17 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
                    selected_image_header = hdr;
                }
            }

#ifdef MCUBOOT_DIRECT_XIP_REVERT
            rc = boot_select_or_erase(&slot_state, selected_slot);
            if (rc != 0) {
                /* The selected image slot has been erased. */
                slot_usage[selected_slot] = 0;
                selected_image_header = NULL;
                continue;
            }
#endif /* MCUBOOT_DIRECT_XIP_REVERT */

#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
@@ -2268,6 +2350,8 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
                                         &img_sz);
            if (rc != 0 ) {
                /* Image loading failed try the next one. */
                slot_usage[selected_slot] = 0;
                selected_image_header = NULL;
                continue;
            } else {
                img_loaded = 1;
@@ -2294,6 +2378,7 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
             * and start over.
             */
            slot_usage[selected_slot] = 0;
            selected_image_header = NULL;
        }

        if (fih_not_eq(fih_rc, FIH_SUCCESS) || (selected_image_header == NULL)) {
@@ -2305,6 +2390,14 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
        /* Update the stored security counter with the newer (active) image's
         * security counter value.
         */
#ifdef MCUBOOT_DIRECT_XIP_REVERT
        /* When the 'revert' mechanism is enabled in direct-xip mode, the
         * security counter can be increased only after reboot, if the image
         * has been confirmed at runtime (the image_ok flag has been set).
         * This way a 'revert' can be performed when it's necessary.
         */
        if (slot_state.image_ok == BOOT_FLAG_SET) {
#endif
            rc = boot_update_security_counter(0, selected_slot,
                                              selected_image_header);
            if (rc != 0) {
@@ -2312,6 +2405,9 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
                             "validation.");
                goto out;
            }
#ifdef MCUBOOT_DIRECT_XIP_REVERT
        }
#endif
#endif /* MCUBOOT_HW_ROLLBACK_PROT */

#ifdef MCUBOOT_MEASURED_BOOT
+12 −3
Original line number Diff line number Diff line
@@ -224,9 +224,18 @@ config BOOT_DIRECT_XIP
	  mode results in a simpler code path and smaller code size.

endchoice
endif

if !SINGLE_APPLICATION_SLOT
config BOOT_DIRECT_XIP_REVERT
	bool "Enable the revert mechanism in direct-xip mode"
	default n
	help
	  If y, enables the revert mechanism in direct-xip similar to the one in
	  swap mode. It requires the trailer magic to be added to the signed image.
	  When a reboot happens without the image being confirmed at runtime, the
	  bootloader considers the image faulty and erases it. After this it will
	  attempt to boot the previous image. The images can also be made permanent
	  (marked as confirmed in advance) just like in swap mode.

config BOOT_BOOTSTRAP
	bool "Bootstrap erased the primary slot from the secondary slot"
	default n
@@ -276,7 +285,7 @@ config BOOT_ENCRYPT_X25519
	  back when swapping from the primary slot to the secondary slot. The
	  encryption mechanism used in this case is ECIES using primitives
	  described under "ECIES-X25519 encryption" in docs/encrypted_images.md.
endif
endif # !SINGLE_APPLICATION_SLOT

config BOOT_MAX_IMG_SECTORS
	int "Maximum number of sectors per image slot"
+4 −0
Original line number Diff line number Diff line
@@ -70,6 +70,10 @@
#define MCUBOOT_DIRECT_XIP
#endif

#ifdef CONFIG_BOOT_DIRECT_XIP_REVERT
#define MCUBOOT_DIRECT_XIP_REVERT
#endif

#ifdef CONFIG_UPDATEABLE_IMAGE_NUMBER
#define MCUBOOT_IMAGE_NUMBER    CONFIG_UPDATEABLE_IMAGE_NUMBER
#else
+30 −0
Original line number Diff line number Diff line
@@ -233,6 +233,9 @@ then checks its validity (integrity check, signature verification etc.). If the
image is invalid MCUboot erases its memory slot and starts to validate the other
image. After a successful validation of the selected image the bootloader
chain-loads it.

An additional "revert" mechanism is also supported. For more information, please
read the [corresponding section](#direct-xip-revert).
Handling the primary and secondary slots as equals has its drawbacks. Since the
images are not moved between the slots, the on-the-fly image
encryption/decryption can't be supported (it only applies to storing the image
@@ -329,6 +332,33 @@ The "swap type" is a high-level representation of the outcome of the
boot. Subsequent sections describe how mcuboot determines the swap type from
the bit-level contents of flash.

### [Revert mechanism in direct-xip mode](#direct-xip-revert)

The direct-xip mode also supports a "revert" mechanism which is the equivalent
of the swap mode's "revert" swap. It can be enabled with the
MCUBOOT_DIRECT_XIP_REVERT config option and an image trailer must also be added
to the signed images (the "--pad" option of the `imgtool` script must be used).
For more information on this please read the [Image Trailer](#image-trailer)
section and the [imgtool](imgtool.md) documentation. Making the images permanent
(marking them as confirmed in advance) is also supported just like in swap mode.
The individual steps of the direct-xip mode's "revert" mechanism are the
following:

1. Select the slot which holds the newest potential image.
2. Was the image previously selected to run (during a previous boot)?
    + Yes: Did the image mark itself "OK" (was the self-test successful)?
        + Yes.
            - Proceed to step 3.
        + No.
            - Erase the image from the slot to prevent it from being selected
              again during the next boot.
            - Return to step 1 (the bootloader will attempt to select and
              possibly boot the previous image if there is one).
    + No.
        - Mark the image as "selected" (set the copy_done flag in the trailer).
        - Proceed to step 3.
3. Proceed to image validation ...

## [Image Trailer](#image-trailer)

For the bootloader to be able to determine the current state and what actions
+2 −0
Original line number Diff line number Diff line
@@ -57,6 +57,8 @@

/* Uncomment to enable the direct-xip code path. */
/* #define MCUBOOT_DIRECT_XIP */
/* Uncomment to enable the revert mechanism in direct-xip mode. */
/* #define MCUBOOT_DIRECT_XIP_REVERT */

/* Uncomment to enable the ram-load code path. */
/* #define MCUBOOT_RAM_LOAD */