Add generic hook framework with provision for pre-init, post-init, and

boot hooks
pull/700/head
Brett Nicholas 2026-03-02 11:47:38 -07:00 committed by Daniele Lacamera
parent 1c18481fc0
commit 2924646695
12 changed files with 396 additions and 3 deletions

View File

@ -0,0 +1,188 @@
name: hook simulator tests
on:
push:
branches: [ 'master', 'main', 'release/**' ]
pull_request:
branches: [ '*' ]
jobs:
hooks_test:
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- mechanism: flash
config: sim.config
test_script: sim-sunnyday-update.sh
expected_preinit: 2
expected_postinit: 2
expected_boot: 2
expected_panic: 0
- mechanism: dualbank
config: sim-dualbank.config
test_script: sim-dualbank-swap-update.sh
expected_preinit: 3
expected_postinit: 3
expected_boot: 3
expected_panic: 0
- mechanism: panic
config: sim.config
test_script: ""
expected_preinit: 1
expected_postinit: 1
expected_boot: 0
expected_panic: 1
name: hooks (${{ matrix.mechanism }})
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Workaround for sources.list
run: |
set -euxo pipefail
apt-cache policy
grep -RInE '^(deb|Types|URIs)' /etc/apt || true
shopt -s nullglob
echo "Replace sources.list (legacy)"
sudo sed -i \
-e "s|https\?://azure\.archive\.ubuntu\.com/ubuntu/?|http://mirror.arizona.edu/ubuntu/|g" \
/etc/apt/sources.list || true
echo "Replace sources.list.d/*.list (legacy)"
for f in /etc/apt/sources.list.d/*.list; do
sudo sed -i \
-e "s|https\?://azure\.archive\.ubuntu\.com/ubuntu/?|http://mirror.arizona.edu/ubuntu/|g" \
"$f"
done
echo "Replace sources.list.d/*.sources (deb822)"
for f in /etc/apt/sources.list.d/*.sources; do
sudo sed -i \
-e "s|https\?://azure\.archive\.ubuntu\.com/ubuntu/?|http://mirror.arizona.edu/ubuntu/|g" \
-e "s|https\?://azure\.archive\.ubuntu\.com|http://mirror.arizona.edu|g" \
"$f"
done
echo "Fix /etc/apt/apt-mirrors.txt (used by URIs: mirror+file:...)"
if grep -qE '^[[:space:]]*https?://azure\.archive\.ubuntu\.com/ubuntu/?' /etc/apt/apt-mirrors.txt; then
sudo sed -i 's|https\?://azure\.archive\.ubuntu\.com/ubuntu/|http://mirror.arizona.edu/ubuntu/|g' /etc/apt/apt-mirrors.txt
fi
grep -RIn "azure.archive.ubuntu.com" /etc/apt || true
grep -RInE '^(deb|Types|URIs)' /etc/apt || true
echo "--- apt-mirrors.txt ---"
cat /etc/apt/apt-mirrors.txt || true
- name: Update repository
run: sudo apt-get update -o Acquire::Retries=3
- name: Create test_hooks.c
run: |
cat > test_hooks.c << 'EOF'
#include <stdio.h>
#include "hooks.h"
#define HOOK_LOG_FILE "/tmp/wolfboot_hooks.log"
static void log_hook(const char *name)
{
FILE *f = fopen(HOOK_LOG_FILE, "a");
if (f) {
fprintf(f, "%s\n", name);
fclose(f);
}
}
void wolfBoot_hook_preinit(void) { log_hook("preinit"); }
void wolfBoot_hook_postinit(void) { log_hook("postinit"); }
void wolfBoot_hook_boot(struct wolfBoot_image *boot_img) { (void)boot_img; log_hook("boot"); }
void wolfBoot_hook_panic(void) { log_hook("panic"); }
EOF
- name: Select config
run: |
cp config/examples/${{ matrix.config }} .config
- name: Build tools
run: |
make -C tools/keytools && make -C tools/bin-assemble
- name: Build wolfboot.elf with hooks
run: |
make clean && make test-sim-internal-flash-with-update \
WOLFBOOT_HOOKS_FILE=test_hooks.c \
WOLFBOOT_HOOK_LOADER_PREINIT=1 \
WOLFBOOT_HOOK_LOADER_POSTINIT=1 \
WOLFBOOT_HOOK_BOOT=1 \
WOLFBOOT_HOOK_PANIC=1
- name: Clear hook log
run: |
rm -f /tmp/wolfboot_hooks.log
- name: Corrupt partitions and run panic test
if: matrix.mechanism == 'panic'
run: |
# Zero out boot partition header (offset 0x80000) to invalidate image
printf '\x00\x00\x00\x00\x00\x00\x00\x00' | \
dd of=internal_flash.dd bs=1 seek=$((0x80000)) conv=notrunc
# Zero out update partition header (offset 0x100000) to invalidate image
printf '\x00\x00\x00\x00\x00\x00\x00\x00' | \
dd of=internal_flash.dd bs=1 seek=$((0x100000)) conv=notrunc
# wolfBoot_panic() calls exit('P') = exit(80) on ARCH_SIM
./wolfboot.elf get_version 2>&1 || EXIT_CODE=$?
echo "wolfboot.elf exited with code ${EXIT_CODE:-0}"
if [ "${EXIT_CODE:-0}" -ne 80 ]; then
echo "FAIL: expected exit code 80 (panic), got ${EXIT_CODE:-0}"
exit 1
fi
echo "OK: wolfboot panicked as expected"
- name: Run ${{ matrix.mechanism }} update test
if: matrix.mechanism != 'panic'
run: |
tools/scripts/${{ matrix.test_script }}
- name: Display hook log
if: always()
run: |
echo "=== Hook log contents ==="
cat /tmp/wolfboot_hooks.log || echo "(no log file found)"
- name: Verify hook call counts
run: |
LOG="/tmp/wolfboot_hooks.log"
PASS=true
check_count() {
local hook_name="$1"
local expected="$2"
local actual
actual=$(grep -c "^${hook_name}$" "$LOG" 2>/dev/null || echo 0)
if [ "$actual" -ne "$expected" ]; then
echo "FAIL: ${hook_name} expected=${expected} actual=${actual}"
PASS=false
else
echo "OK: ${hook_name} expected=${expected} actual=${actual}"
fi
}
check_count "preinit" ${{ matrix.expected_preinit }}
check_count "postinit" ${{ matrix.expected_postinit }}
check_count "boot" ${{ matrix.expected_boot }}
check_count "panic" ${{ matrix.expected_panic }}
if [ "$PASS" != "true" ]; then
echo "Hook verification FAILED"
exit 1
fi
echo "All hook counts verified successfully"

View File

@ -14,6 +14,7 @@ See also: [wolfBoot Product Overview](https://www.wolfssl.com/products/wolfboot/
- [**flash-OTP.md**](./flash-OTP.md) - Using One-Time Programmable (OTP) regions in flash for secure data.
- [**flash_partitions.md**](./flash_partitions.md) - Flash partitioning schemes and configuration guidance.
- [**HAL.md**](./HAL.md) - Hardware Abstraction Layer notes and porting considerations.
- [**hooks.md**](./hooks.md) - User-defined hooks for injecting custom logic into the wolfBoot boot process.
- [**keystore.md**](./keystore.md) - Keystore design, key storage, and access strategies.
- [**lib.md**](./lib.md) - Using wolfBoot as a library and linking/integration guidance.
- [**Loader.md**](./Loader.md) - Loader/secondary stage behavior and handoff to application.

91
docs/hooks.md 100644
View File

@ -0,0 +1,91 @@
# Hooks
wolfBoot provides a hooks framework that allows users to inject custom logic at
well-defined points in the boot process without modifying wolfBoot source code.
Each hook is independently enabled via its own build-time macro and compiled in
from a single user-provided source file.
Typical use cases include board-specific hardware setup not covered in the
default HAL, debug logging, watchdog management, setting a safe state on boot
failure, etc.
## Available Hooks
| Hook | Macro | Signature | When Called |
|------|-------|-----------|------------|
| Preinit | `WOLFBOOT_HOOK_LOADER_PREINIT` | `void wolfBoot_hook_preinit(void)` | Before `hal_init()` in the loader |
| Postinit | `WOLFBOOT_HOOK_LOADER_POSTINIT` | `void wolfBoot_hook_postinit(void)` | After all loader initialization, just before `wolfBoot_start()` |
| Boot | `WOLFBOOT_HOOK_BOOT` | `void wolfBoot_hook_boot(struct wolfBoot_image *boot_img)` | After `hal_prepare_boot()` but before `do_boot()` |
| Panic | `WOLFBOOT_HOOK_PANIC` | `void wolfBoot_hook_panic(void)` | Inside `wolfBoot_panic()`, before halt |
## Boot Flow
The following diagram shows where each hook fires in the wolfBoot boot sequence:
```
loader main()
|
+-- [HOOK: wolfBoot_hook_preinit()] <-- WOLFBOOT_HOOK_LOADER_PREINIT
|
+-- hal_init()
+-- other initialization...
|
+-- [HOOK: wolfBoot_hook_postinit()] <-- WOLFBOOT_HOOK_LOADER_POSTINIT
|
+-- wolfBoot_start()
|
+-- (image verification, update logic)
|
+-- hal_prepare_boot()
|
+-- [HOOK: wolfBoot_hook_boot()] <-- WOLFBOOT_HOOK_BOOT
|
+-- do_boot()
wolfBoot_panic() (called on any fatal error)
|
+-- [HOOK: wolfBoot_hook_panic()] <-- WOLFBOOT_HOOK_PANIC
|
+-- halt / infinite loop
```
## Build Configuration
First, Enable hooks in your `.config`
```makefile
# Path to a single .c file containing your hook implementations
WOLFBOOT_HOOKS_FILE=path/to/my_hooks.c
# Enable individual hooks (each is independent)
WOLFBOOT_HOOK_LOADER_PREINIT=1
WOLFBOOT_HOOK_LOADER_POSTINIT=1
WOLFBOOT_HOOK_BOOT=1
WOLFBOOT_HOOK_PANIC=1
```
Or pass them on the `make` command line:
```bash
make WOLFBOOT_HOOKS_FILE=my_hooks.c WOLFBOOT_HOOK_LOADER_PREINIT=1
```
## Notes
- `WOLFBOOT_HOOKS_FILE` tells the build system to compile and link your hooks
source file. The resulting object file is added to the wolfBoot binary.
- Hook prototypes are declared in `include/hooks.h`.
- Each hook is independently enabled. You only need to implement the hooks you
wish to enable in your build. Additionally, all hooks are optional. If no
`WOLFBOOT_HOOK_*` macros are defined, the boot flow is unchanged.
- The preinit hook runs before any hardware initialization. Be careful to only
use functionality that does not depend on `hal_init()` having been called.
- The boot hook receives a pointer to the verified `wolfBoot_image` struct,
allowing inspection of firmware version, type, and other metadata before boot.
- The panic hook fires inside `wolfBoot_panic()` just before the system halts.
Use it to set the system to a safe state, log errors, toggle GPIOs, notify
external systems, etc.
- **Note (WOLFBOOT_ARMORED):** When armored glitch protection is enabled, the
panic hook is called on a best-effort basis before the glitch-hardened halt
loop. The hook itself is not glitch-protected — a fault during the hook call
could skip it entirely. The halt loop remains hardened regardless.

View File

@ -203,7 +203,6 @@ int hal_hsm_server_cleanup(void);
#endif /* WOLFBOOT_ENABLE_WOLFHSM_SERVER */
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) || \
defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)

49
include/hooks.h 100644
View File

@ -0,0 +1,49 @@
/* hooks.h
*
* wolfBoot hooks API definitions.
*
* Hooks allow users to inject custom logic at well-defined points in the
* wolfBoot boot process. Each hook is independently enabled via its own
* build macro.
*
* Copyright (C) 2026 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
*/
#ifndef WOLFBOOT_HOOKS_H
#define WOLFBOOT_HOOKS_H
struct wolfBoot_image;
#ifdef WOLFBOOT_HOOK_LOADER_PREINIT
void wolfBoot_hook_preinit(void);
#endif
#ifdef WOLFBOOT_HOOK_LOADER_POSTINIT
void wolfBoot_hook_postinit(void);
#endif
#ifdef WOLFBOOT_HOOK_BOOT
void wolfBoot_hook_boot(struct wolfBoot_image *boot_img);
#endif
#ifdef WOLFBOOT_HOOK_PANIC
void wolfBoot_hook_panic(void);
#endif
#endif /* WOLFBOOT_HOOKS_H */

View File

@ -55,21 +55,38 @@ extern "C" {
void wolfBoot_start(void);
#include "hooks.h"
#if defined(ARCH_ARM) && defined(WOLFBOOT_ARMORED)
/* attempt to jump 5 times to self, causing loop that cannot be glitched past */
#define wolfBoot_panic() \
#ifdef WOLFBOOT_HOOK_PANIC
#define wolfBoot_panic() do { \
wolfBoot_hook_panic(); \
asm volatile("b ."); \
asm volatile("b .-2"); \
asm volatile("b .-4"); \
asm volatile("b .-6"); \
asm volatile("b .-8");
asm volatile("b .-8"); \
} while(0)
#else
#define wolfBoot_panic() do { \
asm volatile("b ."); \
asm volatile("b .-2"); \
asm volatile("b .-4"); \
asm volatile("b .-6"); \
asm volatile("b .-8"); \
} while(0)
#endif
#elif defined(ARCH_SIM)
#include <stdlib.h>
#include <stdio.h>
static inline void wolfBoot_panic(void)
{
#ifdef WOLFBOOT_HOOK_PANIC
wolfBoot_hook_panic();
#endif
fprintf(stderr, "wolfBoot: PANIC!\n");
exit('P');
}
@ -77,6 +94,9 @@ static inline void wolfBoot_panic(void)
static int wolfBoot_panicked = 0;
static inline void wolfBoot_panic(void)
{
#ifdef WOLFBOOT_HOOK_PANIC
wolfBoot_hook_panic();
#endif
fprintf(stderr, "wolfBoot: PANIC!\n");
wolfBoot_panicked++;
}
@ -84,6 +104,9 @@ static inline void wolfBoot_panic(void)
#include "printf.h"
static inline void wolfBoot_panic(void)
{
#ifdef WOLFBOOT_HOOK_PANIC
wolfBoot_hook_panic();
#endif
wolfBoot_printf("wolfBoot: PANIC!\n");
#ifdef WOLFBOOT_FSP
extern void panic(void);

View File

@ -1149,6 +1149,25 @@ ifeq ($(WOLFHSM_SERVER),1)
$(WOLFBOOT_LIB_WOLFHSM)/src/wh_server_cert.o
CFLAGS += -DWOLFHSM_CFG_CERTIFICATE_MANAGER
endif
endif
# wolfBoot hooks framework
# WOLFBOOT_HOOKS_FILE: path to a single .c file containing hook definitions
ifneq ($(WOLFBOOT_HOOKS_FILE),)
OBJS += $(patsubst %.c,%.o,$(WOLFBOOT_HOOKS_FILE))
endif
ifeq ($(WOLFBOOT_HOOK_LOADER_PREINIT),1)
CFLAGS += -DWOLFBOOT_HOOK_LOADER_PREINIT
endif
ifeq ($(WOLFBOOT_HOOK_LOADER_POSTINIT),1)
CFLAGS += -DWOLFBOOT_HOOK_LOADER_POSTINIT
endif
ifeq ($(WOLFBOOT_HOOK_BOOT),1)
CFLAGS += -DWOLFBOOT_HOOK_BOOT
endif
ifeq ($(WOLFBOOT_HOOK_PANIC),1)
CFLAGS += -DWOLFBOOT_HOOK_PANIC
endif
# Cert chain verification options

View File

@ -32,6 +32,7 @@
#include "loader.h"
#include "image.h"
#include "hal.h"
#include "hooks.h"
#include "spi_flash.h"
#ifdef UART_FLASH
#include "uart_flash.h"
@ -101,6 +102,9 @@ int main(void)
main_argc = argc;
#endif
#ifdef WOLFBOOT_HOOK_LOADER_PREINIT
wolfBoot_hook_preinit();
#endif
hal_init();
#ifdef TEST_FLASH
hal_flash_test();
@ -124,6 +128,9 @@ int main(void)
#endif
#ifdef WOLFCRYPT_SECURE_MODE
wcs_Init();
#endif
#ifdef WOLFBOOT_HOOK_LOADER_POSTINIT
wolfBoot_hook_postinit();
#endif
wolfBoot_start();

View File

@ -39,6 +39,7 @@
#include "image.h"
#include "loader.h"
#include "hal.h"
#include "hooks.h"
#include "spi_flash.h"
#include "printf.h"
#include "wolfboot/wolfboot.h"
@ -502,6 +503,9 @@ void RAMFUNCTION wolfBoot_start(void)
#endif
hal_prepare_boot();
#ifdef WOLFBOOT_HOOK_BOOT
wolfBoot_hook_boot(&os_image);
#endif
do_boot((uint32_t*)load_address
#ifdef MMU
,(uint32_t*)dts_addr

View File

@ -26,6 +26,7 @@
#include "loader.h"
#include "image.h"
#include "hal.h"
#include "hooks.h"
#include "spi_flash.h"
#include "target.h"
#include "wolfboot/wolfboot.h"
@ -1419,6 +1420,9 @@ void RAMFUNCTION wolfBoot_start(void)
hal_prepare_boot();
#ifdef WOLFBOOT_HOOK_BOOT
wolfBoot_hook_boot(&boot);
#endif
do_boot((void *)boot.fw_base);
}

View File

@ -25,6 +25,7 @@
#include "loader.h"
#include "image.h"
#include "hal.h"
#include "hooks.h"
#include "spi_flash.h"
#include "wolfboot/wolfboot.h"
#ifdef SECURE_PKCS11
@ -104,5 +105,8 @@ void RAMFUNCTION wolfBoot_start(void)
(void)hal_hsm_server_cleanup();
#endif
hal_prepare_boot();
#ifdef WOLFBOOT_HOOK_BOOT
wolfBoot_hook_boot(&fw_image);
#endif
do_boot((void *)(WOLFBOOT_PARTITION_BOOT_ADDRESS + IMAGE_HEADER_SIZE));
}

View File

@ -25,6 +25,7 @@
#include "image.h"
#include "loader.h"
#include "hal.h"
#include "hooks.h"
#include "spi_flash.h"
#include "printf.h"
#include "wolfboot/wolfboot.h"
@ -389,6 +390,9 @@ backup_on_failure:
hal_prepare_boot();
#ifdef WOLFBOOT_HOOK_BOOT
wolfBoot_hook_boot(&os_image);
#endif
#ifdef MMU
do_boot((uint32_t*)load_address,
(uint32_t*)dts_addr);