Commit 11c0611a authored by Tomasz Chyrowicz's avatar Tomasz Chyrowicz Committed by Jamie McCrae
Browse files

bootutil: Unify app_max_size() implementations



Remove redundant application size calculations in favor of
a swap-specific function, implemented inside swap_<type>.c.
In this way, slot sizes use the same restrictions as image validation.

Note: This PR has been modified from the upstream commit due to
a merge failure from other changes that are not being brought into
Zephyr 3.7

Signed-off-by: default avatarTomasz Chyrowicz <tomasz.chyrowicz@nordicsemi.no>
(cherry picked from commit f9db0194)
parent 9abd0685
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -302,6 +302,12 @@ boot_set_next(const struct flash_area *fa, bool active, bool confirm);
/**
 * Attempts to load image header from flash; verifies flash header fields.
 *
 * The selected update method (i.e. swap move) may impose additional restrictions
 * on the image size (i.e. due to the presence of the image trailer).
 * Such restrictions are not verified by this function.
 * These checks are implemented as part of the boot_image_validate(..) that uses
 * sizes from the bootutil_max_image_size(..).
 *
 * @param[in]   fa_p    flash area pointer
 * @param[out]  hdr     buffer for image header
 *
+8 −89
Original line number Diff line number Diff line
@@ -42,6 +42,10 @@
#ifdef MCUBOOT_ENC_IMAGES
#include "bootutil/enc_key.h"
#endif
#if defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_OFFSET) || \
    defined(MCUBOOT_SWAP_USING_SCRATCH)
#include "swap_priv.h"
#endif

#if defined(MCUBOOT_SWAP_USING_SCRATCH)
#include "swap_priv.h"
@@ -161,8 +165,7 @@ boot_trailer_sz(uint32_t min_write_sz)
 * status during the swap of the last sector from primary/secondary (which
 * is the first swap operation) and thus only requires space for one swap.
 */
static uint32_t
boot_scratch_trailer_sz(uint32_t min_write_sz)
uint32_t boot_scratch_trailer_sz(uint32_t min_write_sz)
{
    return boot_status_entry_sz(min_write_sz) + boot_trailer_info_sz();
}
@@ -350,99 +353,15 @@ boot_write_enc_key(const struct flash_area *fap, uint8_t slot,
}
#endif

#ifdef MCUBOOT_SWAP_USING_SCRATCH
size_t
boot_get_first_trailer_sector(struct boot_loader_state *state, size_t slot, size_t trailer_sz)
{
    size_t first_trailer_sector = boot_img_num_sectors(state, slot) - 1;
    size_t sector_sz = boot_img_sector_size(state, slot, first_trailer_sector);
    size_t trailer_sector_sz = sector_sz;

    while (trailer_sector_sz < trailer_sz) {
        /* Consider that the image trailer may span across sectors of different sizes */
        --first_trailer_sector;
        sector_sz = boot_img_sector_size(state, slot, first_trailer_sector);

        trailer_sector_sz += sector_sz;
    }

    return first_trailer_sector;
}

/**
 * Returns the offset to the end of the first sector of a given slot that holds image trailer data.
 *
 * @param state      Current bootloader's state.
 * @param slot       The index of the slot to consider.
 * @param trailer_sz The size of the trailer, in bytes.
 *
 * @return The offset to the end of the first sector of the slot that holds image trailer data.
 */
static uint32_t
get_first_trailer_sector_end_off(struct boot_loader_state *state, size_t slot, size_t trailer_sz)
{
    size_t first_trailer_sector = boot_get_first_trailer_sector(state, slot, trailer_sz);

    return boot_img_sector_off(state, slot, first_trailer_sector) +
           boot_img_sector_size(state, slot, first_trailer_sector);
}
#endif /* MCUBOOT_SWAP_USING_SCRATCH */

uint32_t bootutil_max_image_size(struct boot_loader_state *state, const struct flash_area *fap)
{
#if defined(MCUBOOT_SINGLE_APPLICATION_SLOT) ||      \
    defined(MCUBOOT_FIRMWARE_LOADER)
    (void) state;
    return boot_status_off(fap);
#elif defined(MCUBOOT_SWAP_USING_SCRATCH)
    size_t slot_trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
    size_t slot_trailer_off = flash_area_get_size(fap) - slot_trailer_sz;

    /* If the trailer doesn't fit in the last sector of the primary or secondary slot, some padding
     * might have to be inserted between the end of the firmware image and the beginning of the
     * trailer to ensure there is enough space for the trailer in the scratch area when the last
     * sector of the secondary will be copied to the scratch area.
     *
     * The value of the padding depends on the amount of trailer data that is contained in the first
     * trailer containing part of the trailer in the primary and secondary slot.
     */
    size_t trailer_sector_primary_end_off =
        get_first_trailer_sector_end_off(state, BOOT_PRIMARY_SLOT, slot_trailer_sz);
    size_t trailer_sector_secondary_end_off =
        get_first_trailer_sector_end_off(state, BOOT_SECONDARY_SLOT, slot_trailer_sz);

    size_t trailer_sz_in_first_sector;

    if (trailer_sector_primary_end_off > trailer_sector_secondary_end_off) {
        trailer_sz_in_first_sector = trailer_sector_primary_end_off - slot_trailer_off;
    } else {
        trailer_sz_in_first_sector = trailer_sector_secondary_end_off - slot_trailer_off;
    }

    size_t trailer_padding = 0;
    size_t scratch_trailer_sz = boot_scratch_trailer_sz(BOOT_WRITE_SZ(state));

    if (scratch_trailer_sz > trailer_sz_in_first_sector) {
        trailer_padding = scratch_trailer_sz - trailer_sz_in_first_sector;
    }

    return slot_trailer_off - trailer_padding;
#elif defined(MCUBOOT_SWAP_USING_MOVE)
    (void) state;

    /* The slot whose size is used to compute the maximum image size must be the one containing the
     * padding required for the swap. */
    const struct flash_area *fap_padded_slot = BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT);
    assert(fap_padded_slot != NULL);

    size_t trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
    size_t sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0);
    size_t padding_sz = sector_sz;

    /* The trailer size needs to be sector-aligned */
    trailer_sz = ALIGN_UP(trailer_sz, sector_sz);

    return flash_area_get_size(fap_padded_slot) - trailer_sz - padding_sz;
#elif defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_SCRATCH)
    (void) fap;
    return app_max_size(state);
#elif defined(MCUBOOT_OVERWRITE_ONLY)
    (void) state;
    return boot_swap_info_off(fap);
+7 −11
Original line number Diff line number Diff line
@@ -331,18 +331,14 @@ int boot_read_enc_key(const struct flash_area *fap, uint8_t slot,
                      struct boot_status *bs);
#endif

#ifdef MCUBOOT_SWAP_USING_SCRATCH
/**
 * Finds the first sector of a given slot that holds image trailer data.
 *
 * @param state      Current bootloader's state.
 * @param slot       The index of the slot to consider.
 * @param trailer_sz The size of the trailer, in bytes.
 *
 * @return The index of the first sector of the slot that holds image trailer data.
#if MCUBOOT_SWAP_USING_SCRATCH
/*
 * Similar to `boot_trailer_sz` but this function returns the space used to
 * store status in the scratch partition. The scratch partition only stores
 * status during the swap of the last sector from primary/secondary (which
 * is the first swap operation) and thus only requires space for one swap.
 */
size_t
boot_get_first_trailer_sector(struct boot_loader_state *state, size_t slot, size_t trailer_sz);
uint32_t boot_scratch_trailer_sz(uint32_t min_write_sz);
#endif

/**
+19 −36
Original line number Diff line number Diff line
@@ -234,29 +234,6 @@ boot_status_internal_off(const struct boot_status *bs, int elem_sz)
    return off;
}

static int app_max_sectors(struct boot_loader_state *state)
{
    uint32_t sz = 0;
    uint32_t sector_sz;
    uint32_t trailer_sz;
    uint32_t first_trailer_idx;

    sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0);
    trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
    /* subtract 1 for swap and at least 1 for trailer */
    first_trailer_idx = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 2;

    while (1) {
        sz += sector_sz;
        if  (sz >= trailer_sz) {
            break;
        }
        first_trailer_idx--;
    }

    return first_trailer_idx;
}

int
boot_slots_compatible(struct boot_loader_state *state)
{
@@ -265,19 +242,16 @@ boot_slots_compatible(struct boot_loader_state *state)
    size_t sector_sz_pri = 0;
    size_t sector_sz_sec = 0;
    size_t i;
    size_t num_usable_sectors_pri;

    num_sectors_pri = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
    num_sectors_sec = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
    num_usable_sectors_pri = app_max_sectors(state);

    if ((num_sectors_pri != num_sectors_sec) &&
            (num_sectors_pri != (num_sectors_sec + 1)) &&
            (num_usable_sectors_pri != (num_sectors_sec + 1))) {
            (num_sectors_pri != (num_sectors_sec + 1))) {
        BOOT_LOG_WRN("Cannot upgrade: not a compatible amount of sectors");
        BOOT_LOG_DBG("slot0 sectors: %d, slot1 sectors: %d, usable slot0 sectors: %d",
                     (int)num_sectors_pri, (int)num_sectors_sec,
                     (int)(num_usable_sectors_pri - 1));
                     (int)(num_sectors_pri - 1));
        return 0;
    } else if (num_sectors_pri > BOOT_MAX_IMG_SECTORS) {
        BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
@@ -287,7 +261,7 @@ boot_slots_compatible(struct boot_loader_state *state)
    /* Optimal says primary has one more than secondary. Always. Both have trailers. */
    if (num_sectors_pri != (num_sectors_sec + 1)) {
        BOOT_LOG_DBG("Non-optimal sector distribution, slot0 has %d usable sectors (%d assigned) "
                     "but slot1 has %d assigned", (int)num_usable_sectors_pri,
                     "but slot1 has %d assigned", (int)(num_sectors_pri - 1),
                     (int)num_sectors_pri, (int)num_sectors_sec);
    }

@@ -327,7 +301,6 @@ swap_status_source(struct boot_loader_state *state)
    struct boot_swap_state state_primary_slot;
    struct boot_swap_state state_secondary_slot;
    int rc;
    uint8_t source;
    uint8_t image_index;

#if (BOOT_IMAGE_NUMBER == 1)
@@ -352,10 +325,8 @@ swap_status_source(struct boot_loader_state *state)
            state_primary_slot.copy_done == BOOT_FLAG_UNSET &&
            state_secondary_slot.magic != BOOT_MAGIC_GOOD) {

        source = BOOT_STATUS_SOURCE_PRIMARY_SLOT;

        BOOT_LOG_INF("Boot source: primary slot");
        return source;
        return BOOT_STATUS_SOURCE_PRIMARY_SLOT;
    }

    BOOT_LOG_INF("Boot source: none");
@@ -579,11 +550,23 @@ swap_run(struct boot_loader_state *state, struct boot_status *bs,

int app_max_size(struct boot_loader_state *state)
{
    uint32_t sector_sz_primary;
    uint32_t available_pri_sz;
    uint32_t available_sec_sz;

    size_t trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
    size_t sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0);
    size_t padding_sz = sector_sz;

    sector_sz_primary = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0);
    /* The trailer size needs to be sector-aligned */
    trailer_sz = ALIGN_UP(trailer_sz, sector_sz);

    /* The slot whose size is used to compute the maximum image size must be the one containing the
     * padding required for the swap.
     */
    available_pri_sz = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) * sector_sz - trailer_sz - padding_sz;
    available_sec_sz = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT) * sector_sz - trailer_sz;

    return app_max_sectors(state) * sector_sz_primary;
    return (available_pri_sz < available_sec_sz ? available_pri_sz : available_sec_sz);
}

#endif
+140 −6
Original line number Diff line number Diff line
@@ -47,6 +47,136 @@ int boot_status_fails = 0;
#define BOOT_STATUS_ASSERT(x) ASSERT(x)
#endif

#if MCUBOOT_SWAP_USING_SCRATCH
/**
 * Finds the first sector of a given slot that holds image trailer data.
 *
 * @param state      Current bootloader's state.
 * @param slot       The index of the slot to consider.
 * @param trailer_sz The size of the trailer, in bytes.
 *
 * @return The index of the first sector of the slot that holds image trailer data.
 */
static size_t
boot_get_first_trailer_sector(struct boot_loader_state *state, size_t slot, size_t trailer_sz)
{
    size_t first_trailer_sector = boot_img_num_sectors(state, slot) - 1;
    size_t sector_sz = boot_img_sector_size(state, slot, first_trailer_sector);
    size_t trailer_sector_sz = sector_sz;

    while (trailer_sector_sz < trailer_sz) {
        /* Consider that the image trailer may span across sectors of different sizes */
        --first_trailer_sector;
        sector_sz = boot_img_sector_size(state, slot, first_trailer_sector);

        trailer_sector_sz += sector_sz;
    }

    return first_trailer_sector;
}

/**
 * Returns the offset to the end of the first sector of a given slot that holds image trailer data.
 *
 * @param state      Current bootloader's state.
 * @param slot       The index of the slot to consider.
 * @param trailer_sz The size of the trailer, in bytes.
 *
 * @return The offset to the end of the first sector of the slot that holds image trailer data.
 */
static uint32_t
get_first_trailer_sector_end_off(struct boot_loader_state *state, size_t slot, size_t trailer_sz)
{
    size_t first_trailer_sector = boot_get_first_trailer_sector(state, slot, trailer_sz);

    return boot_img_sector_off(state, slot, first_trailer_sector) +
           boot_img_sector_size(state, slot, first_trailer_sector);
}

/**
 * Returns the size of the part of the slot that can be used for storing image data.
 *
 * @param state     Current bootloader's state.
 * @param slot_size The size of the slot partition.
 *
 * @return The offset to the end of the first sector of the slot that holds image trailer data.
 */
static size_t app_max_size_adjust_to_trailer(struct boot_loader_state *state, size_t slot_size)
{
    size_t slot_trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
    size_t slot_trailer_off = slot_size - slot_trailer_sz;
    size_t trailer_sz_in_first_sector;
    size_t trailer_sector_end_off;


    size_t trailer_sector_primary_end_off =
        get_first_trailer_sector_end_off(state, BOOT_PRIMARY_SLOT, slot_trailer_sz);
    size_t trailer_sector_secondary_end_off =
        get_first_trailer_sector_end_off(state, BOOT_SECONDARY_SLOT, slot_trailer_sz);

    /* If slots have sectors of different sizes, we need to find the "common" sector
     * boundary (slot compatibility checks ensure that the larger sector contains a multiple
     * of the smaller sector size). This will be the larger of the
     * trailer_sector_primary_end_off/trailer_sector_secondary_end_off.
     *
     *  <-------copy size-------> <--------copy size------> <----copy size--->
     * v                         v                         v                  v
     * +------------+------------+-------------------------+------------------+
     * |   sector   |   sector   |          sector         |      sector      |
     * +------------+------------+------------+------------+------------------+
     * |          sector         |   sector   |   sector   |      sector      |
     * +-------------------------+------------+------------+------------------+
     *
     * The swap logic always uses the common boundary when performing the copy.
     * Hence, the first trailer sector used for calculation is the larger
     * sector from the two slots.
     *
     * <-----------copy size--------------->
     * |     sector      |     sector      |
     * +-----------------------------------+
     * |              sector               |
     * +-----------------------------------+
     * |Image->|     |<-trailer------------|
     * +-----------------------------------+
     * |                |<-scratch trailer>|
     * +-----------------------------------+
     */
    if (trailer_sector_primary_end_off > trailer_sector_secondary_end_off) {
        trailer_sector_end_off = trailer_sector_primary_end_off;
    } else {
        trailer_sector_end_off = trailer_sector_secondary_end_off;
    }

    trailer_sz_in_first_sector = trailer_sector_end_off - slot_trailer_off;

    size_t trailer_padding = 0;
    size_t scratch_trailer_sz = boot_scratch_trailer_sz(BOOT_WRITE_SZ(state));

    /* Some padding might have to be inserted between the end of the firmware image and the
     * beginning of the trailer to ensure there is enough space for the trailer in the scratch area
     * when the last sector of the secondary will be copied to the scratch area.
     *
     * +-----------------------------------+-----------------------------------+
     * |              sector               |              sector               |
     * +-----------------------------------+-----------------------------------+
     * |Image->|             |<--trailer---|-----------trailer (cont.)-------->|
     * +-----------------------------------+-----------------------------------+
     * |         |<----scratch trailer---->|
     * +-----------------------------------+
     *            <-padding->
     *  <--------scratch area size-------->
     *
     * The value of the padding depends on the amount of trailer data that is contained in the first
     * sector containing part of the trailer in the primary and secondary slot.
     */
    if (scratch_trailer_sz > trailer_sz_in_first_sector) {
        trailer_padding = scratch_trailer_sz - trailer_sz_in_first_sector;
    }

    return slot_trailer_off - trailer_padding;
}
#endif /* MCUBOOT_SWAP_USING_SCRATCH */

#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
/**
 * Reads the status of a partially-completed swap, if any.  This is necessary
@@ -813,8 +943,8 @@ int app_max_size(struct boot_loader_state *state)
    size_t num_sectors_primary;
    size_t num_sectors_secondary;
    size_t sz0, sz1;
    size_t primary_slot_sz, secondary_slot_sz;
#ifndef MCUBOOT_OVERWRITE_ONLY
    size_t slot_sz;
    size_t scratch_sz;
#endif
    size_t i, j;
@@ -834,8 +964,11 @@ int app_max_size(struct boot_loader_state *state)
     * number of a slot's sectors are able to fit into another, which only
     * excludes cases where sector sizes are not a multiple of each other.
     */
    i = sz0 = primary_slot_sz = 0;
    j = sz1 = secondary_slot_sz = 0;
#ifndef MCUBOOT_OVERWRITE_ONLY
    slot_sz = 0;
#endif
    i = sz0 = 0;
    j = sz1 = 0;
    smaller = 0;
    while (i < num_sectors_primary || j < num_sectors_secondary) {
        if (sz0 == sz1) {
@@ -868,8 +1001,7 @@ int app_max_size(struct boot_loader_state *state)
        }
#ifndef MCUBOOT_OVERWRITE_ONLY
        if (sz0 == sz1) {
            primary_slot_sz += sz0;
            secondary_slot_sz += sz1;
            slot_sz += sz0;
            /* Scratch has to fit each swap operation to the size of the larger
             * sector among the primary slot and the secondary slot.
             */
@@ -884,8 +1016,10 @@ int app_max_size(struct boot_loader_state *state)

#ifdef MCUBOOT_OVERWRITE_ONLY
    return (sz1 < sz0 ? sz1 : sz0);
#elif MCUBOOT_SWAP_USING_SCRATCH
    return app_max_size_adjust_to_trailer(state, slot_sz);
#else
    return (secondary_slot_sz < primary_slot_sz ? secondary_slot_sz : primary_slot_sz);
    return slot_sz;
#endif
}
#else