Skip to content

Commit 2924646

Browse files
bigbrettdanielinux
authored andcommitted
Add generic hook framework with provision for pre-init, post-init, and
boot hooks
1 parent 1c18481 commit 2924646

File tree

12 files changed

+396
-3
lines changed

12 files changed

+396
-3
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
name: hook simulator tests
2+
3+
on:
4+
push:
5+
branches: [ 'master', 'main', 'release/**' ]
6+
pull_request:
7+
branches: [ '*' ]
8+
9+
jobs:
10+
hooks_test:
11+
runs-on: ubuntu-latest
12+
timeout-minutes: 30
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
include:
17+
- mechanism: flash
18+
config: sim.config
19+
test_script: sim-sunnyday-update.sh
20+
expected_preinit: 2
21+
expected_postinit: 2
22+
expected_boot: 2
23+
expected_panic: 0
24+
- mechanism: dualbank
25+
config: sim-dualbank.config
26+
test_script: sim-dualbank-swap-update.sh
27+
expected_preinit: 3
28+
expected_postinit: 3
29+
expected_boot: 3
30+
expected_panic: 0
31+
- mechanism: panic
32+
config: sim.config
33+
test_script: ""
34+
expected_preinit: 1
35+
expected_postinit: 1
36+
expected_boot: 0
37+
expected_panic: 1
38+
39+
name: hooks (${{ matrix.mechanism }})
40+
41+
steps:
42+
- uses: actions/checkout@v4
43+
with:
44+
submodules: true
45+
46+
- name: Workaround for sources.list
47+
run: |
48+
set -euxo pipefail
49+
50+
apt-cache policy
51+
grep -RInE '^(deb|Types|URIs)' /etc/apt || true
52+
53+
shopt -s nullglob
54+
55+
echo "Replace sources.list (legacy)"
56+
sudo sed -i \
57+
-e "s|https\?://azure\.archive\.ubuntu\.com/ubuntu/?|http://mirror.arizona.edu/ubuntu/|g" \
58+
/etc/apt/sources.list || true
59+
60+
echo "Replace sources.list.d/*.list (legacy)"
61+
for f in /etc/apt/sources.list.d/*.list; do
62+
sudo sed -i \
63+
-e "s|https\?://azure\.archive\.ubuntu\.com/ubuntu/?|http://mirror.arizona.edu/ubuntu/|g" \
64+
"$f"
65+
done
66+
67+
echo "Replace sources.list.d/*.sources (deb822)"
68+
for f in /etc/apt/sources.list.d/*.sources; do
69+
sudo sed -i \
70+
-e "s|https\?://azure\.archive\.ubuntu\.com/ubuntu/?|http://mirror.arizona.edu/ubuntu/|g" \
71+
-e "s|https\?://azure\.archive\.ubuntu\.com|http://mirror.arizona.edu|g" \
72+
"$f"
73+
done
74+
75+
echo "Fix /etc/apt/apt-mirrors.txt (used by URIs: mirror+file:...)"
76+
if grep -qE '^[[:space:]]*https?://azure\.archive\.ubuntu\.com/ubuntu/?' /etc/apt/apt-mirrors.txt; then
77+
sudo sed -i 's|https\?://azure\.archive\.ubuntu\.com/ubuntu/|http://mirror.arizona.edu/ubuntu/|g' /etc/apt/apt-mirrors.txt
78+
fi
79+
80+
grep -RIn "azure.archive.ubuntu.com" /etc/apt || true
81+
grep -RInE '^(deb|Types|URIs)' /etc/apt || true
82+
echo "--- apt-mirrors.txt ---"
83+
cat /etc/apt/apt-mirrors.txt || true
84+
85+
- name: Update repository
86+
run: sudo apt-get update -o Acquire::Retries=3
87+
88+
- name: Create test_hooks.c
89+
run: |
90+
cat > test_hooks.c << 'EOF'
91+
#include <stdio.h>
92+
#include "hooks.h"
93+
94+
#define HOOK_LOG_FILE "/tmp/wolfboot_hooks.log"
95+
96+
static void log_hook(const char *name)
97+
{
98+
FILE *f = fopen(HOOK_LOG_FILE, "a");
99+
if (f) {
100+
fprintf(f, "%s\n", name);
101+
fclose(f);
102+
}
103+
}
104+
105+
void wolfBoot_hook_preinit(void) { log_hook("preinit"); }
106+
void wolfBoot_hook_postinit(void) { log_hook("postinit"); }
107+
void wolfBoot_hook_boot(struct wolfBoot_image *boot_img) { (void)boot_img; log_hook("boot"); }
108+
void wolfBoot_hook_panic(void) { log_hook("panic"); }
109+
EOF
110+
111+
- name: Select config
112+
run: |
113+
cp config/examples/${{ matrix.config }} .config
114+
115+
- name: Build tools
116+
run: |
117+
make -C tools/keytools && make -C tools/bin-assemble
118+
119+
- name: Build wolfboot.elf with hooks
120+
run: |
121+
make clean && make test-sim-internal-flash-with-update \
122+
WOLFBOOT_HOOKS_FILE=test_hooks.c \
123+
WOLFBOOT_HOOK_LOADER_PREINIT=1 \
124+
WOLFBOOT_HOOK_LOADER_POSTINIT=1 \
125+
WOLFBOOT_HOOK_BOOT=1 \
126+
WOLFBOOT_HOOK_PANIC=1
127+
128+
- name: Clear hook log
129+
run: |
130+
rm -f /tmp/wolfboot_hooks.log
131+
132+
- name: Corrupt partitions and run panic test
133+
if: matrix.mechanism == 'panic'
134+
run: |
135+
# Zero out boot partition header (offset 0x80000) to invalidate image
136+
printf '\x00\x00\x00\x00\x00\x00\x00\x00' | \
137+
dd of=internal_flash.dd bs=1 seek=$((0x80000)) conv=notrunc
138+
# Zero out update partition header (offset 0x100000) to invalidate image
139+
printf '\x00\x00\x00\x00\x00\x00\x00\x00' | \
140+
dd of=internal_flash.dd bs=1 seek=$((0x100000)) conv=notrunc
141+
# wolfBoot_panic() calls exit('P') = exit(80) on ARCH_SIM
142+
./wolfboot.elf get_version 2>&1 || EXIT_CODE=$?
143+
echo "wolfboot.elf exited with code ${EXIT_CODE:-0}"
144+
if [ "${EXIT_CODE:-0}" -ne 80 ]; then
145+
echo "FAIL: expected exit code 80 (panic), got ${EXIT_CODE:-0}"
146+
exit 1
147+
fi
148+
echo "OK: wolfboot panicked as expected"
149+
150+
- name: Run ${{ matrix.mechanism }} update test
151+
if: matrix.mechanism != 'panic'
152+
run: |
153+
tools/scripts/${{ matrix.test_script }}
154+
155+
- name: Display hook log
156+
if: always()
157+
run: |
158+
echo "=== Hook log contents ==="
159+
cat /tmp/wolfboot_hooks.log || echo "(no log file found)"
160+
161+
- name: Verify hook call counts
162+
run: |
163+
LOG="/tmp/wolfboot_hooks.log"
164+
PASS=true
165+
166+
check_count() {
167+
local hook_name="$1"
168+
local expected="$2"
169+
local actual
170+
actual=$(grep -c "^${hook_name}$" "$LOG" 2>/dev/null || echo 0)
171+
if [ "$actual" -ne "$expected" ]; then
172+
echo "FAIL: ${hook_name} expected=${expected} actual=${actual}"
173+
PASS=false
174+
else
175+
echo "OK: ${hook_name} expected=${expected} actual=${actual}"
176+
fi
177+
}
178+
179+
check_count "preinit" ${{ matrix.expected_preinit }}
180+
check_count "postinit" ${{ matrix.expected_postinit }}
181+
check_count "boot" ${{ matrix.expected_boot }}
182+
check_count "panic" ${{ matrix.expected_panic }}
183+
184+
if [ "$PASS" != "true" ]; then
185+
echo "Hook verification FAILED"
186+
exit 1
187+
fi
188+
echo "All hook counts verified successfully"

docs/README.md

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

docs/hooks.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Hooks
2+
3+
wolfBoot provides a hooks framework that allows users to inject custom logic at
4+
well-defined points in the boot process without modifying wolfBoot source code.
5+
Each hook is independently enabled via its own build-time macro and compiled in
6+
from a single user-provided source file.
7+
8+
Typical use cases include board-specific hardware setup not covered in the
9+
default HAL, debug logging, watchdog management, setting a safe state on boot
10+
failure, etc.
11+
12+
## Available Hooks
13+
14+
| Hook | Macro | Signature | When Called |
15+
|------|-------|-----------|------------|
16+
| Preinit | `WOLFBOOT_HOOK_LOADER_PREINIT` | `void wolfBoot_hook_preinit(void)` | Before `hal_init()` in the loader |
17+
| Postinit | `WOLFBOOT_HOOK_LOADER_POSTINIT` | `void wolfBoot_hook_postinit(void)` | After all loader initialization, just before `wolfBoot_start()` |
18+
| Boot | `WOLFBOOT_HOOK_BOOT` | `void wolfBoot_hook_boot(struct wolfBoot_image *boot_img)` | After `hal_prepare_boot()` but before `do_boot()` |
19+
| Panic | `WOLFBOOT_HOOK_PANIC` | `void wolfBoot_hook_panic(void)` | Inside `wolfBoot_panic()`, before halt |
20+
21+
## Boot Flow
22+
23+
The following diagram shows where each hook fires in the wolfBoot boot sequence:
24+
25+
```
26+
loader main()
27+
|
28+
+-- [HOOK: wolfBoot_hook_preinit()] <-- WOLFBOOT_HOOK_LOADER_PREINIT
29+
|
30+
+-- hal_init()
31+
+-- other initialization...
32+
|
33+
+-- [HOOK: wolfBoot_hook_postinit()] <-- WOLFBOOT_HOOK_LOADER_POSTINIT
34+
|
35+
+-- wolfBoot_start()
36+
|
37+
+-- (image verification, update logic)
38+
|
39+
+-- hal_prepare_boot()
40+
|
41+
+-- [HOOK: wolfBoot_hook_boot()] <-- WOLFBOOT_HOOK_BOOT
42+
|
43+
+-- do_boot()
44+
45+
wolfBoot_panic() (called on any fatal error)
46+
|
47+
+-- [HOOK: wolfBoot_hook_panic()] <-- WOLFBOOT_HOOK_PANIC
48+
|
49+
+-- halt / infinite loop
50+
```
51+
52+
## Build Configuration
53+
54+
First, Enable hooks in your `.config`
55+
56+
```makefile
57+
# Path to a single .c file containing your hook implementations
58+
WOLFBOOT_HOOKS_FILE=path/to/my_hooks.c
59+
60+
# Enable individual hooks (each is independent)
61+
WOLFBOOT_HOOK_LOADER_PREINIT=1
62+
WOLFBOOT_HOOK_LOADER_POSTINIT=1
63+
WOLFBOOT_HOOK_BOOT=1
64+
WOLFBOOT_HOOK_PANIC=1
65+
```
66+
67+
Or pass them on the `make` command line:
68+
69+
```bash
70+
make WOLFBOOT_HOOKS_FILE=my_hooks.c WOLFBOOT_HOOK_LOADER_PREINIT=1
71+
```
72+
73+
## Notes
74+
75+
- `WOLFBOOT_HOOKS_FILE` tells the build system to compile and link your hooks
76+
source file. The resulting object file is added to the wolfBoot binary.
77+
- Hook prototypes are declared in `include/hooks.h`.
78+
- Each hook is independently enabled. You only need to implement the hooks you
79+
wish to enable in your build. Additionally, all hooks are optional. If no
80+
`WOLFBOOT_HOOK_*` macros are defined, the boot flow is unchanged.
81+
- The preinit hook runs before any hardware initialization. Be careful to only
82+
use functionality that does not depend on `hal_init()` having been called.
83+
- The boot hook receives a pointer to the verified `wolfBoot_image` struct,
84+
allowing inspection of firmware version, type, and other metadata before boot.
85+
- The panic hook fires inside `wolfBoot_panic()` just before the system halts.
86+
Use it to set the system to a safe state, log errors, toggle GPIOs, notify
87+
external systems, etc.
88+
- **Note (WOLFBOOT_ARMORED):** When armored glitch protection is enabled, the
89+
panic hook is called on a best-effort basis before the glitch-hardened halt
90+
loop. The hook itself is not glitch-protected — a fault during the hook call
91+
could skip it entirely. The halt loop remains hardened regardless.

include/hal.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ int hal_hsm_server_cleanup(void);
203203

204204
#endif /* WOLFBOOT_ENABLE_WOLFHSM_SERVER */
205205

206-
207206
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) || \
208207
defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
209208

include/hooks.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/* hooks.h
2+
*
3+
* wolfBoot hooks API definitions.
4+
*
5+
* Hooks allow users to inject custom logic at well-defined points in the
6+
* wolfBoot boot process. Each hook is independently enabled via its own
7+
* build macro.
8+
*
9+
* Copyright (C) 2026 wolfSSL Inc.
10+
*
11+
* This file is part of wolfBoot.
12+
*
13+
* wolfBoot is free software; you can redistribute it and/or modify
14+
* it under the terms of the GNU General Public License as published by
15+
* the Free Software Foundation; either version 3 of the License, or
16+
* (at your option) any later version.
17+
*
18+
* wolfBoot is distributed in the hope that it will be useful,
19+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21+
* GNU General Public License for more details.
22+
*
23+
* You should have received a copy of the GNU General Public License
24+
* along with this program; if not, write to the Free Software
25+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
26+
*/
27+
28+
#ifndef WOLFBOOT_HOOKS_H
29+
#define WOLFBOOT_HOOKS_H
30+
31+
struct wolfBoot_image;
32+
33+
#ifdef WOLFBOOT_HOOK_LOADER_PREINIT
34+
void wolfBoot_hook_preinit(void);
35+
#endif
36+
37+
#ifdef WOLFBOOT_HOOK_LOADER_POSTINIT
38+
void wolfBoot_hook_postinit(void);
39+
#endif
40+
41+
#ifdef WOLFBOOT_HOOK_BOOT
42+
void wolfBoot_hook_boot(struct wolfBoot_image *boot_img);
43+
#endif
44+
45+
#ifdef WOLFBOOT_HOOK_PANIC
46+
void wolfBoot_hook_panic(void);
47+
#endif
48+
49+
#endif /* WOLFBOOT_HOOKS_H */

0 commit comments

Comments
 (0)