# 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//`) 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. --- ## 9. Related documents - `docs/architecture/isolation/kernel-ram-isolation.md` — L3: kernel-RAM + handler-stack lockdown. - `docs/architecture/isolation/per-thread-stack-isolation.md` — L3: per-thread stack isolation. - `docs/architecture/isolation/kernel-owned-sync.md` — opaque sync handles under confinement. - `docs/architecture/composability.md` — the HAL-never-depends-on-kernel contract these features rely on *(pending migration; enforced in `_legacy` by `tools/composability_matrix.sh`)*. - `docs/architecture/smp-model.md` — the SMP model behind cross-core heartbeat *(pending migration; scope narrowly superseded by `docs/decisions/0002-multi-instance-kernel.md`)*. - `docs/requirements.md` — R6 indexes these capabilities against the reshape north star. - `docs/roadmap.md` — the port-forward state for every ⏳-staged subsystem above. - `docs/decisions/0001-foreign-architecture-policy.md` — the cell/`uint8_t` type discipline (§2, §5). - `_legacy/docs/notes/trust-chain-secure-boot.md` — verified-boot working notes (§3, staged).