diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index ff5835473..75f078514 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -17,7 +17,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v3 diff --git a/hal/sim.c b/hal/sim.c index 79d509060..b973bad04 100644 --- a/hal/sim.c +++ b/hal/sim.c @@ -116,9 +116,11 @@ int hal_flash_write(uintptr_t address, const uint8_t *data, int len) else { for (i = 0; i < len; i++) { #ifdef NVM_FLASH_WRITEONCE - if (((uint8_t*)address)[i] != FLASH_BYTE_ERASED) { + uint8_t *addr = (uint8_t *)address; + if (addr[i] != FLASH_BYTE_ERASED) { /* no writing to non-erased page in NVM_FLASH_WRITEONCE */ - printf("NVM_FLASH_WRITEONCE non-erased write detected!\n"); + printf("NVM_FLASH_WRITEONCE non-erased write detected at address %p!\n", addr); + printf("Address[%d] = %02x\n", i, addr[i]); return -1; } #endif diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 1dba6b73b..78fe4f124 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -27,7 +27,6 @@ */ #include - #include "hal.h" #include "wolfboot/wolfboot.h" #include "image.h" @@ -191,18 +190,18 @@ static int RAMFUNCTION nvm_select_fresh_sector(int part) int sel; uintptr_t off; uint8_t *base; - uint8_t* addrErase; + uint8_t* addrErase = 0; uint32_t word_0; uint32_t word_1; +#if defined(EXT_FLASH) && !defined(FLAGS_HOME) + if ((part == PART_UPDATE) && FLAGS_UPDATE_EXT()) { + return 0; + } +#endif + hal_cache_invalidate(); - /* if FLAGS_HOME check both boot and update for changes */ -#ifdef FLAGS_HOME - base = (uint8_t *)PART_BOOT_ENDFLAGS; - addrErase = (uint8_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS + - WOLFBOOT_PARTITION_SIZE - WOLFBOOT_SECTOR_SIZE; -#else if (part == PART_BOOT) { base = (uint8_t *)PART_BOOT_ENDFLAGS; addrErase = (uint8_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS + @@ -210,37 +209,18 @@ static int RAMFUNCTION nvm_select_fresh_sector(int part) } else { base = (uint8_t *)PART_UPDATE_ENDFLAGS; +#ifdef FLAGS_HOME + addrErase = (uint8_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS + + WOLFBOOT_PARTITION_SIZE - WOLFBOOT_SECTOR_SIZE; +#else addrErase = (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - WOLFBOOT_SECTOR_SIZE; - } -#endif - -#ifdef EXT_ENCRYPTED -#ifndef FLAGS_HOME - if (part == PART_BOOT) #endif - { - word_0 = *((uint32_t *)(ENCRYPT_TMP_SECRET_OFFSET + - WOLFBOOT_PARTITION_BOOT_ADDRESS)); - word_1 = *((uint32_t *)(ENCRYPT_TMP_SECRET_OFFSET + - WOLFBOOT_PARTITION_BOOT_ADDRESS - WOLFBOOT_SECTOR_SIZE)); - - if (word_0 == FLASH_WORD_ERASED && word_1 != - FLASH_WORD_ERASED) { - sel = 1; - goto finish; - } - else if (word_0 != FLASH_WORD_ERASED && word_1 == - FLASH_WORD_ERASED) { - sel = 0; - goto finish; - } } -#endif /* check magic in case the sector is corrupt */ - word_0 = *((uint32_t*)((uintptr_t)base - sizeof(uint32_t))); - word_1 = *((uint32_t*)((uintptr_t)base - WOLFBOOT_SECTOR_SIZE - sizeof(uint32_t))); + word_0 = *((uint32_t*)((uintptr_t)base - sizeof(uint32_t))); + word_1 = *((uint32_t*)((uintptr_t)base - (WOLFBOOT_SECTOR_SIZE + sizeof(uint32_t)))); if (word_0 == WOLFBOOT_MAGIC_TRAIL && word_1 != WOLFBOOT_MAGIC_TRAIL) { sel = 0; @@ -249,32 +229,21 @@ static int RAMFUNCTION nvm_select_fresh_sector(int part) else if (word_0 != WOLFBOOT_MAGIC_TRAIL && word_1 == WOLFBOOT_MAGIC_TRAIL) { sel = 1; goto finish; - } - -/* try the update magic as well */ -#ifdef FLAGS_HOME - /* check magic in case the sector is corrupt */ - word_0 = *((uint32_t*)(PART_UPDATE_ENDFLAGS - sizeof(uint32_t))); - word_1 = *((uint32_t*)(PART_UPDATE_ENDFLAGS - WOLFBOOT_SECTOR_SIZE - - sizeof(uint32_t))); - - if (word_0 == WOLFBOOT_MAGIC_TRAIL && word_1 != WOLFBOOT_MAGIC_TRAIL) { + } else if (word_0 != WOLFBOOT_MAGIC_TRAIL && word_1 != WOLFBOOT_MAGIC_TRAIL) { + /* none of the partition has a valid trailer, default to '0' */ sel = 0; goto finish; } - else if (word_0 != WOLFBOOT_MAGIC_TRAIL && word_1 == WOLFBOOT_MAGIC_TRAIL) { - sel = 1; - goto finish; - } -#endif /* Default to last sector if no match is found */ sel = 0; - /* Select the sector with more flags set */ - for (off = 1; off < WOLFBOOT_SECTOR_SIZE; off++) { - uint8_t byte_0 = get_base_offset(base, off); - uint8_t byte_1 = get_base_offset(base, (WOLFBOOT_SECTOR_SIZE + off)); + /* Select the sector with more flags set. Partition flag is at offset '4'. + * Sector flags begin from offset '5'. + */ + for (off = 4; off < WOLFBOOT_SECTOR_SIZE; off++) { + volatile uint8_t byte_0 = get_base_offset(base, off); + volatile uint8_t byte_1 = get_base_offset(base, (WOLFBOOT_SECTOR_SIZE + off)); if (byte_0 == FLASH_BYTE_ERASED && byte_1 != FLASH_BYTE_ERASED) { sel = 1; @@ -286,19 +255,6 @@ static int RAMFUNCTION nvm_select_fresh_sector(int part) } else if ((byte_0 == FLASH_BYTE_ERASED) && (byte_1 == FLASH_BYTE_ERASED)) { -#ifdef FLAGS_HOME - /* if we're still checking boot flags, check update flags */ - if (base - off > (uint8_t*)PART_UPDATE_ENDFLAGS) { - base = (uint8_t *)PART_UPDATE_ENDFLAGS; - off = 0; - continue; - } -#endif - /* First time boot? Assume no pending update */ - if (off == 1) { - sel=0; - break; - } /* Examine previous position one byte ahead */ byte_0 = get_base_offset(base, (off - 1)); byte_1 = get_base_offset(base, ((WOLFBOOT_SECTOR_SIZE + off) - 1)); @@ -376,6 +332,7 @@ static int RAMFUNCTION partition_magic_write(uint8_t part, uintptr_t addr) uintptr_t base = (uintptr_t)addr - off; uintptr_t addr_read, addr_write; int ret; + nvm_cached_sector = nvm_select_fresh_sector(part); addr_read = base - (nvm_cached_sector * NVM_CACHE_SIZE); addr_write = base - (!nvm_cached_sector * NVM_CACHE_SIZE); @@ -766,8 +723,7 @@ void RAMFUNCTION wolfBoot_erase_partition(uint8_t part) void RAMFUNCTION wolfBoot_update_trigger(void) { uint8_t st = IMG_STATE_UPDATING; - uintptr_t lastSector = PART_UPDATE_ENDFLAGS - - (PART_UPDATE_ENDFLAGS % WOLFBOOT_SECTOR_SIZE); + uintptr_t lastSector = ((PART_UPDATE_ENDFLAGS - 1) / WOLFBOOT_SECTOR_SIZE) * WOLFBOOT_SECTOR_SIZE; #ifdef NVM_FLASH_WRITEONCE uint8_t selSec = 0; #endif @@ -795,11 +751,10 @@ void RAMFUNCTION wolfBoot_update_trigger(void) hal_flash_erase(lastSector, SECTOR_FLAGS_SIZE); #else selSec = nvm_select_fresh_sector(PART_UPDATE); - XMEMCPY(NVM_CACHE, - (uint8_t*)(lastSector - WOLFBOOT_SECTOR_SIZE * selSec), - WOLFBOOT_SECTOR_SIZE); - XMEMSET(NVM_CACHE, FLASH_BYTE_ERASED, SECTOR_FLAGS_SIZE); + lastSector -= selSec * WOLFBOOT_SECTOR_SIZE; + XMEMCPY(NVM_CACHE, (uint8_t*)lastSector, WOLFBOOT_SECTOR_SIZE); /* write to the non selected sector */ + hal_flash_erase(lastSector - WOLFBOOT_SECTOR_SIZE * !selSec, WOLFBOOT_SECTOR_SIZE); hal_flash_write(lastSector - WOLFBOOT_SECTOR_SIZE * !selSec, NVM_CACHE, WOLFBOOT_SECTOR_SIZE); /* erase the previously selected sector */ @@ -1391,6 +1346,7 @@ static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce) uintptr_t addr, addr_align, addr_off; int ret = 0; int sel_sec = 0; + uint32_t trailer_relative_off = 4; #ifdef MMU XMEMCPY(ENCRYPT_KEY, k, ENCRYPT_KEY_SIZE); XMEMCPY(ENCRYPT_KEY + ENCRYPT_KEY_SIZE, nonce, ENCRYPT_NONCE_SIZE); @@ -1419,14 +1375,33 @@ static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce) if (ret != 0) return ret; #endif + + /* Populate key + nonce in the cache */ XMEMCPY(ENCRYPT_CACHE + addr_off, k, ENCRYPT_KEY_SIZE); XMEMCPY(ENCRYPT_CACHE + addr_off + ENCRYPT_KEY_SIZE, nonce, ENCRYPT_NONCE_SIZE); + + /* Add a valid trailer */ + XMEMCPY(ENCRYPT_CACHE + addr_off - trailer_relative_off, + &wolfboot_magic_trail, 4); +#ifdef FLAGS_HOME + /* If flags are stored in BOOT partition, take into account the offset + * of the flags used for the update partition too, to avoid erasing the + * sector. + */ + trailer_relative_off += (PART_BOOT_ENDFLAGS - PART_UPDATE_ENDFLAGS); + XMEMCPY(ENCRYPT_CACHE + addr_off - trailer_relative_off, + &wolfboot_magic_trail, 4); +#endif + + /* Writing cache back to sector "!sel_sec" */ ret = hal_flash_write(addr_align, ENCRYPT_CACHE, WOLFBOOT_SECTOR_SIZE); #ifdef NVM_FLASH_WRITEONCE - /* now erase the old populated sector */ if (ret != 0) return ret; + /* Erasing original sector "sel_sec", + * same one returned from by nvm_select. + */ addr_align = addr & (~(WOLFBOOT_SECTOR_SIZE - 1)); addr_align -= (sel_sec * WOLFBOOT_SECTOR_SIZE); ret = hal_flash_erase(addr_align, WOLFBOOT_SECTOR_SIZE); @@ -1508,7 +1483,7 @@ int RAMFUNCTION wolfBoot_erase_encrypt_key(void) sel_sec = nvm_select_fresh_sector(PART_BOOT); mem -= (sel_sec * WOLFBOOT_SECTOR_SIZE); #endif - XMEMSET(ff, 0xFF, ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE); + XMEMSET(ff, FLASH_BYTE_ERASED, ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE); if (XMEMCMP(mem, ff, ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE) != 0) hal_set_key(ff, ff + ENCRYPT_KEY_SIZE); #endif @@ -1717,10 +1692,6 @@ int RAMFUNCTION ext_flash_encrypt_write(uintptr_t address, const uint8_t *data, if (sz < ENCRYPT_BLOCK_SIZE) { sz = ENCRYPT_BLOCK_SIZE; } - if (!encrypt_initialized) { - if (crypto_init() < 0) - return -1; - } part = part_address(address); switch (part) { case PART_UPDATE: @@ -1731,6 +1702,10 @@ int RAMFUNCTION ext_flash_encrypt_write(uintptr_t address, const uint8_t *data, ENCRYPT_BLOCK_SIZE) { return ext_flash_write(address, data, len); } + if (!encrypt_initialized) { + if (crypto_init() < 0) + return -1; + } crypto_set_iv(encrypt_iv_nonce, iv_counter); break; case PART_SWAP: @@ -1799,10 +1774,6 @@ int RAMFUNCTION ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len if (row_offset != 0) { row_address = address & ~(ENCRYPT_BLOCK_SIZE - 1); } - if (!encrypt_initialized) { - if (crypto_init() < 0) - return -1; - } part = part_address(row_address); switch (part) { case PART_UPDATE: @@ -1813,6 +1784,11 @@ int RAMFUNCTION ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len ENCRYPT_BLOCK_SIZE) { return ext_flash_read(address, data, len); } + if (!encrypt_initialized) { + if (crypto_init() < 0) { + return -1; + } + } crypto_set_iv(encrypt_iv_nonce, iv_counter); break; case PART_SWAP: @@ -1894,18 +1870,15 @@ int wolfBoot_ram_decrypt(uint8_t *src, uint8_t *dst) uint32_t dst_offset = 0, iv_counter = 0; uint32_t magic, len; - wolfBoot_printf("Decrypting %p to %p\n", src, dst); if (!encrypt_initialized) { if (crypto_init() < 0) { - wolfBoot_printf("Error initializing crypto!\n"); return -1; } } /* Attempt to decrypt firmware header */ if (decrypt_header(src) != 0) { - wolfBoot_printf("Error decrypting header at %p!\n", src); return -1; } len = *((uint32_t*)(dec_hdr + sizeof(uint32_t))); diff --git a/src/update_flash.c b/src/update_flash.c index 468ac1827..f72800456 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -501,6 +501,7 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) uint32_t up_v; #endif + /* No Safety check on open: we might be in the middle of a broken update */ wolfBoot_open_image(&update, PART_UPDATE); wolfBoot_open_image(&boot, PART_BOOT); @@ -819,7 +820,8 @@ void RAMFUNCTION wolfBoot_start(void) bootRet = wolfBoot_get_partition_state(PART_BOOT, &bootState); updateRet = wolfBoot_get_partition_state(PART_UPDATE, &updateState); -#ifndef DISABLE_BACKUP + +#if !defined(DISABLE_BACKUP) /* resume the final erase in case the power failed before it finished */ resumedFinalErase = wolfBoot_swap_and_final_erase(1); if (resumedFinalErase != 0) diff --git a/test-app/ARM-stm32f7.ld b/test-app/ARM-stm32f7.ld index 1e558155e..2c283ed1f 100644 --- a/test-app/ARM-stm32f7.ld +++ b/test-app/ARM-stm32f7.ld @@ -17,6 +17,11 @@ SECTIONS . = ALIGN(4); _end_text = .; } > FLASH + .edidx : + { + . = ALIGN(4); + *(.ARM.exidx*) + } > FLASH _stored_data = .; diff --git a/test-app/app_sim.c b/test-app/app_sim.c index 85502c1c8..843a74154 100644 --- a/test-app/app_sim.c +++ b/test-app/app_sim.c @@ -61,6 +61,12 @@ int do_cmd(const char *cmd) printf("%d\n", wolfBoot_current_firmware_version()); return 0; } + if (strcmp(cmd, "get_state") == 0) { + uint8_t st = 0; + wolfBoot_get_partition_state(PART_UPDATE, &st); + printf("%02x\n", st); + return 0; + } if (strcmp(cmd, "success") == 0) { wolfBoot_success(); return 0; diff --git a/tools/lms/lms_common.h b/tools/lms/lms_common.h index 1e0e679ae..6aef6005f 100644 --- a/tools/lms/lms_common.h +++ b/tools/lms/lms_common.h @@ -48,8 +48,7 @@ static int lms_write_key(const byte * priv, word32 privSz, void * context) /* Create the file if it didn't exist. */ file = fopen(filename, "w+"); if (!file) { - fprintf(stderr, "error: fopen(%s, \"w+\") failed: %d\n", filename, - ferror(file)); + fprintf(stderr, "error: fopen(%s, \"w+\") failed.\n", filename); return WC_LMS_RC_WRITE_FAIL; } } @@ -72,8 +71,7 @@ static int lms_write_key(const byte * priv, word32 privSz, void * context) * storage correctly. */ file = fopen(filename, "r+"); if (!file) { - fprintf(stderr, "error: fopen(%s, \"r+\") failed: %d\n", filename, - ferror(file)); + fprintf(stderr, "error: fopen(%s, \"r+\") failed.\n", filename); return WC_LMS_RC_WRITE_FAIL; } diff --git a/tools/scripts/sim-sunnyday-update.sh b/tools/scripts/sim-sunnyday-update.sh index 3effcc28c..53036d01b 100755 --- a/tools/scripts/sim-sunnyday-update.sh +++ b/tools/scripts/sim-sunnyday-update.sh @@ -1,5 +1,4 @@ #!/bin/bash - V=`./wolfboot.elf update_trigger get_version 2>/dev/null` if [ "x$V" != "x1" ]; then echo "Failed first boot with update_trigger" diff --git a/tools/test.mk b/tools/test.mk index 838e6e1f9..043fe7532 100644 --- a/tools/test.mk +++ b/tools/test.mk @@ -69,6 +69,12 @@ ifeq ($(HASH),SHA3) SIGN_ARGS+= --sha3 endif +ifeq ($(FLAGS_INVERT),1) + INVERSION= +else + INVERSION=| tr "\000" "\377" +endif + $(EXPVER): $(MAKE) -C $(dir $@) @@ -136,7 +142,7 @@ test-spi-off: FORCE @make testbed-on test-update: test-app/image.bin FORCE - @dd if=/dev/zero bs=131067 count=1 2>/dev/null | tr "\000" "\377" > test-update.bin + @dd if=/dev/zero bs=131067 count=1 2>/dev/null $(INVERSION) > test-update.bin @$(SIGN_TOOL) $(SIGN_ARGS) test-app/image.bin $(PRIVATE_KEY) $(TEST_UPDATE_VERSION) @dd if=test-app/image_v$(TEST_UPDATE_VERSION)_signed.bin of=test-update.bin bs=1 conv=notrunc @printf "pBOOT" >> test-update.bin @@ -155,11 +161,11 @@ test-sim-external-flash-with-update: wolfboot.bin test-app/image.elf FORCE $(Q)$(SIGN_TOOL) $(SIGN_OPTIONS) test-app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION) # Assembling internal flash image # - $(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null | tr "\000" "\377" > v1_part.dd + $(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null $(INVERSION) > v1_part.dd $(Q)dd if=test-app/image_v1_signed.bin bs=256 of=v1_part.dd conv=notrunc $(Q)$(BINASSEMBLE) internal_flash.dd 0 wolfboot.bin \ $$(($(WOLFBOOT_PARTITION_BOOT_ADDRESS) - $(ARCH_FLASH_OFFSET))) v1_part.dd - $(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_SECTOR_SIZE))) count=1 2>/dev/null | tr "\000" "\377" > erased_sec.dd + $(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_SECTOR_SIZE))) count=1 2>/dev/null $(INVERSION) > erased_sec.dd $(Q)$(BINASSEMBLE) external_flash.dd 0 test-app/image_v$(TEST_UPDATE_VERSION)_signed.bin \ $(WOLFBOOT_PARTITION_SIZE) erased_sec.dd @@ -175,12 +181,12 @@ test-sim-external-flash-with-enc-delta-update-extradata: wolfboot.bin test-app/i $(Q)$(SIGN_TOOL) $(SIGN_OPTIONS) $(SIGN_ENC_ARGS) test-app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION) $(Q)$(SIGN_TOOL) $(SIGN_ARGS) $(DELTA_UPDATE_OPTIONS) $(SIGN_ENC_ARGS) \ test-app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION) - $(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null | tr "\000" "\377" > v1_part.dd + $(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null $(INVERSION) > v1_part.dd $(Q)dd if=test-app/image_v1_signed.bin bs=256 of=v1_part.dd conv=notrunc $(Q)$(BINASSEMBLE) internal_flash.dd \ 0 wolfboot.bin \ $$(($(WOLFBOOT_PARTITION_BOOT_ADDRESS) - $(ARCH_FLASH_OFFSET))) v1_part.dd - $(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_SECTOR_SIZE))) count=1 2>/dev/null | tr "\000" "\377" > erased_sec.dd + $(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_SECTOR_SIZE))) count=1 2>/dev/null $(INVERSION) > erased_sec.dd $(Q)$(BINASSEMBLE) external_flash.dd 0 test-app/image_v$(TEST_UPDATE_VERSION)_signed_diff_encrypted.bin \ $(WOLFBOOT_PARTITION_SIZE) erased_sec.dd $(Q)ls -l test-app/*.bin @@ -199,12 +205,12 @@ test-sim-external-flash-with-enc-update: wolfboot.bin test-app/image.elf FORCE test-app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION) # Assembling internal flash image # - $(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null | tr "\000" "\377" > v1_part.dd + $(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_PARTITION_SIZE))) count=1 2>/dev/null $(INVERSION) > v1_part.dd $(Q)dd if=test-app/image_v1_signed.bin bs=256 of=v1_part.dd conv=notrunc $(Q)$(BINASSEMBLE) internal_flash.dd \ 0 wolfboot.bin \ $$(($(WOLFBOOT_PARTITION_BOOT_ADDRESS) - $(ARCH_FLASH_OFFSET))) v1_part.dd - $(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_SECTOR_SIZE))) count=1 2>/dev/null | tr "\000" "\377" > erased_sec.dd + $(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_SECTOR_SIZE))) count=1 2>/dev/null $(INVERSION) > erased_sec.dd $(Q)$(BINASSEMBLE) external_flash.dd 0 test-app/image_v$(TEST_UPDATE_VERSION)_signed_and_encrypted.bin \ $(WOLFBOOT_PARTITION_SIZE) erased_sec.dd @@ -220,7 +226,7 @@ test-sim-internal-flash-with-update: wolfboot.bin test-app/image.elf FORCE $(Q)cp test-app/image.bak.elf test-app/image.elf $(Q)dd if=/dev/urandom of=test-app/image.elf bs=1k count=16 oflag=append conv=notrunc $(Q)$(SIGN_TOOL) $(SIGN_OPTIONS) test-app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION) - $(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_SECTOR_SIZE))) count=1 2>/dev/null | tr "\000" "\377" > erased_sec.dd + $(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_SECTOR_SIZE))) count=1 2>/dev/null $(INVERSION) > erased_sec.dd $(Q)$(SIGN_TOOL) $(SIGN_ARGS) $(DELTA_UPDATE_OPTIONS) \ test-app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION) $(Q)$(BINASSEMBLE) internal_flash.dd \ @@ -263,7 +269,7 @@ test-self-update: FORCE @st-flash --reset write test-app/image_v2_signed.bin 0x08020000 || \ (make test-reset && sleep 1 && st-flash --reset write test-app/image_v2_signed.bin 0x08020000) || \ (make test-reset && sleep 1 && st-flash --reset write test-app/image_v2_signed.bin 0x08020000) - @dd if=/dev/zero bs=131067 count=1 2>/dev/null | tr "\000" "\377" > test-self-update.bin + @dd if=/dev/zero bs=131067 count=1 2>/dev/null $(INVERSION) > test-self-update.bin @$(SIGN_TOOL) $(SIGN_ARGS) --wolfboot-update wolfboot.bin private_key.old $(WOLFBOOT_VERSION) @dd if=wolfboot_v$(WOLFBOOT_VERSION)_signed.bin of=test-self-update.bin bs=1 conv=notrunc @printf "pBOOT" >> test-self-update.bin diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 50a4aa5dc..af0f1b6dc 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -17,7 +17,8 @@ WOLFCRYPT=../../lib/wolfssl/ TESTS:=unit-parser unit-extflash unit-aes128 unit-aes256 unit-chacha20 unit-pci \ - unit-mock-state unit-sectorflags unit-image unit-nvm + unit-mock-state unit-sectorflags unit-image unit-nvm unit-nvm-flagshome \ + unit-enc-nvm unit-enc-nvm-flagshome all: $(TESTS) @@ -35,21 +36,29 @@ run: $(TESTS) done +WOLFCRYPT_SRC:=$(WOLFCRYPT)/wolfcrypt/src/sha.c \ + $(WOLFCRYPT)/wolfcrypt/src/sha256.c \ + $(WOLFCRYPT)/wolfcrypt/src/sp_int.c \ + $(WOLFCRYPT)/wolfcrypt/src/sp_c64.c \ + $(WOLFCRYPT)/wolfcrypt/src/random.c \ + $(WOLFCRYPT)/wolfcrypt/src/memory.c + unit-aes128:CFLAGS+=-DEXT_ENCRYPTED -DENCRYPT_WITH_AES128 unit-aes256:CFLAGS+=-DEXT_ENCRYPTED -DENCRYPT_WITH_AES256 unit-chacha20:CFLAGS+=-DEXT_ENCRYPTED -DENCRYPT_WITH_CHACHA unit-parser:CFLAGS+=-DNVM_FLASH_WRITEONCE unit-nvm:CFLAGS+=-DNVM_FLASH_WRITEONCE -DMOCK_PARTITIONS +unit-nvm-flagshome:CFLAGS+=-DNVM_FLASH_WRITEONCE -DMOCK_PARTITIONS -DFLAGS_HOME +unit-enc-nvm:CFLAGS+=-DNVM_FLASH_WRITEONCE -DMOCK_PARTITIONS -DEXT_ENCRYPTED \ + -DENCRYPT_WITH_CHACHA -DEXT_FLASH -DHAVE_CHACHA +unit-enc-nvm:WOLFCRYPT_SRC+=$(WOLFCRYPT)/wolfcrypt/src/chacha.c +unit-enc-nvm-flagshome:CFLAGS+=-DNVM_FLASH_WRITEONCE -DMOCK_PARTITIONS \ + -DEXT_ENCRYPTED -DENCRYPT_WITH_CHACHA -DEXT_FLASH -DHAVE_CHACHA -DFLAGS_HOME +unit-enc-nvm-flagshome:WOLFCRYPT_SRC+=$(WOLFCRYPT)/wolfcrypt/src/chacha.c -WOLFCRYPT_SRC:=$(WOLFCRYPT)/wolfcrypt/src/sha.c \ - $(WOLFCRYPT)/wolfcrypt/src/sha256.c \ - $(WOLFCRYPT)/wolfcrypt/src/sp_int.c \ - $(WOLFCRYPT)/wolfcrypt/src/sp_c64.c \ - $(WOLFCRYPT)/wolfcrypt/src/random.c \ - $(WOLFCRYPT)/wolfcrypt/src/memory.c -WOLFCRYPT_CFLAGS+=-DWOLFSSL_USER_SETTINGS -DWOLFBOOT_SIGN_ECC256 -DWOLFBOOT_SIGN_ECC256 -DHAVE_ECC_KEY_IMPORT -DWOLFBOOT_HASH_SHA256 -D__WOLFBOOT +WOLFCRYPT_CFLAGS+=-DWOLFSSL_USER_SETTINGS -DWOLFBOOT_SIGN_ECC256 -DWOLFBOOT_SIGN_ECC256 -DHAVE_ECC_KEY_IMPORT -D__WOLFBOOT @@ -90,6 +99,15 @@ unit-image: unit-image.c unit-common.c $(WOLFCRYPT_SRC) unit-nvm: ../../include/target.h unit-nvm.c gcc -o $@ unit-nvm.c $(CFLAGS) $(LDFLAGS) +unit-nvm-flagshome: ../../include/target.h unit-nvm.c + gcc -o $@ unit-nvm.c $(CFLAGS) $(LDFLAGS) + +unit-enc-nvm: ../../include/target.h unit-enc-nvm.c + gcc -o $@ $(WOLFCRYPT_SRC) unit-enc-nvm.c $(CFLAGS) $(WOLFCRYPT_CFLAGS) $(LDFLAGS) + +unit-enc-nvm-flagshome: ../../include/target.h unit-enc-nvm.c + gcc -o $@ $(WOLFCRYPT_SRC) unit-enc-nvm.c $(CFLAGS) $(WOLFCRYPT_CFLAGS) $(LDFLAGS) + %.o:%.c gcc -c -o $@ $^ $(CFLAGS) diff --git a/tools/unit-tests/unit-enc-nvm.c b/tools/unit-tests/unit-enc-nvm.c new file mode 100644 index 000000000..426473d0b --- /dev/null +++ b/tools/unit-tests/unit-enc-nvm.c @@ -0,0 +1,464 @@ +#define WOLFBOOT_HASH_SHA256 +#define IMAGE_HEADER_SIZE 256 +#define UNIT_TEST +#define WC_NO_HARDEN +#define MOCK_ADDRESS 0xCC000000 +#define MOCK_ADDRESS_BOOT 0xCD000000 +#define MOCK_ADDRESS_SWAP 0xCE000000 +const char ENCRYPT_KEY[] = "0123456789abcdef0123456789abcdef0123456789abcdef"; +#include +#include "encrypt.h" +#include "libwolfboot.c" +#include +#include +#include +#include + +static int locked = 1; +static int erased_boot = 0; +static int erased_update = 0; +static int erased_swap = 0; +static int erased_nvm_bank0 = 0; +static int erased_nvm_bank1 = 0; +const char *argv0; + + + +/* Mocks */ +void hal_init(void) +{ +} +int hal_flash_write(haladdr_t address, const uint8_t *data, int len) +{ + int i; + uint8_t *a = (uint8_t *)address; + fail_if(locked, "Attempting to write to a locked FLASH"); + if ((address >= WOLFBOOT_PARTITION_UPDATE_ADDRESS) && + (address < WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE)) { + for (i = 0; i < len; i++) { + a[i] = data[i]; + } + } +#ifdef FLAGS_HOME + if ((address >= WOLFBOOT_PARTITION_BOOT_ADDRESS) && + (address < WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE)) { + for (i = 0; i < len; i++) { + a[i] = data[i]; + } + } +#endif + return 0; +} +int hal_flash_erase(haladdr_t address, int len) +{ + fail_if(locked, "Attempting to erase a locked FLASH"); + if ((address >= WOLFBOOT_PARTITION_BOOT_ADDRESS) && + (address < WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE)) { + erased_boot++; +#ifdef FLAGS_HOME + memset(address, 0xFF, len); + if (address >= WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE - WOLFBOOT_SECTOR_SIZE) { + erased_nvm_bank0++; + } else if (address >= WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE - 2 * WOLFBOOT_SECTOR_SIZE) { + erased_nvm_bank1++; + } +#endif + } else if ((address >= WOLFBOOT_PARTITION_UPDATE_ADDRESS) && + (address < WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE)) { + erased_update++; + memset(address, 0xFF, len); + if (address >= WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - WOLFBOOT_SECTOR_SIZE) { + erased_nvm_bank0++; + } else if (address >= WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - 2 * WOLFBOOT_SECTOR_SIZE) { + erased_nvm_bank1++; + } + } else if ((address >= WOLFBOOT_PARTITION_SWAP_ADDRESS) && + (address < WOLFBOOT_PARTITION_SWAP_ADDRESS + WOLFBOOT_SECTOR_SIZE)) { + erased_swap++; + memset(address, 0xFF, len); + } else { + fail("Invalid address\n"); + return -1; + } + return 0; +} +void hal_flash_unlock(void) +{ + fail_unless(locked, "Double unlock detected\n"); + locked--; +} +void hal_flash_lock(void) +{ + fail_if(locked, "Double lock detected\n"); + locked++; +} + +void hal_prepare_boot(void) +{ +} + +int ext_flash_erase(uintptr_t address, int len) +{ + printf("%s", __FUNCTION__); + if ((address >= WOLFBOOT_PARTITION_UPDATE_ADDRESS) && + (address < WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE)) { + erased_update++; + memset(address, 0xFF, len); + if (address >= WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - WOLFBOOT_SECTOR_SIZE) { + erased_nvm_bank0++; + } else if (address >= WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - 2 * WOLFBOOT_SECTOR_SIZE) { + erased_nvm_bank1++; + } + } else if ((address >= WOLFBOOT_PARTITION_SWAP_ADDRESS) && + (address < WOLFBOOT_PARTITION_SWAP_ADDRESS + WOLFBOOT_SECTOR_SIZE)) { + erased_swap++; + memset(address, 0xFF, len); + } else { + fail("Invalid address\n"); + return -1; + } + return 0; +} + +int ext_flash_write(uintptr_t address, const uint8_t *data, int len) +{ + int i; + uint8_t *a = (uint8_t *)address; + fail_if(locked, "Attempting to write to a locked FLASH"); + printf("%s", __FUNCTION__); + for (i = 0; i < len; i++) { + a[i] = data[i]; + } + return 0; +} + +int ext_flash_read(uintptr_t address, uint8_t *data, int len) +{ + int i; + uint8_t *a = (uint8_t *)address; + for (i = 0; i < len; i++) { + data[i] = a[i]; + } + return 0; +} + +/* A simple mock memory */ +static int mmap_file(const char *path, uint8_t *address, uint32_t len, + uint8_t** ret_address) +{ + struct stat st = { 0 }; + uint8_t *mmaped_addr; + int ret; + int fd; + int i; + + if (path == NULL) + return -1; + + fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0666); + if (fd == -1) { + fprintf(stderr, "can't open %s\n", path); + return -1; + } + fprintf(stderr, "Open file: %s success.\n", path); + for (i = 0; i < len; i+=4) { + const uint32_t erased_word = 0xBADBADBA; + write(fd, &erased_word, 4); + } + lseek(fd, SEEK_SET, 0); + + mmaped_addr = mmap(address, len, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + if (mmaped_addr == MAP_FAILED) { + fprintf(stderr, "MMAP failed.\n"); + return -1; + } + + fprintf(stderr, "Simulator assigned %s to base %p\n", path, mmaped_addr); + + if (ret_address) + *ret_address = mmaped_addr; + + close(fd); + return 0; +} + + +/* End Mocks */ + +Suite *wolfboot_suite(void); + + +START_TEST (test_nvm_update_with_encryption) +{ + int ret, i; + const char BOOT[] = "BOOT"; + const uint32_t *boot_word = (const uint32_t *)BOOT; + uint8_t st; + uint32_t *magic; + uint8_t *dst, *src; + uint8_t part = PART_UPDATE; + uint32_t base_addr = WOLFBOOT_PARTITION_UPDATE_ADDRESS; + uint32_t home_off = 0; + + ret = mmap_file("/tmp/wolfboot-unit-file.bin", MOCK_ADDRESS, + WOLFBOOT_PARTITION_SIZE, NULL); + fail_if(ret < 0); +#ifdef FLAGS_HOME + ret = mmap_file("/tmp/wolfboot-unit-int-file.bin", MOCK_ADDRESS_BOOT, + WOLFBOOT_PARTITION_SIZE, NULL); + fail_if(ret < 0); + part = PART_BOOT; + base_addr = WOLFBOOT_PARTITION_BOOT_ADDRESS; + home_off = PART_BOOT_ENDFLAGS - PART_UPDATE_ENDFLAGS; +#endif + ret = mmap_file("/tmp/wolfboot-unit-swap.bin", MOCK_ADDRESS_SWAP, + WOLFBOOT_SECTOR_SIZE, NULL); + fail_if(ret < 0); + + /* Sanity */ + fail_if(home_off > WOLFBOOT_SECTOR_SIZE); + + /* unlock the flash to allow operations */ + hal_flash_unlock(); + + /* Check swap erase */ + wolfBoot_erase_partition(PART_SWAP); + fail_if(erased_swap != 1); + for (i = 0; i < WOLFBOOT_SECTOR_SIZE; i+=4) { + uint32_t *word = ((uint32_t *)(WOLFBOOT_PARTITION_SWAP_ADDRESS + i)); + fail_if(*word != 0xFFFFFFFF); + } + + erased_update = 0; + wolfBoot_erase_partition(part); +#ifndef FLAGS_HOME + fail_if(erased_update != 1); +#else + fail_if(erased_boot != 1); +#endif + /* Erased flag sectors: select '0' by default */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 0, "Failed to select default fresh sector\n"); + + /* Force a good 'magic' at the end of sector 1 by setting the magic word */ + wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_NEW); + magic = get_partition_magic(PART_UPDATE); + fail_if(*magic != *boot_word, + "Failed to read back 'BOOT' trailer at the end of the partition"); + + /* Current selected should now be 1 */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 1, "Failed to select good fresh sector\n"); + + erased_nvm_bank1 = 0; + erased_nvm_bank0 = 0; + + /* Calling 'set_partition_state' should change the current sector */ + wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_UPDATING); + + /* Current selected should now be 0 */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 0, "Failed to select updating fresh sector\n"); + fail_if(erased_nvm_bank1 == 0, "Did not erase the non-selected bank"); + + erased_nvm_bank1 = 0; + erased_nvm_bank0 = 0; + + /* Check state is read back correctly */ + ret = wolfBoot_get_partition_state(PART_UPDATE, &st); + fail_if(ret != 0, "Failed to read back state\n"); + fail_if(st != IMG_STATE_UPDATING, "Bootloader in the wrong state\n"); + + /* Check that reading did not change the current sector */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 0, "Failed to select right sector after reading\n"); + + /* Update one sector flag, it should change nvm sector */ + wolfBoot_set_update_sector_flag(0, SECT_FLAG_SWAPPING); + + /* Current selected should now be 1 */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 1, "Failed to select updating fresh sector\n"); + fail_if(erased_nvm_bank0 == 0, "Did not erase the non-selected bank"); + + /* Check sector state is read back correctly */ + ret = wolfBoot_get_update_sector_flag(0, &st); + fail_if (ret != 0, "Failed to read sector flag state\n"); + fail_if (st != SECT_FLAG_SWAPPING, "Wrong sector flag state\n"); + + /* Check that reading did not change the current sector (1) */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 1, "Failed to select right sector after reading sector state\n"); + + /* Update sector flag, again. it should change nvm sector */ + erased_nvm_bank1 = 0; + erased_nvm_bank0 = 0; + wolfBoot_set_update_sector_flag(0, SECT_FLAG_UPDATED); + + /* Current selected should now be 0 */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 0, "Failed to select updating fresh sector\n"); + fail_if(erased_nvm_bank1 == 0, "Did not erase the non-selected bank"); + + /* Check sector state is read back correctly */ + ret = wolfBoot_get_update_sector_flag(0, &st); + fail_if (ret != 0, "Failed to read sector flag state\n"); + fail_if (st != SECT_FLAG_UPDATED, "Wrong sector flag state\n"); + + /* Check that reading did not change the current sector (0) */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 0, "Failed to select right sector after reading sector state\n"); + + /* Update sector flag, again. it should change nvm sector */ + erased_nvm_bank1 = 0; + erased_nvm_bank0 = 0; + wolfBoot_set_update_sector_flag(1, SECT_FLAG_SWAPPING); + + /* Current selected should now be 1 */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 1, "Failed to select updating fresh sector\n"); + fail_if(erased_nvm_bank0 == 0, "Did not erase the non-selected bank"); + + /* Check sector state is read back correctly */ + ret = wolfBoot_get_update_sector_flag(1, &st); + fail_if (ret != 0, "Failed to read sector flag state\n"); + fail_if (st != SECT_FLAG_SWAPPING, "Wrong sector flag state\n"); + + /* Check that reading did not change the current sector (1) */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 1, "Failed to select right sector after reading sector state\n"); + + /* Update sector flag, again. it should change nvm sector */ + erased_nvm_bank1 = 0; + erased_nvm_bank0 = 0; + wolfBoot_set_update_sector_flag(1, SECT_FLAG_UPDATED); + + /* Copy flags from 0 to 1 */ + src = (uint8_t *)(base_addr + WOLFBOOT_PARTITION_SIZE - WOLFBOOT_SECTOR_SIZE); + dst = (uint8_t *)(base_addr + WOLFBOOT_PARTITION_SIZE - (2 * WOLFBOOT_SECTOR_SIZE)); + for (i = 0; i < WOLFBOOT_SECTOR_SIZE; i++) + dst[i] = src[i]; + + /* Force-erase 4B of sector flags in 0 */ + dst = (uint8_t *)(base_addr + WOLFBOOT_PARTITION_SIZE - (8 + home_off + + TRAILER_SKIP + ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE)); + for (i = 0; i < 4; i++) + dst[i] = 0xFF; + + /* This should fall back to 1 */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 1, "Failed to select most recent sector after deleting flags\n"); + + /* Start over, update some sector flags */ + wolfBoot_erase_partition(PART_UPDATE); + wolfBoot_set_update_sector_flag(0, SECT_FLAG_UPDATED); + wolfBoot_set_update_sector_flag(1, SECT_FLAG_UPDATED); + wolfBoot_set_update_sector_flag(2, SECT_FLAG_UPDATED); + wolfBoot_set_update_sector_flag(3, SECT_FLAG_UPDATED); + wolfBoot_set_update_sector_flag(4, SECT_FLAG_SWAPPING); + st = IMG_STATE_UPDATING; + wolfBoot_set_partition_state(PART_UPDATE, &st); + + /* Current selected should now be 1 */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 1, "Failed to select updating fresh sector\n"); + fail_if(erased_nvm_bank0 == 0, "Did not erase the non-selected bank"); + + /* Check sector state is read back correctly */ + for (i = 0; i < 4; i++) { + ret = wolfBoot_get_update_sector_flag(i, &st); + fail_if (ret != 0, "Failed to read sector flag state\n"); + fail_if (st != SECT_FLAG_UPDATED, "Wrong sector flag state\n"); + + } + ret = wolfBoot_get_update_sector_flag(4, &st); + fail_if (ret != 0, "Failed to read sector flag state\n"); + fail_if (st != SECT_FLAG_SWAPPING, "Wrong sector flag state\n"); + + /* Check that reading did not change the current sector (1) */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 1, "Failed to select right sector after reading sector state\n"); + + /* Copy flags from 1 to 0 */ + src = (uint8_t *)(base_addr + WOLFBOOT_PARTITION_SIZE - (2 * WOLFBOOT_SECTOR_SIZE)); + dst = (uint8_t *)(base_addr + WOLFBOOT_PARTITION_SIZE - (WOLFBOOT_SECTOR_SIZE)); + for (i = 0; i < WOLFBOOT_SECTOR_SIZE; i++) + dst[i] = src[i]; + + /* Force to F0 last sector flag in 0, so that the sector '4' is 'updated' */ + dst = (uint8_t *)(base_addr + WOLFBOOT_PARTITION_SIZE - (8 + home_off + + TRAILER_SKIP + ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE)); + dst[0] = 0xF0; + + /* Check if still there */ + ret = wolfBoot_get_update_sector_flag(4, &st); + fail_if (ret != 0, "Failed to read sector flag state\n"); + fail_if (st != SECT_FLAG_UPDATED, "Wrong sector flag state\n"); + + /* This should fall back to 0 */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 0, "Failed to select most recent sector after deleting flags\n"); + + + /* Erase partition and start over */ + erased_update = 0; + erased_boot = 0; + wolfBoot_erase_partition(part); +#ifndef FLAGS_HOME + fail_if(erased_update != 1); +#else + fail_if(erased_boot != 1); +#endif + + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 0, "Failed to select right sector after reading sector state\n"); + + /* re-lock the flash: update_trigger implies unlocking/locking */ + hal_flash_lock(); + + /* Triggering update to set flags */ + wolfBoot_update_trigger(); + + /* Current selected should now be 0 */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 0, "Failed to select updating fresh sector\n"); + fail_if(erased_nvm_bank1 == 0, "Did not erase the non-selected bank"); + + magic = get_partition_magic(PART_UPDATE); + fail_if(*magic != *boot_word, + "Failed to read back 'BOOT' trailer at the end of the partition"); + + /* Sanity check at the end of the operations. */ + fail_unless(locked, "The FLASH was left unlocked.\n"); + + +} +END_TEST + + +Suite *wolfboot_suite(void) +{ + /* Suite initialization */ + Suite *s = suite_create("wolfboot"); + + /* Test cases */ + TCase *nvm_update_with_encryption = tcase_create("NVM update with encryption"); + tcase_add_test(nvm_update_with_encryption, test_nvm_update_with_encryption); + suite_add_tcase(s, nvm_update_with_encryption); + + return s; +} + + +int main(int argc, char *argv[]) +{ + int fails; + argv0 = strdup(argv[0]); + Suite *s = wolfboot_suite(); + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + fails = srunner_ntests_failed(sr); + srunner_free(sr); + return fails; +} diff --git a/tools/unit-tests/unit-nvm.c b/tools/unit-tests/unit-nvm.c index f3f88a272..56b12b926 100644 --- a/tools/unit-tests/unit-nvm.c +++ b/tools/unit-tests/unit-nvm.c @@ -3,6 +3,8 @@ #define UNIT_TEST #define WC_NO_HARDEN #define MOCK_ADDRESS 0xCC000000 +#define MOCK_ADDRESS_BOOT 0xCD000000 +#define MOCK_ADDRESS_SWAP 0xCE000000 #include #include "libwolfboot.c" #include @@ -10,14 +12,13 @@ #include #include -static int locked = 0; +static int locked = 1; static int erased_boot = 0; static int erased_update = 0; +static int erased_swap = 0; static int erased_nvm_bank0 = 0; static int erased_nvm_bank1 = 0; - - - +const char *argv0; /* Mocks */ @@ -28,19 +29,37 @@ int hal_flash_write(haladdr_t address, const uint8_t *data, int len) { int i; uint8_t *a = (uint8_t *)address; + fail_if(locked, "Attempting to write to a locked FLASH"); if ((address >= WOLFBOOT_PARTITION_UPDATE_ADDRESS) && (address < WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE)) { for (i = 0; i < len; i++) { a[i] = data[i]; } } +#ifdef FLAGS_HOME + if ((address >= WOLFBOOT_PARTITION_BOOT_ADDRESS) && + (address < WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE)) { + for (i = 0; i < len; i++) { + a[i] = data[i]; + } + } +#endif return 0; } int hal_flash_erase(haladdr_t address, int len) { + fail_if(locked, "Attempting to erase a locked FLASH"); if ((address >= WOLFBOOT_PARTITION_BOOT_ADDRESS) && (address < WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE)) { erased_boot++; +#ifdef FLAGS_HOME + memset(address, 0xFF, len); + if (address >= WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE - WOLFBOOT_SECTOR_SIZE) { + erased_nvm_bank0++; + } else if (address >= WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE - 2 * WOLFBOOT_SECTOR_SIZE) { + erased_nvm_bank1++; + } +#endif } else if ((address >= WOLFBOOT_PARTITION_UPDATE_ADDRESS) && (address < WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE)) { erased_update++; @@ -50,6 +69,10 @@ int hal_flash_erase(haladdr_t address, int len) } else if (address >= WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - 2 * WOLFBOOT_SECTOR_SIZE) { erased_nvm_bank1++; } + } else if ((address >= WOLFBOOT_PARTITION_SWAP_ADDRESS) && + (address < WOLFBOOT_PARTITION_SWAP_ADDRESS + WOLFBOOT_SECTOR_SIZE)) { + erased_swap++; + memset(address, 0xFF, len); } else { fail("Invalid address\n"); return -1; @@ -72,7 +95,8 @@ void hal_prepare_boot(void) } /* A simple mock memory */ -static int mmap_file(const char *path, uint8_t *address, uint8_t** ret_address) +static int mmap_file(const char *path, uint8_t *address, uint32_t len, + uint8_t** ret_address) { struct stat st = { 0 }; uint8_t *mmaped_addr; @@ -89,13 +113,13 @@ static int mmap_file(const char *path, uint8_t *address, uint8_t** ret_address) return -1; } fprintf(stderr, "Open file: %s success.\n", path); - for (i = 0; i < WOLFBOOT_PARTITION_SIZE; i+=4) { + for (i = 0; i < len; i+=4) { const uint32_t erased_word = 0xBADBADBA; write(fd, &erased_word, 4); } lseek(fd, SEEK_SET, 0); - mmaped_addr = mmap(address, WOLFBOOT_PARTITION_SIZE, PROT_READ | PROT_WRITE, + mmaped_addr = mmap(address, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (mmaped_addr == MAP_FAILED) { fprintf(stderr, "MMAP failed.\n"); @@ -119,16 +143,52 @@ Suite *wolfboot_suite(void); START_TEST (test_nvm_select_fresh_sector) { - int ret; + int ret, i; const char BOOT[] = "BOOT"; const uint32_t *boot_word = (const uint32_t *)BOOT; uint8_t st; uint32_t *magic; - ret = mmap_file("/tmp/wolfboot-unit-file.bin", MOCK_ADDRESS, NULL); + uint8_t *dst, *src; + uint8_t part = PART_UPDATE; + uint32_t base_addr = WOLFBOOT_PARTITION_UPDATE_ADDRESS; + uint32_t home_off = 0; + + ret = mmap_file("/tmp/wolfboot-unit-file.bin", MOCK_ADDRESS, + WOLFBOOT_PARTITION_SIZE, NULL); + fail_if(ret < 0); +#ifdef FLAGS_HOME + ret = mmap_file("/tmp/wolfboot-unit-int-file.bin", MOCK_ADDRESS_BOOT, + WOLFBOOT_PARTITION_SIZE, NULL); + fail_if(ret < 0); + part = PART_BOOT; + base_addr = WOLFBOOT_PARTITION_BOOT_ADDRESS; + home_off = PART_BOOT_ENDFLAGS - PART_UPDATE_ENDFLAGS; +#endif + ret = mmap_file("/tmp/wolfboot-unit-swap.bin", MOCK_ADDRESS_SWAP, + WOLFBOOT_SECTOR_SIZE, NULL); + fail_if(ret < 0); + + /* Sanity */ + fail_if(home_off > WOLFBOOT_SECTOR_SIZE); + + /* unlock the flash to allow operations */ + hal_flash_unlock(); + + /* Check swap erase */ + wolfBoot_erase_partition(PART_SWAP); + fail_if(erased_swap != 1); + for (i = 0; i < WOLFBOOT_SECTOR_SIZE; i+=4) { + uint32_t *word = ((uint32_t *)(WOLFBOOT_PARTITION_SWAP_ADDRESS + i)); + fail_if(*word != 0xFFFFFFFF); + } erased_update = 0; - wolfBoot_erase_partition(PART_UPDATE); + wolfBoot_erase_partition(part); +#ifndef FLAGS_HOME fail_if(erased_update != 1); +#else + fail_if(erased_boot != 1); +#endif /* Erased flag sectors: select '0' by default */ ret = nvm_select_fresh_sector(PART_UPDATE); fail_if(ret != 0, "Failed to select default fresh sector\n"); @@ -202,15 +262,135 @@ START_TEST (test_nvm_select_fresh_sector) ret = nvm_select_fresh_sector(PART_UPDATE); fail_if(ret != 0, "Failed to select right sector after reading sector state\n"); + /* Update sector flag, again. it should change nvm sector */ + erased_nvm_bank1 = 0; + erased_nvm_bank0 = 0; + wolfBoot_set_update_sector_flag(1, SECT_FLAG_SWAPPING); + + /* Current selected should now be 1 */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 1, "Failed to select updating fresh sector\n"); + fail_if(erased_nvm_bank0 == 0, "Did not erase the non-selected bank"); + + /* Check sector state is read back correctly */ + ret = wolfBoot_get_update_sector_flag(1, &st); + fail_if (ret != 0, "Failed to read sector flag state\n"); + fail_if (st != SECT_FLAG_SWAPPING, "Wrong sector flag state\n"); + + /* Check that reading did not change the current sector (1) */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 1, "Failed to select right sector after reading sector state\n"); + + /* Update sector flag, again. it should change nvm sector */ + erased_nvm_bank1 = 0; + erased_nvm_bank0 = 0; + wolfBoot_set_update_sector_flag(1, SECT_FLAG_UPDATED); + + /* Copy flags from 0 to 1 */ + src = (uint8_t *)(base_addr + WOLFBOOT_PARTITION_SIZE - (8 + home_off)); + dst = (uint8_t *)(base_addr + WOLFBOOT_PARTITION_SIZE - (8 + home_off + WOLFBOOT_SECTOR_SIZE)); + for (i = 0; i < 8; i++) + dst[i] = src[i]; + + /* Force-erase 4B of sector flags in 0 */ + dst = (uint8_t *)(base_addr + WOLFBOOT_PARTITION_SIZE - (8 + home_off)); + for (i = 0; i < 4; i++) + dst[i] = 0xFF; + + /* This should fall back to 1 */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 1, "Failed to select most recent sector after deleting flags\n"); + + /* Start over, update some sector flags */ + wolfBoot_erase_partition(PART_UPDATE); + wolfBoot_set_update_sector_flag(0, SECT_FLAG_UPDATED); + wolfBoot_set_update_sector_flag(1, SECT_FLAG_UPDATED); + wolfBoot_set_update_sector_flag(2, SECT_FLAG_UPDATED); + wolfBoot_set_update_sector_flag(3, SECT_FLAG_UPDATED); + wolfBoot_set_update_sector_flag(4, SECT_FLAG_SWAPPING); + st = IMG_STATE_UPDATING; + wolfBoot_set_partition_state(PART_UPDATE, &st); + + /* Current selected should now be 1 */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 1, "Failed to select updating fresh sector\n"); + fail_if(erased_nvm_bank0 == 0, "Did not erase the non-selected bank"); + + /* Check sector state is read back correctly */ + for (i = 0; i < 4; i++) { + ret = wolfBoot_get_update_sector_flag(i, &st); + fail_if (ret != 0, "Failed to read sector flag state\n"); + fail_if (st != SECT_FLAG_UPDATED, "Wrong sector flag state\n"); + + } + ret = wolfBoot_get_update_sector_flag(4, &st); + fail_if (ret != 0, "Failed to read sector flag state\n"); + fail_if (st != SECT_FLAG_SWAPPING, "Wrong sector flag state\n"); + + /* Check that reading did not change the current sector (1) */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 1, "Failed to select right sector after reading sector state\n"); + + /* Copy flags from 1 to 0 */ + src = (uint8_t *)(base_addr + WOLFBOOT_PARTITION_SIZE - (8 + home_off + WOLFBOOT_SECTOR_SIZE)); + dst = (uint8_t *)(base_addr + WOLFBOOT_PARTITION_SIZE - (8 + home_off)); + for (i = 0; i < 8; i++) + dst[i] = src[i]; + + /* Force to F0 last sector flag in 0, so that the sector '4' is 'updated' */ + dst = (uint8_t *)(base_addr + WOLFBOOT_PARTITION_SIZE - (8 + home_off)); + dst[0] = 0xF0; + + /* Check if still there */ + ret = wolfBoot_get_update_sector_flag(4, &st); + fail_if (ret != 0, "Failed to read sector flag state\n"); + fail_if (st != SECT_FLAG_UPDATED, "Wrong sector flag state\n"); + + /* This should fall back to 0 */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 0, "Failed to select most recent sector after deleting flags\n"); + + + /* Erase partition and start over */ + erased_update = 0; + erased_boot = 0; + wolfBoot_erase_partition(part); +#ifndef FLAGS_HOME + fail_if(erased_update != 1); +#else + fail_if(erased_boot != 1); +#endif + + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 0, "Failed to select right sector after reading sector state\n"); + + /* re-lock the flash: update_trigger implies unlocking/locking */ + hal_flash_lock(); + + /* Triggering update to set flags */ + wolfBoot_update_trigger(); + + /* Current selected should now be 0 */ + ret = nvm_select_fresh_sector(PART_UPDATE); + fail_if(ret != 0, "Failed to select updating fresh sector\n"); + fail_if(erased_nvm_bank1 == 0, "Did not erase the non-selected bank"); + + magic = get_partition_magic(PART_UPDATE); + fail_if(*magic != *boot_word, + "Failed to read back 'BOOT' trailer at the end of the partition"); + + /* Sanity check at the end of the operations. */ + fail_unless(locked, "The FLASH was left unlocked.\n"); + + } END_TEST Suite *wolfboot_suite(void) { - /* Suite initialization */ - Suite *s = suite_create("wolfBoot-NVM-workarounds"); + Suite *s = suite_create("wolfboot"); /* Test cases */ TCase *nvm_select_fresh_sector = tcase_create("NVM select fresh sector"); @@ -221,9 +401,10 @@ Suite *wolfboot_suite(void) } -int main(void) +int main(int argc, char *argv[]) { int fails; + argv0 = strdup(argv[0]); Suite *s = wolfboot_suite(); SRunner *sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL);