From 6c3940fafc7acb0b8503931c31fd3f3f42df9faa Mon Sep 17 00:00:00 2001 From: John Bland Date: Fri, 28 Jun 2024 18:39:11 -0400 Subject: [PATCH] refactor final step of powerfail safe update and add emergency fallback test --- .../workflows/test-powerfail-simulator.yml | 70 ++++- hal/sim.c | 31 ++- include/wolfboot/wolfboot.h | 6 +- src/image.c | 30 +- src/libwolfboot.c | 50 ++-- src/update_flash.c | 256 +++++++++++++----- test-app/app_sim.c | 5 + .../scripts/sim-update-emergency-fallback.sh | 17 ++ tools/scripts/sim-update-powerfail-resume.sh | 22 ++ tools/test.mk | 26 +- 10 files changed, 362 insertions(+), 151 deletions(-) create mode 100755 tools/scripts/sim-update-emergency-fallback.sh diff --git a/.github/workflows/test-powerfail-simulator.yml b/.github/workflows/test-powerfail-simulator.yml index b21caae4f..c4b06d7dc 100644 --- a/.github/workflows/test-powerfail-simulator.yml +++ b/.github/workflows/test-powerfail-simulator.yml @@ -51,6 +51,14 @@ jobs: run: | 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 - name: make clean @@ -89,6 +97,14 @@ jobs: run: | 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 - name: make clean @@ -127,6 +143,14 @@ jobs: run: | 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 - name: make clean @@ -165,6 +189,14 @@ jobs: run: | 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 - name: make clean @@ -189,7 +221,7 @@ jobs: - name: Rebuild wolfboot.elf 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) run: | @@ -197,7 +229,7 @@ jobs: - name: Rebuild wolfboot.elf 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) run: | @@ -248,6 +280,14 @@ jobs: run: | 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 - name: make clean run: | @@ -322,6 +362,14 @@ jobs: run: | 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 - name: make clean @@ -355,6 +403,14 @@ jobs: run: | 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 - name: make clean @@ -388,6 +444,14 @@ jobs: run: | 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 - name: make clean run: | @@ -451,6 +515,7 @@ jobs: - name: Run update test with DISABLE_BACKUP and powefail run: | tools/scripts/sim-update-powerfail-resume-nobackup.sh + # TEST with backup disabled + NVM_WRITEONCE - name: make clean run: | @@ -478,6 +543,7 @@ jobs: - name: Run update test with DISABLE_BACKUP and powefail run: | tools/scripts/sim-update-powerfail-resume-nobackup.sh + # TEST with backup disabled + FLAGS_HOME - name: make clean run: | diff --git a/hal/sim.c b/hal/sim.c index 5a2940e18..79d509060 100644 --- a/hal/sim.c +++ b/hal/sim.c @@ -45,6 +45,7 @@ uint8_t *sim_ram_base; static uint8_t *flash_base; +int forceEmergency = 0; uint32_t erasefail_address = 0xFFFFFFFF; #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) { - /* implicit cast abide compiler warning */ - memcpy((void*)address, data, len); + int i; + if (forceEmergency == 1 && address == WOLFBOOT_PARTITION_BOOT_ADDRESS) { + /* implicit cast abide compiler warning */ + 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; } @@ -150,8 +172,11 @@ void hal_init(void) erasefail_address = strtol(main_argv[++i], NULL, 16); fprintf(stderr, "Set power fail to erase at address %x\n", 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; } } diff --git a/include/wolfboot/wolfboot.h b/include/wolfboot/wolfboot.h index 018da26d1..bbd76542e 100644 --- a/include/wolfboot/wolfboot.h +++ b/include/wolfboot/wolfboot.h @@ -257,7 +257,9 @@ extern "C" { #ifndef WOLFBOOT_FLAGS_INVERT #define IMG_STATE_NEW 0xFF #define IMG_STATE_UPDATING 0x70 -#define IMG_STATE_FINAL_FLAGS 0x30 +/* 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_TESTING 0x10 #define IMG_STATE_SUCCESS 0x00 #define FLASH_BYTE_ERASED 0xFF @@ -265,8 +267,8 @@ extern "C" { #else #define IMG_STATE_NEW 0x00 #define IMG_STATE_UPDATING 0x8F -#define IMG_STATE_FINAL_FLAGS 0xBF #define IMG_STATE_TESTING 0xEF +#define IMG_STATE_FINAL_FLAGS 0xBF #define IMG_STATE_SUCCESS 0xFF #define FLASH_BYTE_ERASED 0x00 #define FLASH_WORD_ERASED 0x00000000UL diff --git a/src/image.c b/src/image.c index cf4c4328f..8165ec160 100644 --- a/src/image.c +++ b/src/image.c @@ -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) { - int blksz; - unsigned int i = 0; uint8_t *pubkey = keystore_get_buffer(key_slot); int pubkey_sz = keystore_get_size(key_slot); wc_Sha256 sha256_ctx; @@ -621,13 +619,7 @@ static void key_sha256(uint8_t key_slot, uint8_t *hash) return; wc_InitSha256(&sha256_ctx); - while (i < (uint32_t)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_Sha256Update(&sha256_ctx, pubkey, (word32)pubkey_sz); wc_Sha256Final(&sha256_ctx, hash); } #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) { - int blksz; - unsigned int i = 0; uint8_t *pubkey = keystore_get_buffer(key_slot); int pubkey_sz = keystore_get_size(key_slot); wc_Sha384 sha384_ctx; @@ -710,13 +700,7 @@ static void key_sha384(uint8_t key_slot, uint8_t *hash) return; wc_InitSha384(&sha384_ctx); - while (i < (uint32_t)(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_Sha384Update(&sha384_ctx, pubkey, (word32)pubkey_sz); wc_Sha384Final(&sha384_ctx, hash); } #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) { - int blksz; - unsigned int i = 0; uint8_t *pubkey = keystore_get_buffer(key_slot); int pubkey_sz = keystore_get_size(key_slot); 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)) return; wc_InitSha3_384(&sha3_ctx, NULL, INVALID_DEVID); - while (i < (uint32_t)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_Update(&sha3_ctx, pubkey, (word32)pubkey_sz); wc_Sha3_384_Final(&sha3_ctx, hash); } #endif /* WOLFBOOT_NO_SIGN */ diff --git a/src/libwolfboot.c b/src/libwolfboot.c index ad2793b4a..7ff05953e 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -350,12 +350,8 @@ static int RAMFUNCTION trailer_write(uint8_t part, uintptr_t addr, uint8_t val) /* Calculate write address */ addr_write = addr_align - ((!nvm_cached_sector) * NVM_CACHE_SIZE); - /* Ensure that the destination was erased, or force erase */ - if (*((uint32_t *)(addr_write + NVM_CACHE_SIZE - sizeof(uint32_t))) - != FLASH_WORD_ERASED) - { - hal_flash_erase(addr_write, NVM_CACHE_SIZE); - } + /* Ensure that the destination was erased */ + hal_flash_erase(addr_write, NVM_CACHE_SIZE); #if FLASHBUFFER_SIZE != WOLFBOOT_SECTOR_SIZE addr_off = 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; int size = 0; - if (part == PART_BOOT) { - address = (uint32_t)WOLFBOOT_PARTITION_BOOT_ADDRESS; - size = WOLFBOOT_PARTITION_SIZE; - } - if (part == PART_UPDATE) { - address = (uint32_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS; - size = WOLFBOOT_PARTITION_SIZE; - } - if (part == PART_SWAP) { - address = (uint32_t)WOLFBOOT_PARTITION_SWAP_ADDRESS; - size = WOLFBOOT_SECTOR_SIZE; + switch (part) { + case PART_BOOT: + address = (uint32_t)WOLFBOOT_PARTITION_BOOT_ADDRESS; + size = WOLFBOOT_PARTITION_SIZE; + break; + case PART_UPDATE: + address = (uint32_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS; + size = WOLFBOOT_PARTITION_SIZE; + break; + case PART_SWAP: + address = (uint32_t)WOLFBOOT_PARTITION_SWAP_ADDRESS; + size = WOLFBOOT_SECTOR_SIZE; + break; + default: + break; } if (size > 0) { @@ -819,16 +819,10 @@ void RAMFUNCTION wolfBoot_success(void) if (FLAGS_BOOT_EXT()) { ext_flash_unlock(); 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(); } else { hal_flash_unlock(); 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(); } #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"); break; } - if (*p == HDR_PADDING) { - /* Padding byte (skip one position) */ - p++; - continue; - } - /* Sanity check to prevent dereferencing unaligned half-words */ - if ((((size_t)p) & 0x01) != 0) { + /* Sanity check to prevent dereferencing unaligned half-words and skip + * past padding bytes */ + if ((*p == HDR_PADDING) || ((((size_t)p) & 0x01) != 0)) { p++; continue; } diff --git a/src/update_flash.c b/src/update_flash.c index 86c9258dc..308f2fa96 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -194,6 +194,93 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src, 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 #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, struct wolfBoot_image *update, struct wolfBoot_image *swap, int inverse, - int resume_inverse) + int resume) { int sector = 0; int ret; @@ -242,14 +329,14 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, upd_v = wolfBoot_update_firmware_version(); delta_base_v = wolfBoot_get_diffbase_version(PART_UPDATE); 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 + IMAGE_HEADER_SIZE, update->hdr + *img_offset, *img_size); } else { ret = -1; } } else { - if (!resume_inverse && (cur_v != delta_base_v)) { + if (!resume && (cur_v != delta_base_v)) { /* Wrong base image, cannot apply delta patch */ ret = -1; } 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); sector++; } - /* mark that our sector flags aren't reliable in testing mode */ - st = IMG_STATE_FINAL_FLAGS; - wolfBoot_set_partition_state(PART_UPDATE, st); - /* mark boot partition as testing */ - st = IMG_STATE_TESTING; - wolfBoot_set_partition_state(PART_BOOT, st); + /* start re-entrant final erase, return code is only for resumption in + * wolfBoot_start*/ + wolfBoot_swap_and_final_erase(0); out: - wb_flash_erase(swap, 0, WOLFBOOT_SECTOR_SIZE); #ifdef EXT_FLASH ext_flash_lock(); #endif @@ -377,7 +460,7 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, #define MAX_UPDATE_SIZE (size_t)((WOLFBOOT_PARTITION_SIZE - (2 *WOLFBOOT_SECTOR_SIZE))) #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) { 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) { + int flagRet; uint32_t total_size = 0; const uint32_t sector_size = WOLFBOOT_SECTOR_SIZE; 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; uint16_t update_type; uint32_t fw_size; + uint32_t size; #if defined(DISABLE_BACKUP) && defined(EXT_ENCRYPTED) uint8_t key[ENCRYPT_KEY_SIZE]; uint8_t nonce[ENCRYPT_NONCE_SIZE]; #endif #ifdef DELTA_UPDATES int inverse = 0; - int inverse_resume = 0; + int resume = 0; + int stateRet = -1; uint32_t cur_v; uint32_t up_v; #endif @@ -427,10 +518,9 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) update_type = wolfBoot_get_image_type(PART_UPDATE); + wolfBoot_get_update_sector_flag(0, &flag); /* Check the first sector to detect interrupted update */ - if ((wolfBoot_get_update_sector_flag(0, &flag) < 0) || - (flag == SECT_FLAG_NEW)) - { + if (flag == SECT_FLAG_NEW) { if (((update_type & 0x000F) != HDR_IMG_TYPE_APP) || ((update_type & 0xFF00) != HDR_IMG_TYPE_AUTH)) return -1; @@ -458,20 +548,31 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) cur_v = wolfBoot_current_firmware_version(); up_v = wolfBoot_update_firmware_version(); inverse = cur_v >= up_v; - - /* if the first sector flag is not new but we are updating then */ - /* we were interrupted */ - if (flag != SECT_FLAG_NEW && - wolfBoot_get_partition_state(PART_UPDATE, &st) == 0 && - st == IMG_STATE_UPDATING) { - if ((cur_v == 0) || (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 we've already written a sector or we've mangled the boot partition + * header we can't determine the direction by version numbers. instead + * use the update partition state, updating means regular, new means + * reverting */ + if ((stateRet == 0) && ((flag != SECT_FLAG_NEW) || (cur_v == 0))) { + resume = 1; + if (st == IMG_STATE_UPDATING) { + inverse = 0; + } + else { 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, - inverse_resume); + return wolfBoot_delta_update(&boot, &update, &swap, inverse, resume); } #endif @@ -486,29 +587,37 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) * If something goes wrong, the operation will be resumed upon reboot. */ while ((sector * sector_size) < total_size) { - if ((wolfBoot_get_update_sector_flag(sector, &flag) != 0) || (flag == SECT_FLAG_NEW)) { - flag = SECT_FLAG_SWAPPING; - wolfBoot_copy_sector(&update, &swap, sector); - if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE) - wolfBoot_set_update_sector_flag(sector, flag); - } - if (flag == SECT_FLAG_SWAPPING) { - uint32_t size = total_size - (sector * sector_size); - if (size > sector_size) - size = sector_size; - flag = SECT_FLAG_BACKUP; - wolfBoot_copy_sector(&boot, &update, sector); - if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE) - wolfBoot_set_update_sector_flag(sector, flag); - } - if (flag == SECT_FLAG_BACKUP) { - uint32_t size = total_size - (sector * sector_size); - if (size > sector_size) - size = sector_size; - flag = SECT_FLAG_UPDATED; - wolfBoot_copy_sector(&swap, &boot, sector); - if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE) - wolfBoot_set_update_sector_flag(sector, flag); + flag = SECT_FLAG_NEW; + wolfBoot_get_update_sector_flag(sector, &flag); + switch (flag) { + case SECT_FLAG_NEW: + flag = SECT_FLAG_SWAPPING; + wolfBoot_copy_sector(&update, &swap, sector); + if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE) + wolfBoot_set_update_sector_flag(sector, flag); + /* FALL THROUGH */ + case SECT_FLAG_SWAPPING: + size = total_size - (sector * sector_size); + if (size > sector_size) + size = sector_size; + flag = SECT_FLAG_BACKUP; + wolfBoot_copy_sector(&boot, &update, sector); + if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE) + wolfBoot_set_update_sector_flag(sector, flag); + /* FALL THROUGH */ + case SECT_FLAG_BACKUP: + size = total_size - (sector * sector_size); + if (size > sector_size) + size = sector_size; + flag = SECT_FLAG_UPDATED; + wolfBoot_copy_sector(&swap, &boot, sector); + if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE) + wolfBoot_set_update_sector_flag(sector, flag); + break; + case SECT_FLAG_UPDATED: + /* FALL THROUGH */ + default: + break; } sector++; /* 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 */ 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 */ @@ -542,13 +648,9 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) wb_flash_erase(&update, sector * sector_size, sector_size); sector++; } - /* mark that our sector flags aren't reliable in testing mode */ - st = IMG_STATE_FINAL_FLAGS; - wolfBoot_set_partition_state(PART_UPDATE, st); - /* mark boot partition as testing */ - st = IMG_STATE_TESTING; - wolfBoot_set_partition_state(PART_BOOT, st); - wb_flash_erase(&swap, 0, WOLFBOOT_SECTOR_SIZE); + /* start re-entrant final erase, return code is only for resumption in + * wolfBoot_start*/ + wolfBoot_swap_and_final_erase(0); /* encryption key was not erased, will be erased by success */ #ifdef EXT_FLASH ext_flash_lock(); @@ -695,11 +797,13 @@ int wolfBoot_unlock_disk(void) } #endif - void RAMFUNCTION wolfBoot_start(void) { int bootRet; int updateRet; +#ifndef DISABLE_BACKUP + int resumedFinalErase; +#endif uint8_t bootState; uint8_t updateState; struct wolfBoot_image boot; @@ -715,21 +819,25 @@ void RAMFUNCTION wolfBoot_start(void) bootRet = wolfBoot_get_partition_state(PART_BOOT, &bootState); updateRet = wolfBoot_get_partition_state(PART_UPDATE, &updateState); - /* Check if the BOOT partition is still in TESTING, - * to trigger fallback. - */ - 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(); +#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, + * to trigger fallback. + */ + if ((bootRet == 0) && (bootState == IMG_STATE_TESTING)) { + wolfBoot_update(1); + /* Check for new updates in the UPDATE partition or if we were + * interrupted during the flags setting */ + } + else if ((updateRet == 0) && (updateState == IMG_STATE_UPDATING)) { + /* Check for new updates in the UPDATE partition */ + wolfBoot_update(0); } - wolfBoot_update(1); - /* Check for new updates in the UPDATE partition or if we were - * interrupted during the flags setting */ - } else if (updateRet == 0 && (updateState == IMG_STATE_UPDATING || updateState == IMG_STATE_FINAL_FLAGS)) { - /* Check for new updates in the UPDATE partition */ - wolfBoot_update(0); } if ((wolfBoot_open_image(&boot, PART_BOOT) < 0) || (wolfBoot_verify_integrity(&boot) < 0) diff --git a/test-app/app_sim.c b/test-app/app_sim.c index b0f3b1f60..85502c1c8 100644 --- a/test-app/app_sim.c +++ b/test-app/app_sim.c @@ -52,6 +52,11 @@ int do_cmd(const char *cmd) if (strcmp(cmd, "powerfail") == 0) { 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) { printf("%d\n", wolfBoot_current_firmware_version()); return 0; diff --git a/tools/scripts/sim-update-emergency-fallback.sh b/tools/scripts/sim-update-emergency-fallback.sh new file mode 100755 index 000000000..0b5efbc82 --- /dev/null +++ b/tools/scripts/sim-update-emergency-fallback.sh @@ -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 diff --git a/tools/scripts/sim-update-powerfail-resume.sh b/tools/scripts/sim-update-powerfail-resume.sh index fc4822de8..72db4f1d7 100755 --- a/tools/scripts/sim-update-powerfail-resume.sh +++ b/tools/scripts/sim-update-powerfail-resume.sh @@ -47,5 +47,27 @@ if [ "x$V" != "x1" ]; then 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. exit 0 diff --git a/tools/test.mk b/tools/test.mk index 1e1a4028e..838e6e1f9 100644 --- a/tools/test.mk +++ b/tools/test.mk @@ -977,29 +977,29 @@ test-all: clean test-size-all: - make test-size SIGN=NONE LIMIT=4776 + make test-size SIGN=NONE LIMIT=4913 make keysclean - make test-size SIGN=ED25519 LIMIT=11424 + make test-size SIGN=ED25519 LIMIT=11529 make keysclean - make test-size SIGN=ECC256 LIMIT=17824 + make test-size SIGN=ECC256 LIMIT=17857 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 test-size SIGN=RSA2048 LIMIT=11104 + make test-size SIGN=RSA2048 LIMIT=11217 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 test-size SIGN=RSA4096 LIMIT=11884 + make test-size SIGN=RSA4096 LIMIT=11497 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 test-size SIGN=ECC384 LIMIT=17388 + make test-size SIGN=ECC384 LIMIT=17309 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 test-size SIGN=ED448 LIMIT=13536 + make test-size SIGN=ED448 LIMIT=13645 make keysclean - make test-size SIGN=RSA3072 LIMIT=11240 + make test-size SIGN=RSA3072 LIMIT=11353 make keysclean - make test-size SIGN=RSA3072 NO_ASM=1 LIMIT=11792 + make test-size SIGN=RSA3072 NO_ASM=1 LIMIT=11905 make keysclean