-
Notifications
You must be signed in to change notification settings - Fork 147
Add support for user "hooks" #709
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| /* 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 | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| 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 | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif | ||
|
|
||
| #endif /* WOLFBOOT_HOOKS_H */ | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.