ADR-019 — High-VA continuation + dedicated identity text range
- Status: Accepted (partial tear; live
.text/.rodata/.data/ heap still identity-mapped) - Date: 2026-09-11
Context
ADR-018 cloned TTBR1 RAM tables and unmapped one dedicated identity text page. rustc still emits link-time identity addresses (relocation-model: static). _start / QEMU -kernel still load at 0x4008_0000.
The preferred next cut was to unmap contiguous live identity .text after the boot stub once EL1 fetched from the high alias. A first attempt on this cloud VM Failed: after unmapping [0x4008_1000, __text_end), the first writeln! / println! took an unhandled current-EL sync. core::fmt::write takes &mut dyn Write; those vtable methods are identity fn pointers. Unmapping live .text is therefore blocked until fmt / dyn dispatch is proven high-only.
This ADR is the largest Verified cut that still boots: jump the post-MMU continuation to its TTBR1 alias, then unmap a 16 KiB dedicated identity text range (four pages), not one probe page.
Decision
- High-VA continuation. After
paging::init(MMU on, highVBAR_EL1, ADR-018 first tear page),kernel_mainBRs to the TTBR1 alias ofkernel_main_high. Serialident: jump. DirectBLstays in the high window (PC-relative). UART MMIO stays the absolute identity0x0900_0000. Stacks / heap /.datastay identity VAs. - Boot stub stays mapped. The
0x4008_0000page (_start+exception_vectors) stays identity-mapped. Live.textafter that page also stays — rustc fmt /dynstill needs those identity fn pointers. - Dedicated 16 KiB tear range. Linker
__ident_tear_start…__ident_tear_endis four pages: page 0 holdsident_tear_el1_path(ADR-018), page 1 holdsident_range_el1_path, pages 2–3 are aligned pad. After the high jump, unmap the whole range from kernel and user TTBR0 andTLBI VAAE1each low VA. High twins stay PXN-clear. - Keep live
.text/.rodata/.dataidentity-mapped. Do not yank them here. A botched full.textyank is worse than a smaller Verified slice. - Identity
fnpointers used after the jump go high where we control them (scheduler trampoline / task entries; custom test runnerdyn Testablemethod). That is preparation, not a claim that rustc fmt is high-only. - Fail-closed probe. Serial
ident: jump.ident: range lo=… hi=… pages=NwithN >= 4. Existingident: split/fault/high/no el0/okstay.ident: textis EL1 fetch of the second torn page via the high twin.scripts/qemu-smoke.shgreps those strings and rejectsident: probe missed/ident: leaked/ident: range missed. - Identity boot stub stays.
_start, QEMU-kernelload, live identity.text/.rodata/.data/heap, PL0110x0900_0000. Do not say the kernel moved. - Still Planned. Unmap live identity
.textafter fmt /dyncalls are proven high-only; then.rodata/.data/heap; PAN on-cpu cortex-a57; umbrella EL0 isolation. Do not change default-cpu. - NFR-10 text is revised in place (ID unchanged). Do not mint NFR-15+.
Honesty
Say “the post-MMU continuation ran at a high VA” or “a 16 KiB dedicated identity text range was unmapped while EL1 still fetched the high twins” only when the serial / tests pass. Do not say:
- the kernel has moved to the high half
- identity
.textafter_startwas fully torn down - identity mappings were fully torn down
- “secure OS” / “hardened” / “EL0 isolated”
- PAN (still unclaimed on
-cpu cortex-a57)