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
23 changes: 23 additions & 0 deletions .github/workflows/cross-compile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,29 @@ jobs:
fi
test "$lto_flash" -lt "$no_lto_flash"
echo "LTO flash: $no_lto_flash -> $lto_flash bytes"
- name: Verify an unimplemented isolation level stops the build
run: |
for target in stm32h563 mimxrt700; do
for level in 1 2; do
if make TARGET=$target WT_ISOLATION_LEVEL=$level \
BUILD_DIR=build-level-$level secure-image \
TOOLPREFIX=arm-none-eabi- 2> level.err; then
echo "FAIL: $target built at isolation level $level" >&2
exit 1
fi
grep -q 'only isolation level 3 is implemented' level.err
test ! -e build-level-$level/wolftrust.elf
done
done
- name: Verify an unreadable level 3 layout stops the link
run: |
if make BUILD_DIR=build-no-layout WT_L3_BAND_ARGS= secure-image \
TOOLPREFIX=arm-none-eabi- 2> no-layout.err; then
echo 'FAIL: the link ran without the level 3 band layout' >&2
exit 1
fi
grep -q 'cannot read the level 3 bands' no-layout.err
test ! -e build-no-layout/wolftrust.elf
- name: Verify a rejected secure image cannot be reused
run: |
reject_flag='-Wl,--defsym=malloc=0x0C060800'
Expand Down
12 changes: 7 additions & 5 deletions docs/MIMXRT700-Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,21 @@ with the firmware-update backend.

### Secure runtime (wolfTrust)

The port reuses `src/arch/armv8m/` and `src/arch/common/` unchanged and adds
The port reuses `src/arch/armv8m/`, `src/arch/common/`, and the shared
Armv8-M isolation level 3 layer in `port/common/armv8m/` unchanged, and adds
only `port/mimxrt700/` and one build fragment:

| File | Responsibility |
| --- | --- |
| `memory_map.h` | The bit-28 Secure-alias map: XSPI0 NOR windows, Secure and guest RAM, the boot-handoff address, and the per-partition RAM bands. |
| `memory_map.h` | The bit-28 Secure-alias map: XSPI0 NOR windows, Secure and guest RAM, the boot-handoff address, and the RAM code band; the per-partition bands are offsets from `WT_RAM_S_BASE` in `port/common/armv8m/l3_layout.h`. |
| `l3_port.h` | The level 3 layer's one board input: the TRNG as the Secure peripheral only the SPM drives. |
| `mimxrt798_regs.h` | Register bases for CLKCTL, SYSCON, IOPCTL, LPUART0, XSPI0, TRNG, the AHBSC fabric controllers, and their GLIKEY unlock state machines. |
| `platform_mimxrt700.c` | Every `wt_platform_*` operation: clocks, the SAU table, the Secure MPU whitelist, enabling AHBSC secure checking behind its GLIKEY unlock, staging of the RAM code band, the boot-handoff region, fault logging, panic, and reset. |
| `partitions.c` | The guest and capability tables, the profile capability bitmap (the fabric filter is claimed on the per-dispatch SAU window, not on the AHBSC SRAM rules), and the pinned guest-measurement slot. |
| `xspi_nor.c/.h` | The XSPI0 octal-DTR NOR program and erase driver: bounded target-group IP commands on its own LUT sequences, run from the RAM code band with interrupts masked, flushing the XSPI read cache afterwards. |
| `hsm_flash.c/.h` | The `port_nvm.h` backend for the wolfHSM store: reads through the Secure XIP alias, program and erase through `xspi_nor.c`. |
| `rng_entropy.c` | The `CUSTOM_RAND_GENERATE_BLOCK` entropy source over the on-die TRNG, preserving the unprivileged-to-privileged trap. |
| `secure.ld` | The port's own Secure linker script, including the RAM code band: the SG veneers, the `cmse_nonsecure_entry` bodies, and the NOR driver, loaded from flash and executed from SRAM. |
| `secure.ld` | The port's own Secure linker script, which INCLUDEs the shared level 3 regions, band sections, and layout ASSERTs and adds the RAM code band: the SG veneers, the `cmse_nonsecure_entry` bodies, and the NOR driver, loaded from flash and executed from SRAM. |
| `manifest.json` | The service partitions (attestation, HSM, vault, ITS, PS, FWU) and their resources. |
| `mk/target-mimxrt700.mk` | `WT_CPU`, the flash and RAM defaults, the linker `--defsym` set, and the source lists. |

Expand Down Expand Up @@ -205,8 +207,8 @@ under M33MU. The `romsmoke` scenario proves the BootROM XIP path; the
`positive` scenario is the wolfTrust chain; `ahbscneg` adds the guest
isolation negative. The emulator runner then carries the STM32H563 scenario
matrix (restart and launch refusal, SP fault recovery, the Secure-verdict
negatives, the PSA guest's lifecycle and negatives, and Arm's conformance
suites), listed in [Testing](Testing.md).
negatives, every isolation level 3 negative, the PSA guest's lifecycle and
negatives, and Arm's conformance suites), listed in [Testing](Testing.md).

The full chain build and flash performs:

Expand Down
60 changes: 56 additions & 4 deletions docs/Porting.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ must not claim security properties until they are tested on that target.
| Public and internal contracts | `include/psa/` and `include/wolftrust/` | PSA APIs, SPM types, the two port contracts (`arch.h`, `platform.h`), manifests, and service interfaces |
| Architecture-neutral gate | `src/arch/common/` | Secure Partition gate dispatch, fault recovery, scheduler, the SP-side PSA API, and the NS FF-M gateway bodies, written once over the `wolftrust/arch.h` primitives and linked by every architecture |
| Architecture | `src/arch/<arch>/` and `include/wolftrust/arch/<arch>/` | Every `wt_arch_*` operation: reset entry, guest context save/restore, exception entry and return, the secure tick, interrupt masking and routing, memory-protection programming, the SP trap and its decoder, NS range checks, and the NS entry mechanism (Armv8-M: CMSE veneers) |
| Shared port layer | `port/common/<arch>/` | The isolation level 3 secure RAM layout, its linker fragments and the level 3 `wt_platform_*` hooks, written once per architecture and linked by every port of it |
| SoC and board | `port/<soc>/` | Every `wt_platform_*` operation plus the SoC facts: clocks, fabric-level TrustZone filter windows, the memory-protection region tables, UART, flash, entropy, reset, the memory map, guest tables, and the manifest |
| Build | `mk/common.mk`, `mk/arch-<arch>.mk`, `mk/target-<soc>.mk` | Shared rules; toolchain and architecture sources; SoC sources, placement, and image checks |
| Guest integration | `tests/firmware/` or an application repository | Application-domain linker layout, PSA client shim, architecture-specific client boundary, and OS wiring; Armv8-M uses a CMSE import library |
Expand Down Expand Up @@ -54,8 +55,10 @@ handlers, the virtual SysTick, NVIC routing, table-driven SAU and MPU
programming, the SVC trap decoder, the CMSE checks, and the five NS veneers),
`src/arch/common/` supplies the architecture-neutral gate, scheduler, SP-side
PSA API and NS gateway bodies on top of them, and
`port/stm32h563/platform_stm32h563.c` implements the `wt_platform_*`
operations together with the SoC's SAU and MPU region tables.
`port/common/armv8m/` supplies the isolation level 3 layout and hooks every
Armv8-M port shares, and `port/stm32h563/platform_stm32h563.c` implements the
remaining `wt_platform_*` operations together with the SoC's SAU and MPU
region tables.

A port declares what its hardware can do through the capability bits in
`include/wolftrust/partition.h`; the core refuses a manifest that assumes a
Expand All @@ -80,6 +83,51 @@ guard rejects that.
Do not return unconditional success for a missing security mechanism. Report
the capability accurately and reject a manifest that requires more.

### Isolation level 3

A new port must implement isolation level 3 unless it specifically targets
level 1 or 2. `WT_ISOLATION_LEVEL` (default 3) selects the level the secure
image implements and gates the shared level 3 layer; only level 3 exists
today, so any other value stops the build.

Every port of an architecture gets level 3 from `port/common/<arch>/`
instead of writing it again. For Armv8-M that layer provides:

- `l3_layout.h`: the per-partition keystore bands, partition stacks, the
conformance data window and the VNET band, all placed at fixed offsets
from `WT_RAM_S_BASE`;
- `secure_l3_memory.ld`, `secure_l3_symbols.ld`, `secure_l3_bands.ld` and
`secure_l3_tail.ld`: the matching linker regions, stack symbols, band
output sections and layout ASSERTs; and
- `platform_l3.c`: the boot-handoff region, the shared image windows, the
SPM-private RAM, the privileged-stack check, the peripheral table, the
conformance grants and the test-build probe addresses.

A port supplies:

- in `memory_map.h`, a literal `WT_RAM_S_BASE` and `WT_RAM_S_SIZE` (at
least the 480 KiB the layout uses), then `#include
"../common/armv8m/l3_layout.h"`; define `WT_RAMFUNC_BASE` and
`WT_RAMFUNC_SIZE` if the SPM runs code from RAM;
- `l3_port.h`, naming a Secure peripheral only the SPM drives as
`WT_L3_SPM_PERIPHERAL_BASE`;
- a `secure.ld` that INCLUDEs the four fragments and keeps only its board
sections (vectors, NSC veneers, any RAM code band, text, read-only data,
the conformance sections, .data and .bss);
- attribution read-back for every Secure peripheral the SPM uses, panicking
at boot on a mismatch; and
- owner lines in `tools/secure_owners.txt` for every object it adds.

The build reads `WT_RAM_S_BASE` for the linker and derives the post-link band
check from `memory_map.h`, so no band address is written twice.

Level 3 is claimed for a port only when its full M33MU tier runs every
scenario in `L3_REQUIRED` (`tests/target/lib/scenario_matrix.py`). A scenario
a port cannot run yet goes in that port's `l3_exempt` map with the open issue
that tracks it; `scenario_matrix.py --selftest`, run in CI, fails on a
missing scenario, an exemption without a reason, or an exemption for a
scenario the port already runs.

### Guest and capability tables

Implement the declarations in `include/wolftrust/partition.h`:
Expand Down Expand Up @@ -180,7 +228,8 @@ worked examples above give a concrete map for each board.
the architecture cannot reuse an existing implementation; implement every
`wt_arch_*` operation there and leave `src/arch/common/` untouched.
2. Create `port/<soc>/` with the platform, flash, entropy, board,
memory-map, protection-region-table, partition-table, and manifest files.
memory-map, protection-region-table, partition-table, and manifest files,
building on `port/common/<arch>/` for isolation level 3.
3. Add `mk/arch-<arch>.mk` (if new) and `mk/target-<soc>.mk`; the root
Makefile selects them from `ARCH` and `TARGET`, and `mk/common.mk` needs
no change.
Expand All @@ -205,7 +254,8 @@ worked examples above give a concrete map for each board.
core code, and `wt_arch_*` definitions inside a port).
- Run `tools/check-port-only-diff.sh <base> <arch> <soc>` on a port change
and confirm it touches nothing outside `src/arch/common/`,
`src/arch/<arch>/`, `include/wolftrust/arch/<arch>/`, `port/<soc>/`, the
`src/arch/<arch>/`, `include/wolftrust/arch/<arch>/`,
`port/common/<arch>/`, `port/<soc>/`, the
two build fragments, tests, docs, and workflows.
- Run `tools/check-docs-no-internal-links.sh`; `docs/` is published to the
wiki and must not reference internal ledgers or developer paths.
Expand All @@ -226,6 +276,8 @@ worked examples above give a concrete map for each board.
guest disables its own Non-secure MPU and stores into another guest's RAM,
and the store must not land. Programming the fabric rules is not evidence
that they govern those addresses.
- Run `python3 tests/target/lib/scenario_matrix.py --selftest` and run every
`L3_REQUIRED` scenario the port does not exempt.
- Test invalid manifests, memory overlap, pointer ranges, stale handles,
cross-owner access, and unsupported capabilities.
- Run authenticated boot, guest tamper, rollback, restart, Secure Partition
Expand Down
17 changes: 17 additions & 0 deletions docs/Testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,23 @@ READY. `hsmattackneg` (hsm engine only, it drives the raw wolfHSM client
wire) proves a forged client id cannot reach the IAK and an NVM-group packet
never reaches the server.

The isolation level 3 negatives run as they do on the STM32H563, through the
same checks in `tests/target/lib/scenario.sh`; the band, partition stack and
SPM peripheral addresses they match come from `port/mimxrt700/memory_map.h`
through `tools/l3_layout_args.py`. `deputyneg`, `hsmpinneg`, `hsmfaultneg`,
`bandneg1` to `bandneg6`, `restartneg1` to `restartneg3`, `periphspneg`,
`sealneg`, `sealpivotneg`, and `svcneg` run the PSA guest in both windows,
and guest 0 ends the run on its breakpoint once its lifecycle is done.
`manifestneg2`, `manifestneg3`, and `sealhaltneg` end on their Secure verdict
breakpoint, and `mspovfneg` and `xnneg` at the SPM fault. The MIMXRT700 flash
context holds no geometry a partition can write, so `deputyneg` forges
out-of-range offsets and misaligned lengths instead, and each must be refused
against the const flash configuration. `periphneg` is the CPU leg of the
STM32H563 scenario: guest 0 switches off its own MPU and reads the SPM's TRNG
through the Non-secure alias, the SAU refuses the read on every launch, and
guest 0 is quarantined while guest 1 runs on. The M33MU RT700 model has no
DMA, so the Non-secure eDMA copies out of Secure memory are left to the EVK.

The conformance scenarios are the same drop-in proof the STM32H563 gives:
`confboot` hosts Arm's unmodified psa-arch-tests FF-M IPC suite in the PSA
guest against the conformance Secure image (`WT_CONFORMANCE=1`, the manifest
Expand Down
35 changes: 34 additions & 1 deletion mk/arch-armv8m.mk
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,47 @@ ARCH_SRCS := \
$(ROOT)/src/arch/armv8m/sau_armv8m.c \
$(ROOT)/src/arch/armv8m/start_armv8m.c

# Isolation level the secure image implements. Only level 3 exists, so any
# other value stops the build; the shared level 3 layer is gated on it.
WT_ISOLATION_LEVEL ?= 3
ifneq ($(WT_ISOLATION_LEVEL),3)
$(error only isolation level 3 is implemented (WT_ISOLATION_LEVEL=$(WT_ISOLATION_LEVEL)))
endif
ARCH_CFLAGS += -DWT_ISOLATION_LEVEL=$(WT_ISOLATION_LEVEL)

ifeq ($(WT_ISOLATION_LEVEL),3)
# Isolation level 3 layer shared by every Armv8-M port: the band layout, its
# linker fragments and the platform hooks. A port's memory_map.h supplies
# WT_RAM_S_BASE, from which every band is placed.
PORT_COMMON_DIR := $(ROOT)/port/common/armv8m
PORT_HEADERS += $(wildcard $(PORT_COMMON_DIR)/*.h)
TARGET_EXTRA_SRCS += $(PORT_COMMON_DIR)/platform_l3.c
WT_RAM_S_ORIGIN := $(shell sed -n \
's/^\#define WT_RAM_S_BASE[[:space:]]*\(0x[0-9A-Fa-f]*\)u.*/\1/p' \
$(PORT_DIR)/memory_map.h)
ifeq ($(WT_RAM_S_ORIGIN),)
$(error $(PORT_DIR)/memory_map.h has no literal WT_RAM_S_BASE)
endif
TARGET_LDFLAGS += -Wl,-L$(PORT_COMMON_DIR) \
-Wl,--defsym=WT_RAM_S_ORIGIN=$(WT_RAM_S_ORIGIN)
# Post-link band check inputs, read from the port's memory_map.h at link time;
# an unreadable layout stops the link rather than checking the default bands.
WT_L3_BAND_ARGS = $(shell python3 $(ROOT)/tools/l3_layout_args.py \
--cc $(TOOLPREFIX)gcc $(PORT_DIR)/memory_map.h)
WT_SECURE_LAYOUT_ARGS = $(or $(strip $(WT_L3_BAND_ARGS)),$(error \
cannot read the level 3 bands from $(PORT_DIR)/memory_map.h)) \
$(WT_SECURE_LAYOUT_EXTRA_ARGS)
endif

# CMSE import library for the Non-secure guests, produced by the secure link.
SECURE_CMSE_IMPLIB := $(BUILD_DIR)/secure_cmse_implib.o
ARCH_LINK_OUTPUTS := $(SECURE_CMSE_IMPLIB)
ARCH_LDFLAGS := -Wl,--cmse-implib -Wl,--out-implib=$(SECURE_CMSE_IMPLIB)

# A changed post-link checker must relink so the image is checked again.
$(BUILD_DIR)/wolftrust.elf $(ARCH_LINK_OUTPUTS): \
$(ROOT)/tools/check_no_fp_insn.py $(ROOT)/tools/check_stack_seal.py
$(ROOT)/tools/check_no_fp_insn.py $(ROOT)/tools/check_stack_seal.py \
$(ROOT)/tools/l3_layout_args.py $(wildcard $(PORT_COMMON_DIR)/*.ld)

# Whitelist of non-secure-callable veneers the linked secure image may
# export: exactly the five mediated FF-M gateway entries, pinned by full
Expand Down
6 changes: 6 additions & 0 deletions mk/common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,12 @@ $(BUILD_DIR)/wt_sec_%.o: $(WOLFHSM_RUNNER_DIR)/%.c $(WOLFHSM_CFG_H) $(BUILD_MODE
$(BUILD_DIR)/wt_sec_%.o: $(PORT_DIR)/%.c $(PORT_HEADERS) $(WOLFHSM_CFG_H) $(BUILD_MODE_STAMP) | $(BUILD_DIR)
$(CC) $(SECURE_CFLAGS) -c -o $@ $<

ifneq ($(PORT_COMMON_DIR),)
$(BUILD_DIR)/wt_sec_%.o: $(PORT_COMMON_DIR)/%.c $(PORT_HEADERS) $(MANIFEST_GEN_H) \
$(WOLFHSM_CFG_H) $(BUILD_MODE_STAMP) | $(BUILD_DIR)
$(CC) $(SECURE_CFLAGS) -c -o $@ $<
endif

$(BUILD_DIR)/wt_sec_%.o: $(WOLFHAL_DIR)/src/%.c $(WOLFHSM_CFG_H) $(BUILD_MODE_STAMP) | $(BUILD_DIR)
$(CC) $(SECURE_CFLAGS) -c -o $@ $<

Expand Down
7 changes: 2 additions & 5 deletions mk/target-mimxrt700.mk
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,8 @@ TARGET_LDFLAGS := \
-Wl,--defsym=WT_SECURE_FLASH_SIZE=$(WT_SECURE_FLASH_SIZE) \
-Wl,--defsym=WT_SECURE_IMAGE_HEADER_SIZE=$(WT_SECURE_IMAGE_HEADER_SIZE)
SECURE_LD := $(PORT_DIR)/secure.ld
# Post-link placement check: the per-partition keystore bands and the
# conformance band from memory_map.h; this port uses no wolfHAL.
WT_SECURE_LAYOUT_ARGS := --band vault=0x301D5000:0x301D7000 \
--band attest=0x301D7000:0x301D7800 --band hsm=0x301D7800:0x301E9000 \
--confdata 0x301F3000:0x301F5C00 --no-wolfhal
# This port links no wolfHAL, so the post-link check expects no wolfHAL state.
WT_SECURE_LAYOUT_EXTRA_ARGS := --no-wolfhal

TARGET_PLATFORM_SRC := $(PORT_DIR)/platform_mimxrt700.c
TARGET_PARTITIONS_SRC := $(PORT_DIR)/partitions.c
Expand Down
2 changes: 1 addition & 1 deletion mk/target-stm32h563.mk
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ TARGET_LDFLAGS := \
-Wl,--defsym=WT_SECURE_FLASH_ORIGIN=$(WT_SECURE_FLASH_BASE) \
-Wl,--defsym=WT_SECURE_FLASH_SIZE=$(WT_SECURE_FLASH_SIZE) \
-Wl,--defsym=WT_SECURE_IMAGE_HEADER_SIZE=$(WT_SECURE_IMAGE_HEADER_SIZE)
SECURE_LD := $(WOLFHSM_RUNNER_DIR)/secure.ld
SECURE_LD := $(PORT_DIR)/secure.ld

TARGET_PLATFORM_SRC := $(PORT_DIR)/platform_stm32h563.c
TARGET_PARTITIONS_SRC := $(PORT_DIR)/partitions.c
Expand Down
Loading
Loading