WIP: structure is there, but some major issues. See WOLFBOOT-ELF-LOADER-GAPS.md and video

pull/562/head
Brett Nicholas 2025-03-21 16:25:06 -06:00 committed by Daniele Lacamera
parent 7caf579ba0
commit fb6ca4378f
6 changed files with 340 additions and 1093 deletions

View File

@ -136,6 +136,29 @@ typedef struct elf64_program_header {
} elf64_program_header;
/* support byte swapping if testing/reading an elf with different endianess */
#if defined(ELF_PARSER) || defined(ELF_ENDIAN_SUPPORT)
#ifdef BIG_ENDIAN_ORDER
#define GET16(x) (( is_le) ? __builtin_bswap16(x) : (x))
#define GET32(x) (( is_le) ? __builtin_bswap32(x) : (x))
#define GET64(x) (( is_le) ? __builtin_bswap64(x) : (x))
#else
#define GET16(x) ((!is_le) ? __builtin_bswap16(x) : (x))
#define GET32(x) ((!is_le) ? __builtin_bswap32(x) : (x))
#define GET64(x) ((!is_le) ? __builtin_bswap64(x) : (x))
#endif
#else
#define GET16(x) (x)
#define GET32(x) (x)
#define GET64(x) (x)
#endif
#define GET_H64(name) (is_elf32 ? GET32(h32->name) : GET64(h64->name))
#define GET_H32(name) (is_elf32 ? GET32(h32->name) : GET32(h64->name))
#define GET_H16(name) (is_elf32 ? GET16(h32->name) : GET16(h64->name))
#define GET_E64(name) (is_elf32 ? GET32(e32->name) : GET64(e64->name))
#define GET_E32(name) (is_elf32 ? GET32(e32->name) : GET32(e64->name))
typedef int (*elf_mmu_map_cb)(uint64_t, uint64_t, uint32_t);
int elf_load_image_mmu(uint8_t *image, uintptr_t *entry, elf_mmu_map_cb mmu_cb);
int elf_load_image(uint8_t *image, uintptr_t *entry);

View File

@ -79,6 +79,7 @@ extern "C" {
#define HDR_SIGNATURE 0x20
#define HDR_POLICY_SIGNATURE 0x21
#define HDR_SECONDARY_SIGNATURE 0x22
#define HDR_ELF_SCATTERED_HASH 0x23
#define HDR_PADDING 0xFF
/* Auth Key types */
@ -291,6 +292,9 @@ extern "C" {
/* now just an intermediary state, update state will always be either new or
* updating before the application boots*/
#define IMG_STATE_FINAL_FLAGS 0x30
/* ELF loading state - only valid on boot partition so doesn't conflict with
* IMAGE_STATE_UPDATING */
#define IMG_STATE_ELF_LOADING 0x70
#define IMG_STATE_TESTING 0x10
#define IMG_STATE_SUCCESS 0x00
#define FLASH_BYTE_ERASED 0xFF
@ -300,6 +304,7 @@ extern "C" {
#define IMG_STATE_UPDATING 0x8F
#define IMG_STATE_TESTING 0xEF
#define IMG_STATE_FINAL_FLAGS 0xBF
#define IMG_STATE_ELF_LOADING 0x70
#define IMG_STATE_SUCCESS 0xFF
#define FLASH_BYTE_ERASED 0x00
#define FLASH_WORD_ERASED 0x00000000UL

View File

@ -42,28 +42,6 @@
#endif
#endif
/* support byte swapping if testing/reading an elf with different endianess */
#if defined(ELF_PARSER) || defined(ELF_ENDIAN_SUPPORT)
#ifdef BIG_ENDIAN_ORDER
#define GET16(x) (( is_le) ? __builtin_bswap16(x) : (x))
#define GET32(x) (( is_le) ? __builtin_bswap32(x) : (x))
#define GET64(x) (( is_le) ? __builtin_bswap64(x) : (x))
#else
#define GET16(x) ((!is_le) ? __builtin_bswap16(x) : (x))
#define GET32(x) ((!is_le) ? __builtin_bswap32(x) : (x))
#define GET64(x) ((!is_le) ? __builtin_bswap64(x) : (x))
#endif
#else
#define GET16(x) (x)
#define GET32(x) (x)
#define GET64(x) (x)
#endif
#define GET_H64(name) (is_elf32 ? GET32(h32->name) : GET64(h64->name))
#define GET_H32(name) (is_elf32 ? GET32(h32->name) : GET32(h64->name))
#define GET_H16(name) (is_elf32 ? GET16(h32->name) : GET16(h64->name))
#define GET_E64(name) (is_elf32 ? GET32(e32->name) : GET64(e64->name))
#define GET_E32(name) (is_elf32 ? GET32(e32->name) : GET32(e64->name))
/* Loader for elf32 or elf64 format program headers
* Returns the entry point function

View File

@ -51,6 +51,9 @@
#ifdef WOLFBOOT_HASH_SHA3_384
#include <wolfssl/wolfcrypt/sha3.h>
#endif
#ifdef WOLFBOOT_ELF
#include "elf.h"
#endif
/* Globals */
static uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE];
@ -770,6 +773,39 @@ static uint8_t *get_sha_block(struct wolfBoot_image *img, uint32_t offset)
#endif
return (uint8_t *)(img->fw_base + offset);
}
/**
* @brief Get a block of data to be hashed from a specific memory address.
*
* This function retrieves a block of data to be hashed from a specific memory address.
* It behaves similarly to get_sha_block but takes a direct pointer instead of an offset.
*
* @param img The image to retrieve the data from.
* @param addr The memory address to read the data from.
* @return A pointer to the data block.
*/
static uint8_t *get_sha_block_ptr(struct wolfBoot_image *img, const uint8_t *addr)
{
uint32_t offset;
/* Calculate offset from base address */
if ((uintptr_t)addr < (uintptr_t)img->fw_base) {
return NULL;
}
offset = (uint32_t)((uintptr_t)addr - (uintptr_t)img->fw_base);
if (offset > img->fw_size) {
return NULL;
}
#ifdef EXT_FLASH
if (PART_IS_EXT(img)) {
ext_flash_check_read((uintptr_t)addr, ext_hash_block, WOLFBOOT_SHA_BLOCK_SIZE);
return ext_hash_block;
} else
#endif
return (uint8_t *)addr;
}
#ifdef EXT_FLASH
static uint8_t hdr_cpy[IMAGE_HEADER_SIZE];
@ -1061,6 +1097,147 @@ static void key_sha3_384(uint8_t key_slot, uint8_t *hash)
#endif /* WOLFBOOT_NO_SIGN */
#endif /* SHA3-384 */
#ifdef WOLFBOOT_ELF
/**
* @brief Compute the scattered hash by hashing PT_LOAD segments at their XIP
* addresses. Note: This function assumes that the destination addresses for elf
* loading have the same access patterns as the memory represented by img.
* (e.g. if BOOT partition is external, then reads/writes to the load address
* will use ext_flash_read/ext_flash_write.
*
* @param img Pointer to the wolfBoot image
* @param hash Buffer to store the computed hash (must be at least
* WOLFBOOT_SHA_DIGEST_SIZE bytes)
* @return 0 on success, negative value on error
*/
static int wolfBoot_compute_scattered_hash(struct wolfBoot_image *img, uint8_t *hash)
{
uint8_t elf_header_buf[sizeof(elf64_header)];
uint8_t program_header_buf[sizeof(elf64_program_header)];
elf32_header* h32;
elf64_header* h64;
uint16_t entry_count, entry_size;
uint32_t ph_offset;
int is_elf32, is_le, i;
#if defined(WOLFBOOT_HASH_SHA256)
wc_Sha256 sha256_ctx;
#elif defined(WOLFBOOT_HASH_SHA384)
wc_Sha384 sha384_ctx;
#elif defined(WOLFBOOT_HASH_SHA3_384)
wc_Sha3 sha3_384_ctx;
#endif
#ifdef EXT_FLASH
if (PART_IS_EXT(img)) {
/* Read ELF header from external flash */
ext_flash_check_read((uintptr_t)(img->fw_base), elf_header_buf, sizeof(elf64_header));
} else
#endif
{
memcpy(elf_header_buf, (void*)(img->fw_base), sizeof(elf64_header));
}
h32 = (elf32_header*)elf_header_buf;
h64 = (elf64_header*)elf_header_buf;
/* Verify ELF header */
if (memcmp(h32->ident, ELF_IDENT_STR, 4) != 0) {
return -1; /* not valid header identifier */
}
/* Load class and endianess */
is_elf32 = (h32->ident[4] == ELF_CLASS_32);
is_le = (h32->ident[5] == ELF_ENDIAN_LITTLE);
(void)is_le;
/* Initialize hash context */
#if defined(WOLFBOOT_HASH_SHA256)
wc_InitSha256(&sha256_ctx);
#elif defined(WOLFBOOT_HASH_SHA384)
wc_InitSha384(&sha384_ctx);
#elif defined(WOLFBOOT_HASH_SHA3_384)
wc_Sha3_384_Init(&sha3_384_ctx, NULL, INVALID_DEVID);
#endif
/* Get program headers info */
ph_offset = is_elf32 ? GET32(h32->ph_offset) : GET32(h64->ph_offset);
entry_size = is_elf32 ? GET16(h32->ph_entry_size) : GET16(h64->ph_entry_size);
entry_count = is_elf32 ? GET16(h32->ph_entry_count) : GET16(h64->ph_entry_count);
/* Hash each loadable segment directly from its physical address */
for (i = 0; i < entry_count; i++) {
elf32_program_header* phdr32;
elf64_program_header* phdr64;
uint32_t type;
uintptr_t paddr;
uintptr_t file_size;
/* Read program header into buffer */
#ifdef EXT_FLASH
if (PART_IS_EXT(img)) {
ext_flash_check_read((uintptr_t)(img->fw_base) + ph_offset + (i * entry_size),
program_header_buf, entry_size);
} else
#endif
{
memcpy(program_header_buf,
(uint8_t*)(img->fw_base) + ph_offset + (i * entry_size),
entry_size);
}
phdr32 = (elf32_program_header*)program_header_buf;
phdr64 = (elf64_program_header*)program_header_buf;
type = (is_elf32 ? GET32(phdr32->type) : GET32(phdr64->type));
paddr = (is_elf32 ? GET32(phdr32->paddr) : GET64(phdr64->paddr));
file_size = (is_elf32 ? GET32(phdr32->file_size) : GET64(phdr64->file_size));
/* Only hash PT_LOAD segments with non-zero size */
if (type == ELF_PT_LOAD && file_size > 0) {
#ifdef DEBUG_ELF
wolfBoot_printf("Hashing segment at %p (%d bytes)\r\n", (void*)paddr, (uint32_t)file_size);
#endif
/* Hash the segment data from physical address in blocks */
uint32_t pos = 0;
while (pos < file_size) {
uint8_t *block;
uint32_t blksz = WOLFBOOT_SHA_BLOCK_SIZE;
if (pos + blksz > file_size) {
blksz = file_size - pos;
}
block = get_sha_block_ptr(img, (const uint8_t *)(paddr + pos));
if (block == NULL) {
return -1;
}
#if defined(WOLFBOOT_HASH_SHA256)
wc_Sha256Update(&sha256_ctx, block, blksz);
#elif defined(WOLFBOOT_HASH_SHA384)
wc_Sha384Update(&sha384_ctx, block, blksz);
#elif defined(WOLFBOOT_HASH_SHA3_384)
wc_Sha3_384_Update(&sha3_384_ctx, block, blksz);
#endif
pos += blksz;
}
}
}
/* Finalize hash */
#if defined(WOLFBOOT_HASH_SHA256)
wc_Sha256Final(&sha256_ctx, hash);
#elif defined(WOLFBOOT_HASH_SHA384)
wc_Sha384Final(&sha384_ctx, hash);
#elif defined(WOLFBOOT_HASH_SHA3_384)
wc_Sha3_384_Final(&sha3_384_ctx, hash);
#endif
return 0;
}
#endif /* WOLFBOOT_ELF */
/**
* @brief Convert a 32-bit integer from little-endian to native byte order.
*

View File

@ -37,17 +37,19 @@
#ifdef SECURE_PKCS11
int WP11_Library_Init(void);
#endif
#ifdef WOLFBOOT_ELF
#include "elf.h"
#ifdef WOLFBOOT_HASH_SHA256
#include "wolfssl/wolfcrypt/sha256.h"
#endif
/* Define the additional state for ELF loading */
#ifndef IMG_STATE_ELF_LOADING
#define IMG_STATE_ELF_LOADING 0x70
#ifdef WOLFBOOT_HASH_SHA384
#include "wolfssl/wolfcrypt/sha384.h"
#endif
/* Custom TLV type for scattered hash */
#define HDR_SCATTERED_HASH 0x0040
#ifdef WOLFBOOT_HASH_SHA3_384
#include "wolfssl/wolfcrypt/sha3_384.h"
#endif
#endif /* WOLFBOOT_ELF */
#ifdef WOLFBOOT_ELF
/**
@ -105,18 +107,34 @@ int WP11_Library_Init(void);
*/
static int wolfBoot_elf_load_segments(struct wolfBoot_image* img)
{
elf32_header* h32 = (elf32_header*)(img->fw_base);
elf64_header* h64 = (elf64_header*)(img->fw_base);
uint16_t entry_count, entry_size;
uint8_t* entry_off;
int is_elf32, is_le, i;
int ret = 0;
uint8_t elf_header_buf[sizeof(elf64_header)];
uint8_t program_header_buf[sizeof(elf64_program_header)];
elf32_header* h32;
elf64_header* h64;
uint16_t entry_count, entry_size;
uint32_t ph_offset;
int is_elf32, is_le, i;
int ret = 0;
#ifdef DEBUG_ELF
wolfBoot_printf("Loading ELF segments to XIP flash from %p\r\n",
(void*)(img->fw_base));
#endif
#ifdef EXT_FLASH
if (PART_IS_EXT(img)) {
/* Read ELF header from external flash */
ext_flash_check_read((uintptr_t)(img->fw_base), elf_header_buf, sizeof(elf64_header));
} else {
memcpy(elf_header_buf, (void*)(img->fw_base), sizeof(elf64_header));
}
#else
memcpy(elf_header_buf, (void*)(img->fw_base), sizeof(elf64_header));
#endif
h32 = (elf32_header*)elf_header_buf;
h64 = (elf64_header*)elf_header_buf;
/* Verify ELF header */
if (memcmp(h32->ident, ELF_IDENT_STR, 4) != 0) {
return -1; /* not valid header identifier */
@ -124,7 +142,7 @@ static int wolfBoot_elf_load_segments(struct wolfBoot_image* img)
/* Load class and endianess */
is_elf32 = (h32->ident[4] == ELF_CLASS_32);
is_le = (h32->ident[5] == ELF_ENDIAN_LITTLE);
is_le = (h32->ident[5] == ELF_ENDIAN_LITTLE);
(void)is_le;
/* Verify this is an executable */
@ -137,31 +155,54 @@ static int wolfBoot_elf_load_segments(struct wolfBoot_image* img)
is_elf32 ? 32 : 64, is_le ? "little" : "big");
#endif
/* programs */
entry_off = (uint8_t*)(img->fw_base) +
(is_elf32 ? GET32(h32->ph_offset) : GET32(h64->ph_offset));
entry_size =
(is_elf32 ? GET16(h32->ph_entry_size) : GET16(h64->ph_entry_size));
entry_count =
(is_elf32 ? GET16(h32->ph_entry_count) : GET16(h64->ph_entry_count));
/* Get program headers info */
ph_offset = is_elf32 ? GET32(h32->ph_offset) : GET32(h64->ph_offset);
entry_size = is_elf32 ? GET16(h32->ph_entry_size) : GET16(h64->ph_entry_size);
entry_count = is_elf32 ? GET16(h32->ph_entry_count) : GET16(h64->ph_entry_count);
#ifdef DEBUG_ELF
wolfBoot_printf("Program Headers %d (size %d)\r\n", entry_count,
entry_size);
#endif
/* We need to unlock flash before writing */
hal_flash_unlock();
#ifdef EXT_FLASH
ext_flash_unlock();
#endif
for (i = 0; i < entry_count; i++) {
uint8_t* ptr = ((uint8_t*)entry_off) + (i * entry_size);
elf32_program_header* e32 = (elf32_program_header*)ptr;
elf64_program_header* e64 = (elf64_program_header*)ptr;
uint32_t type = (is_elf32 ? GET32(e32->type) : GET32(e64->type));
uintptr_t paddr = (is_elf32 ? GET32(e32->paddr) : GET64(e64->paddr));
uintptr_t vaddr = (is_elf32 ? GET32(e32->vaddr) : GET64(e64->vaddr));
uintptr_t mem_size =
(is_elf32 ? GET32(e32->mem_size) : GET64(e64->mem_size));
uintptr_t offset = (is_elf32 ? GET32(e32->offset) : GET64(e64->offset));
uintptr_t file_size =
(is_elf32 ? GET32(e32->file_size) : GET64(e64->file_size));
uint32_t type;
uintptr_t paddr, vaddr, mem_size, offset, file_size;
/* Read program header */
#ifdef EXT_FLASH
if (PART_IS_EXT(img)) {
ext_flash_check_read((uintptr_t)(img->fw_base + ph_offset + (i * entry_size)),
program_header_buf, entry_size);
} else
#endif
{
memcpy(program_header_buf, (void*)(img->fw_base + ph_offset + (i * entry_size)), entry_size);
}
if (is_elf32) {
elf32_program_header* e32 = (elf32_program_header*)program_header_buf;
type = GET32(e32->type);
paddr = GET32(e32->paddr);
vaddr = GET32(e32->vaddr);
mem_size = GET32(e32->mem_size);
offset = GET32(e32->offset);
file_size = GET32(e32->file_size);
} else {
elf64_program_header* e64 = (elf64_program_header*)program_header_buf;
type = GET32(e64->type);
paddr = GET64(e64->paddr);
vaddr = GET64(e64->vaddr);
mem_size = GET64(e64->mem_size);
offset = GET64(e64->offset);
file_size = GET64(e64->file_size);
}
if (type != ELF_PT_LOAD || mem_size == 0) {
continue;
@ -182,85 +223,64 @@ static int wolfBoot_elf_load_segments(struct wolfBoot_image* img)
/* Use physical address for XIP */
if (file_size > 0) {
/* We need to unlock flash before writing */
hal_flash_unlock();
#ifdef EXT_FLASH
ext_flash_unlock();
#endif
uint8_t buffer[WOLFBOOT_SECTOR_SIZE];
uint32_t pos = 0;
uint32_t chunk_size;
/* Erase the target flash area before writing */
if (paddr >= WOLFBOOT_PARTITION_BOOT_ADDRESS &&
paddr + mem_size <=
WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE) {
/* Target is in internal flash */
hal_flash_erase(paddr, mem_size);
/* Copy the segment data to flash */
if (hal_flash_write(paddr, (void*)(img->fw_base + offset),
file_size) < 0) {
ret = -3;
}
}
/* TODO: THis could erase data outside the region we want to program
* - AURIX HAL handles read/modify/erase/write but not sure all HALs
* do...need to do this manually here and do erases before writes */
/* wb_flash_erase(img, paddr, mem_size); */
/* Copy the segment data to flash in chunks */
while (pos < file_size) {
chunk_size = (file_size - pos > sizeof(buffer)) ?
sizeof(buffer) : (file_size - pos);
#ifdef EXT_FLASH
else if (paddr >= WOLFBOOT_PARTITION_UPDATE_ADDRESS &&
paddr + mem_size <= WOLFBOOT_PARTITION_UPDATE_ADDRESS +
WOLFBOOT_PARTITION_SIZE) {
/* Target is in external flash */
ext_flash_erase(paddr, mem_size);
/* Copy the segment data to external flash */
if (ext_flash_write(paddr, (void*)(img->fw_base + offset),
file_size) < 0) {
ret = -3;
}
}
if (PART_IS_EXT(img)) {
ext_flash_check_read((uintptr_t)(img->fw_base + offset + pos),
buffer, chunk_size);
} else
#endif
else {
/* Target is outside of defined flash partitions */
ret = -4;
{
memcpy(buffer, (void*)(img->fw_base + offset + pos), chunk_size);
}
if (wb_flash_write(img, paddr + pos, buffer, chunk_size) < 0) {
ret = -3;
break;
}
pos += chunk_size;
}
/* If mem_size > file_size, we need to zero out the rest */
if (mem_size > file_size && ret == 0) {
uint8_t zero_buf[64];
uint8_t zero_buf[64];
uint32_t to_clear = mem_size - file_size;
uint32_t chunk, pos = 0;
uint32_t chunk, zero_pos = 0;
/* Initialize zero buffer */
memset(zero_buf, 0, sizeof(zero_buf));
/* Zero out remainder in chunks */
while (to_clear > 0) {
chunk = (to_clear > sizeof(zero_buf)) ? sizeof(zero_buf)
: to_clear;
if (paddr >= WOLFBOOT_PARTITION_BOOT_ADDRESS &&
paddr + mem_size <= WOLFBOOT_PARTITION_BOOT_ADDRESS +
WOLFBOOT_PARTITION_SIZE) {
if (hal_flash_write(paddr + file_size + pos, zero_buf,
chunk) < 0) {
ret = -5;
break;
}
chunk = (to_clear > sizeof(zero_buf)) ? sizeof(zero_buf) : to_clear;
if (wb_flash_write(img, paddr + file_size + zero_pos, zero_buf, chunk) < 0) {
ret = -5;
break;
}
#ifdef EXT_FLASH
else {
if (ext_flash_write(paddr + file_size + pos, zero_buf,
chunk) < 0) {
ret = -5;
break;
}
}
#endif
pos += chunk;
zero_pos += chunk;
to_clear -= chunk;
}
}
/* Lock flash after writing */
#ifdef EXT_FLASH
ext_flash_lock();
#endif
hal_flash_lock();
if (ret != 0) {
return ret;
break;
}
}
@ -269,130 +289,15 @@ static int wolfBoot_elf_load_segments(struct wolfBoot_image* img)
#endif
}
/* Lock flash after writing */
#ifdef EXT_FLASH
ext_flash_lock();
#endif
hal_flash_lock();
return ret;
}
/**
* @brief Compute the scattered hash by hashing PT_LOAD segments at their XIP
* addresses
*
* @param img Pointer to the wolfBoot image
* @param hash Buffer to store the computed hash (must be at least
* SHA256_DIGEST_SIZE bytes)
* @return 0 on success, negative value on error
*/
static int wolfBoot_compute_scattered_hash(struct wolfBoot_image* img,
uint8_t* hash)
{
elf32_header* h32 = (elf32_header*)(img->fw_base);
elf64_header* h64 = (elf64_header*)(img->fw_base);
uint16_t entry_count, entry_size;
uint8_t* entry_off;
int is_elf32, is_le, i;
#if defined(WOLFBOOT_HASH_SHA256)
wc_Sha256 sha256_ctx;
#elif defined(WOLFBOOT_HASH_SHA384)
wc_Sha384 sha384_ctx;
#elif defined(WOLFBOOT_HASH_SHA3_384)
wc_Sha3 sha3_384_ctx;
#endif
/* Verify ELF header */
if (memcmp(h32->ident, ELF_IDENT_STR, 4) != 0) {
return -1; /* not valid header identifier */
}
/* Load class and endianess */
is_elf32 = (h32->ident[4] == ELF_CLASS_32);
is_le = (h32->ident[5] == ELF_ENDIAN_LITTLE);
(void)is_le;
/* Initialize hash context */
#if defined(WOLFBOOT_HASH_SHA256)
wc_InitSha256(&sha256_ctx);
#elif defined(WOLFBOOT_HASH_SHA384)
wc_InitSha384(&sha384_ctx);
#elif defined(WOLFBOOT_HASH_SHA3_384)
wc_Sha3_384_Init(&sha3_384_ctx, NULL, INVALID_DEVID);
#endif
/* Get program headers */
entry_off = (uint8_t*)(img->fw_base) +
(is_elf32 ? GET32(h32->ph_offset) : GET32(h64->ph_offset));
entry_size =
(is_elf32 ? GET16(h32->ph_entry_size) : GET16(h64->ph_entry_size));
entry_count =
(is_elf32 ? GET16(h32->ph_entry_count) : GET16(h64->ph_entry_count));
/* Sort loadable segments by physical address */
/* Simple bubble sort - ELF files typically have few program headers */
typedef struct {
uint32_t type;
uintptr_t paddr;
uintptr_t file_size;
} segment_info;
segment_info
segments[32]; /* Assuming max 32 segments, increase if needed */
int num_loadable = 0;
/* Collect loadable segments */
for (i = 0; i < entry_count && num_loadable < 32; i++) {
uint8_t* ptr = ((uint8_t*)entry_off) + (i * entry_size);
elf32_program_header* e32 = (elf32_program_header*)ptr;
elf64_program_header* e64 = (elf64_program_header*)ptr;
uint32_t type = (is_elf32 ? GET32(e32->type) : GET32(e64->type));
uintptr_t paddr = (is_elf32 ? GET32(e32->paddr) : GET64(e64->paddr));
uintptr_t file_size =
(is_elf32 ? GET32(e32->file_size) : GET64(e64->file_size));
if (type == ELF_PT_LOAD && file_size > 0) {
segments[num_loadable].type = type;
segments[num_loadable].paddr = paddr;
segments[num_loadable].file_size = file_size;
num_loadable++;
}
}
/* Sort segments by physical address (ascending) */
for (i = 0; i < num_loadable - 1; i++) {
int j;
for (j = 0; j < num_loadable - i - 1; j++) {
if (segments[j].paddr > segments[j + 1].paddr) {
segment_info temp = segments[j];
segments[j] = segments[j + 1];
segments[j + 1] = temp;
}
}
}
/* Hash segments in order of physical address */
for (i = 0; i < num_loadable; i++) {
/* Hash the segment data from physical address */
#if defined(WOLFBOOT_HASH_SHA256)
wc_Sha256Update(&sha256_ctx, (uint8_t*)segments[i].paddr,
segments[i].file_size);
#elif defined(WOLFBOOT_HASH_SHA384)
wc_Sha384Update(&sha384_ctx, (uint8_t*)segments[i].paddr,
segments[i].file_size);
#elif defined(WOLFBOOT_HASH_SHA3_384)
wc_Sha3_384_Update(&sha3_384_ctx, (uint8_t*)segments[i].paddr,
segments[i].file_size);
#endif
}
/* Finalize hash */
#if defined(WOLFBOOT_HASH_SHA256)
wc_Sha256Final(&sha256_ctx, hash);
#elif defined(WOLFBOOT_HASH_SHA384)
wc_Sha384Final(&sha384_ctx, hash);
#elif defined(WOLFBOOT_HASH_SHA3_384)
wc_Sha3_384_Final(&sha3_384_ctx, hash);
#endif
return 0;
}
/**
* @brief Verify that the scattered hash matches the one stored in the image
* header
@ -403,7 +308,7 @@ static int wolfBoot_compute_scattered_hash(struct wolfBoot_image* img,
static int wolfBoot_verify_scattered_hash(struct wolfBoot_image* img)
{
int ret;
uint8_t computed_hash[SHA256_DIGEST_SIZE];
uint8_t computed_hash[WOLFBOOT_SHA_DIGEST_SIZE];
uint8_t* stored_hash;
uint16_t stored_hash_len;
@ -415,13 +320,13 @@ static int wolfBoot_verify_scattered_hash(struct wolfBoot_image* img)
/* Get stored scattered hash from header */
stored_hash_len =
wolfBoot_get_header(img, HDR_SCATTERED_HASH, &stored_hash);
if (stored_hash_len != SHA256_DIGEST_SIZE) {
wolfBoot_get_header(img, HDR_ELF_SCATTERED_HASH, &stored_hash);
if (stored_hash_len != WOLFBOOT_SHA_DIGEST_SIZE) {
return -1; /* Scattered hash not found or invalid size */
}
/* Compare hashes */
if (memcmp(computed_hash, stored_hash, SHA256_DIGEST_SIZE) != 0) {
if (memcmp(computed_hash, stored_hash, WOLFBOOT_SHA_DIGEST_SIZE) != 0) {
return -2; /* Hash mismatch */
}
@ -436,9 +341,20 @@ static int wolfBoot_verify_scattered_hash(struct wolfBoot_image* img)
*/
static int is_elf_image(struct wolfBoot_image* img)
{
elf32_header* h32 = (elf32_header*)(img->fw_base);
elf32_header h32;
#ifdef EXT_FLASH
if (PART_IS_EXT(img)) {
/* Read ELF header from external flash */
ext_flash_check_read((uintptr_t)(img->fw_base), (uint8_t*)&h32, sizeof(elf32_header));
} else {
memcpy(&h32, (void*)(img->fw_base), sizeof(elf32_header));
}
#else
memcpy(&h32, (void*)(img->fw_base), sizeof(elf32_header));
#endif
if (memcmp(h32->ident, ELF_IDENT_STR, 4) == 0) {
if (memcmp(h32.ident, ELF_IDENT_STR, 4) == 0) {
return 1;
}
return 0;

View File

@ -1,852 +0,0 @@
/* update_flash_elf.c
*
* Implementation for ELF based updater with XIP support
*
* Copyright (C) 2023 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*
* --------------------------------------------------------------------------------
* ELF XIP Update Scheme Overview
* --------------------------------------------------------------------------------
* This module implements a secure update mechanism for ELF files that can be
* executed in place (XIP) from flash. The implementation uses standard wolfBoot
* signature verification plus an additional scattered hash verification:
*
* 1. Standard wolfBoot Signature: Used to verify the authenticity and integrity
* of the entire ELF image as stored in the partition
* - Leverages existing wolfBoot signature verification mechanism
* - Verifies the entire image during update and at boot time
*
* 2. Scattered Hash: A hash of all PT_LOAD segments in their XIP memory locations
* - Computed by hashing loadable segments in ascending physical address order
* - Stored in a custom TLV in the wolfBoot image header, which is covered by
* the image signature, thus guaranteeing its authenticity
* - Verifies that segments loaded to their XIP addresses match the original
* contents of the ELF file
*
* Update Process:
* 1. Standard wolfBoot verification of the stored elf file in the update partition
* 2. Perform the standard three-way interruptible partition swap (update -> boot)
* 3. Set boot partition state to IMG_STATE_ELF_LOADING
* 4. Parse ELF headers from the boot partition and load each PT_LOAD segment to its XIP address
* 5. Compute scattered hash of loaded segments and verify against the authenticated
* scattered hash TLV from the image header
* 6. If process is interrupted during scatter loading/verification, the scatter load from the
* boot partition is restarted
* 7. If verification succeeds, set boot partition to IMG_STATE_TESTING, extract entry point from
* ELF header and boot
* 8. If verification fails, the boot partition is rolled back to the previous state (update)
* and the update process is restarted
*
* Boot Process:
* 1. Standard wolfBoot verification of the boot image signature
* 2. Additionally verify the scattered hash by hashing PT_LOAD
* segments in their XIP locations and comparing with the authenticated hash
* from the image header
* 3. If verification succeeds, extract entry point from ELF header and boot
* 4. If verification fails, the boot partition is rolled back to the previous state (update)
* and the new boot partition is scatter loaded and verified
*
* The update process is failsafe and interruptible. If power is lost during
* ELF loading, the system can resume from where it left off (or close to it) on next boot.
*/
#include "loader.h"
#include "image.h"
#include "hal.h"
#include "spi_flash.h"
#include "wolfboot/wolfboot.h"
#include "printf.h"
#include "string.h"
#ifdef WOLFBOOT_ELF
#include "elf.h"
#endif
/* Define the additional state for ELF loading */
#ifndef IMG_STATE_ELF_LOADING
#define IMG_STATE_ELF_LOADING 0x70
#endif
/* Custom TLV type for scattered hash */
#define HDR_SCATTERED_HASH 0x0040
/* Forward declarations */
static int wolfBoot_elf_load_segments(struct wolfBoot_image *img);
static int wolfBoot_update_elf(int fallback_allowed);
static int wolfBoot_compute_scattered_hash(struct wolfBoot_image *img, uint8_t *hash);
static int wolfBoot_verify_scattered_hash(struct wolfBoot_image *img);
#ifdef WOLFBOOT_ELF
/**
* @brief Load ELF segments to their runtime memory addresses in flash
*
* @param img Pointer to the wolfBoot image
* @return 0 on success, negative value on error
*/
static int wolfBoot_elf_load_segments(struct wolfBoot_image *img)
{
elf32_header* h32 = (elf32_header*)(img->fw_base);
elf64_header* h64 = (elf64_header*)(img->fw_base);
uint16_t entry_count, entry_size;
uint8_t *entry_off;
int is_elf32, is_le, i;
int ret = 0;
#ifdef DEBUG_ELF
wolfBoot_printf("Loading ELF segments to XIP flash from %p\r\n", (void*)(img->fw_base));
#endif
/* Verify ELF header */
if (memcmp(h32->ident, ELF_IDENT_STR, 4) != 0) {
return -1; /* not valid header identifier */
}
/* Load class and endianess */
is_elf32 = (h32->ident[4] == ELF_CLASS_32);
is_le = (h32->ident[5] == ELF_ENDIAN_LITTLE);
(void)is_le;
/* Verify this is an executable */
if ((is_elf32 ? GET16(h32->type) : GET16(h64->type)) != ELF_HET_EXEC) {
return -2; /* not executable */
}
#ifdef DEBUG_ELF
wolfBoot_printf("Found valid elf%d (%s endian) for XIP loading\r\n",
is_elf32 ? 32 : 64, is_le ? "little" : "big");
#endif
/* programs */
entry_off = (uint8_t*)(img->fw_base) +
(is_elf32 ? GET32(h32->ph_offset) : GET32(h64->ph_offset));
entry_size = (is_elf32 ? GET16(h32->ph_entry_size) : GET16(h64->ph_entry_size));
entry_count = (is_elf32 ? GET16(h32->ph_entry_count) : GET16(h64->ph_entry_count));
#ifdef DEBUG_ELF
wolfBoot_printf("Program Headers %d (size %d)\r\n", entry_count, entry_size);
#endif
for (i = 0; i < entry_count; i++) {
uint8_t *ptr = ((uint8_t*)entry_off) + (i * entry_size);
elf32_program_header* e32 = (elf32_program_header*)ptr;
elf64_program_header* e64 = (elf64_program_header*)ptr;
uint32_t type = (is_elf32 ? GET32(e32->type) : GET32(e64->type));
uintptr_t paddr = (is_elf32 ? GET32(e32->paddr) : GET64(e64->paddr));
uintptr_t vaddr = (is_elf32 ? GET32(e32->vaddr) : GET64(e64->vaddr));
uintptr_t mem_size = (is_elf32 ? GET32(e32->mem_size) : GET64(e64->mem_size));
uintptr_t offset = (is_elf32 ? GET32(e32->offset) : GET64(e64->offset));
uintptr_t file_size = (is_elf32 ? GET32(e32->file_size) : GET64(e64->file_size));
if (type != ELF_PT_LOAD || mem_size == 0) {
continue;
}
#ifdef DEBUG_ELF
if (file_size > 0) {
wolfBoot_printf(
"Load %u bytes (offset %p) to %p (p %p)\r\n",
(uint32_t)mem_size, (void*)offset, (void*)vaddr, (void*)paddr);
}
if (mem_size > file_size) {
wolfBoot_printf(
"Clear %u bytes at %p (p %p)\r\n",
(uint32_t)(mem_size - file_size), (void*)vaddr, (void*)paddr);
}
#endif
/* Use physical address for XIP */
if (file_size > 0) {
/* We need to unlock flash before writing */
hal_flash_unlock();
#ifdef EXT_FLASH
ext_flash_unlock();
#endif
/* Erase the target flash area before writing */
if (paddr >= WOLFBOOT_PARTITION_BOOT_ADDRESS &&
paddr + mem_size <= WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE) {
/* Target is in internal flash */
hal_flash_erase(paddr, mem_size);
/* Copy the segment data to flash */
if (hal_flash_write(paddr, (void*)(img->fw_base + offset), file_size) < 0) {
ret = -3;
}
}
#ifdef EXT_FLASH
else if (paddr >= WOLFBOOT_PARTITION_UPDATE_ADDRESS &&
paddr + mem_size <= WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_PARTITION_SIZE) {
/* Target is in external flash */
ext_flash_erase(paddr, mem_size);
/* Copy the segment data to external flash */
if (ext_flash_write(paddr, (void*)(img->fw_base + offset), file_size) < 0) {
ret = -3;
}
}
#endif
else {
/* Target is outside of defined flash partitions */
ret = -4;
}
/* If mem_size > file_size, we need to zero out the rest */
if (mem_size > file_size && ret == 0) {
uint8_t zero_buf[64];
uint32_t to_clear = mem_size - file_size;
uint32_t chunk, pos = 0;
/* Initialize zero buffer */
memset(zero_buf, 0, sizeof(zero_buf));
/* Zero out remainder in chunks */
while (to_clear > 0) {
chunk = (to_clear > sizeof(zero_buf)) ? sizeof(zero_buf) : to_clear;
if (paddr >= WOLFBOOT_PARTITION_BOOT_ADDRESS &&
paddr + mem_size <= WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE) {
if (hal_flash_write(paddr + file_size + pos, zero_buf, chunk) < 0) {
ret = -5;
break;
}
}
#ifdef EXT_FLASH
else {
if (ext_flash_write(paddr + file_size + pos, zero_buf, chunk) < 0) {
ret = -5;
break;
}
}
#endif
pos += chunk;
to_clear -= chunk;
}
}
/* Lock flash after writing */
#ifdef EXT_FLASH
ext_flash_lock();
#endif
hal_flash_lock();
if (ret != 0) {
return ret;
}
}
#ifdef ARCH_PPC
flush_cache(paddr, mem_size);
#endif
}
return ret;
}
/**
* @brief Compute the scattered hash by hashing PT_LOAD segments at their XIP addresses
*
* @param img Pointer to the wolfBoot image
* @param hash Buffer to store the computed hash (must be at least SHA256_DIGEST_SIZE bytes)
* @return 0 on success, negative value on error
*/
static int wolfBoot_compute_scattered_hash(struct wolfBoot_image *img, uint8_t *hash)
{
elf32_header* h32 = (elf32_header*)(img->fw_base);
elf64_header* h64 = (elf64_header*)(img->fw_base);
uint16_t entry_count, entry_size;
uint8_t *entry_off;
int is_elf32, is_le, i;
#if defined(WOLFBOOT_HASH_SHA256)
wc_Sha256 sha256_ctx;
#elif defined(WOLFBOOT_HASH_SHA384)
wc_Sha384 sha384_ctx;
#elif defined(WOLFBOOT_HASH_SHA3_384)
wc_Sha3 sha3_384_ctx;
#endif
/* Verify ELF header */
if (memcmp(h32->ident, ELF_IDENT_STR, 4) != 0) {
return -1; /* not valid header identifier */
}
/* Load class and endianess */
is_elf32 = (h32->ident[4] == ELF_CLASS_32);
is_le = (h32->ident[5] == ELF_ENDIAN_LITTLE);
(void)is_le;
/* Initialize hash context */
#if defined(WOLFBOOT_HASH_SHA256)
wc_InitSha256(&sha256_ctx);
#elif defined(WOLFBOOT_HASH_SHA384)
wc_InitSha384(&sha384_ctx);
#elif defined(WOLFBOOT_HASH_SHA3_384)
wc_InitSha3_384(&sha3_384_ctx, NULL, INVALID_DEVID);
#endif
/* Get program headers */
entry_off = (uint8_t*)(img->fw_base) +
(is_elf32 ? GET32(h32->ph_offset) : GET32(h64->ph_offset));
entry_size = (is_elf32 ? GET16(h32->ph_entry_size) : GET16(h64->ph_entry_size));
entry_count = (is_elf32 ? GET16(h32->ph_entry_count) : GET16(h64->ph_entry_count));
/* Sort loadable segments by physical address */
/* Simple bubble sort - ELF files typically have few program headers */
typedef struct {
uint32_t type;
uintptr_t paddr;
uintptr_t file_size;
} segment_info;
segment_info segments[32]; /* Assuming max 32 segments, increase if needed */
int num_loadable = 0;
/* Collect loadable segments */
for (i = 0; i < entry_count && num_loadable < 32; i++) {
uint8_t *ptr = ((uint8_t*)entry_off) + (i * entry_size);
elf32_program_header* e32 = (elf32_program_header*)ptr;
elf64_program_header* e64 = (elf64_program_header*)ptr;
uint32_t type = (is_elf32 ? GET32(e32->type) : GET32(e64->type));
uintptr_t paddr = (is_elf32 ? GET32(e32->paddr) : GET64(e64->paddr));
uintptr_t file_size = (is_elf32 ? GET32(e32->file_size) : GET64(e64->file_size));
if (type == ELF_PT_LOAD && file_size > 0) {
segments[num_loadable].type = type;
segments[num_loadable].paddr = paddr;
segments[num_loadable].file_size = file_size;
num_loadable++;
}
}
/* Sort segments by physical address (ascending) */
for (i = 0; i < num_loadable - 1; i++) {
int j;
for (j = 0; j < num_loadable - i - 1; j++) {
if (segments[j].paddr > segments[j + 1].paddr) {
segment_info temp = segments[j];
segments[j] = segments[j + 1];
segments[j + 1] = temp;
}
}
}
/* Hash segments in order of physical address */
for (i = 0; i < num_loadable; i++) {
/* Hash the segment data from physical address */
#if defined(WOLFBOOT_HASH_SHA256)
wc_Sha256Update(&sha256_ctx, (uint8_t*)segments[i].paddr, segments[i].file_size);
#elif defined(WOLFBOOT_HASH_SHA384)
wc_Sha384Update(&sha384_ctx, (uint8_t*)segments[i].paddr, segments[i].file_size);
#elif defined(WOLFBOOT_HASH_SHA3_384)
wc_Sha3_384_Update(&sha3_384_ctx, (uint8_t*)segments[i].paddr, segments[i].file_size);
#endif
}
/* Finalize hash */
#if defined(WOLFBOOT_HASH_SHA256)
wc_Sha256Final(&sha256_ctx, hash);
#elif defined(WOLFBOOT_HASH_SHA384)
wc_Sha384Final(&sha384_ctx, hash);
#elif defined(WOLFBOOT_HASH_SHA3_384)
wc_Sha3_384_Final(&sha3_384_ctx, hash);
#endif
return 0;
}
/**
* @brief Verify that the scattered hash matches the one stored in the image header
*
* @param img Pointer to the wolfBoot image
* @return 0 on success, negative value on error
*/
static int wolfBoot_verify_scattered_hash(struct wolfBoot_image *img)
{
int ret;
uint8_t computed_hash[SHA256_DIGEST_SIZE];
uint8_t *stored_hash;
uint16_t stored_hash_len;
/* Compute scattered hash */
ret = wolfBoot_compute_scattered_hash(img, computed_hash);
if (ret != 0) {
return ret;
}
/* Get stored scattered hash from header */
stored_hash_len = wolfBoot_get_header(img, HDR_SCATTERED_HASH, &stored_hash);
if (stored_hash_len != SHA256_DIGEST_SIZE) {
return -1; /* Scattered hash not found or invalid size */
}
/* Compare hashes */
if (memcmp(computed_hash, stored_hash, SHA256_DIGEST_SIZE) != 0) {
return -2; /* Hash mismatch */
}
return 0; /* Success */
}
/**
* @brief Check if an image is an ELF file
*
* @param img Pointer to the wolfBoot image
* @return 1 if ELF, 0 if not
*/
static int is_elf_image(struct wolfBoot_image *img)
{
elf32_header* h32 = (elf32_header*)(img->fw_base);
if (memcmp(h32->ident, ELF_IDENT_STR, 4) == 0) {
return 1;
}
return 0;
}
/**
* @brief Update function for ELF images with XIP support
*
* @param fallback_allowed Whether fallback is allowed if update fails
* @return 0 on success, negative value on error
*/
static int wolfBoot_update_elf(int fallback_allowed)
{
int ret = -1;
uint8_t st;
struct wolfBoot_image boot, update, swap;
memset(&boot, 0, sizeof(struct wolfBoot_image));
memset(&update, 0, sizeof(struct wolfBoot_image));
memset(&swap, 0, sizeof(struct wolfBoot_image));
/* Determine the update state */
if (wolfBoot_get_partition_state(PART_UPDATE, &st) != 0) {
return -1;
}
/* We only proceed if the update partition is marked for update */
if (st != IMG_STATE_UPDATING && st != IMG_STATE_ELF_LOADING) {
return -2;
}
/* Open update partition */
if (wolfBoot_open_image(&update, PART_UPDATE) != 0) {
return -3;
}
/* Check if this is an ELF image */
uint16_t update_type = wolfBoot_get_image_type(PART_UPDATE);
if (!(update_type & HDR_IMG_TYPE_ELF)) {
/* Not an ELF image, use standard update */
return -4;
}
if (st == IMG_STATE_ELF_LOADING) {
/* Resume ELF loading */
#ifdef DEBUG_ELF
wolfBoot_printf("Resuming ELF loading\r\n");
#endif
/* Open boot partition */
if (wolfBoot_open_image(&boot, PART_BOOT) != 0) {
return -5;
}
/* Load ELF segments to their XIP addresses */
ret = wolfBoot_elf_load_segments(&boot);
if (ret != 0) {
return ret;
}
/* Verify scattered hash */
ret = wolfBoot_verify_scattered_hash(&boot);
if (ret != 0) {
/* If verification fails, rollback if possible */
if (fallback_allowed) {
hal_flash_unlock();
#ifdef EXT_FLASH
ext_flash_unlock();
#endif
wolfBoot_erase_partition(PART_BOOT);
wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_TESTING);
#ifdef EXT_FLASH
ext_flash_lock();
#endif
hal_flash_lock();
return -6;
}
return ret;
}
/* ELF loading complete, set state to TESTING */
hal_flash_unlock();
#ifdef EXT_FLASH
ext_flash_unlock();
#endif
wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_TESTING);
#ifdef EXT_FLASH
ext_flash_lock();
#endif
hal_flash_lock();
return 0;
}
/*
* Normal update flow:
* 1. Verify linear hash of update image (done by wolfBoot_open_image)
* 2. Perform image swap (done by wolfBoot_swap_and_final_erase)
* 3. Scatter load segments
* 4. Verify scattered hash
*/
/* Perform the standard image swap first (linear hash already verified by wolfBoot_open_image) */
ret = wolfBoot_swap_and_final_erase(0);
if (ret != 0) {
return ret;
}
/* At this point, the standard image swap is complete. Now we need to load the ELF segments */
/* Open boot partition */
if (wolfBoot_open_image(&boot, PART_BOOT) != 0) {
return -8;
}
/* Set state to ELF_LOADING to indicate we're in the ELF loading phase */
hal_flash_unlock();
#ifdef EXT_FLASH
ext_flash_unlock();
#endif
wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_ELF_LOADING);
#ifdef EXT_FLASH
ext_flash_lock();
#endif
hal_flash_lock();
/* Load ELF segments to their XIP addresses */
ret = wolfBoot_elf_load_segments(&boot);
if (ret != 0) {
/* If loading fails and we can fallback, rollback to previous image */
if (fallback_allowed) {
hal_flash_unlock();
#ifdef EXT_FLASH
ext_flash_unlock();
#endif
wolfBoot_erase_partition(PART_BOOT);
wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_TESTING);
#ifdef EXT_FLASH
ext_flash_lock();
#endif
hal_flash_lock();
}
return ret;
}
/* Verify scattered hash */
ret = wolfBoot_verify_scattered_hash(&boot);
if (ret != 0) {
/* If verification fails, rollback if possible */
if (fallback_allowed) {
hal_flash_unlock();
#ifdef EXT_FLASH
ext_flash_unlock();
#endif
wolfBoot_erase_partition(PART_BOOT);
wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_TESTING);
#ifdef EXT_FLASH
ext_flash_lock();
#endif
hal_flash_lock();
}
return ret;
}
/* ELF loading complete, set state to TESTING */
hal_flash_unlock();
#ifdef EXT_FLASH
ext_flash_unlock();
#endif
wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_TESTING);
#ifdef EXT_FLASH
ext_flash_lock();
#endif
hal_flash_lock();
return 0;
}
/**
* @brief Entry point for ELF-aware update process
*
* @param fallback_allowed Whether fallback is allowed if update fails
* @return 0 on success, negative value on error
*/
int wolfBoot_update_elf_handler(int fallback_allowed)
{
uint8_t st;
uint16_t update_type;
struct wolfBoot_image update;
/* Check if update is needed */
if (wolfBoot_get_partition_state(PART_UPDATE, &st) != 0) {
return 0; /* No update needed */
}
if (st != IMG_STATE_UPDATING && st != IMG_STATE_ELF_LOADING) {
return 0; /* No update needed */
}
/* Open update image */
if (wolfBoot_open_image(&update, PART_UPDATE) != 0) {
return -1;
}
/* Check if this is an ELF image */
update_type = wolfBoot_get_image_type(PART_UPDATE);
if (update_type & HDR_IMG_TYPE_ELF) {
return wolfBoot_update_elf(fallback_allowed);
} else {
/* Not an ELF image, use standard update */
return wolfBoot_update(fallback_allowed);
}
}
/**
* @brief Verify integrity of an ELF image at boot time
*
* This function first verifies the linear hash using standard wolfBoot
* verification, then verifies the scattered hash for ELF files
*
* @param img Pointer to the wolfBoot image
* @return 0 on success, negative value on error
*/
int wolfBoot_verify_elf_integrity(struct wolfBoot_image *img)
{
int ret;
/* First verify linear hash (standard wolfBoot verification) */
ret = wolfBoot_verify_integrity(img);
if (ret != 0) {
return ret;
}
/* For ELF images, also verify the scattered hash */
if (is_elf_image(img)) {
ret = wolfBoot_verify_scattered_hash(img);
if (ret != 0) {
return -10 - ret; /* Use a different error range to distinguish */
}
}
return 0;
}
/**
* @brief ELF-aware function to check and handle updates
*/
void wolfBoot_check_elf_updates(void)
{
uint8_t st;
/* Check if boot partition is in ELF_LOADING state */
if (wolfBoot_get_partition_state(PART_BOOT, &st) == 0 &&
st == IMG_STATE_ELF_LOADING) {
/* Resume ELF loading */
wolfBoot_update_elf_handler(1);
return;
}
/* Check if update is needed */
if (wolfBoot_get_partition_state(PART_UPDATE, &st) == 0 &&
(st == IMG_STATE_UPDATING || st == IMG_STATE_ELF_LOADING)) {
/* Handle update with ELF awareness */
wolfBoot_update_elf_handler(1);
return;
}
}
/**
* @brief Hook function for bootloader to verify ELF image integrity
*
* This function is to be called during boot to verify both linear and
* scattered hash of the ELF image in the boot partition
*
* @return 0 on success, negative value on error
*/
int wolfBoot_verify_elf_boot_image(void)
{
int ret;
struct wolfBoot_image boot;
uint16_t boot_type;
/* Open boot partition */
if (wolfBoot_open_image(&boot, PART_BOOT) != 0) {
return -1;
}
/* Check if this is an ELF image */
boot_type = wolfBoot_get_image_type(PART_BOOT);
if (!(boot_type & HDR_IMG_TYPE_ELF)) {
/* Not an ELF image, rely on standard verification already done */
return 0;
}
/* Verify scattered hash */
ret = wolfBoot_verify_scattered_hash(&boot);
if (ret != 0) {
/* Verification failed, report error */
return ret;
}
return 0;
}
/**
* @brief Entry point for ELF-aware bootloader
*
* This function is called during boot to handle ELF image verification and loading
* It follows a similar flow to the standard wolfBoot_start but adds ELF-specific handling
*/
void wolfBoot_start(void)
{
int ret;
struct wolfBoot_image boot_image;
uint32_t *load_address = NULL;
uint32_t *source_address = NULL;
uint16_t image_type;
uint8_t p_state;
memset(&boot_image, 0, sizeof(struct wolfBoot_image));
/* First, check and handle any pending updates */
wolfBoot_check_elf_updates();
/* Open the boot image */
ret = wolfBoot_open_image(&boot_image, PART_BOOT);
/* Verify integrity and authenticity */
if (ret < 0 ||
(ret = wolfBoot_verify_integrity(&boot_image)) < 0 ||
(ret = wolfBoot_verify_authenticity(&boot_image)) < 0) {
wolfBoot_printf("Verification failed: Part %d, Hdr %d, Hash %d, Sig %d\n",
active, boot_image.hdr_ok, boot_image.sha_ok, boot_image.signature_ok);
/* If fallback is possible, try the other partition */
if (wolfBoot_fallback_is_possible()) {
active ^= 1;
wolfBoot_update(1); /* Allow fallback */
wolfBoot_start_elf(); /* Recursively try again */
return;
}
/* No fallback possible, panic */
wolfBoot_printf("No fallback possible, panic!\n");
wolfBoot_panic();
return;
}
/* Get image type to check if this is an ELF image */
image_type = wolfBoot_get_image_type(active);
if (image_type & HDR_IMG_TYPE_ELF) {
/* This is an ELF image - verify scattered hash */
ret = wolfBoot_verify_scattered_hash(&boot_image);
if (ret != 0) {
wolfBoot_printf("ELF scattered hash verification failed: %d\n", ret);
/* If fallback is possible, try the other partition */
if (wolfBoot_fallback_is_possible()) {
active ^= 1;
wolfBoot_update(1); /* Allow fallback */
wolfBoot_start_elf(); /* Recursively try again */
return;
}
/* No fallback possible, panic */
wolfBoot_printf("No fallback possible, panic!\n");
wolfBoot_panic();
return;
}
/* For ELF images, use the entry point from the ELF header */
elf32_header* h32 = (elf32_header*)(boot_image.fw_base);
elf64_header* h64 = (elf64_header*)(boot_image.fw_base);
int is_elf32 = (h32->ident[4] == ELF_CLASS_32);
int is_le = (h32->ident[5] == ELF_ENDIAN_LITTLE);
uintptr_t entry_point = GET_H64(entry);
wolfBoot_printf("ELF Entry point: %p\n", (void*)entry_point);
load_address = (uint32_t*)entry_point;
} else {
/* Not an ELF image, use standard load address */
#ifdef WOLFBOOT_LOAD_ADDRESS
load_address = (uint32_t*)WOLFBOOT_LOAD_ADDRESS;
#elif !defined(NO_XIP)
load_address = (uint32_t*)boot_image.fw_base;
#else
#error missing WOLFBOOT_LOAD_ADDRESS or XIP
#endif
}
/* First time we boot this update, set to TESTING to await
* confirmation from the system */
#ifdef WOLFBOOT_FIXED_PARTITIONS
if ((wolfBoot_get_partition_state(active, &p_state) == 0) &&
(p_state == IMG_STATE_UPDATING))
{
#ifdef EXT_FLASH
ext_flash_unlock();
#else
hal_flash_unlock();
#endif
wolfBoot_set_partition_state(active, IMG_STATE_TESTING);
#ifdef EXT_FLASH
ext_flash_lock();
#else
hal_flash_lock();
#endif
}
#endif
wolfBoot_printf("Firmware verification complete\n");
wolfBoot_printf("Booting at %p\n", load_address);
/* Prepare for boot */
hal_prepare_boot();
/* Boot the firmware */
#ifdef MMU
uint8_t *dts_addr = hal_get_dts_address();
uint32_t dts_size = 0;
if (dts_addr) {
dts_size = wolfBoot_get_dts_size(dts_addr);
if (dts_size > 0) {
/* Relocate DTS to RAM if needed */
uint8_t* dts_dst = (uint8_t*)WOLFBOOT_LOAD_DTS_ADDRESS;
wolfBoot_printf("Loading DTB (size %d) from %p to RAM at %p\n",
dts_size, dts_addr, WOLFBOOT_LOAD_DTS_ADDRESS);
memcpy(dts_dst, dts_addr, dts_size);
dts_addr = dts_dst;
}
}
do_boot(load_address, (uint32_t*)dts_addr);
#else
do_boot(load_address);
#endif
}
#endif /* WOLFBOOT_ELF */