Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/test-hooks-simulator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,23 @@ jobs:
test_script: sim-sunnyday-update.sh
expected_preinit: 2
expected_postinit: 2
expected_preboot: 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_preboot: 3
expected_boot: 3
expected_panic: 0
- mechanism: panic
config: sim.config
test_script: ""
expected_preinit: 1
expected_postinit: 1
expected_preboot: 0
expected_boot: 0
expected_panic: 1

Expand Down Expand Up @@ -67,10 +70,34 @@ jobs:

void wolfBoot_hook_preinit(void) { log_hook("preinit"); }
void wolfBoot_hook_postinit(void) { log_hook("postinit"); }
void wolfBoot_hook_preboot(struct wolfBoot_image *boot_img)
{
(void)boot_img;
#if defined(MMU) || defined(WOLFBOOT_FDT)
(void)wolfBoot_get_dts_address();
#endif
log_hook("preboot");
}
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: Every update strategy defines wolfBoot_get_dts_address()
run: |
# hooks.h advertises it for any MMU/WOLFBOOT_FDT build, so each
# strategy must define it or a conforming hook fails to link.
fail=0
for f in src/update_ram.c src/update_disk.c src/update_flash.c \
src/update_flash_hwswap.c; do
# Tolerate either pointer spelling: void* x() and void *x().
if ! grep -Eq 'void[[:space:]]*\*[[:space:]]*wolfBoot_get_dts_address\(void\)' "$f"; then
echo "FAIL: $f does not define wolfBoot_get_dts_address()"
fail=1
fi
done
test "$fail" = "0" || exit 1
echo "OK: all four update strategies define the accessor"

- name: Select config
run: |
cp config/examples/${{ matrix.config }} .config
Expand All @@ -85,6 +112,7 @@ jobs:
WOLFBOOT_HOOKS_FILE=test_hooks.c \
WOLFBOOT_HOOK_LOADER_PREINIT=1 \
WOLFBOOT_HOOK_LOADER_POSTINIT=1 \
WOLFBOOT_HOOK_PREBOOT=1 \
WOLFBOOT_HOOK_BOOT=1 \
WOLFBOOT_HOOK_PANIC=1

Expand Down Expand Up @@ -146,6 +174,7 @@ jobs:

check_count "preinit" ${{ matrix.expected_preinit }}
check_count "postinit" ${{ matrix.expected_postinit }}
check_count "preboot" ${{ matrix.expected_preboot }}
check_count "boot" ${{ matrix.expected_boot }}
check_count "panic" ${{ matrix.expected_panic }}

Expand Down
6 changes: 6 additions & 0 deletions docs/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ failure, etc.
|------|-------|-----------|------------|
| 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()` |
| Preboot | `WOLFBOOT_HOOK_PREBOOT` | `void wolfBoot_hook_preboot(struct wolfBoot_image *boot_img)` | After verification, immediately **before** `hal_prepare_boot()` |
| 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 |

Expand All @@ -36,6 +37,8 @@ loader main()
|
+-- (image verification, update logic)
|
+-- [HOOK: wolfBoot_hook_preboot()] <-- WOLFBOOT_HOOK_PREBOOT
|
+-- hal_prepare_boot()
|
+-- [HOOK: wolfBoot_hook_boot()] <-- WOLFBOOT_HOOK_BOOT
Expand All @@ -60,6 +63,7 @@ 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_PREBOOT=1
WOLFBOOT_HOOK_BOOT=1
WOLFBOOT_HOOK_PANIC=1
```
Expand All @@ -82,6 +86,8 @@ make WOLFBOOT_HOOKS_FILE=my_hooks.c WOLFBOOT_HOOK_LOADER_PREINIT=1
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 preboot hook gets the same struct but fires on the other side of `hal_prepare_boot()`, where a port tears the environment down for handoff: flushing or disabling caches, disabling the MMU (`hal/cm4.c`), leaving 4-byte flash addressing (`hal/zynq.c`). Anything touching DMA, a live MMU mapping or external flash belongs in the preboot hook; use the boot hook only for the last thing before `do_boot()`.
- On `MMU` / `WOLFBOOT_FDT` builds, `wolfBoot_get_dts_address()` returns the device tree wolfBoot is about to pass to the OS, or `NULL`. It is published just before the preboot hook, so it is only meaningful from there on. Parse it with the `fdt_*` API in `include/fdt.h`; `fdt_get_alias()` and `fdt_get_reg()` turn a board label into a peripheral base address.
- 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.
Expand Down
17 changes: 17 additions & 0 deletions include/fdt.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,23 @@ int fdt_path_offset(const fdt_ctx* ctx, const char* path);
/* Direct child of `parentoff` by name. */
int fdt_subnode_offset(const fdt_ctx* ctx, int parentoff, const char* name);

/* Offset of the node containing `nodeoffset`. Walks from the root (no
* back-pointers in a flat tree), so O(tree). -FDT_ERR_NOTFOUND for root. */
int fdt_parent_offset(const fdt_ctx* ctx, int nodeoffset);

/* Resolve an /aliases entry to the node it names. Returns the node offset,
* -FDT_ERR_NOTFOUND if there is no /aliases or no such entry, or
* -FDT_ERR_BADSTRUCTURE if the value is not a NUL-terminated abs path. */
int fdt_get_alias(const fdt_ctx* ctx, const char* name);

/* Decode entry `index` of a node's "reg", honoring the parent's
* #address-cells / #size-cells (spec defaults 2 and 1). Handles the 2+2 cell
* shape fdt_getprop_address() cannot. `addr` and `size` are optional. Cell
* counts above 2 do not fit uint64_t and give -FDT_ERR_BADSTRUCTURE, as does
* a "reg" that is not a whole number of entries. */
int fdt_get_reg(const fdt_ctx* ctx, int nodeoffset, int index,
uint64_t* addr, uint64_t* size);

/* Search the whole tree from `startoff` (< 0 for the beginning) for an
* exact node-name match, at any depth - prefer fdt_path_offset() when
* the location is known. This and the two searches below report a
Expand Down
13 changes: 13 additions & 0 deletions include/hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ void wolfBoot_hook_preinit(void);
void wolfBoot_hook_postinit(void);
#endif

#ifdef WOLFBOOT_HOOK_PREBOOT
void wolfBoot_hook_preboot(struct wolfBoot_image *boot_img);
#endif

#ifdef WOLFBOOT_HOOK_BOOT
void wolfBoot_hook_boot(struct wolfBoot_image *boot_img);
#endif
Expand All @@ -50,6 +54,15 @@ void wolfBoot_hook_boot(struct wolfBoot_image *boot_img);
void wolfBoot_hook_panic(void);
#endif

#if defined(MMU) || defined(WOLFBOOT_FDT)
/* The device tree wolfBoot is about to hand to the OS, or NULL. Valid from
* wolfBoot_hook_preboot() on, once the DTB is located, relocated and (where
* a digest is bound) authenticated. Not const because fdt_open() takes a
* mutable blob: a hook that does edit the tree edits what the OS boots, and
* must stay within WOLFBOOT_DTS_MAX_SIZE. */
void* wolfBoot_get_dts_address(void);
#endif

#ifdef __cplusplus
}
#endif
Expand Down
4 changes: 4 additions & 0 deletions options.mk
Original file line number Diff line number Diff line change
Expand Up @@ -1700,6 +1700,10 @@ ifeq ($(WOLFBOOT_HOOK_LOADER_POSTINIT),1)
CFLAGS += -DWOLFBOOT_HOOK_LOADER_POSTINIT
WOLFBOOT_HOOKS_ENABLED := 1
endif
ifeq ($(WOLFBOOT_HOOK_PREBOOT),1)
CFLAGS += -DWOLFBOOT_HOOK_PREBOOT
WOLFBOOT_HOOKS_ENABLED := 1
endif
ifeq ($(WOLFBOOT_HOOK_BOOT),1)
CFLAGS += -DWOLFBOOT_HOOK_BOOT
WOLFBOOT_HOOKS_ENABLED := 1
Expand Down
128 changes: 128 additions & 0 deletions src/fdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,134 @@ int fdt_path_offset(const fdt_ctx* ctx, const char* path)
return off;
}

int fdt_parent_offset(const fdt_ctx* ctx, int nodeoffset)
{
int off, depth, target_depth, parent;

if (!fdt_ctx_ok(ctx) || nodeoffset < 0) {
return -FDT_ERR_BADARG;
}
if (nodeoffset == 0) {
return -FDT_ERR_NOTFOUND; /* the root node has no parent */
}
/* No back-pointers in a flat tree: walk down from the root. Pass one
* finds the target's depth (root 0, children 1). */
target_depth = -1;
depth = 0;
for (off = fdt_next_node(ctx, 0, &depth); off >= 0;
off = fdt_next_node(ctx, off, &depth)) {
if (off == nodeoffset) {
target_depth = depth;
break;
}
}
if (target_depth < 1) {
return -FDT_ERR_NOTFOUND;
}
if (target_depth == 1) {
return 0; /* direct child of the root */
}
/* Pass two: the last node seen one level shallower. */
parent = -FDT_ERR_NOTFOUND;
depth = 0;
for (off = fdt_next_node(ctx, 0, &depth); off >= 0;
off = fdt_next_node(ctx, off, &depth)) {
if (off == nodeoffset) {
return parent;
}
if (depth == target_depth - 1) {
parent = off;
}
}
return -FDT_ERR_NOTFOUND;
}

int fdt_get_alias(const fdt_ctx* ctx, const char* name)
{
const char* path;
int aliases, len = 0;

if (!fdt_ctx_ok(ctx) || name == NULL) {
return -FDT_ERR_BADARG;
}
aliases = fdt_subnode_offset(ctx, 0, "aliases");
if (aliases < 0) {
return aliases;
}
path = (const char*)fdt_getprop(ctx, aliases, name, &len);
if (path == NULL || len <= 1) {
return -FDT_ERR_NOTFOUND;
}
/* Must be a NUL-terminated absolute path. */
if (path[len - 1] != '\0' || path[0] != '/') {
return -FDT_ERR_BADSTRUCTURE;
}
return fdt_path_offset(ctx, path);
}

int fdt_get_reg(const fdt_ctx* ctx, int nodeoffset, int index,
uint64_t* addr, uint64_t* size)
{
const uint8_t* reg;
const uint8_t* cell;
const void* val;
/* Devicetree spec defaults when a parent omits the properties. */
uint32_t ac = 2, sc = 1;
uint32_t entry;
int parent, len = 0;

if (!fdt_ctx_ok(ctx) || index < 0) {
return -FDT_ERR_BADARG;
}
parent = fdt_parent_offset(ctx, nodeoffset);
if (parent >= 0) {
val = fdt_getprop(ctx, parent, "#address-cells", &len);
if (val != NULL && len == 4) {
ac = fdt_rd32(val);
}
len = 0;
val = fdt_getprop(ctx, parent, "#size-cells", &len);
if (val != NULL && len == 4) {
sc = fdt_rd32(val);
}
}
/* Only 1 or 2 cells fit uint64_t. sc may be 0; ac may not. */
if (ac < 1U || ac > 2U || sc > 2U) {
return -FDT_ERR_BADSTRUCTURE;
}
entry = (ac + sc) * 4U;

len = 0;
reg = (const uint8_t*)fdt_getprop(ctx, nodeoffset, "reg", &len);
if (reg == NULL || len <= 0) {
return -FDT_ERR_NOTFOUND;
}
/* A whole number of entries, or the property is malformed: trailing
* cells would otherwise be ignored silently. */
if (((uint32_t)len % entry) != 0U) {
return -FDT_ERR_BADSTRUCTURE;
}
/* By division: (index + 1) * entry is 32-bit and a large index wraps. */
if ((uint32_t)index >= ((uint32_t)len / entry)) {
return -FDT_ERR_NOTFOUND;
}
Comment on lines +1005 to +1007
cell = reg + ((uint32_t)index * entry);

if (addr != NULL) {
*addr = (ac == 2U) ? fdt_rd64u(cell) : (uint64_t)fdt_rd32(cell);
}
if (size != NULL) {
cell += ac * 4U;
if (sc == 0U) {
*size = 0;
}
else {
*size = (sc == 2U) ? fdt_rd64u(cell) : (uint64_t)fdt_rd32(cell);
}
}
return 0;
}

/* Shared walk for the tree-wide searches. `propname` NULL matches the
* node name; otherwise the named property must equal `needle` whole. The
* compatible search keeps its own loop so a target that never does one
Expand Down
21 changes: 21 additions & 0 deletions src/update_disk.c
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,17 @@ static int slot_prepare(struct boot_slot *s, int part, const char *label,
* the OS image from disk partitions. It then verifies the integrity and
* authenticity of the loaded image before initiating the boot.
*/
#if defined(MMU) || defined(WOLFBOOT_FDT)
/* File scope so wolfBoot_get_dts_address() can hand it to a hook; exactly
* one update strategy object is linked per build. */
static void* wolfboot_dts_addr = NULL;

void* wolfBoot_get_dts_address(void)
{
return wolfboot_dts_addr;
}
#endif

void RAMFUNCTION wolfBoot_start(void)
{
uint8_t p_hdr[IMAGE_HEADER_SIZE] XALIGNED_STACK(16);
Expand Down Expand Up @@ -964,6 +975,16 @@ void RAMFUNCTION wolfBoot_start(void)
/* Deferred from just after verification (see NOTE above): close the boot
* disk now that all env / DTB reads and writes are done, before handoff. */
disk_close(BOOT_DISK);
#if defined(MMU) || defined(WOLFBOOT_FDT)
/* After every relocation/fallback/digest check, so a hook never sees
* an unvalidated blob. */
wolfboot_dts_addr = (void*)dts_addr;
#endif
#ifdef WOLFBOOT_HOOK_PREBOOT
/* Before hal_prepare_boot(), so a hook still has the MMU and caches as
* wolfBoot set them up. */
wolfBoot_hook_preboot(&os_image);
#endif
hal_prepare_boot();

#ifdef WOLFBOOT_HOOK_BOOT
Expand Down
14 changes: 14 additions & 0 deletions src/update_flash.c
Original file line number Diff line number Diff line change
Expand Up @@ -1535,6 +1535,15 @@ int wolfBoot_unlock_disk(void)
#ifdef __CCRX__
#pragma section FRAM
#endif
#if defined(MMU) || defined(WOLFBOOT_FDT)
/* No device tree here, but hooks.h advertises the accessor for every
* MMU/WOLFBOOT_FDT build, so a conforming hook must still link. */
void* wolfBoot_get_dts_address(void)
{
return NULL;
}
#endif

void RAMFUNCTION wolfBoot_start(void)
{
int bootRet;
Expand Down Expand Up @@ -1741,6 +1750,11 @@ void RAMFUNCTION wolfBoot_start(void)
wolfBoot_printf("Error protecting bootloader flash region\n");
wolfBoot_panic();
}
#endif
#ifdef WOLFBOOT_HOOK_PREBOOT
/* Before hal_prepare_boot(), so a hook still has the MMU and caches as
* wolfBoot set them up. */
wolfBoot_hook_preboot(&boot);
#endif
hal_prepare_boot();

Expand Down
14 changes: 14 additions & 0 deletions src/update_flash_hwswap.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ static inline void boot_panic(void)
;
}

#if defined(MMU) || defined(WOLFBOOT_FDT)
/* No device tree here, but hooks.h advertises the accessor for every
* MMU/WOLFBOOT_FDT build, so a conforming hook must still link. */
void* wolfBoot_get_dts_address(void)
{
return NULL;
}
#endif

void RAMFUNCTION wolfBoot_start(void)
{
int active;
Expand Down Expand Up @@ -133,6 +142,11 @@ void RAMFUNCTION wolfBoot_start(void)
#ifndef TZEN
if (hal_flash_protect(WOLFBOOT_ORIGIN, BOOTLOADER_PARTITION_SIZE) < 0)
boot_panic();
#endif
#ifdef WOLFBOOT_HOOK_PREBOOT
/* Before hal_prepare_boot(), so a hook still has the MMU and caches as
* wolfBoot set them up. */
wolfBoot_hook_preboot(&fw_image);
#endif
hal_prepare_boot();
#ifdef WOLFBOOT_HOOK_BOOT
Expand Down
Loading
Loading