Merge pull request #463 from jpbland1/bad-image-recovery

add emergency fallback test, currently fails due to
pull/408/head
David Garske 2024-07-10 14:08:48 -07:00 committed by GitHub
commit b9dc7eee46
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 361 additions and 150 deletions

View File

@ -51,6 +51,14 @@ jobs:
run: | run: |
tools/scripts/sim-update-powerfail-resume.sh tools/scripts/sim-update-powerfail-resume.sh
- name: Rebuild wolfboot.elf
run: |
make clean && make test-sim-internal-flash-with-update
- name: Run emergency fallback test
run: |
tools/scripts/sim-update-emergency-fallback.sh
# TEST with NVM_WRITEONCE enabled # TEST with NVM_WRITEONCE enabled
- name: make clean - name: make clean
@ -89,6 +97,14 @@ jobs:
run: | run: |
tools/scripts/sim-update-powerfail-resume.sh tools/scripts/sim-update-powerfail-resume.sh
- name: Rebuild wolfboot.elf
run: |
make clean && make test-sim-internal-flash-with-update
- name: Run emergency fallback test
run: |
tools/scripts/sim-update-emergency-fallback.sh
# TEST with NVM_WRITEONCE AND FLAGS_HOME enabled # TEST with NVM_WRITEONCE AND FLAGS_HOME enabled
- name: make clean - name: make clean
@ -127,6 +143,14 @@ jobs:
run: | run: |
tools/scripts/sim-update-powerfail-resume.sh tools/scripts/sim-update-powerfail-resume.sh
- name: Rebuild wolfboot.elf
run: |
make clean && make test-sim-internal-flash-with-update
- name: Run emergency fallback test
run: |
tools/scripts/sim-update-emergency-fallback.sh
# TEST with NVM_WRITEONCE AND FLAGS_HOME AND FLAGS_INVERT enabled # TEST with NVM_WRITEONCE AND FLAGS_HOME AND FLAGS_INVERT enabled
- name: make clean - name: make clean
@ -165,6 +189,14 @@ jobs:
run: | run: |
tools/scripts/sim-update-powerfail-resume.sh tools/scripts/sim-update-powerfail-resume.sh
- name: Rebuild wolfboot.elf
run: |
make clean && make test-sim-external-flash-with-update
- name: Run emergency fallback test
run: |
tools/scripts/sim-update-emergency-fallback.sh
# TEST with DELTA updates # TEST with DELTA updates
- name: make clean - name: make clean
@ -189,7 +221,7 @@ jobs:
- name: Rebuild wolfboot.elf - name: Rebuild wolfboot.elf
run: | run: |
make test-sim-internal-flash-with-delta-update make clean && make test-sim-internal-flash-with-delta-update
- name: Run update-revert test (DELTA) - name: Run update-revert test (DELTA)
run: | run: |
@ -197,7 +229,7 @@ jobs:
- name: Rebuild wolfboot.elf - name: Rebuild wolfboot.elf
run: | run: |
make test-sim-internal-flash-with-delta-update make clean && make test-sim-internal-flash-with-delta-update
- name: Run update-revert test with power failures (DELTA) - name: Run update-revert test with power failures (DELTA)
run: | run: |
@ -248,6 +280,14 @@ jobs:
run: | run: |
tools/scripts/sim-update-powerfail-resume.sh tools/scripts/sim-update-powerfail-resume.sh
- name: Rebuild wolfboot.elf
run: |
make clean && make test-sim-external-flash-with-enc-update
- name: Run emergency fallback test
run: |
tools/scripts/sim-update-emergency-fallback.sh
# TEST with encryption (aes128) and delta updates # TEST with encryption (aes128) and delta updates
- name: make clean - name: make clean
run: | run: |
@ -322,6 +362,14 @@ jobs:
run: | run: |
tools/scripts/sim-update-powerfail-resume.sh tools/scripts/sim-update-powerfail-resume.sh
- name: Rebuild wolfboot.elf
run: |
make clean && make test-sim-external-flash-with-enc-update
- name: Run emergency fallback test
run: |
tools/scripts/sim-update-emergency-fallback.sh
# TEST with encryption (aes128) and NVM_WRITEONCE and FLAGS_HOME # TEST with encryption (aes128) and NVM_WRITEONCE and FLAGS_HOME
- name: make clean - name: make clean
@ -355,6 +403,14 @@ jobs:
run: | run: |
tools/scripts/sim-update-powerfail-resume.sh tools/scripts/sim-update-powerfail-resume.sh
- name: Rebuild wolfboot.elf
run: |
make clean && make test-sim-external-flash-with-enc-update
- name: Run emergency fallback test
run: |
tools/scripts/sim-update-emergency-fallback.sh
# TEST with encryption (aes128) and NVM_WRITEONCE and FLAGS_HOME and FLAGS_INVERT # TEST with encryption (aes128) and NVM_WRITEONCE and FLAGS_HOME and FLAGS_INVERT
- name: make clean - name: make clean
@ -388,6 +444,14 @@ jobs:
run: | run: |
tools/scripts/sim-update-powerfail-resume.sh tools/scripts/sim-update-powerfail-resume.sh
- name: Rebuild wolfboot.elf
run: |
make clean && make test-sim-external-flash-with-enc-update
- name: Run emergency fallback test
run: |
tools/scripts/sim-update-emergency-fallback.sh
# TEST with encryption (aes128) and NVM_WRITEONCE and DELTA updates # TEST with encryption (aes128) and NVM_WRITEONCE and DELTA updates
- name: make clean - name: make clean
run: | run: |
@ -451,6 +515,7 @@ jobs:
- name: Run update test with DISABLE_BACKUP and powefail - name: Run update test with DISABLE_BACKUP and powefail
run: | run: |
tools/scripts/sim-update-powerfail-resume-nobackup.sh tools/scripts/sim-update-powerfail-resume-nobackup.sh
# TEST with backup disabled + NVM_WRITEONCE # TEST with backup disabled + NVM_WRITEONCE
- name: make clean - name: make clean
run: | run: |
@ -478,6 +543,7 @@ jobs:
- name: Run update test with DISABLE_BACKUP and powefail - name: Run update test with DISABLE_BACKUP and powefail
run: | run: |
tools/scripts/sim-update-powerfail-resume-nobackup.sh tools/scripts/sim-update-powerfail-resume-nobackup.sh
# TEST with backup disabled + FLAGS_HOME # TEST with backup disabled + FLAGS_HOME
- name: make clean - name: make clean
run: | run: |

View File

@ -45,6 +45,7 @@
uint8_t *sim_ram_base; uint8_t *sim_ram_base;
static uint8_t *flash_base; static uint8_t *flash_base;
int forceEmergency = 0;
uint32_t erasefail_address = 0xFFFFFFFF; uint32_t erasefail_address = 0xFFFFFFFF;
#define INTERNAL_FLASH_FILE "./internal_flash.dd" #define INTERNAL_FLASH_FILE "./internal_flash.dd"
@ -105,8 +106,29 @@ void hal_prepare_boot(void)
int hal_flash_write(uintptr_t address, const uint8_t *data, int len) int hal_flash_write(uintptr_t address, const uint8_t *data, int len)
{ {
int i;
if (forceEmergency == 1 && address == WOLFBOOT_PARTITION_BOOT_ADDRESS) {
/* implicit cast abide compiler warning */ /* implicit cast abide compiler warning */
memcpy((void*)address, data, len); memset((void*)address, 0, len);
/* let the rest of the writes work properly for the emergency update */
forceEmergency = 0;
}
else {
for (i = 0; i < len; i++) {
#ifdef NVM_FLASH_WRITEONCE
if (((uint8_t*)address)[i] != FLASH_BYTE_ERASED) {
/* no writing to non-erased page in NVM_FLASH_WRITEONCE */
printf("NVM_FLASH_WRITEONCE non-erased write detected!\n");
return -1;
}
#endif
#ifdef WOLFBOOT_FLAGS_INVERT
((uint8_t*)address)[i] |= data[i];
#else
((uint8_t*)address)[i] &= data[i];
#endif
}
}
return 0; return 0;
} }
@ -150,8 +172,11 @@ void hal_init(void)
erasefail_address = strtol(main_argv[++i], NULL, 16); erasefail_address = strtol(main_argv[++i], NULL, 16);
fprintf(stderr, "Set power fail to erase at address %x\n", fprintf(stderr, "Set power fail to erase at address %x\n",
erasefail_address); erasefail_address);
break;
} }
/* force a bad write of the boot partition to trigger and test the
* emergency fallback feature */
else if (strcmp(main_argv[i], "emergency") == 0)
forceEmergency = 1;
} }
} }

View File

@ -257,6 +257,8 @@ extern "C" {
#ifndef WOLFBOOT_FLAGS_INVERT #ifndef WOLFBOOT_FLAGS_INVERT
#define IMG_STATE_NEW 0xFF #define IMG_STATE_NEW 0xFF
#define IMG_STATE_UPDATING 0x70 #define IMG_STATE_UPDATING 0x70
/* now just an intermediary state, update state will always be either new or
* updating before the application boots*/
#define IMG_STATE_FINAL_FLAGS 0x30 #define IMG_STATE_FINAL_FLAGS 0x30
#define IMG_STATE_TESTING 0x10 #define IMG_STATE_TESTING 0x10
#define IMG_STATE_SUCCESS 0x00 #define IMG_STATE_SUCCESS 0x00
@ -265,8 +267,8 @@ extern "C" {
#else #else
#define IMG_STATE_NEW 0x00 #define IMG_STATE_NEW 0x00
#define IMG_STATE_UPDATING 0x8F #define IMG_STATE_UPDATING 0x8F
#define IMG_STATE_FINAL_FLAGS 0xBF
#define IMG_STATE_TESTING 0xEF #define IMG_STATE_TESTING 0xEF
#define IMG_STATE_FINAL_FLAGS 0xBF
#define IMG_STATE_SUCCESS 0xFF #define IMG_STATE_SUCCESS 0xFF
#define FLASH_BYTE_ERASED 0x00 #define FLASH_BYTE_ERASED 0x00
#define FLASH_WORD_ERASED 0x00000000UL #define FLASH_WORD_ERASED 0x00000000UL

View File

@ -611,8 +611,6 @@ static int image_sha256(struct wolfBoot_image *img, uint8_t *hash)
*/ */
static void key_sha256(uint8_t key_slot, uint8_t *hash) static void key_sha256(uint8_t key_slot, uint8_t *hash)
{ {
int blksz;
unsigned int i = 0;
uint8_t *pubkey = keystore_get_buffer(key_slot); uint8_t *pubkey = keystore_get_buffer(key_slot);
int pubkey_sz = keystore_get_size(key_slot); int pubkey_sz = keystore_get_size(key_slot);
wc_Sha256 sha256_ctx; wc_Sha256 sha256_ctx;
@ -621,13 +619,7 @@ static void key_sha256(uint8_t key_slot, uint8_t *hash)
return; return;
wc_InitSha256(&sha256_ctx); wc_InitSha256(&sha256_ctx);
while (i < (uint32_t)pubkey_sz) { wc_Sha256Update(&sha256_ctx, pubkey, (word32)pubkey_sz);
blksz = WOLFBOOT_SHA_BLOCK_SIZE;
if ((i + blksz) > (uint32_t)pubkey_sz)
blksz = pubkey_sz - i;
wc_Sha256Update(&sha256_ctx, (pubkey + i), blksz);
i += blksz;
}
wc_Sha256Final(&sha256_ctx, hash); wc_Sha256Final(&sha256_ctx, hash);
} }
#endif /* WOLFBOOT_NO_SIGN */ #endif /* WOLFBOOT_NO_SIGN */
@ -700,8 +692,6 @@ static int image_sha384(struct wolfBoot_image *img, uint8_t *hash)
*/ */
static void key_sha384(uint8_t key_slot, uint8_t *hash) static void key_sha384(uint8_t key_slot, uint8_t *hash)
{ {
int blksz;
unsigned int i = 0;
uint8_t *pubkey = keystore_get_buffer(key_slot); uint8_t *pubkey = keystore_get_buffer(key_slot);
int pubkey_sz = keystore_get_size(key_slot); int pubkey_sz = keystore_get_size(key_slot);
wc_Sha384 sha384_ctx; wc_Sha384 sha384_ctx;
@ -710,13 +700,7 @@ static void key_sha384(uint8_t key_slot, uint8_t *hash)
return; return;
wc_InitSha384(&sha384_ctx); wc_InitSha384(&sha384_ctx);
while (i < (uint32_t)(pubkey_sz)) { wc_Sha384Update(&sha384_ctx, pubkey, (word32)pubkey_sz);
blksz = WOLFBOOT_SHA_BLOCK_SIZE;
if ((i + blksz) > (uint32_t)pubkey_sz)
blksz = pubkey_sz - i;
wc_Sha384Update(&sha384_ctx, (pubkey + i), blksz);
i += blksz;
}
wc_Sha384Final(&sha384_ctx, hash); wc_Sha384Final(&sha384_ctx, hash);
} }
#endif /* WOLFBOOT_NO_SIGN */ #endif /* WOLFBOOT_NO_SIGN */
@ -789,8 +773,6 @@ static int image_sha3_384(struct wolfBoot_image *img, uint8_t *hash)
*/ */
static void key_sha3_384(uint8_t key_slot, uint8_t *hash) static void key_sha3_384(uint8_t key_slot, uint8_t *hash)
{ {
int blksz;
unsigned int i = 0;
uint8_t *pubkey = keystore_get_buffer(key_slot); uint8_t *pubkey = keystore_get_buffer(key_slot);
int pubkey_sz = keystore_get_size(key_slot); int pubkey_sz = keystore_get_size(key_slot);
wc_Sha3 sha3_ctx; wc_Sha3 sha3_ctx;
@ -798,13 +780,7 @@ static void key_sha3_384(uint8_t key_slot, uint8_t *hash)
if (!pubkey || (pubkey_sz < 0)) if (!pubkey || (pubkey_sz < 0))
return; return;
wc_InitSha3_384(&sha3_ctx, NULL, INVALID_DEVID); wc_InitSha3_384(&sha3_ctx, NULL, INVALID_DEVID);
while (i < (uint32_t)pubkey_sz) { wc_Sha3_384_Update(&sha3_ctx, pubkey, (word32)pubkey_sz);
blksz = WOLFBOOT_SHA_BLOCK_SIZE;
if ((i + blksz) > (uint32_t)pubkey_sz)
blksz = pubkey_sz - i;
wc_Sha3_384_Update(&sha3_ctx, pubkey + i, blksz);
i += blksz;
}
wc_Sha3_384_Final(&sha3_ctx, hash); wc_Sha3_384_Final(&sha3_ctx, hash);
} }
#endif /* WOLFBOOT_NO_SIGN */ #endif /* WOLFBOOT_NO_SIGN */

View File

@ -350,12 +350,8 @@ static int RAMFUNCTION trailer_write(uint8_t part, uintptr_t addr, uint8_t val)
/* Calculate write address */ /* Calculate write address */
addr_write = addr_align - ((!nvm_cached_sector) * NVM_CACHE_SIZE); addr_write = addr_align - ((!nvm_cached_sector) * NVM_CACHE_SIZE);
/* Ensure that the destination was erased, or force erase */ /* Ensure that the destination was erased */
if (*((uint32_t *)(addr_write + NVM_CACHE_SIZE - sizeof(uint32_t)))
!= FLASH_WORD_ERASED)
{
hal_flash_erase(addr_write, NVM_CACHE_SIZE); hal_flash_erase(addr_write, NVM_CACHE_SIZE);
}
#if FLASHBUFFER_SIZE != WOLFBOOT_SECTOR_SIZE #if FLASHBUFFER_SIZE != WOLFBOOT_SECTOR_SIZE
addr_off = 0; addr_off = 0;
while ((addr_off < WOLFBOOT_SECTOR_SIZE) && (ret == 0)) { while ((addr_off < WOLFBOOT_SECTOR_SIZE) && (ret == 0)) {
@ -718,17 +714,21 @@ void RAMFUNCTION wolfBoot_erase_partition(uint8_t part)
uint32_t address = 0; uint32_t address = 0;
int size = 0; int size = 0;
if (part == PART_BOOT) { switch (part) {
case PART_BOOT:
address = (uint32_t)WOLFBOOT_PARTITION_BOOT_ADDRESS; address = (uint32_t)WOLFBOOT_PARTITION_BOOT_ADDRESS;
size = WOLFBOOT_PARTITION_SIZE; size = WOLFBOOT_PARTITION_SIZE;
} break;
if (part == PART_UPDATE) { case PART_UPDATE:
address = (uint32_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS; address = (uint32_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
size = WOLFBOOT_PARTITION_SIZE; size = WOLFBOOT_PARTITION_SIZE;
} break;
if (part == PART_SWAP) { case PART_SWAP:
address = (uint32_t)WOLFBOOT_PARTITION_SWAP_ADDRESS; address = (uint32_t)WOLFBOOT_PARTITION_SWAP_ADDRESS;
size = WOLFBOOT_SECTOR_SIZE; size = WOLFBOOT_SECTOR_SIZE;
break;
default:
break;
} }
if (size > 0) { if (size > 0) {
@ -819,16 +819,10 @@ void RAMFUNCTION wolfBoot_success(void)
if (FLAGS_BOOT_EXT()) { if (FLAGS_BOOT_EXT()) {
ext_flash_unlock(); ext_flash_unlock();
wolfBoot_set_partition_state(PART_BOOT, st); wolfBoot_set_partition_state(PART_BOOT, st);
/* set update so IMG_STATE_FINAL_FLAGS isn't triggering pointless calls
* to wolfBoot update */
wolfBoot_set_partition_state(PART_UPDATE, st);
ext_flash_lock(); ext_flash_lock();
} else { } else {
hal_flash_unlock(); hal_flash_unlock();
wolfBoot_set_partition_state(PART_BOOT, st); wolfBoot_set_partition_state(PART_BOOT, st);
/* set update so IMG_STATE_FINAL_FLAGS isn't triggering pointless calls
* to wolfBoot update */
wolfBoot_set_partition_state(PART_UPDATE, st);
hal_flash_lock(); hal_flash_lock();
} }
#ifdef EXT_ENCRYPTED #ifdef EXT_ENCRYPTED
@ -866,13 +860,9 @@ uint16_t wolfBoot_find_header(uint8_t *haystack, uint16_t type, uint8_t **ptr)
unit_dbg("Explicit end of options reached\n"); unit_dbg("Explicit end of options reached\n");
break; break;
} }
if (*p == HDR_PADDING) { /* Sanity check to prevent dereferencing unaligned half-words and skip
/* Padding byte (skip one position) */ * past padding bytes */
p++; if ((*p == HDR_PADDING) || ((((size_t)p) & 0x01) != 0)) {
continue;
}
/* Sanity check to prevent dereferencing unaligned half-words */
if ((((size_t)p) & 0x01) != 0) {
p++; p++;
continue; continue;
} }

View File

@ -194,6 +194,93 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src,
return pos; return pos;
} }
#ifndef DISABLE_BACKUP
static int wolfBoot_swap_and_final_erase(int resume)
{
struct wolfBoot_image boot[1];
struct wolfBoot_image update[1];
struct wolfBoot_image swap[1];
uint8_t st;
int eraseLen = WOLFBOOT_SECTOR_SIZE
#ifdef NVM_FLASH_WRITEONCE
/* need to erase the redundant sector too */
* 2
#endif
;
int swapDone = 0;
uintptr_t tmpBootPos = WOLFBOOT_PARTITION_SIZE - eraseLen -
WOLFBOOT_SECTOR_SIZE;
/* final swap and erase flag is WOLFBOOT_MAGIC_TRAIL */
uint8_t tmpBuffer[sizeof(WOLFBOOT_MAGIC_TRAIL)
#ifdef EXT_ENCRYPTED
+ ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE
#endif
];
/* open boot */
wolfBoot_open_image(boot, PART_BOOT);
/* open update */
wolfBoot_open_image(update, PART_UPDATE);
/* open swap */
wolfBoot_open_image(swap, PART_SWAP);
wolfBoot_get_partition_state(PART_UPDATE, &st);
/* read from tmpBootPos */
memcpy((void*)tmpBuffer, (void*)(boot->hdr + tmpBootPos),
sizeof(tmpBuffer));
/* check for TRAIL */
#ifdef EXT_ENCRYPTED
if (*(uint32_t*)(tmpBuffer + ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE) ==
WOLFBOOT_MAGIC_TRAIL) {
swapDone = 1;
}
#else
if (((uint32_t*)tmpBuffer)[0] == WOLFBOOT_MAGIC_TRAIL) {
swapDone = 1;
}
#endif
/* if resuming, quit if swap isn't done */
if ((resume == 1) && (swapDone == 0) && (st != IMG_STATE_FINAL_FLAGS))
return -1;
if (swapDone == 0) {
/* IMG_STATE_FINAL_FLAGS allows re-entry without blowing away swap */
if (st != IMG_STATE_FINAL_FLAGS) {
/* store the sector at tmpBootPos into swap */
wolfBoot_copy_sector(boot, swap, tmpBootPos / WOLFBOOT_SECTOR_SIZE);
/* set FINAL_SWAP for re-entry */
wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_FINAL_FLAGS);
}
#ifdef EXT_ENCRYPTED
/* get encryption key and iv if encryption is enabled */
wolfBoot_get_encrypt_key(tmpBuffer, tmpBuffer + ENCRYPT_KEY_SIZE);
#endif
/* write TRAIL, encryption key and iv if enabled to tmpBootPos*/
#ifdef EXT_ENCRYPTED
*(uint32_t*)(tmpBuffer + ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE)
= WOLFBOOT_MAGIC_TRAIL;
#else
((uint32_t*)tmpBuffer)[0] = WOLFBOOT_MAGIC_TRAIL;
#endif
wb_flash_erase(boot, tmpBootPos, WOLFBOOT_SECTOR_SIZE);
wb_flash_write(boot, tmpBootPos, (void*)tmpBuffer, sizeof(tmpBuffer));
}
/* erase the last boot sector(s) */
wb_flash_erase(boot, WOLFBOOT_PARTITION_SIZE - eraseLen, eraseLen);
/* set the encryption key */
#ifdef EXT_ENCRYPTED
wolfBoot_set_encrypt_key(tmpBuffer, tmpBuffer + ENCRYPT_KEY_SIZE);
#endif
/* write the original contents of tmpBootPos back */
if (tmpBootPos < boot->fw_size + IMAGE_HEADER_SIZE)
wolfBoot_copy_sector(swap, boot, tmpBootPos / WOLFBOOT_SECTOR_SIZE);
else
wb_flash_erase(boot, tmpBootPos, WOLFBOOT_SECTOR_SIZE);
/* mark boot as TESTING */
wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_TESTING);
/* erase the last sector(s) of update */
wb_flash_erase(update, WOLFBOOT_PARTITION_SIZE - eraseLen, eraseLen);
return 0;
}
#endif
#ifdef DELTA_UPDATES #ifdef DELTA_UPDATES
#ifndef DELTA_BLOCK_SIZE #ifndef DELTA_BLOCK_SIZE
@ -202,7 +289,7 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src,
static int wolfBoot_delta_update(struct wolfBoot_image *boot, static int wolfBoot_delta_update(struct wolfBoot_image *boot,
struct wolfBoot_image *update, struct wolfBoot_image *swap, int inverse, struct wolfBoot_image *update, struct wolfBoot_image *swap, int inverse,
int resume_inverse) int resume)
{ {
int sector = 0; int sector = 0;
int ret; int ret;
@ -242,14 +329,14 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot,
upd_v = wolfBoot_update_firmware_version(); upd_v = wolfBoot_update_firmware_version();
delta_base_v = wolfBoot_get_diffbase_version(PART_UPDATE); delta_base_v = wolfBoot_get_diffbase_version(PART_UPDATE);
if (inverse) { if (inverse) {
if (((cur_v == upd_v) && (delta_base_v < cur_v)) || resume_inverse) { if (((cur_v == upd_v) && (delta_base_v < cur_v)) || resume) {
ret = wb_patch_init(&ctx, boot->hdr, boot->fw_size + ret = wb_patch_init(&ctx, boot->hdr, boot->fw_size +
IMAGE_HEADER_SIZE, update->hdr + *img_offset, *img_size); IMAGE_HEADER_SIZE, update->hdr + *img_offset, *img_size);
} else { } else {
ret = -1; ret = -1;
} }
} else { } else {
if (!resume_inverse && (cur_v != delta_base_v)) { if (!resume && (cur_v != delta_base_v)) {
/* Wrong base image, cannot apply delta patch */ /* Wrong base image, cannot apply delta patch */
ret = -1; ret = -1;
} else { } else {
@ -346,14 +433,10 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot,
wb_flash_erase(boot, sector * WOLFBOOT_SECTOR_SIZE, WOLFBOOT_SECTOR_SIZE); wb_flash_erase(boot, sector * WOLFBOOT_SECTOR_SIZE, WOLFBOOT_SECTOR_SIZE);
sector++; sector++;
} }
/* mark that our sector flags aren't reliable in testing mode */ /* start re-entrant final erase, return code is only for resumption in
st = IMG_STATE_FINAL_FLAGS; * wolfBoot_start*/
wolfBoot_set_partition_state(PART_UPDATE, st); wolfBoot_swap_and_final_erase(0);
/* mark boot partition as testing */
st = IMG_STATE_TESTING;
wolfBoot_set_partition_state(PART_BOOT, st);
out: out:
wb_flash_erase(swap, 0, WOLFBOOT_SECTOR_SIZE);
#ifdef EXT_FLASH #ifdef EXT_FLASH
ext_flash_lock(); ext_flash_lock();
#endif #endif
@ -377,7 +460,7 @@ out:
#define MAX_UPDATE_SIZE (size_t)((WOLFBOOT_PARTITION_SIZE - (2 *WOLFBOOT_SECTOR_SIZE))) #define MAX_UPDATE_SIZE (size_t)((WOLFBOOT_PARTITION_SIZE - (2 *WOLFBOOT_SECTOR_SIZE)))
#endif #endif
static inline int wolfBoot_get_total_size(struct wolfBoot_image* boot, static int wolfBoot_get_total_size(struct wolfBoot_image* boot,
struct wolfBoot_image* update) struct wolfBoot_image* update)
{ {
uint32_t total_size = 0; uint32_t total_size = 0;
@ -392,20 +475,28 @@ static inline int wolfBoot_get_total_size(struct wolfBoot_image* boot,
static int RAMFUNCTION wolfBoot_update(int fallback_allowed) static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
{ {
int flagRet;
uint32_t total_size = 0; uint32_t total_size = 0;
const uint32_t sector_size = WOLFBOOT_SECTOR_SIZE; const uint32_t sector_size = WOLFBOOT_SECTOR_SIZE;
uint32_t sector = 0; uint32_t sector = 0;
uint8_t flag, st; /* we need to pre-set flag to SECT_FLAG_NEW in case magic hasn't been set
* on the update partion as part of the delta update direction check. if
* magic has not been set flag will have an un-determined value when we go
* to check it */
uint8_t flag = SECT_FLAG_NEW;
uint8_t st;
struct wolfBoot_image boot, update, swap; struct wolfBoot_image boot, update, swap;
uint16_t update_type; uint16_t update_type;
uint32_t fw_size; uint32_t fw_size;
uint32_t size;
#if defined(DISABLE_BACKUP) && defined(EXT_ENCRYPTED) #if defined(DISABLE_BACKUP) && defined(EXT_ENCRYPTED)
uint8_t key[ENCRYPT_KEY_SIZE]; uint8_t key[ENCRYPT_KEY_SIZE];
uint8_t nonce[ENCRYPT_NONCE_SIZE]; uint8_t nonce[ENCRYPT_NONCE_SIZE];
#endif #endif
#ifdef DELTA_UPDATES #ifdef DELTA_UPDATES
int inverse = 0; int inverse = 0;
int inverse_resume = 0; int resume = 0;
int stateRet = -1;
uint32_t cur_v; uint32_t cur_v;
uint32_t up_v; uint32_t up_v;
#endif #endif
@ -427,10 +518,9 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
update_type = wolfBoot_get_image_type(PART_UPDATE); update_type = wolfBoot_get_image_type(PART_UPDATE);
wolfBoot_get_update_sector_flag(0, &flag);
/* Check the first sector to detect interrupted update */ /* Check the first sector to detect interrupted update */
if ((wolfBoot_get_update_sector_flag(0, &flag) < 0) || if (flag == SECT_FLAG_NEW) {
(flag == SECT_FLAG_NEW))
{
if (((update_type & 0x000F) != HDR_IMG_TYPE_APP) || if (((update_type & 0x000F) != HDR_IMG_TYPE_APP) ||
((update_type & 0xFF00) != HDR_IMG_TYPE_AUTH)) ((update_type & 0xFF00) != HDR_IMG_TYPE_AUTH))
return -1; return -1;
@ -458,20 +548,31 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
cur_v = wolfBoot_current_firmware_version(); cur_v = wolfBoot_current_firmware_version();
up_v = wolfBoot_update_firmware_version(); up_v = wolfBoot_update_firmware_version();
inverse = cur_v >= up_v; inverse = cur_v >= up_v;
/* if magic isn't set stateRet will be -1 but that means we're on a
* fresh partition and aren't resuming */
stateRet = wolfBoot_get_partition_state(PART_UPDATE, &st);
/* if the first sector flag is not new but we are updating then */ /* if we've already written a sector or we've mangled the boot partition
/* we were interrupted */ * header we can't determine the direction by version numbers. instead
if (flag != SECT_FLAG_NEW && * use the update partition state, updating means regular, new means
wolfBoot_get_partition_state(PART_UPDATE, &st) == 0 && * reverting */
st == IMG_STATE_UPDATING) { if ((stateRet == 0) && ((flag != SECT_FLAG_NEW) || (cur_v == 0))) {
if ((cur_v == 0) || (cur_v == up_v)) { resume = 1;
if (st == IMG_STATE_UPDATING) {
inverse = 0;
}
else {
inverse = 1; inverse = 1;
inverse_resume = 1;
} }
} }
/* If we're dealing with a "ping-pong" fallback that wasn't interrupted
* we need to set to UPDATING, otherwise there's no way to tell the
* original direction of the update once interrupted */
else if ((inverse == 0) && (fallback_allowed == 1)) {
wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_UPDATING);
}
return wolfBoot_delta_update(&boot, &update, &swap, inverse, return wolfBoot_delta_update(&boot, &update, &swap, inverse, resume);
inverse_resume);
} }
#endif #endif
@ -486,29 +587,37 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
* If something goes wrong, the operation will be resumed upon reboot. * If something goes wrong, the operation will be resumed upon reboot.
*/ */
while ((sector * sector_size) < total_size) { while ((sector * sector_size) < total_size) {
if ((wolfBoot_get_update_sector_flag(sector, &flag) != 0) || (flag == SECT_FLAG_NEW)) { flag = SECT_FLAG_NEW;
wolfBoot_get_update_sector_flag(sector, &flag);
switch (flag) {
case SECT_FLAG_NEW:
flag = SECT_FLAG_SWAPPING; flag = SECT_FLAG_SWAPPING;
wolfBoot_copy_sector(&update, &swap, sector); wolfBoot_copy_sector(&update, &swap, sector);
if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE) if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE)
wolfBoot_set_update_sector_flag(sector, flag); wolfBoot_set_update_sector_flag(sector, flag);
} /* FALL THROUGH */
if (flag == SECT_FLAG_SWAPPING) { case SECT_FLAG_SWAPPING:
uint32_t size = total_size - (sector * sector_size); size = total_size - (sector * sector_size);
if (size > sector_size) if (size > sector_size)
size = sector_size; size = sector_size;
flag = SECT_FLAG_BACKUP; flag = SECT_FLAG_BACKUP;
wolfBoot_copy_sector(&boot, &update, sector); wolfBoot_copy_sector(&boot, &update, sector);
if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE) if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE)
wolfBoot_set_update_sector_flag(sector, flag); wolfBoot_set_update_sector_flag(sector, flag);
} /* FALL THROUGH */
if (flag == SECT_FLAG_BACKUP) { case SECT_FLAG_BACKUP:
uint32_t size = total_size - (sector * sector_size); size = total_size - (sector * sector_size);
if (size > sector_size) if (size > sector_size)
size = sector_size; size = sector_size;
flag = SECT_FLAG_UPDATED; flag = SECT_FLAG_UPDATED;
wolfBoot_copy_sector(&swap, &boot, sector); wolfBoot_copy_sector(&swap, &boot, sector);
if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE) if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE)
wolfBoot_set_update_sector_flag(sector, flag); wolfBoot_set_update_sector_flag(sector, flag);
break;
case SECT_FLAG_UPDATED:
/* FALL THROUGH */
default:
break;
} }
sector++; sector++;
/* headers that can be in different positions depending on when the /* headers that can be in different positions depending on when the
@ -526,9 +635,6 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
/* get total size */ /* get total size */
total_size = wolfBoot_get_total_size(&boot, &update); total_size = wolfBoot_get_total_size(&boot, &update);
if (total_size <= IMAGE_HEADER_SIZE)
return -1;
} }
} }
/* erase to the last sector, writeonce has 2 sectors */ /* erase to the last sector, writeonce has 2 sectors */
@ -542,13 +648,9 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
wb_flash_erase(&update, sector * sector_size, sector_size); wb_flash_erase(&update, sector * sector_size, sector_size);
sector++; sector++;
} }
/* mark that our sector flags aren't reliable in testing mode */ /* start re-entrant final erase, return code is only for resumption in
st = IMG_STATE_FINAL_FLAGS; * wolfBoot_start*/
wolfBoot_set_partition_state(PART_UPDATE, st); wolfBoot_swap_and_final_erase(0);
/* mark boot partition as testing */
st = IMG_STATE_TESTING;
wolfBoot_set_partition_state(PART_BOOT, st);
wb_flash_erase(&swap, 0, WOLFBOOT_SECTOR_SIZE);
/* encryption key was not erased, will be erased by success */ /* encryption key was not erased, will be erased by success */
#ifdef EXT_FLASH #ifdef EXT_FLASH
ext_flash_lock(); ext_flash_lock();
@ -695,11 +797,13 @@ int wolfBoot_unlock_disk(void)
} }
#endif #endif
void RAMFUNCTION wolfBoot_start(void) void RAMFUNCTION wolfBoot_start(void)
{ {
int bootRet; int bootRet;
int updateRet; int updateRet;
#ifndef DISABLE_BACKUP
int resumedFinalErase;
#endif
uint8_t bootState; uint8_t bootState;
uint8_t updateState; uint8_t updateState;
struct wolfBoot_image boot; struct wolfBoot_image boot;
@ -715,22 +819,26 @@ void RAMFUNCTION wolfBoot_start(void)
bootRet = wolfBoot_get_partition_state(PART_BOOT, &bootState); bootRet = wolfBoot_get_partition_state(PART_BOOT, &bootState);
updateRet = wolfBoot_get_partition_state(PART_UPDATE, &updateState); updateRet = wolfBoot_get_partition_state(PART_UPDATE, &updateState);
#ifndef DISABLE_BACKUP
/* resume the final erase in case the power failed before it finished */
resumedFinalErase = wolfBoot_swap_and_final_erase(1);
if (resumedFinalErase != 0)
#endif
{
/* Check if the BOOT partition is still in TESTING, /* Check if the BOOT partition is still in TESTING,
* to trigger fallback. * to trigger fallback.
*/ */
if (bootRet == 0 && bootState == IMG_STATE_TESTING) { if ((bootRet == 0) && (bootState == IMG_STATE_TESTING)) {
/* wolfBoot_update_trigger now erases all the sector flags, only trigger
* if we're not already updating */
if (updateRet || updateState != IMG_STATE_UPDATING) {
wolfBoot_update_trigger();
}
wolfBoot_update(1); wolfBoot_update(1);
/* Check for new updates in the UPDATE partition or if we were /* Check for new updates in the UPDATE partition or if we were
* interrupted during the flags setting */ * interrupted during the flags setting */
} else if (updateRet == 0 && (updateState == IMG_STATE_UPDATING || updateState == IMG_STATE_FINAL_FLAGS)) { }
else if ((updateRet == 0) && (updateState == IMG_STATE_UPDATING)) {
/* Check for new updates in the UPDATE partition */ /* Check for new updates in the UPDATE partition */
wolfBoot_update(0); wolfBoot_update(0);
} }
}
if ((wolfBoot_open_image(&boot, PART_BOOT) < 0) if ((wolfBoot_open_image(&boot, PART_BOOT) < 0)
|| (wolfBoot_verify_integrity(&boot) < 0) || (wolfBoot_verify_integrity(&boot) < 0)
|| (wolfBoot_verify_authenticity(&boot) < 0) || (wolfBoot_verify_authenticity(&boot) < 0)

View File

@ -52,6 +52,11 @@ int do_cmd(const char *cmd)
if (strcmp(cmd, "powerfail") == 0) { if (strcmp(cmd, "powerfail") == 0) {
return 1; return 1;
} }
/* forces a bad write of the boot partition to trigger and test the
* emergency fallback feature */
if (strcmp(cmd, "emergency") == 0) {
return 1;
}
if (strcmp(cmd, "get_version") == 0) { if (strcmp(cmd, "get_version") == 0) {
printf("%d\n", wolfBoot_current_firmware_version()); printf("%d\n", wolfBoot_current_firmware_version());
return 0; return 0;

View File

@ -0,0 +1,17 @@
#!/bin/bash
V=`./wolfboot.elf update_trigger get_version 2>/dev/null`
if [ "x$V" != "x1" ]; then
echo "Failed first boot with update_trigger"
exit 1
fi
V=`./wolfboot.elf success get_version emergency 2>/dev/null`
if [ "x$V" != "x1" ]; then
echo "Failed fallback (V: $V)"
exit 1
fi
echo Test successful.
exit 0

View File

@ -47,5 +47,27 @@ if [ "x$V" != "x1" ]; then
fi fi
fi fi
# make sure it can double revert
./wolfboot.elf powerfail 0 get_version 2>/dev/null
./wolfboot.elf powerfail 15000 get_version 2>/dev/null
./wolfboot.elf powerfail 18000 get_version 2>/dev/null
./wolfboot.elf powerfail 1a000 get_version 2>/dev/null
# fail on the last sector to stop the encrypt key save and state update
./wolfboot.elf powerfail 3e000 get_version 2>/dev/null
# may not trigger on non NVM_FLASH_WRITEONCE
V=`./wolfboot.elf powerfail 3f000 get_version` 2>/dev/null
if [ "x$V" != "x2" ]; then
V=`./wolfboot.elf get_version 2>/dev/null`
# if we failed on the final boot state write we need to double fallback
if [ "x$V" == "x1" ]; then
V=`./wolfboot.elf get_version 2>/dev/null`
fi
fi
if [ "x$V" != "x2" ]; then
echo "Failed update (V: $V)"
exit 1
fi
echo Test successful. echo Test successful.
exit 0 exit 0

View File

@ -977,29 +977,29 @@ test-all: clean
test-size-all: test-size-all:
make test-size SIGN=NONE LIMIT=4776 make test-size SIGN=NONE LIMIT=4913
make keysclean make keysclean
make test-size SIGN=ED25519 LIMIT=11424 make test-size SIGN=ED25519 LIMIT=11529
make keysclean make keysclean
make test-size SIGN=ECC256 LIMIT=17824 make test-size SIGN=ECC256 LIMIT=17857
make keysclean make keysclean
make test-size SIGN=ECC256 NO_ASM=1 LIMIT=13588 make test-size SIGN=ECC256 NO_ASM=1 LIMIT=13593
make keysclean make keysclean
make test-size SIGN=RSA2048 LIMIT=11104 make test-size SIGN=RSA2048 LIMIT=11217
make keysclean make keysclean
make test-size SIGN=RSA2048 NO_ASM=1 LIMIT=11804 make test-size SIGN=RSA2048 NO_ASM=1 LIMIT=11797
make keysclean make keysclean
make test-size SIGN=RSA4096 LIMIT=11884 make test-size SIGN=RSA4096 LIMIT=11497
make keysclean make keysclean
make test-size SIGN=RSA4096 NO_ASM=1 LIMIT=11980 make test-size SIGN=RSA4096 NO_ASM=1 LIMIT=12093
make keysclean make keysclean
make test-size SIGN=ECC384 LIMIT=17388 make test-size SIGN=ECC384 LIMIT=17309
make keysclean make keysclean
make test-size SIGN=ECC384 NO_ASM=1 LIMIT=15024 make test-size SIGN=ECC384 NO_ASM=1 LIMIT=15013
make keysclean make keysclean
make test-size SIGN=ED448 LIMIT=13536 make test-size SIGN=ED448 LIMIT=13645
make keysclean make keysclean
make test-size SIGN=RSA3072 LIMIT=11240 make test-size SIGN=RSA3072 LIMIT=11353
make keysclean make keysclean
make test-size SIGN=RSA3072 NO_ASM=1 LIMIT=11792 make test-size SIGN=RSA3072 NO_ASM=1 LIMIT=11905
make keysclean make keysclean