feat(tock-l552): TockOS host port on STM32L552 (#41)#52
Merged
Conversation
Brings up the Tock kernel as an Umbra Non-Secure host on
NUCLEO-L552ZE-Q. A libtock-rs userspace app drives enclaves through a
custom Tock capsule that wraps the four NSC veneers; Umbra Secure
programs the NS-MPU during boot so Tock's MPU layer is a no-op stub.
Hardware-verified end-to-end on the real board:
- fibonacci enclave: PASS
- TACLeBench paper suite (binarysearch, bsort, countnegative, ndes,
statemate): 5/5 PASS at FreeRTOS-parity wall-clock times
- HOST_APP=tock ./tools/test_taclebench.sh integrates with the standard
per-bench harness (heartbeat + drift assertions extended to tock)
## Workspace layout
- host/stm32l552/tock/ — Cargo workspace
- chips/stm32l5xx/, chips/stm32l552/ — chip glue (RCC, GPIO, LPUART1,
vectors)
- capsules/umbra/ — NSC syscall driver capsule + NoopMpu stub
- boards/nucleo_l552ze_q_umbra/ — Tock board crate (main, fault dumper,
raw_print, heartbeat, layout.ld)
- apps/enclave_demo/ — libtock-rs TBF app (multi-enclave scan loop)
- enclave_payload/ — fibonacci enclave bundled at 0x08078000
- Submodules: lib/tock @ b35fad8 master plus a vendor patch on
MuxUart::do_next_op (3f61f85f) for sync-callback ordering;
lib/libtock-rs @ 0766d8c
- Toolchain: nightly-2026-05-19
## Capsule API (DRIVER_NUM 0xA0000)
cmd 0 — driver presence check
cmd 1 — umbra_enclave_create(base_addr)
cmd 2 — umbra_enclave_enter(enclave_id)
cmd 3 — umbra_enclave_exit(enclave_id)
cmd 4 — umbra_enclave_status(enclave_id)
cmd 5 — probe UMBR magic at flash addr (apps can't dereference flash
under Tock's MPU sandbox)
cmd 6 — dump DWT drift stats over raw_print
Every command beyond presence-check returns CommandReturn::success() and
delivers its u32 result through subscribe slot 0 as an upcall.
## NSC veneer register barrier
Empirically the SG/BXNS round-trip through the Umbra NSC veneers does
not preserve r4-r11 the way Rust's extern "C" ABI assumes — rustc + LLVM
keeps &self in r5 across bl umbra_enclave_create and faults on the next
dereference with SecureFault.AUVIOL (Secure-state addresses leak into NS
scratch registers along the way). The capsule wraps each veneer call in
inline asm that manually push/pops r4-r11 around the blx and lists
r4/r5/r8-r11 as clobbered, forcing LLVM to spill live values before the
call. Bare-metal C and FreeRTOS hosts on the same chip aren't affected
because GCC's codegen happens to spill differently.
## Heartbeat + DWT drift instrumentation
The board owns NS SysTick exclusively (Tock's SchedulerTimer is the
never-expires () stub), configured once at boot exactly like FreeRTOS's
prvSetupTimerInterrupt. The SysTick handler updates per-tick stats
(DWT delta, max, buckets, heartbeat counter) atomically; all [HEARTBEAT]
and [DRIFT] lines are emitted in one atomic burst via the capsule's
cmd=6 dump path at end-of-run rather than from IRQ context, so they
never splice into Umbra Secure's own UART writes.
## Umbra Secure changes
Static NS-MPU layout for STM32L552 (6 regions per the spec's region 3/4
split: kernel-priv-RW + app-unpriv-RW, with EFBC and SRAM2 reserved as
Secure-only):
- src/hardware/architecture/arm/src/mpu.rs — NS_MPU_BASE_ADDR +
new_with_base() + enable_strict() + program_ns_mpu() helper
- src/hardware/platform/stm32l552/boot/src/ns_mpu_layout.rs (new) —
NS_MPU_LAYOUT_L552 const
- src/hardware/platform/stm32l552/boot/src/platform_impl.rs — call
program_ns_mpu() from configure_ns_boot() before jumping to NS
## Harness integration
tools/test_taclebench.sh:
- HOST_APP=tock case wired (HOST_LOG_PREFIX="[TOCK]")
- Heartbeat liveness + drift bounds + healthy% assertions extended from
freertos to also cover tock
- awk calls prefixed with LC_ALL=C to tolerate the 0xFE world-transition
glitch byte under UTF-8 locales
## Documentation
- book/src/examples/tock.md (new) — full architecture walkthrough,
register barrier explanation, heartbeat/drift section, TACLeBench
integration, submodule pins
- SUMMARY.md, examples/README.md, introduction.md, build-and-run.md,
crate-structure.md, top-level README.md all updated to list the Tock
host alongside bare_metal / freertos / object_detection
Closes #41.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Brings up the Tock kernel as an Umbra Non-Secure host on NUCLEO-L552ZE-Q. A libtock-rs userspace app drives enclaves through a custom Tock capsule that wraps the four NSC veneers; Umbra Secure programs the NS-MPU during boot so Tock's MPU layer is a no-op stub.
Hardware-verified end-to-end on the real board:
Workspace layout
Capsule API (DRIVER_NUM 0xA0000)
cmd 0 — driver presence check cmd 1 — umbra_enclave_create(base_addr) cmd 2 — umbra_enclave_enter(enclave_id) cmd 3 — umbra_enclave_exit(enclave_id) cmd 4 — umbra_enclave_status(enclave_id) cmd 5 — probe UMBR magic at flash addr (apps can't dereference flash under Tock's MPU sandbox) cmd 6 — dump DWT drift stats over raw_print
Every command beyond presence-check returns CommandReturn::success() and delivers its u32 result through subscribe slot 0 as an upcall.
NSC veneer register barrier
Empirically the SG/BXNS round-trip through the Umbra NSC veneers does not preserve r4-r11 the way Rust's extern "C" ABI assumes — rustc + LLVM keeps &self in r5 across bl umbra_enclave_create and faults on the next dereference with SecureFault.AUVIOL (Secure-state addresses leak into NS scratch registers along the way). The capsule wraps each veneer call in inline asm that manually push/pops r4-r11 around the blx and lists r4/r5/r8-r11 as clobbered, forcing LLVM to spill live values before the call. Bare-metal C and FreeRTOS hosts on the same chip aren't affected because GCC's codegen happens to spill differently.
Heartbeat + DWT drift instrumentation
The board owns NS SysTick exclusively (Tock's SchedulerTimer is the never-expires () stub), configured once at boot exactly like FreeRTOS's prvSetupTimerInterrupt. The SysTick handler updates per-tick stats (DWT delta, max, buckets, heartbeat counter) atomically; all [HEARTBEAT] and [DRIFT] lines are emitted in one atomic burst via the capsule's cmd=6 dump path at end-of-run rather than from IRQ context, so they never splice into Umbra Secure's own UART writes.
Umbra Secure changes
Static NS-MPU layout for STM32L552 (6 regions per the spec's region 3/4 split: kernel-priv-RW + app-unpriv-RW, with EFBC and SRAM2 reserved as Secure-only):
Harness integration
tools/test_taclebench.sh:
Documentation
Closes #41.