Skip to content

Commit

Permalink
Add support for slots with different sector sizes
Browse files Browse the repository at this point in the history
This adds bootutil support for slots on different flash devices
the happen to have different sector sizes.

It consists basically in relaxing the `boot_slots_compatible` to
allow swaps as long as the sectors that are required to fit both
images are able to fit inside scratch and both slot's sectors have
sizes that are multiple of each other.

This is now tested on the simulator and was tested in a Nordic's
pca10056 using slot0 in internal flash, and slot1 in the external
QSPI flash, configured with 4K, 8K and 16K sized sectors (the HW
is 4KB but Mynewt allows emulating multiples of that!)

Signed-off-by: Fabio Utzig <utzig@apache.org>
  • Loading branch information
utzig committed Nov 28, 2018
1 parent 1eec86e commit 3da566a
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 34 deletions.
40 changes: 27 additions & 13 deletions boot/bootutil/src/bootutil_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,11 @@ struct boot_loader_state {
size_t num_sectors;
} imgs[BOOT_NUM_SLOTS];

const struct flash_area *scratch_area;
struct {
const struct flash_area *area;
boot_sector_t *sectors;
size_t num_sectors;
} scratch;

uint8_t write_sz;
};
Expand Down Expand Up @@ -198,7 +202,7 @@ int boot_read_enc_key(uint8_t slot, uint8_t *enckey);

/* These are macros so they can be used as lvalues. */
#define BOOT_IMG_AREA(state, slot) ((state)->imgs[(slot)].area)
#define BOOT_SCRATCH_AREA(state) ((state)->scratch_area)
#define BOOT_SCRATCH_AREA(state) ((state)->scratch.area)
#define BOOT_WRITE_SZ(state) ((state)->write_sz)

static inline struct image_header*
Expand All @@ -213,6 +217,12 @@ boot_img_num_sectors(struct boot_loader_state *state, size_t slot)
return state->imgs[slot].num_sectors;
}

static inline size_t
boot_scratch_num_sectors(struct boot_loader_state *state)
{
return state->scratch.num_sectors;
}

/*
* Offset of the slot from the beginning of the flash device.
*/
Expand All @@ -224,7 +234,7 @@ boot_img_slot_off(struct boot_loader_state *state, size_t slot)

static inline size_t boot_scratch_area_size(struct boot_loader_state *state)
{
return state->scratch_area->fa_size;
return BOOT_SCRATCH_AREA(state)->fa_size;
}

#ifndef MCUBOOT_USE_FLASH_AREA_GET_SECTORS
Expand Down Expand Up @@ -252,27 +262,26 @@ static inline int
boot_initialize_area(struct boot_loader_state *state, int flash_area)
{
int num_sectors = BOOT_MAX_IMG_SECTORS;
size_t slot;
int rc;

switch (flash_area) {
case FLASH_AREA_IMAGE_0:
slot = 0;
rc = flash_area_to_sectors(flash_area, &num_sectors, state->imgs[0].sectors);
state->imgs[0].num_sectors = (size_t)num_sectors;
break;
case FLASH_AREA_IMAGE_1:
slot = 1;
rc = flash_area_to_sectors(flash_area, &num_sectors, state->imgs[1].sectors);
state->imgs[1].num_sectors = (size_t)num_sectors;
break;
case FLASH_AREA_IMAGE_SCRATCH:
rc = flash_area_to_sectors(flash_area, &num_sectors, state->scratch.sectors);
state->scratch.num_sectors = (size_t)num_sectors;
break;
default:
return BOOT_EFLASH;
}

rc = flash_area_to_sectors(flash_area, &num_sectors,
state->imgs[slot].sectors);
if (rc != 0) {
return rc;
}
state->imgs[slot].num_sectors = (size_t)num_sectors;
return 0;
return rc;
}

#else /* defined(MCUBOOT_USE_FLASH_AREA_GET_SECTORS) */
Expand Down Expand Up @@ -311,6 +320,11 @@ boot_initialize_area(struct boot_loader_state *state, int flash_area)
out_sectors = state->imgs[1].sectors;
out_num_sectors = &state->imgs[1].num_sectors;
break;
case FLASH_AREA_IMAGE_SCRATCH:
num_sectors = BOOT_MAX_IMG_SECTORS;
out_sectors = state->scratch.sectors;
out_num_sectors = &state->scratch.num_sectors;
break;
default:
return -1;
}
Expand Down
115 changes: 94 additions & 21 deletions boot/bootutil/src/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -316,42 +316,87 @@ boot_write_sz(void)
* We need to use the bigger of those 2 values.
*/
elem_sz = flash_area_align(boot_data.imgs[0].area);
align = flash_area_align(boot_data.scratch_area);
align = flash_area_align(boot_data.scratch.area);
if (align > elem_sz) {
elem_sz = align;
}

return elem_sz;
}

/*
* Slots are compatible when all sectors that store upto to size of the image
* round up to sector size, in both slot's are able to fit in the scratch
* area, and have sizes that are a multiple of each other (powers of two
* presumably!).
*/
static int
boot_slots_compatible(void)
{
size_t num_sectors_0 = boot_img_num_sectors(&boot_data, 0);
size_t num_sectors_1 = boot_img_num_sectors(&boot_data, 1);
size_t size_0, size_1;
size_t i;
size_t num_sectors_0;
size_t num_sectors_1;
size_t sz0, sz1;
size_t slot0_sz, slot1_sz;
size_t scratch_sz;
size_t i, j;
int8_t smaller;

num_sectors_0 = boot_img_num_sectors(&boot_data, 0);
num_sectors_1 = boot_img_num_sectors(&boot_data, 1);
if (num_sectors_0 > BOOT_MAX_IMG_SECTORS || num_sectors_1 > BOOT_MAX_IMG_SECTORS) {
BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
return 0;
}

/* Ensure both image slots have identical sector layouts. */
if (num_sectors_0 != num_sectors_1) {
BOOT_LOG_WRN("Cannot upgrade: number of sectors differ between slots");
return 0;
}
scratch_sz = boot_scratch_area_size(&boot_data);

for (i = 0; i < num_sectors_0; i++) {
size_0 = boot_img_sector_size(&boot_data, 0, i);
size_1 = boot_img_sector_size(&boot_data, 1, i);
if (size_0 != size_1) {
BOOT_LOG_WRN("Cannot upgrade: an incompatible sector was found");
return 0;
i = sz0 = slot0_sz = 0;
j = sz1 = slot1_sz = 0;
smaller = 0;
while (i < num_sectors_0 || j < num_sectors_1) {
if (sz0 == sz1) {
sz0 += boot_img_sector_size(&boot_data, 0, i);
sz1 += boot_img_sector_size(&boot_data, 1, j);
i++;
j++;
} else if (sz0 < sz1) {
sz0 += boot_img_sector_size(&boot_data, 0, i);
/* guarantee that multiple sectors of slot1 fit into slot0 */
if (smaller == 2) {
BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
return 0;
}
smaller = 1;
i++;
} else {
sz1 += boot_img_sector_size(&boot_data, 1, j);
/* guarantee that multiple sectors of slot0 fit into slot1 */
if (smaller == 1) {
BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
return 0;
}
smaller = 2;
j++;
}
if (sz0 == sz1) {
slot0_sz += sz0;
slot1_sz += sz1;
/* scratch has to fit each swap operation to the size of the larger
* sector among slot0 and slot1
*/
if (sz0 > scratch_sz || sz1 > scratch_sz) {
BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch");
return 0;
}
smaller = sz0 = sz1 = 0;
}
}

if (i != num_sectors_0 || j != num_sectors_1 || slot0_sz != slot1_sz) {
BOOT_LOG_WRN("Cannot upgrade: slots are not compatible");
return 0;
}

return 1;
}

Expand All @@ -376,6 +421,11 @@ boot_read_sectors(void)
return BOOT_EFLASH;
}

rc = boot_initialize_area(&boot_data, FLASH_AREA_IMAGE_SCRATCH);
if (rc != 0) {
return BOOT_EFLASH;
}

BOOT_WRITE_SZ(&boot_data) = boot_write_sz();

return 0;
Expand Down Expand Up @@ -735,6 +785,11 @@ boot_copy_sz(int last_sector_idx, int *out_first_sector_idx)
scratch_sz = boot_scratch_area_size(&boot_data);
for (i = last_sector_idx; i >= 0; i--) {
new_sz = sz + boot_img_sector_size(&boot_data, 0, i);
/*
* slot1 is not being checked here, because `boot_slots_compatible`
* already provides assurance that the copy size will be compatible
* with slot0 and scratch.
*/
if (new_sz > scratch_sz) {
break;
}
Expand Down Expand Up @@ -951,8 +1006,8 @@ boot_swap_sectors(int idx, uint32_t sz, struct boot_status *bs)
copy_sz = sz;
trailer_sz = boot_slots_trailer_sz(BOOT_WRITE_SZ(&boot_data));

/* sz in this function is always is always sized on a multiple of the
* sector size. The check against the start offset of the last sector
/* sz in this function is always sized on a multiple of the sector size.
* The check against the start offset of the last sector
* is to determine if we're swapping the last sector. The last sector
* needs special handling because it's where the trailer lives. If we're
* copying it, we need to use scratch to write the trailer temporarily.
Expand Down Expand Up @@ -1196,6 +1251,7 @@ boot_copy_image(struct boot_status *bs)
uint32_t sz;
int first_sector_idx;
int last_sector_idx;
int last_idx_slot1;
uint32_t swap_idx;
struct image_header *hdr;
#ifdef MCUBOOT_ENC_IMAGES
Expand All @@ -1206,6 +1262,8 @@ boot_copy_image(struct boot_status *bs)
#endif
uint32_t size;
uint32_t copy_size;
uint32_t slot0_size;
uint32_t slot1_size;
int rc;

/* FIXME: just do this if asked by user? */
Expand Down Expand Up @@ -1298,14 +1356,27 @@ boot_copy_image(struct boot_status *bs)
#endif
}

size = 0;
slot0_size = 0;
slot1_size = 0;
last_sector_idx = 0;
/* not used in the swap routines, only here to match slot0's and
* slot1's sizes
*/
last_idx_slot1 = 0;
while (1) {
size += boot_img_sector_size(&boot_data, 0, last_sector_idx);
if (size >= copy_size) {
if (slot0_size < copy_size || slot0_size < slot1_size) {
slot0_size += boot_img_sector_size(&boot_data, 0, last_sector_idx);
}
if (slot1_size < copy_size || slot1_size < slot0_size) {
slot1_size += boot_img_sector_size(&boot_data, 1, last_idx_slot1);
}
if (slot0_size >= copy_size &&
slot1_size >= copy_size &&
slot0_size == slot1_size) {
break;
}
last_sector_idx++;
last_idx_slot1++;
}

swap_idx = 0;
Expand Down Expand Up @@ -1467,8 +1538,10 @@ boot_go(struct boot_rsp *rsp)
*/
static boot_sector_t slot0_sectors[BOOT_MAX_IMG_SECTORS];
static boot_sector_t slot1_sectors[BOOT_MAX_IMG_SECTORS];
static boot_sector_t scratch_sectors[BOOT_MAX_IMG_SECTORS];
boot_data.imgs[0].sectors = slot0_sectors;
boot_data.imgs[1].sectors = slot1_sectors;
boot_data.scratch.sectors = scratch_sectors;

#ifdef MCUBOOT_ENC_IMAGES
/* FIXME: remove this after RAM is cleared by sim */
Expand Down

0 comments on commit 3da566a

Please sign in to comment.