tk — Safety & Security Features (cross-arch reference, staged index)

Purpose. One place that describes every safety- and security-relevant feature tk provides, what it costs at runtime, the exact config knob and API to use it, which architectures/boards support it, and — so the picture is complete — which mechanisms are not yet leveraged but are plausible future work.

This is a synthesis + index. Where a subsystem has its own design record, this doc links to it rather than duplicating it. Everything here is grounded in the code; when the code and this doc disagree, the code wins and this doc is the bug.

Migration & staging note (2026-08-19). This index was written against the pre-reshape tree (now _legacy/). Return-to-root has landed the memory-protection plane (the L1–L3 ladder below) and observability (trace/SystemView) into the root kernel/ + arch/, but the crypto / verified-boot / watchdog / heartbeat / reset-reason / brownout / error-DET / introspect-region subsystems are not yet ported to root — they remain in _legacy/ and are marked ⏳ staged (roadmap) throughout. Reconciled config defaults have shifted to minimal-safe (§2, §7): the hardware guard is now opt-in, not default-on. Read §2 and §7 as live; read §3–§6 as a staged index of what re-attaches next (docs/roadmap.md, R2/R3).

How to read the status tags.

Tag

Meaning

✅ landed

present in the root tree (kernel/ / arch/ / system/)

✅ HW-verified

exercised on real silicon (board named) — some proofs are in the reshape sketch / _legacy

⏳ staged

proven in _legacy/, not yet ported to root (roadmap)

🧪 bench/one-board

implemented + verified on a single board; portable but not broadly proven

🔩 arch-ready, board-gated

the arch code supports it; a given board also needs linker/HAL support

📐 software-only

portable C, no hardware assist

🕳️ gap / not built

called out so nobody assumes coverage that isn’t there


1. The shape of the model

tk separates three concerns. They are independent — you can take any subset — and each is compile-time gated so an unused plane costs nothing.

  1. Memory protection — keeps a thread (buggy or hostile) from corrupting another thread, the kernel, or the core. §2. ✅ landed (ARM-MPU + RISC-V-PMP).

  2. Boot & data integrity — proves the code that runs is the code you signed, and provides the crypto primitives to protect data. §3. ⏳ staged.

  3. Availability & fault handling — detects a wedged/starved/browning-out system and drives it to a defined state (halt, reset, or contain). §4. ⏳ staged (except the fault-policy hook plumbing, which rides the arch layer).

Two structural facts shape everything below:

  • Layering. Memory protection lives in the arch (arch/<arch>/) with a small board contribution (the linker zones); watchdog / reset-reason / brownout live in the HAL/MCAL as board backends; error reporting and stack checking are portable core (kernel/); cross-core liveness is the system layer (system/). The HAL never depends on the kernel (see docs/architecture/composability.md), so the crypto/watchdog/reset features work in a bare HAL build with no scheduler.

  • The privileged-background divide (ARM vs TriCore). This is the single most important architectural difference for memory protection. ARM Cortex-M has a privileged background (MPU_CTRL.PRIVDEFENA): the MPU only needs to describe what unprivileged code sees; privileged code (kernel, handlers) reaches all memory by default. RISC-V PMP is the analog (M-mode bypasses PMP; a U-mode thread reaches only what a PMP entry grants). TriCore has no privileged background — every protection set bounds Supervisor too. Consequences: on TriCore the stack guard works without user mode, and “isolate app threads” and “run app threads unprivileged” are the same mechanism. (The TriCore MPU/unpriv path is proven in _legacy but not yet ported to root — arch/tricore/ at root has switch + SMP + CSA reclaim only, no ARCH_HAS_MPU/UNPRIV.)


2. Memory protection — the ladder (✅ landed)

Three stacked levels. Each is a superset of the one below; you choose how far up you go. All are gated by TK_CONFIG_ENABLE_STACK_CHECK at the base. Reconciled default posture is minimal-safe: only L1’s config gate is default-on; L2/L3 are opt-in (via the hardened/full profiles or an explicit -D), because turning them on installs the MPU + fault vectors and the drop-in must not seize a vendor SDK’s handlers by default.

Level

Enable

Catches / protects from

Precision

Cost

Does not cover

L1 — software canary

ENABLE_STACK_CHECK=1 (default on)

your own stack overflow

late — next context switch, after the growth

a few cells checked per switch; no per-thread RAM

anything cross-thread; post-hoc (neighbour may already be clobbered)

L2 — HW stack guard

+ ENABLE_MPU_GUARD=1 (default off, opt-in)

your stack overflow

precise — faults at the overflowing instruction

1 MPU/PMP range reprogrammed per switch

cross-thread scribbling: all threads still privileged, so a thread can still write a peer or the kernel

L3 — unprivileged confinement

+ UNPRIV_THREADS=1 (default off)

cross-thread corruption, kernel-state corruption, privilege escalation, core reconfig, and overflow

precise

per-call syscall trap + per-switch MPU/PMP reprogram

interrupt-masking starvation; corrupting shared globals it legitimately reaches; CPU hogging

The jump that matters is L2 → L3. L2 tells a thread when it overflows its own stack but leaves every thread able to reach all of memory. L3 is genuine freedom from interference: an app thread sees only its own stack + shared globals + flash + (granted) peripherals — kernel data, peer stacks, and sync-object internals are walled off, and it cannot escalate to Supervisor except through a syscall the kernel controls.

The derived gate (root reconciliation). tk_config.h resolves the two idioms — app choice × arch capability — into compile-time TK_FEAT_* flags. The load-bearing derivations: TK_FEAT_MPU_GUARD = (ENABLE_MPU_GUARD || UNPRIV_THREADS) && ARCH_HAS_MPU (so enabling UNPRIV implies the guard — confinement requires it); TK_FEAT_UNPRIV = UNPRIV_THREADS && ARCH_HAS_UNPRIV; TK_FEAT_ISOLATION = ENABLE_MPU_ISOLATION && TK_FEAT_UNPRIV && ARCH_HAS_MPU. An arch without the hardware folds all of these to 0 — never a broken build, and the minimal default installs no vectors.

2.1 L1 — software canary (📐 all arches; ⏳ implementation re-hosting)

At creation each stack is filled and its low end becomes a trip-wire; the scheduler checks the running thread’s canary on every switch. Same fill pattern backs high-water-mark measurement. Sizes are in addressable cells (ADR 0001), not bytes.

  • Config: TK_CONFIG_ENABLE_STACK_CHECK (default 1).

  • ⏳ Root status: the scheduler already calls tk_stack_prepare / tk_stack_check on the create + switch paths, but kernel/tk_stack.h is currently a placeholder no-op — the fill / canary / high-water scan, the TK_CONFIG_STACK_CANARY_BYTES / _FILL_BYTE sizing knobs, and the tk_thread_stack_peak/free + overflow-hook API are not yet re-hosted (they live in _legacy/kernel/tk_stack.*). This is a docs/roadmap.md R2 item.

  • Runtime impact (when implemented): microscopic (a compare per switch). Detection is after the fact — treat an L1 (CANARY) fault as “memory may already be wrong → reset.”

2.2 L2 — hardware MPU/PMP stack guard (🔩 arm_v7m + arm_v8m; RISC-V has no HW stack guard)

A no-access region at each thread’s stack limit, reprogrammed on switch, so an overflow faults at the offending instruction — before corruption. Overflow detection only.

  • Config: TK_CONFIG_ENABLE_MPU_GUARD (default 0 at root — opt-in; was default-on in the pre-reshape tree). Requires ENABLE_STACK_CHECK; a no-op on a part with no MPU.

  • Arch support at root: arm_v7m and arm_v8m (ARCH_HAS_MPU 1) program an MPU (arch_mpu_init + per-switch region reprogram in tk_kernel.c, gated TK_FEAT_MPU_GUARD). arm_v6m (M0+): no MPU region programmed → L1 only. RISC-V has PMP (used for L3 confinement) but no dedicated hardware stack guard. TriCore MPU stack-guard: ⏳ staged (not ported).

  • Fault path: ARM MPU region violation → MemManage_Handler → the stack policy in §4.5.

  • ✅ HW-verified: M4F (arm_v7m) — proven on nRF52 (root board) and EK-RA4M1 (reshape sketch). TriCore class-1 guard: verified in _legacy, not at root.

2.3 L3 — unprivileged threads & confinement (🧪 ARM M4F, RISC-V U-mode; ⏳ TriCore)

Application threads run unprivileged behind an MPU/PMP memory map that grants only what they own. This is the freedom-from-interference tier. It changes the thread ABI: kernel calls that touch privileged state (yield, sleep, exit, the sync primitives, join/detach) trap through a generic syscall instead of a direct call.

  • Config:

    • TK_CONFIG_UNPRIV_THREADS (default 0, opt-in — needs ARCH_HAS_UNPRIV).

    • TK_CONFIG_ENABLE_MPU_ISOLATION (default 0 — new at root, replaces the pre-reshape UNPRIV_REQUIRE_ISOLATABLE model). On = full lockdown: kernel-RAM (.privileged_bss) + per-thread (.thread_stacks) MPU section placement — needs the board’s linker regions. Off = reduced isolation: unprivileged confinement via the SCS-unmapped map, no custom sections. The derived TK_FEAT_ISOLATION requires this knob and UNPRIV and ARCH_HAS_MPU.

  • API:

    • TK_THREAD_STACK(name, size) (kernel/tk_kernel.h) — declare a stack in the isolatable .thread_stacks zone (gated on TK_FEAT_ISOLATION; a plain array otherwise).

    • tk_thread_set_privileged(t) (kernel/tk_kernel.h) — opt a specific “system” thread back to privileged after create; the kernel + all handlers stay privileged regardless (no-op without unpriv).

    • The whole existing sync/yield/sleep/exit/join API is unchanged to the caller — the syscall routing is internal (ARCH_HAS_SYSCALL = ARM SVC or RISC-V ecall; TriCore class-6 SYS in _legacy).

  • What an unprivileged thread is denied: kernel singleton, peer thread stacks, core-config registers (ARM System Control Space / RISC-V CSRs — no MPU/NVIC/PMP/PSW reconfiguration), and privilege escalation. Sync objects are kernel-owned: the app holds an opaque handle; every call bounds-checks it privileged and the kernel never dereferences an app pointer.

  • Board requirement: for full lockdown (ENABLE_MPU_ISOLATION=1) the board’s linker must carve the partitioned zones (.privileged_bss, .thread_stacks, + on ARM the introspect window + MSP zone). No root board carries these zones yet — so full-lockdown isolation is arch-ready but board-gated at root; the reduced-isolation confinement (SCS-unmapped) runs without custom sections.

  • Verification: full-lockdown MPU/unpriv HW-verified on M4F (EK-RA4M1) and RISC-V U-mode + PMP (QEMU) in the reshape — proving the ARM-MPU ↔ RISC-V-PMP generalization. ✅ TriCore confinement HW-verified in _legacy (examples/unpriv_tricore); ⏳ not yet at root.

  • Detail docs: docs/architecture/isolation/kernel-ram-isolation.md (kernel-RAM + handler-stack lockdown), docs/architecture/isolation/per-thread-stack-isolation.md (per-thread stack isolation), docs/architecture/isolation/kernel-owned-sync.md (opaque handles).

2.4 Residual risks in L3 (⚠️ read before trusting it)

Confinement is memory isolation, not a full sandbox. Known, deliberate gaps:

  • On TriCore, User-1 can mask interrupts. tk runs app threads in TriCore User-1 so they can reach peripheral MMIO for the HAL/console; User-1 can also execute enable/disable — a buggy thread can mask interrupts and starve the system. This gap is TriCore-specific: on ARM Cortex-M and RISC-V U-mode, interrupt masking is privileged, so an unprivileged thread cannot mask interrupts at all. (TriCore path is _legacy/roadmap.)

  • Shared state is shared. A thread can corrupt globals it legitimately reaches; isolation firewalls kernel and peer memory, not application data threads share by design.

  • TriCore shared-CSA gap. TriCore thread contexts live in a shared CSA free-list; the confined set must grant the CSA region, which also exposes peers’ saved contexts. ARM/RISC-V have no equivalent (contexts ride each thread’s isolated stack). Closing it needs per-thread CSA partitioning (future).

  • CPU hogging is bounded by preemption (all real arches preempt), not by this plane.


3. Boot & data integrity (cryptography + verified boot) (⏳ staged — not at root)

Status: the entire crypto / verified-boot plane is _legacy/ only — there are no crypto or tk_vboot headers at root (hal/tk_sha.h, tk_aes*.h, tk_hmac_sha256.h, tk_ecdsa.*, tk_vboot.*, tk_crc.h, tk_rng.h are all absent). The design below is the port-forward target (docs/roadmap.md §5). Preserved here as the staged index.

3.0 How crypto was enabled (there is no TK_CONFIG_* for it)

Enablement is four-layered: portable pieces (tk_vboot.c, tk_ecdsa.c, tk_crc_sw.c, header-only HMAC/CBC) globbed into every build and dead-stripped if unused; per-silicon backends (tk_sha_*, tk_aes_*, tk_rng_*) as board sources with a software fallback via TK_SHA256_SW_IMPL; the app opts in at runtime (tk_*_init()); verified-boot signing is a make-layer pipeline (VBOOT_SIGN=…). “What crypto do I have” = which board backends exist + what the app calls.

3.1 Primitives (⏳ staged)

Primitive

Header / API (_legacy)

Software

HW-accelerated

Notes

SHA-256

tk_sha.h — tk_sha256_begin/update/finish, tk_sha256()

tk_sha256_sw.h (host, nRF52)

ESP32

ctx layout differs by backend

HMAC-SHA256

tk_hmac_sha256.h (header-only)

portable (rides SHA)

inherits SHA accel

propagates UNSUPPORTED where no SHA

AES-128

tk_aes.h (ECB), tk_aes_cbc.h (CBC)

host (enc+dec)

ESP32 (enc+dec), nRF52 (ECB enc-only)

v1 CBC whole-block, no padding

ECDSA P-256 (verify)

tk_ecdsa.h — tk_ecdsa_p256_verify(...)

tk_ecdsa.c

—

device holds public key only; not constant-time (inputs public)

CRC-16/CCITT

tk_crc.h — tk_crc16()

tk_crc_sw.c (weak)

MSP430FR2433 (strong override)

poly 0x1021, init 0xFFFF

3.2 Verified / secure boot — trust chain tk_vboot (⏳ staged)

tk_vboot_check(image, key, keylen, manifest); manifest MAC stored outside the hashed region; constant-time compare. Build pipeline VBOOT_SIGN=1|ecdsa + signer scripts; two-stage merge. Story A (HW-verified on nRF52 in _legacy): A1 software HMAC-SHA256 verified-boot; A2 P-256 ECDSA verify-only (single + two-stage bootloader); A3 nRF52 APPROTECT debug lockout. Honest caveat: a software verifier whose key lives in the same rewritable flash is a teaching device, not a real boundary — a real root of trust needs an immutable boot ROM + hardware key store (§8). Detail doc: _legacy/docs/notes/trust-chain-secure-boot.md.

3.3 / 3.4 RNG + board accelerators (⏳ staged)

tk_rng (nRF52 TRNG; ESP32 HW-RNG RF-gated; host xorshift, non-crypto). Accelerator inventory (SHA/AES/RNG on ESP32; AES-ECB+TRNG on nRF52; HW CRC on FR2433; APPROTECT anchor) — all _legacy.


4. Availability & fault handling

Recurring idiom: weak-default + strong-override and opt-in-by-call.

4.1 Watchdog — tk_wdg (⏳ staged — no hal/tk_wdg.h at root)

Mode-based HW watchdog (OFF/SLOW/FAST). API tk_wdg_init/set_mode/trigger/set_warn/get_mode; opt-in by calling tk_wdg_init(). _legacy boards: nRF52 (500/4000 ms, unstoppable = safe case), ESP32, host sim. Port-forward target.

4.2 Reset-reason — tk_mcu_reset_reason() (⏳ staged — no tk_mcu reset backend at root)

tk_reset_reason_t tk_mcu_reset_reason(void) → {UNKNOWN, POWER_ON, PIN, WATCHDOG, BROWNOUT, SOFT, LOCKUP, OTHER}. _legacy distinguishes causes on nRF52/STM32/RA4M1/ESP32. Port-forward target.

4.3 Brownout / voltage detection (⏳ staged)

tk_mcu_brownout_enable() + tk_mcu_set_brownout_cb(cb) — early warning ISR above the hard reset level. _legacy real ISR on nRF52/STM32H7/STM32F1. Port-forward target.

4.4 Error reporting — tk_error / tk_det (📐 stub at root; full subsystem ⏳ staged)

Two reporting paths over one mechanism (global hook + per-module/per-status counters), split Det/Dem. Root status: kernel/tk_det.h is a no-op stub — TK_DET(mod, id, err) → ((void)0) — used at the create/sync rejection sites (TK_MOD_KERNEL, TK_MOD_SYNC). The full tk_error.* subsystem (counters, installable hook, 28 module IDs, TK_CONFIG_DET_ENABLED / ERROR_COUNTERS_ENABLED / ERROR_SOURCE_INFO, SystemView mirroring) is _legacy/hal/ only, not yet re-ported.

4.5 Stack-overflow / protection-fault policy (📐 hook; arch terminal action)

The single policy point every stack/MPU fault funnels to (from L1 canary and L2/L3 MPU alike).

  • API (target): tk_set_stack_overflow_hook(fn), fn(tk_thread_t* t, enum tk_stack_fault_cause cause) where cause ∈ {TK_STACK_FAULT_CANARY, TK_STACK_FAULT_MPU, TK_STACK_FAULT_PROTECTION}. ⏳ Root status: the hook/enum API rides with the L1-canary re-hosting (§2.1) and is not yet at root; the arch fault plumbing (ARM MemManage_Handler → fault dump/terminal) is present under TK_FEAT_MPU_GUARD.

  • Hook context: runs in scheduler/fault context with the stack at/past its limit and interrupts about to be masked — must not block, yield, or call scheduling APIs.

  • Default terminal action if no hook: ARM MemManage_Handler → fault dump → tk_fault_terminal (weak default masks IRQs + wfi-halts forever, debugger-inspectable; a board supplies a strong one calling NVIC_SystemReset()). TriCore class-1 → decode + halt (⏳ _legacy).

  • Recovery beyond reset: for the precise (MPU) case nothing was corrupted, so fault containment (terminate just the offender, keep scheduling) is architecturally possible. TK_CONFIG_FAULT_CONTAIN + TK_FEAT_FAULT_CONTAIN gates exist at root; the full reclaim path (context reclamation + held-lock cleanup) is not built — reset is the honest recovery today.

4.6 Cross-core heartbeat — tk_heartbeat (⏳ staged — no system/tk_heartbeat at root)

A software watchdog between cores. _legacy API tk_heartbeat_start() + weak tk_core_stalled reaction hook; TK_CONFIG_ENABLE_HEARTBEAT (+ _PERIOD_MS/_MISS) — none of these knobs exist at root. Hard-requires ENABLE_AFFINITY + ARCH_CORE_COUNT > 1. Port-forward target.

Note — EDF is not a deadline-miss detector. TK_CONFIG_ENABLE_EDF (default 0 at root — was default-on pre-reshape) only orders the ready set by earliest deadline when the EDF policy is selected. There is no overrun reporting; do not treat it as an availability safety feature.


5. Observability (supports safety analysis)

  • Tracing (TK_CONFIG_ENABLE_TRACE, TK_CONFIG_ENABLE_SYSVIEW, both default 0): ✅ landed — a flight-recorder ring (kernel/tk_trace.h + kernel/tk_trace_ring.c) and/or a SEGGER SystemView backend (kernel/tk_sysview.h + system/tk_sysview.c, SEGGER sources under tools/SEGGER/) behind one set of TK_TRACE_* hooks. Knobs: TK_CONFIG_TRACE_RECORDS (32, power of two), TK_CONFIG_TRACE_SCHED (1 = record carries the task handle), TK_CONFIG_TRACE_TICK (0 — the tick ISR floods SystemView at ~1 kHz; on only to study tick timing). SystemView HW-verified on nRF52. Demos: examples/{trace,sysview}.

  • Introspection / remote monitor (TK_CONFIG_ENABLE_INTROSPECT, default 0): 🟡 partial — the knob, the TK_TCB_HAS_NAME field gate, and tk_kernel_thread_snapshot(struct tk_thread_info*, int) exist at root, but the seqlock-guarded kernel-owned RO region (tk_introspect.{c,h}) and its unprivileged reader (examples/monitor) are ⏳ staged — a docs/roadmap.md R2 item. Cat-0 was HW-verified on nRF52 in _legacy.

    • ⚠️ Type-hygiene carry-forward: the public struct tk_thread_info still declares uint8_t base_priority / uint8_t state — the kind-1-as-uint8_t anti-pattern ADR 0001 (Addendum 2) forbids; fix to unsigned char when the introspection surface is next touched.


6. Cross-arch / board support matrix

Rows are features; cells are the best status per arch family. “—” = not applicable. ⏳ marks a row whose subsystem is _legacy-only pending port-forward.

Feature

arm_v7m (M3/M4/M7)

arm_v6m (M0+)

arm_v8m (M33)

risc_v (RV32)

tricore (TC2xx)

Preemptive kernel

✅

✅

✅

✅

✅ (switch/SMP)

L1 software canary

✅ knob (impl ⏳)

✅ knob

✅ knob

✅ knob

⏳

L2 MPU/PMP stack guard

✅ nRF52 (MPU parts)

🕳️ no MPU

✅ (MPU)

🕳️ no HW guard

⏳

L3 unpriv confinement

🧪 M4F

🕳️

🔩 (M33)

🧪 U-mode/PMP

⏳

Full-lockdown isolation (MPU_ISOLATION)

🔩 board-gated

🕳️

🔩 board-gated

🔩 board-gated

⏳

Tracing / SystemView

✅ nRF52

🔩

🔩

🔩

🔩

Introspect region

⏳

⏳

⏳

⏳

⏳

Watchdog / reset-reason / brownout

⏳

⏳

⏳

⏳

⏳

Crypto / verified boot

⏳

⏳

⏳

⏳

⏳

Cross-core heartbeat

—

⏳ (SMP)

—

⏳ (SMP)

⏳ (SMP)

No root board carries the .thread_stacks/.privileged_bss linker zones yet, so full-lockdown isolation is arch-ready but board-gated everywhere at root (reduced-isolation confinement runs without them).


7. Config quick-reference (safety/security knobs — reconciled to root tk_config.h)

All in kernel/tk_config.h; every option is #ifndef-guarded so a force-included profile or a -D overrides it. A profile is part of the ABI. Defaults below are the root minimal-safe posture — several shifted from the pre-reshape tree (noted).

Knob

Default

Effect

TK_CONFIG_ENABLE_STACK_CHECK

1

L1 canary + high-water; master switch for L2/L3 (impl re-hosting ⏳)

TK_CONFIG_ENABLE_MPU_GUARD

0 (was 1)

L2 HW stack guard; installs MPU + fault vectors (no-op without an MPU)

TK_CONFIG_UNPRIV_THREADS

0

L3 unprivileged threads + confinement (needs ARCH_HAS_UNPRIV)

TK_CONFIG_ENABLE_MPU_ISOLATION

0 (new)

full lockdown: kernel-RAM + per-thread MPU section placement (needs board linker zones); off = reduced-isolation confinement

TK_CONFIG_FAULT_CONTAIN

0

unpriv fatal fault → terminate that thread (reclaim path not built)

TK_CONFIG_ENABLE_EDF

0 (was on)

deadline field + EDF policy availability (orders only; not a miss-detector)

TK_CONFIG_ENABLE_AFFINITY

0

SMP per-thread core mask (meaningful only when ARCH_CORE_COUNT > 1)

TK_CONFIG_ENABLE_PROFILING

0

cpu_ticks + run_count fields (feeds ps/top)

TK_CONFIG_ENABLE_INTROSPECT

0

TCB-name field + snapshot; RO monitor region ⏳

TK_CONFIG_ENABLE_TRACE / TRACE_RECORDS / TRACE_SCHED / TRACE_TICK

0 / 32 / 1 / 0

flight-recorder ring depth + record shape + tick instrumentation

TK_CONFIG_ENABLE_SYSVIEW

0

SEGGER SystemView (Cortex-M)

Derived (not user knobs): TK_FEAT_MPU_GUARD, TK_FEAT_UNPRIV, TK_FEAT_ISOLATION, TK_FEAT_FAULT_CONTAIN, TK_FEAT_AFFINITY — each ANDs the app knob with the arch’s ARCH_HAS_*.

Knobs that moved with a staged subsystem (⏳, not present at root): STACK_CANARY_BYTES / STACK_FILL_BYTE (with the L1 impl, §2.1), DET_ENABLED / ERROR_COUNTERS_ENABLED / ERROR_SOURCE_INFO (with tk_error, §4.4), UNPRIV_REQUIRE_ISOLATABLE (superseded by ENABLE_MPU_ISOLATION), ENABLE_HEARTBEAT / _PERIOD_MS / _MISS (with tk_heartbeat, §4.6), VBOOT_SIGN / VBOOT_KEY make vars (with the crypto pipeline, §3).


8. Hardware safety/security features NOT yet leveraged (roadmap)

So the picture is honest: capabilities present in the supported silicon that tk does not use today. None are promised — plausible next reaches.

AURIX TriCore (TC27x): lockstep checker core (the headline unused safety mechanism); SMU (alarm aggregation); ECC / MBIST / windowed safety-watchdog; per-thread CSA partitioning (would close the shared-CSA gap §2.4); HSM/SHE secure key store; SCU_RSTSTAT reset decode; tk_wdg backend. (Note: the TriCore arch itself is only partially at root — MPU/unpriv not yet ported.)

ARM Cortex-M: ARMv8-M TrustZone-M + SAU (secure/non-secure world partitioning — arch/arm_v8m is at root and QEMU-proven, but the world dimension is the deliberate next slot); stack-limit registers (MSPLIM/PSPLIM on M33 — HW overflow trap without an MPU region); nRF52 APPROTECT in a shipped provisioning flow; nRF52 CryptoCell / STM32 HW crypto; MPU linker zones on more boards.

RISC-V: ePMP / additional PMP entries for finer maps; Sv32 paging isolation beyond the QEMU proof; supervisor timer/sstc for S-mode ticks.

General: fault containment (kill-the-thread-and-continue) — §4.5, the biggest availability upgrade; deadline-miss detection (EDF orders but does not report overruns, §4.6); redundant/voted execution, control-flow integrity, memory scrubbing — not attempted.