WIP: first stab at elf loading impl

pull/562/head
Brett Nicholas 2025-03-20 12:33:38 -06:00 committed by Daniele Lacamera
parent 9510360de9
commit 4f6921df86
1 changed files with 503 additions and 7 deletions

View File

@ -37,6 +37,408 @@
#ifdef SECURE_PKCS11 #ifdef SECURE_PKCS11
int WP11_Library_Init(void); int WP11_Library_Init(void);
#endif #endif
#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
#ifdef WOLFBOOT_ELF
/**
* --------------------------------------------------------------------------------
* 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.
*/
/**
* @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_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
*
* @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;
}
/* Scatter loads an ELF image from boot partition if it is an ELF image */
static void check_and_load_boot_elf(struct wolfBoot_image *boot)
{
/* Check if this is an ELF image */
if (is_elf_image(&boot)) {
/* Set state to ELF_LOADING before starting scatter load */
wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_ELF_LOADING);
/* Load ELF segments to their XIP addresses */
if (wolfBoot_elf_load_segments(&boot) != 0) {
wolfBoot_printf("Failed to load ELF segments\n");
wolfBoot_panic();
}
/* If we get here, ELF loading and verification succeeded */
wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_TESTING);
}
}
#endif /* WOLFBOOT_ELF */
#ifdef RAM_CODE #ifdef RAM_CODE
#ifndef TARGET_rp2350 #ifndef TARGET_rp2350
@ -211,12 +613,37 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src,
# define TRAILER_OFFSET_WORDS 0 # define TRAILER_OFFSET_WORDS 0
#endif #endif
/**
* @brief Performs the final swap and erase operations during a secure update,
* ensuring that if power is lost during the update, the process can be resumed
* on next boot.
*
* This function handles the final phase of the three-way swap update process.
* It ensures that the update is atomic and power-fail safe by:
* 1. Saving the last sector of the boot partition to the swap area
* 2. Setting a magic trailer value to mark the swap as in progress
* 3. Erasing the last sector(s) of the boot partition
* 4. Restoring the saved sector from swap back to boot
* 5. Setting the boot partition state to TESTING
* 6. Erasing the last sector(s) of the update partition
*
* The function can be called in two modes:
* - Normal mode (resume=0): Initiates the swap and erase process
* - Resume mode (resume=1): Checks if a swap was interrupted and completes it
*
* @param resume If 1, checks for interrupted swap and resumes it; if 0, starts
* new swap
* @return 0 on success, negative value if no swap needed or on error
*/
static int wolfBoot_swap_and_final_erase(int resume) static int wolfBoot_swap_and_final_erase(int resume)
{ {
struct wolfBoot_image boot[1]; struct wolfBoot_image boot[1];
struct wolfBoot_image update[1]; struct wolfBoot_image update[1];
struct wolfBoot_image swap[1]; struct wolfBoot_image swap[1];
uint8_t st; uint8_t updateState;
#ifdef WOLFBOOT_ELF
uint8_t bootState;
#endif
int eraseLen = (WOLFBOOT_SECTOR_SIZE int eraseLen = (WOLFBOOT_SECTOR_SIZE
#ifdef NVM_FLASH_WRITEONCE /* need to erase the redundant sector too */ #ifdef NVM_FLASH_WRITEONCE /* need to erase the redundant sector too */
* 2 * 2
@ -231,7 +658,46 @@ static int wolfBoot_swap_and_final_erase(int resume)
wolfBoot_open_image(boot, PART_BOOT); wolfBoot_open_image(boot, PART_BOOT);
wolfBoot_open_image(update, PART_UPDATE); wolfBoot_open_image(update, PART_UPDATE);
wolfBoot_open_image(swap, PART_SWAP); wolfBoot_open_image(swap, PART_SWAP);
wolfBoot_get_partition_state(PART_UPDATE, &st); wolfBoot_get_partition_state(PART_UPDATE, &updateState);
#ifdef WOLFBOOT_ELF
wolfBoot_get_partition_state(PART_BOOT, &bootState);
#endif
#ifdef WOLFBOOT_ELF
if ((resume == 1) && (is_elf_image(boot))) {
/* If we're resuming and already in ELF loading state, we can skip
* directly to ELF loading since the swap was already completed */
if (bootState == IMG_STATE_ELF_LOADING) {
hal_flash_unlock();
#ifdef EXT_FLASH
ext_flash_unlock();
#endif
/* Load ELF segments to their XIP addresses */
if (wolfBoot_elf_load_segments(&boot) != 0) {
wolfBoot_printf("Failed to load ELF segments\n");
wolfBoot_panic();
}
wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_TESTING);
/* Only after successful ELF loading, erase update partition state
*/
if (updateState == IMG_STATE_FINAL_FLAGS) {
wb_flash_erase(update, WOLFBOOT_PARTITION_SIZE - eraseLen,
eraseLen);
}
#ifdef EXT_FLASH
ext_flash_lock();
#endif
hal_flash_lock();
return 0;
}
}
#endif
/* read trailer */ /* read trailer */
#if defined(EXT_FLASH) && PARTN_IS_EXT(PART_BOOT) #if defined(EXT_FLASH) && PARTN_IS_EXT(PART_BOOT)
@ -247,7 +713,12 @@ static int wolfBoot_swap_and_final_erase(int resume)
swapDone = 1; swapDone = 1;
} }
/* if resuming, quit if swap isn't done */ /* if resuming, quit if swap isn't done */
if ((resume == 1) && (swapDone == 0) && (st != IMG_STATE_FINAL_FLAGS)) { if ((resume == 1) && (swapDone == 0) &&
(updateState != IMG_STATE_FINAL_FLAGS) &&
#ifdef WOLFBOOT_ELF
(bootState != IMG_STATE_ELF_LOADING)
#endif
) {
return -1; return -1;
} }
@ -257,7 +728,7 @@ static int wolfBoot_swap_and_final_erase(int resume)
#endif #endif
/* IMG_STATE_FINAL_FLAGS allows re-entry without blowing away swap */ /* IMG_STATE_FINAL_FLAGS allows re-entry without blowing away swap */
if (st != IMG_STATE_FINAL_FLAGS) { if (updateState != IMG_STATE_FINAL_FLAGS) {
/* store the sector at tmpBootPos into swap */ /* store the sector at tmpBootPos into swap */
wolfBoot_copy_sector(boot, swap, tmpBootPos / WOLFBOOT_SECTOR_SIZE); wolfBoot_copy_sector(boot, swap, tmpBootPos / WOLFBOOT_SECTOR_SIZE);
/* set FINAL_SWAP for re-entry */ /* set FINAL_SWAP for re-entry */
@ -291,9 +762,18 @@ static int wolfBoot_swap_and_final_erase(int resume)
else { else {
wb_flash_erase(boot, tmpBootPos, WOLFBOOT_SECTOR_SIZE); wb_flash_erase(boot, tmpBootPos, WOLFBOOT_SECTOR_SIZE);
} }
#ifdef WOLFBOOT_ELF
/* load elf file from boot partition if applicable. This sets the boot
* partition state to loading during the load and then to testing on success
*/
check_and_load_boot_elf(boot);
#else
/* mark boot as TESTING */ /* mark boot as TESTING */
wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_TESTING); wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_TESTING);
/* erase the last sector(s) of update */ #endif
/* erase the last sector(s) of update. This resets the update partition state
* to IMG_STATE_NEW */
wb_flash_erase(update, WOLFBOOT_PARTITION_SIZE - eraseLen, eraseLen); wb_flash_erase(update, WOLFBOOT_PARTITION_SIZE - eraseLen, eraseLen);
#ifdef EXT_FLASH #ifdef EXT_FLASH
@ -783,7 +1263,11 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
* wolfBoot_start*/ * wolfBoot_start*/
wolfBoot_swap_and_final_erase(0); wolfBoot_swap_and_final_erase(0);
#else /* DISABLE_BACKUP */ #else /* DISABLE_BACKUP */ /* Compute and verify scattered hash */
if (wolfBoot_verify_scattered_hash(&boot) != 0) {
wolfBoot_printf("Scattered hash verification failed\n");
return -1;
}
/* Direct Swap without power fail safety */ /* Direct Swap without power fail safety */
hal_flash_unlock(); hal_flash_unlock();
@ -811,6 +1295,11 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
wb_flash_erase(&boot, sector * sector_size, sector_size); wb_flash_erase(&boot, sector * sector_size, sector_size);
sector++; sector++;
} }
#ifdef WOLFBOOT_ELF
check_and_load_boot_elf(&boot);
#endif
wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_SUCCESS); wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_SUCCESS);
#ifdef EXT_FLASH #ifdef EXT_FLASH
@ -1002,6 +1491,9 @@ void RAMFUNCTION wolfBoot_start(void)
if (bootRet < 0 if (bootRet < 0
|| (wolfBoot_verify_integrity(&boot) < 0) || (wolfBoot_verify_integrity(&boot) < 0)
|| (wolfBoot_verify_authenticity(&boot) < 0) || (wolfBoot_verify_authenticity(&boot) < 0)
#ifdef WOLFBOOT_ELF
|| (is_elf_image(&boot) && wolfBoot_verify_scattered_elf(&boot) < 0)
#endif
) { ) {
wolfBoot_printf("Boot failed: Hdr %d, Hash %d, Sig %d\n", wolfBoot_printf("Boot failed: Hdr %d, Hash %d, Sig %d\n",
boot.hdr_ok, boot.sha_ok, boot.signature_ok); boot.hdr_ok, boot.sha_ok, boot.signature_ok);
@ -1017,7 +1509,11 @@ void RAMFUNCTION wolfBoot_start(void)
/* Emergency update successful, try to re-open boot image */ /* Emergency update successful, try to re-open boot image */
if (likely(((wolfBoot_open_image(&boot, PART_BOOT) < 0) || if (likely(((wolfBoot_open_image(&boot, PART_BOOT) < 0) ||
(wolfBoot_verify_integrity(&boot) < 0) || (wolfBoot_verify_integrity(&boot) < 0) ||
(wolfBoot_verify_authenticity(&boot) < 0)))) { (wolfBoot_verify_authenticity(&boot) < 0)
#ifdef WOLFBOOT_ELF
|| (is_elf_image(&boot) && wolfBoot_verify_scattered_elf(&boot) < 0)
#endif
))) {
wolfBoot_printf("Boot (try 2) failed: Hdr %d, Hash %d, Sig %d\n", wolfBoot_printf("Boot (try 2) failed: Hdr %d, Hash %d, Sig %d\n",
boot.hdr_ok, boot.sha_ok, boot.signature_ok); boot.hdr_ok, boot.sha_ok, boot.signature_ok);
/* panic: something went wrong after the emergency update */ /* panic: something went wrong after the emergency update */