Added unit tests for FLAGS_HOME + fix NVM election

Taking into account the additional offset for PART_UPDATE_FLAGS at the
end of each flags sector when comparing two redundant flag sectors in
NVM_FLASH_WRITEONCE + FLAGS_HOME
pull/470/head
Daniele Lacamera 2024-07-17 11:28:40 +02:00
parent 2ec2bcdf79
commit 04111a67b4
4 changed files with 90 additions and 53 deletions

View File

@ -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

View File

@ -192,17 +192,12 @@ static int RAMFUNCTION nvm_select_fresh_sector(int part)
uintptr_t off;
uint8_t *base;
uint8_t* addrErase;
uint32_t magic_off = 0;
uint32_t word_0;
uint32_t word_1;
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 +
@ -213,7 +208,6 @@ static int RAMFUNCTION nvm_select_fresh_sector(int part)
addrErase = (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS +
WOLFBOOT_PARTITION_SIZE - WOLFBOOT_SECTOR_SIZE;
}
#endif
#ifdef EXT_ENCRYPTED
#ifndef FLAGS_HOME
@ -239,8 +233,9 @@ static int RAMFUNCTION nvm_select_fresh_sector(int part)
#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 - (magic_off + sizeof(uint32_t))));
word_1 = *((uint32_t*)((uintptr_t)base - (WOLFBOOT_SECTOR_SIZE + magic_off +
sizeof(uint32_t))));
if (word_0 == WOLFBOOT_MAGIC_TRAIL && word_1 != WOLFBOOT_MAGIC_TRAIL) {
sel = 0;
@ -255,30 +250,13 @@ static int RAMFUNCTION nvm_select_fresh_sector(int part)
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) {
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. Partition flag is at offset '4'.
* Sector flags begin from offset '5'.
*/
for (off = 4; off < WOLFBOOT_SECTOR_SIZE; off++) {
for (off = 4 + magic_off; 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));
@ -292,14 +270,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
/* 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));

View File

@ -17,7 +17,7 @@ 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
all: $(TESTS)
@ -40,6 +40,7 @@ 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
WOLFCRYPT_SRC:=$(WOLFCRYPT)/wolfcrypt/src/sha.c \
@ -90,6 +91,9 @@ 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)
%.o:%.c
gcc -c -o $@ $^ $(CFLAGS)

View File

@ -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 <stdio.h>
#include "libwolfboot.c"
#include <fcntl.h>
@ -13,8 +15,10 @@
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 */
@ -32,6 +36,14 @@ int hal_flash_write(haladdr_t address, const uint8_t *data, int len)
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)
@ -40,6 +52,14 @@ int hal_flash_erase(haladdr_t address, int len)
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++;
@ -49,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;
@ -71,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;
@ -88,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");
@ -124,15 +149,46 @@ START_TEST (test_nvm_select_fresh_sector)
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, NULL);
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");
@ -231,13 +287,13 @@ START_TEST (test_nvm_select_fresh_sector)
wolfBoot_set_update_sector_flag(1, SECT_FLAG_UPDATED);
/* Copy flags from 0 to 1 */
src = (uint8_t *)(WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - 8);
dst = (uint8_t *)(WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - (8 + WOLFBOOT_SECTOR_SIZE));
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 *)(WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - 8);
dst = (uint8_t *)(base_addr + WOLFBOOT_PARTITION_SIZE - (8 + home_off));
for (i = 0; i < 4; i++)
dst[i] = 0xFF;
@ -276,13 +332,13 @@ START_TEST (test_nvm_select_fresh_sector)
fail_if(ret != 1, "Failed to select right sector after reading sector state\n");
/* Copy flags from 1 to 0 */
src = (uint8_t *)(WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - (8 + WOLFBOOT_SECTOR_SIZE));
dst = (uint8_t *)(WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - 8);
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 *)(WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE - 8);
dst = (uint8_t *)(base_addr + WOLFBOOT_PARTITION_SIZE - (8 + home_off));
dst[0] = 0xF0;
/* Check if still there */
@ -297,8 +353,13 @@ START_TEST (test_nvm_select_fresh_sector)
/* Erase partition and start over */
erased_update = 0;
wolfBoot_erase_partition(PART_UPDATE);
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");
@ -328,9 +389,8 @@ 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");
@ -341,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);