T-016: activate MMU with identity-mapped kernel + MapperFlush flush-token discipline#23
Conversation
T-016 / ADR-0027 first commit. Lands the HAL surface change so the
trait commits to the post-T-016 shape; BSP wiring follows in
subsequent commits. Workspace is host-test green at this point;
bare-metal kernel build clean (no kernel caller of Mmu::map yet).
Mmu trait surface:
- Mmu::map : Result<(), MmuError> → Result<MapperFlush, MmuError>
- Mmu::unmap : Result<PhysFrame, MmuError> → Result<(MapperFlush, PhysFrame), MmuError>
MapperFlush is a #[must_use] newtype carrying a VirtAddr. Two
methods discharge it:
- flush(self, mmu: &impl Mmu) executes mmu.invalidate_tlb_address(va)
- ignore(self) documented no-op for bulk callers
followed by a single invalidate_tlb_all
The token mirrors x86_64::structures::paging::MapperFlush. The
workspace's unused_must_use = "deny" lint promotes "forgot to flush"
from a reviewer-attention concern into a compile-time error.
Module restructure:
hal/src/mmu.rs → hal/src/mmu/mod.rs
hal/src/mmu/vmsav8.rs [new] — pure const fn descriptor
encoders (block / page / table
+ flags_to_descriptor_bits)
plus MAIR_EL1_VALUE,
TCR_EL1_VALUE, and
SCTLR_EL1_MMU_ENABLE_MASK
constants pinned by ADR-0027
§Decision outcome (a). Mirrors
the timer::ticks_to_ns
precedent (HAL hosts the
cross-impl pure arithmetic).
Host tests:
- 12 new in mmu::vmsav8::tests pinning every MappingFlags
permutation against the
memory-management.md encoding table.
- 6 new in test-hal::FakeMmu covering MapperFlush::new /
virt_addr / flush / ignore + a
bulk-with-ignore-then-invalidate-
all sequence.
- 5 existing FakeMmu round-trip updated to discharge the new
tests token (via .flush(&mmu) or .ignore()).
Verification:
- cargo fmt --check clean
- cargo clippy (host) clean -D warnings
- cargo kernel-clippy clean -D warnings
- cargo test 182/182 host (was 159; +23)
- cargo kernel-build clean
ADR-0009 §Revision rider already lives on main from PR #22 (the
ADR-0027 prep bundle); this commit's HAL surface matches the rider's
text byte-for-byte.
Refs: ADR-0009, ADR-0027, T-016
T-016 / ADR-0027 second commit. Implements the QEMU virt BSP-side of
MMU activation: VMSAv8 page-table walker, boot-time activation
routine, .boot_pt linker reservation, kernel_entry wiring, and the
four matching audit-log entries (per unsafe-policy.md §3, audit
entries land in the same commit as their unsafe blocks). Smoke
verifies the v1 demo runs cleanly through to "tyrne: all tasks
complete" with the new "tyrne: mmu activated" line inserted in the
expected position.
What lands:
bsp-qemu-virt/src/mmu.rs [new]
QemuVirtAddressSpace + QemuVirtMmu impl of tyrne_hal::Mmu:
- create_address_space wraps a PhysFrame; no allocation
- address_space_root accessor
- activate MSR TTBR0_EL1 + ISB + TLBI VMALLE1 +
DSB ISH + ISB
- map / unmap L0 → L1 → L2 → L3 walk with
FrameProvider-driven intermediate-
table allocation; descriptors encoded
by tyrne_hal::mmu::vmsav8 (host-tested);
returns MapperFlush per ADR-0027 §(c)
- invalidate_tlb_address TLBI VAE1, x + DSB ISH + ISB
- invalidate_tlb_all TLBI VMALLE1 + DSB ISH + ISB
bsp-qemu-virt/src/mmu_bootstrap.rs [new]
unsafe fn mmu_bootstrap() — once-per-boot routine walking
ADR-0027 §Simulation Steps 1-3:
Step 1 populate L0[0], L1[0], L1[1], L2_low[64..73] (9 device
blocks for 0x0800_0000..0x0920_0000), L2_high[0..64]
(64 RAM blocks for 0x4000_0000..0x4800_0000)
Step 2 MSR MAIR_EL1 + TCR_EL1 + TTBR0_EL1 + TTBR1_EL1=xzr + ISB
Step 3 TLBI VMALLE1 + DSB ISH + IC IALLU + DSB ISH + ISB +
SCTLR_EL1.{M,I,C}=1 (RMW via OR-mask) + ISB
bsp-qemu-virt/linker.ld
.boot_pt section reserving 4 × 4 KiB page-aligned frames inside the
.bss range (so _start's BSS-zero loop pre-zeros them):
__boot_pt_l0 → __boot_pt_l1 (table)
__boot_pt_l1 → __boot_pt_l2_low (MMIO) + __boot_pt_l2_high (RAM)
__boot_pt_l2_low → 9 device blocks
__boot_pt_l2_high → 64 RAM blocks
bsp-qemu-virt/src/main.rs (kernel_entry order, post-T-016):
hello print → VBAR_EL1 install (kept early as defensive vector for
any mmu_bootstrap fault per ADR-0027 §Simulation §Step 3) →
cpu.now_ns() snapshot → mmu_bootstrap() → "tyrne: mmu activated"
print → GIC init + DAIF.I unmask → timer banner → kernel-object
setup → IPC + scheduler → start().
docs/audits/unsafe-log.md
Four new entries — UNSAFE-2026-0022 (bootstrap page-table writes),
UNSAFE-2026-0023 (system-register writes — QemuVirtMmu::activate +
bootstrap Steps 2 / 3 SCTLR via Amendment), UNSAFE-2026-0024 (TLB /
I-cache asm — QemuVirtMmu::invalidate_tlb_* + bootstrap Step 3
sweep via Amendment), UNSAFE-2026-0025 (per-call Mmu::map / unmap
descriptor writes). Each follows the Operation / Invariants /
Rejected-alternatives shape per unsafe-policy.md §3. Smoke-
verification Amendments dated 2026-05-09 land on 0022 / 0023 / 0024
recording the bootstrap-site full pass; 0025's status remains
Pending QEMU smoke verification because v1's demo never calls
Mmu::map post-bootstrap.
Verification:
- cargo fmt --check clean
- cargo clippy (host) clean -D warnings
- cargo kernel-clippy clean -D warnings
- cargo test 182/182 host (BSP adds no host tests)
- cargo kernel-build clean
- QEMU smoke (debug) full demo through "tyrne: all tasks
complete"; new line "tyrne: mmu
activated" inserted between "hello"
and "timer ready"; -d guest_errors
shows only the pre-existing PL011
"data written to disabled UART"
warning at +21 instances vs main
(one per byte of the new UART line)
- perf-harness (20 iter) p10 / p50 / p90 = 5.397 / 6.153 /
6.652 ms (pre-T-016: 3.884 / 4.642 /
5.584 ms; Δ ≈ +1.5 / +1.5 / +1.1 ms
for MMU activation under QEMU TCG)
Refs: ADR-0027, T-016
Audit: UNSAFE-2026-0022
Audit: UNSAFE-2026-0023
Audit: UNSAFE-2026-0024
Audit: UNSAFE-2026-0025
T-016 / ADR-0027 final commit. Closes the documentation surface for
B2's MMU-activation milestone:
- docs/analysis/tasks/phase-b/T-016-mmu-activation.md
Status flips Draft → In Progress → Done; review-history row added
naming the closure date and branch.
- docs/roadmap/current.md
New banner records T-016 Done; six-step implementation summary;
headline numbers (182/182 host, p10/p50/p90 boot-to-end band, smoke
delta = one new "tyrne: mmu activated" line); B2 milestone marked
implementation-complete with closure-trio as the next gate.
Active task line reset; "Last completed tasks" promotes T-016 ahead
of T-015. The pre-T-016 banner is preserved as-is per the append-
only roadmap discipline.
- docs/roadmap/phases/phase-b.md
B2 §Status block updated; T-016 row in the milestone breakdown
flips from Draft to Done with the post-merge artefact references
(mmu/vmsav8.rs, mmu.rs, mmu_bootstrap.rs, linker.ld, audit-log
entries, smoke trace).
- docs/analysis/reports/perf-baseline-2026-05-08-post-t-016-mmu-activated.md [new]
20-iteration harness band on the T-016 working tree (HEAD 6494ed2
+ uncommitted impl): min 5.263 ms, p10 5.397, p50 6.153, p90 6.652,
p99 7.742, max 7.742, mean 6.166, stddev 0.555. Comparable apples-
to-apples to the 2026-05-08 pre-ADR-0027 baseline (also 20 iter,
debug, QEMU TCG); the +1.5 ms Δ at p10/p50 captures MMU activation
cost under TCG translation-cache overhead. Real-hardware activation
is sub-100 µs per ADR-0027 §Consequences.
- docs/architecture/hal.md
§Mmu tense hedge flipped: "Will return MapperFlush once T-016 lands"
→ "Returns MapperFlush". Pure VMSAv8 encoder location pointer added
(tyrne_hal::mmu::vmsav8 — host-tested const fns).
- docs/architecture/boot.md
Stage 3 description extended to name the new "kernel_entry"
sequence (vector install → boot_ns snapshot → mmu_bootstrap →
GIC + DAIF unmask → timer banner). Mermaid sequence diagram
refreshed with the post-T-016 ordering. Open-questions list
updated: "MMU activation at boot" and the related "Guard-page
stacks" entries refreshed (high-half migration deferred to
ADR-0033 placeholder; guard-page stacks become reachable now that
MMU is on).
memory-management.md is verified byte-stable from the ADR-0027 PR
(this commit does not edit it; T-016 is the implementation that the
existing chapter described forward-looking, and the chapter's claims
hold byte-for-byte against the implemented code).
Refs: ADR-0009, ADR-0027, T-016
ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one. |
There was a problem hiding this comment.
Sorry @cemililik, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR implements MMU activation for QEMU virt: adds a MapperFlush flush-token discipline to the HAL, host-testable VMSAv8 descriptor encoders, a QemuVirtMmu page-table implementation, a three-stage mmu_bootstrap with linker-reserved bootstrap frames, kernel_entry wiring (timestamp pre-MMU), and updated tests/docs/audits. ChangesMMU Activation (T-016)
Sequence Diagram(s)sequenceDiagram
participant KE as kernel_entry
participant TS as cpu.now_ns()
participant MMU_B as mmu_bootstrap()
participant SR as Sys Registers
participant TLB as TLB/IC
participant GIC as GIC v2
participant CON as Console
KE->>KE: Install VBAR_EL1
KE->>TS: Capture boot timestamp
TS-->>KE: BOOT_NS = now
KE->>MMU_B: Activate MMU
MMU_B->>SR: Program MAIR_EL1, TCR_EL1, TTBR0_EL1
SR-->>MMU_B: Regs configured
MMU_B->>TLB: TLBI VMALLE1, IC IALLU, barriers
TLB-->>MMU_B: Invalidated
MMU_B->>SR: Set SCTLR_EL1 MMU bit
SR-->>MMU_B: MMU enabled
MMU_B-->>KE: Return (MMU active)
KE->>CON: Print "mmu activated"
CON-->>KE: Message output
KE->>GIC: Init GIC v2 (via MMU-mapped MMIO)
GIC-->>KE: Initialized
KE->>SR: DAIFClr I-bit
SR-->>KE: IRQs unmasked
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request implements MMU activation for the bsp-qemu-virt target, introducing a boot-time bootstrap routine that establishes an identity-mapped layout for kernel RAM and device MMIO. It adds the MapperFlush discipline to the Mmu trait to enforce compile-time TLB invalidation checks and provides pure VMSAv8 descriptor encoders in the HAL. Documentation, audit logs, and performance baselines have been updated to reflect these changes. Feedback was provided regarding the error handling in the page-table walker, specifically that returning AlreadyMapped when encountering a block descriptor during an unmap operation is misleading and should be clarified.
| if !is_table { | ||
| return Err(MmuError::AlreadyMapped); | ||
| } |
There was a problem hiding this comment.
Returning MmuError::AlreadyMapped when encountering a block descriptor during a page-table walk is appropriate for the map path, but it is misleading for the unmap path. If unmap is called on a VA that is part of a block mapping, the VA is indeed mapped, but the error suggests a collision during insertion. Since v1 does not support block splitting or unmapping, a more descriptive error or a dedicated check for the unmap case would improve clarity.
Verdict was Approve with nits; this commit closes the actionable
findings. No semantic change to the implementation; comment / doc
hygiene + one dead-import cleanup.
Applied:
1. Axis 4 — kernel_entry VBAR_EL1 install comment now records the
~80 % failure-surface coverage limit: faults from corrupt
`L2_high[0]` (kernel image, where the panic vector lives) or
`L2_low[72]` (PL011 UART block) still produce silent recursive
traps, because the panic vector itself fetches from those
regions. The remaining 20 % is caught by host-tested encoders +
ADR-0027 §Simulation review discipline.
2. Axis 6 — `walk_or_alloc_table`'s block-vs-table comment was
misleading ("on unmap we walk into it" — code returns
`AlreadyMapped`, doesn't walk). Rewritten to reflect actual
behaviour and to flag the future `MmuError::BlockMapped`
variant as the disambiguation hook when block-split lands.
3. Axis 7 — T-016 review-history gains a 2026-05-09 row recording
two deviations from the user-story for future readers:
`MapperFlush::new` is `pub` (not `pub(crate)`) because
cross-crate BSPs must mint tokens; VBAR_EL1 install is
pre-snapshot (not post) for defensive vector reasons. Both
deviations preserve soundness; lint-level discipline replaces
construction-level discipline for the token. The same row also
corrects the prior 2026-05-08 row's stale claims (`-d
guest_errors empty` was wrong — pre-existing PL011 warnings;
`≈ 7–8 ms` was single-run anecdote, not the harness band).
4. Axis 11 — perf-baseline doc now explicitly records
"Working-tree state" so future readers can reproduce the
numbers (HEAD `6494ed2` is the merge-base; the T-016 diff
was uncommitted at measurement time; the numbers reproduce
on commit `b573d83` post-merge).
5. Axis 12 Nit 3 — dropped `BLOCK_OA_MASK_L2` import from
`bsp-qemu-virt/src/mmu.rs` (was only used by the dummy
`const _: u64 = ...` lint-silencer hack with a misleading
"re-exported" comment). The constant remains pub-visible at
`tyrne_hal::mmu::vmsav8::BLOCK_OA_MASK_L2` for any future
consumer.
Verification:
- cargo fmt --check clean
- cargo clippy (host) clean -D warnings
- cargo kernel-clippy clean -D warnings
- cargo test 182/182 host (unchanged)
- cargo kernel-build clean
- QEMU smoke byte-stable from prior commit
(boot-to-end 7.09 ms single-run)
Out of scope for this review-round (defer to follow-up):
- Axis 8 — proper PL011 init to silence the pre-existing
"data written to disabled UART" guest-errors noise. Not a
T-016 regression; opens as a separate B-phase task.
- Axis 12 Nit 4 — split `walk_and_install_leaf` into
`walk_install_leaf` / `walk_remove_leaf` to remove the
dummy `pa: PhysFrame` + `unmap: bool` parameters. Optional
refactor; defer.
- Axis 6 (extension) — introduce `MmuError::BlockMapped`
variant when the first B3+ block-split caller lands.
Refs: ADR-0027, T-016
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
test-hal/src/mmu.rs (1)
146-169:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winKeep
FakeMmualigned with the realMmucontract.
map/unmapcurrently accept unaligned VAs, while the trait requiresMmuError::MisalignedAddress. That can let host tests pass against the fake and fail on the BSP implementation.Suggested fix
-use tyrne_hal::{FrameProvider, MapperFlush, MappingFlags, Mmu, MmuError, PhysFrame, VirtAddr}; +use tyrne_hal::{ + FrameProvider, MapperFlush, MappingFlags, Mmu, MmuError, PhysFrame, VirtAddr, PAGE_SIZE, +}; @@ ) -> Result<MapperFlush, MmuError> { + if !va.0.is_multiple_of(PAGE_SIZE) { + return Err(MmuError::MisalignedAddress); + } if as_.mappings.contains_key(&va) { return Err(MmuError::AlreadyMapped); } @@ fn unmap( &self, as_: &mut FakeAddressSpace, va: VirtAddr, ) -> Result<(MapperFlush, PhysFrame), MmuError> { + if !va.0.is_multiple_of(PAGE_SIZE) { + return Err(MmuError::MisalignedAddress); + } as_.mappings .remove(&va) .map(|(pa, _)| (MapperFlush::new(va), pa))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test-hal/src/mmu.rs` around lines 146 - 169, FakeMmu's map and unmap (methods on FakeMmu operating on FakeAddressSpace with signatures map(&self, as_: &mut FakeAddressSpace, va: VirtAddr, ...) and unmap(&self, as_: &mut FakeAddressSpace, va: VirtAddr, ...)) currently accept unaligned virtual addresses; update both methods to validate VA alignment before doing anything and return Err(MmuError::MisalignedAddress) when va.is_aligned() (or equivalent alignment check used by the real Mmu contract) fails, only proceeding to check existing mappings/insert/remove and return MapperFlush::new(aligned_va) when the VA is properly aligned. Ensure you use the same alignment utility or constant used by the real Mmu so FakeMmu enforces the same contract.docs/analysis/tasks/phase-b/T-016-mmu-activation.md (1)
16-16:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUpdate the stale
hal/src/mmu.rssource links.This PR moved the module to
hal/src/mmu/mod.rs, but these task links still point at the old path and will 404 for readers following the implementation notes.Also applies to: 28-30, 38-38, 149-149
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/analysis/tasks/phase-b/T-016-mmu-activation.md` at line 16, Update the stale source links in docs/analysis/tasks/phase-b/T-016-mmu-activation.md that point to the moved module: replace occurrences of the old target hal/src/mmu.rs (e.g. the [`Mmu`](../../../../hal/src/mmu.rs) link and the other links at lines ~28-30, 38, 149) with the new module path hal/src/mmu/mod.rs so the markdown links resolve to the renamed module; ensure all instances in this file are updated consistently (search for "hal/src/mmu.rs" and change to "hal/src/mmu/mod.rs").
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@bsp-qemu-virt/src/mmu_bootstrap.rs`:
- Around line 173-185: The asm block in the unsafe code uses the option "nomem"
which allows memory operations to be reordered around it, breaking the required
ordering with previous volatile writes that set up page tables. To fix this,
either remove the "nomem" option entirely to make the compiler treat this asm as
a memory barrier, or add explicit memory constraints or clobbers in the asm
macro to ensure the compiler enforces ordering of memory accesses before and
after this block. Locate this fix in the unsafe asm block configuring system
registers (the one setting mair_el1, tcr_el1, ttbr0_el1, and ttbr1_el1).
In `@bsp-qemu-virt/src/mmu.rs`:
- Around line 124-147: The SAFETY comment above the unsafe asm block that writes
ttbr0_el1 (the asm! that uses "msr ttbr0_el1, {0}", "tlbi vmalle1", "dsb ish",
"isb", in(reg) ttbr0, options(nostack, nomem)) must be expanded to state
explicitly: (a) why unsafe is required — direct system register/TLB manipulation
cannot be expressed in safe Rust and requires inline assembly; (b) the
invariants — that ttbr0 is a valid root translation table prepared by
create_address_space (zero-initialised, exclusively-owned PhysFrame with VMSAv8
layout), that callers ensured no concurrent CPU uses this mapping, and that the
sequence TLBI/DSB/ISB fully synchronises TLBs; and (c) why a safer alternative
was rejected — high-level abstractions or CPU instructions wrappers are not
available/insufficient to perform the exact ordered MSR/TLBI/DSB/ISB sequence
with required memory/barrier semantics and register operands, so inline asm! is
the only way to guarantee the correct instruction ordering and operands. Apply
the same pattern of expansion to the other unsafe asm blocks referenced (lines
~166-185, 197-213, 223-248, 305-356, 419-425), referencing their specific asm
sequences and the same three points (need for unsafe, invariants, and rejection
of safer alternatives).
- Around line 150-187: The map implementation currently lets
flags_to_descriptor_bits silently drop EXECUTE when DEVICE is set; update map
(or a helper called before calling flags_to_descriptor_bits) to validate the
requested MappingFlags and return Err(MmuError::InvalidFlags) for any
combinations that cannot be represented (e.g., DEVICE with EXECUTE/USER_EXECUTE
if flags_to_descriptor_bits forces PXN/UXN=1), so reject unrepresentable flag
combos up-front instead of encoding them silently; locate the check in map
(caller of walk_and_install_leaf and flags_to_descriptor_bits) and add the guard
that compares requested flags vs. the set of representable flags and returns
InvalidFlags when there is any mismatch.
- Around line 216-248: The TLBI sequences in invalidate_tlb_address and
invalidate_tlb_all are missing the required store barrier; insert a "dsb ishst"
(DSB ISHST) immediately before the TLBI instruction in both asm! blocks so
descriptor writes are globally observable before invalidation (keep the existing
"dsb ish"/"isb" sequence after TLBI and preserve options(nostack, nomem) and the
unsafe/audit comments).
In `@hal/src/mmu/mod.rs`:
- Around line 233-239: The docstring for MapperFlush::new claims the constructor
is only visible to tyrne_hal implementers but the function is currently pub; fix
this by tightening the visibility to match the docs: change the signature of
MapperFlush::new to pub(crate) const fn new(va: VirtAddr) -> Self so only
in-crate code (and in-tree test impls) can construct flush tokens; update any
call sites if necessary or, alternatively, if you intentionally want downstream
access, change the docstring to state it's public API instead of restricting it
to tyrne_hal implementers.
---
Outside diff comments:
In `@docs/analysis/tasks/phase-b/T-016-mmu-activation.md`:
- Line 16: Update the stale source links in
docs/analysis/tasks/phase-b/T-016-mmu-activation.md that point to the moved
module: replace occurrences of the old target hal/src/mmu.rs (e.g. the
[`Mmu`](../../../../hal/src/mmu.rs) link and the other links at lines ~28-30,
38, 149) with the new module path hal/src/mmu/mod.rs so the markdown links
resolve to the renamed module; ensure all instances in this file are updated
consistently (search for "hal/src/mmu.rs" and change to "hal/src/mmu/mod.rs").
In `@test-hal/src/mmu.rs`:
- Around line 146-169: FakeMmu's map and unmap (methods on FakeMmu operating on
FakeAddressSpace with signatures map(&self, as_: &mut FakeAddressSpace, va:
VirtAddr, ...) and unmap(&self, as_: &mut FakeAddressSpace, va: VirtAddr, ...))
currently accept unaligned virtual addresses; update both methods to validate VA
alignment before doing anything and return Err(MmuError::MisalignedAddress) when
va.is_aligned() (or equivalent alignment check used by the real Mmu contract)
fails, only proceeding to check existing mappings/insert/remove and return
MapperFlush::new(aligned_va) when the VA is properly aligned. Ensure you use the
same alignment utility or constant used by the real Mmu so FakeMmu enforces the
same contract.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9ce8031a-ab6a-46bb-aafc-863b612a592a
📒 Files selected for processing (15)
bsp-qemu-virt/linker.ldbsp-qemu-virt/src/main.rsbsp-qemu-virt/src/mmu.rsbsp-qemu-virt/src/mmu_bootstrap.rsdocs/analysis/reports/perf-baseline-2026-05-08-post-t-016-mmu-activated.mddocs/analysis/tasks/phase-b/T-016-mmu-activation.mddocs/architecture/boot.mddocs/architecture/hal.mddocs/audits/unsafe-log.mddocs/roadmap/current.mddocs/roadmap/phases/phase-b.mdhal/src/lib.rshal/src/mmu/mod.rshal/src/mmu/vmsav8.rstest-hal/src/mmu.rs
Verified each PR #23 inline finding (CodeRabbit + the 2026-05-09 paste) against current code. Applied six; skipped one with reason. Smoke byte-stable; 185/185 host tests (was 182; +3 contract-parity tests). Applied: 1. Finding 1 — `mmu_bootstrap.rs` Step 2 asm: dropped `nomem` from `options(nostack, nomem)` so the compiler treats this asm as a memory clobber and cannot reorder Step 1's volatile descriptor writes past it. (Volatile writes are already source-anchored, but recording the memory effect on the asm is the conservative position the bot asked for.) Architectural global-visibility of the prior stores remains enforced by Step 3's `DSB ISH`. 3. Finding 3 — `QemuVirtMmu::map` (and `FakeMmu::map` for parity) now reject `MappingFlags::DEVICE | MappingFlags::EXECUTE` up-front with `MmuError::InvalidFlags` instead of letting `flags_to_descriptor_bits` silently coerce the EXECUTE bit into PXN=1/UXN=1. MMIO is never executable per ADR-0027 §Decision outcome (b); the trait surface should reject the request rather than silently drop EXECUTE. Two new host tests pin the rejection (one in test-hal::tests). 4. Finding 4 — `DSB ISHST` inserted before TLBI in: - `QemuVirtMmu::activate` — `MSR TTBR0_EL1; ISB; DSB ISHST; TLBI VMALLE1; DSB ISH; ISB` - `QemuVirtMmu::invalidate_tlb_address` — `DSB ISHST; TLBI VAE1, x; DSB ISH; ISB` - `QemuVirtMmu::invalidate_tlb_all` — `DSB ISHST; TLBI VMALLE1; DSB ISH; ISB` Aligns with the ARM ARM canonical pattern + Linux's `arch/arm64/include/asm/tlbflush.h`: descriptor stores must be globally observable inner-shareable BEFORE the TLBI broadcast, otherwise on SMP a peer core could receive the TLBI, walk the still-stale descriptor on a TLB miss, and re-cache it. Single-core v1 doesn't hit this race; SMP-readiness is the forward-compat win per ADR-0027 §"Why DSB ISH". `nomem` removed from the same asm blocks so the compiler enforces ordering. `mmu_bootstrap`'s Step 3 intentionally not touched: its TLBI sweeps pre-MMU-on state on a single core with no concurrent walker — DSB ISHST adds no correctness, and Step 3's existing `DSB ISH` after TLBI already drains prior stores before the SCTLR.M=1 flip. 5. Finding 5 — `MapperFlush::new` doc rewritten to accurately reflect the `pub` (not `pub(crate)`) visibility: cross-crate BSP `Mmu` impls (e.g., `bsp-qemu-virt::QemuVirtMmu`) need to mint tokens at every `Mmu::map`/`unmap` return; the discipline that "kernel code never constructs tokens directly" is enforced by convention + reviewer attention, not by visibility. Soundness note added: a misbehaving caller can mint extra tokens but cannot cause memory unsoundness — the token's only "power" is to call `Mmu::invalidate_tlb_address` (a TLB hint, not a memory-safety operation). Cross-references the 2026-05-09 review-round entry in T-016's review-history (already added in commit `d9dc53d`). 6. Finding 6 — T-016 task file (`docs/analysis/tasks/phase-b/T-016- mmu-activation.md`) had stale `hal/src/mmu.rs` markdown links that 404 after the rename to `hal/src/mmu/mod.rs`. Sed-replaced all six occurrences (lines 16, 28, 30, 38, 105, 149). 7. Finding 7 — `FakeMmu::map` and `FakeMmu::unmap` now validate VA alignment up-front and return `MmuError::MisalignedAddress` when `va.0 % PAGE_SIZE != 0`, mirroring `QemuVirtMmu`'s contract. Without this, kernel-side code that passes unaligned VAs would silently succeed under host tests and fail on real hardware. Two new host tests (`map_rejects_unaligned_va`, `unmap_rejects_unaligned_va`) plus a third (`map_rejects_device_plus_execute`) for Finding 3 parity. Skipped: 2. Finding 2 — bot asked to expand every `// SAFETY:` comment to inline (a) "why unsafe is required", (b) invariants, and (c) "why safer alternative was rejected". The project pattern (per `docs/standards/unsafe-policy.md §3`) is concise SAFETY comments + canonical Rejected-Alternatives discussion in the audit-log entry. Inlining the audit-log content into every SAFETY comment would create redundancy + drift hazards (the audit-log Amendment discipline keeps the log canonical; comments would lag). The existing comments already carry invariants + the audit reference, matching the pattern set by UNSAFE-2026-0019 / 0020 / 0021's pre-T-016 sites in `bsp-qemu-virt/src/main.rs` and `bsp-qemu-virt/src/exceptions.rs`. Verification: - cargo fmt --check clean - cargo clippy (host) clean -D warnings - cargo kernel-clippy clean -D warnings - cargo test 185/185 host (was 182; +3: map_rejects_unaligned_va, unmap_rejects_unaligned_va, map_rejects_device_plus_execute) - cargo kernel-build clean - QEMU smoke byte-stable; full demo through "tyrne: all tasks complete"; boot-to-end 5.34 ms single-run (within the prior harness band) Refs: ADR-0027, T-016 Audit: UNSAFE-2026-0023 Audit: UNSAFE-2026-0024
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
🧹 Nitpick comments (2)
test-hal/src/mmu.rs (1)
156-162: 💤 Low valueConsider extracting VA alignment validation to a helper.
The alignment check logic is duplicated between
map(lines 156-162) andunmap(lines 184-188). While this is not a critical issue, extracting it to a private helper would improve maintainability:♻️ Optional refactor to eliminate duplication
Add a helper method to the impl block:
+ fn validate_va_alignment(va: VirtAddr) -> Result<(), MmuError> { + if !va.0.is_multiple_of(PAGE_SIZE) { + return Err(MmuError::MisalignedAddress); + } + Ok(()) + } + fn map( &self, as_: &mut FakeAddressSpace, va: VirtAddr, pa: PhysFrame, flags: MappingFlags, _frames: &mut dyn FrameProvider, ) -> Result<MapperFlush, MmuError> { - // Mirror the real `Mmu` contract: VA must be `PAGE_SIZE`-aligned. - // Without this check, kernel-side code that passes unaligned VAs - // would silently succeed under host tests and fail on real hardware. - // (PR `#23` review-round 2026-05-09 Finding 7.) - if !va.0.is_multiple_of(PAGE_SIZE) { - return Err(MmuError::MisalignedAddress); - } + Self::validate_va_alignment(va)?;And similarly in
unmap:fn unmap( &self, as_: &mut FakeAddressSpace, va: VirtAddr, ) -> Result<(MapperFlush, PhysFrame), MmuError> { - // Mirror the real `Mmu` contract: VA must be `PAGE_SIZE`-aligned. - // (PR `#23` review-round 2026-05-09 Finding 7.) - if !va.0.is_multiple_of(PAGE_SIZE) { - return Err(MmuError::MisalignedAddress); - } + Self::validate_va_alignment(va)?;The Finding 7 comment could move to the helper's doc-comment.
Also applies to: 184-188
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test-hal/src/mmu.rs` around lines 156 - 162, The VA alignment check in map and unmap is duplicated; extract it into a private helper (e.g., fn validate_va_alignment(va: VirtualAddress) -> Result<(), MmuError>) inside the same impl for Mmu so both map and unmap call this helper instead of duplicating the if !va.0.is_multiple_of(PAGE_SIZE) check, returning Err(MmuError::MisalignedAddress) on failure; move the "Finding 7" explanatory comment into the helper's doc-comment for context and update calls in map and unmap to use validate_va_alignment(va)?.bsp-qemu-virt/src/mmu_bootstrap.rs (1)
108-112: 💤 Low valueConsider making "why safer alternatives rejected" explicit in unsafe justifications.
The coding guideline requires each
unsafeblock to document:
(a) why it is needed
(b) what invariants it upholds
(c) why safer alternatives were rejectedAll three unsafe blocks (Step 1, Step 2, Step 3) clearly document (a) and (b), but (c) is implicit. While it's evident that direct manipulation of hardware page tables and system registers requires
unsafe, explicitly stating this would improve guideline compliance.For example, Step 1's comment could add:
"(c) Safer alternatives rejected: direct manipulation of hardware page-table descriptors requires raw pointer writes; no safe abstraction exists at this bootstrap stage."As per coding guidelines: "Every
unsafeblock must have a comment explaining (a) why it is needed, (b) what invariants it upholds, (c) why safer alternatives were rejected."Also applies to: 166-185, 201-213
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@bsp-qemu-virt/src/mmu_bootstrap.rs` around lines 108 - 112, Add explicit "(c) Safer alternatives rejected" text to each SAFETY comment for the unsafe blocks that build the page-table chain: include a sentence such as "Safer alternatives rejected: direct manipulation of hardware page-table descriptors and system registers is required at this bootstrap stage and no safe abstraction is available," so the SAFETY comment above the L0→L1→L2 write (the block commenting "write the L0 → L1 → L2 chain") and the two other unsafe blocks referenced (the Step 1, Step 2, Step 3 blocks around the regions you noted) explicitly document why a safe alternative was rejected; ensure the new text is added to the existing SAFETY comment for each unsafe block without changing the technical invariants already described.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@bsp-qemu-virt/src/mmu_bootstrap.rs`:
- Around line 108-112: Add explicit "(c) Safer alternatives rejected" text to
each SAFETY comment for the unsafe blocks that build the page-table chain:
include a sentence such as "Safer alternatives rejected: direct manipulation of
hardware page-table descriptors and system registers is required at this
bootstrap stage and no safe abstraction is available," so the SAFETY comment
above the L0→L1→L2 write (the block commenting "write the L0 → L1 → L2 chain")
and the two other unsafe blocks referenced (the Step 1, Step 2, Step 3 blocks
around the regions you noted) explicitly document why a safe alternative was
rejected; ensure the new text is added to the existing SAFETY comment for each
unsafe block without changing the technical invariants already described.
In `@test-hal/src/mmu.rs`:
- Around line 156-162: The VA alignment check in map and unmap is duplicated;
extract it into a private helper (e.g., fn validate_va_alignment(va:
VirtualAddress) -> Result<(), MmuError>) inside the same impl for Mmu so both
map and unmap call this helper instead of duplicating the if
!va.0.is_multiple_of(PAGE_SIZE) check, returning
Err(MmuError::MisalignedAddress) on failure; move the "Finding 7" explanatory
comment into the helper's doc-comment for context and update calls in map and
unmap to use validate_va_alignment(va)?.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8e71bca2-4454-4cfe-a0f7-b69aeb443c77
📒 Files selected for processing (5)
bsp-qemu-virt/src/mmu.rsbsp-qemu-virt/src/mmu_bootstrap.rsdocs/analysis/tasks/phase-b/T-016-mmu-activation.mdhal/src/mmu/mod.rstest-hal/src/mmu.rs
🚧 Files skipped from review as they are similar to previous changes (2)
- hal/src/mmu/mod.rs
- bsp-qemu-virt/src/mmu.rs
Verified each PR #23 nitpick against current code; one applied, one skipped. Applied: - **Finding 1 — Step 1 / Step 2 / Step 3 SAFETY comments now include explicit "Safer alternatives rejected:" text** in `mmu_bootstrap.rs`, per `unsafe-policy.md §1` (which requires SAFETY comments to state invariants AND rejected alternatives AND the audit reference; the prior round's SAFETY comments carried invariants + audit ref but not the rejected-alts axis): - Step 1 (page-table descriptor writes): rejects materialising a `&mut [u64; 512]` reference into the bootstrap frames as the "safer" alternative — that materialisation is itself the audited operation; wrapping it would obscure rather than remove the audit point. `core::ptr::write_volatile` is the most honest expression of what the bootstrap does. - Step 2 (system-register configuration): rejects pulling in a `cortex-a` / `aarch64-cpu` crate as a register-access wrapper. Such a dependency would be load-bearing for a single bootstrap site and conflicts with ADR-0014's "minimum necessary surface" dependency policy. Inline asm is the language-supplied minimal surface for `MSR`/`MRS`. - Step 3 (TLB / I-cache invalidate + SCTLR enable): rejects safe alternatives because `TLBI`, `IC IALLU`, `DSB`, `ISB`, and `MSR SCTLR_EL1` have no safe-Rust equivalent — each barrier's effect is global to the CPU pipeline state and cannot be modelled in any safe abstraction. Skipped: - **Finding 2 — extract VA-alignment check into `validate_va_alignment` helper in `test-hal/src/mmu.rs`.** The duplication is two identical 3-line if-blocks (one in `FakeMmu::map`, one in `FakeMmu::unmap`). CLAUDE.md non-negotiable rules: "Don't add features, refactor, or introduce abstractions beyond what the task requires. ... Three similar lines is better than a premature abstraction." A helper function (signature + body + close = 4 lines) plus two call sites (`validate_va_alignment(va)?;` × 2) totals the same LOC as the current duplication, while adding a function-call layer + a doc- comment to maintain. The win in "less duplication" doesn't pay for the cost in extra surface; the duplication is both small and visually obvious, and the methods that contain it are short enough that the if-block reads inline as a precondition. Verification: - cargo fmt --check clean - cargo clippy (host) clean -D warnings - cargo kernel-clippy clean -D warnings - cargo test 185/185 host (unchanged) - cargo kernel-build clean Refs: ADR-0027, T-016 Audit: UNSAFE-2026-0022 Audit: UNSAFE-2026-0023 Audit: UNSAFE-2026-0024
Summary
Implements T-016 (B2 milestone) per ADR-0027: turns the MMU on with an identity-mapped layout, lights up
TTBR0_EL1with kernel + GIC + UART regions, configuresMAIR_EL1for device-nGnRnE / normal-cached, and lands the#[must_use]MapperFlushflush-token discipline at theMmutrait surface.After this lands, every load and instruction-fetch goes through the live translation regime, MMIO accesses inherit the device-nGnRnE attributes, and forgetting to flush after a mapping mutation becomes a
unused_must_uselint failure (denied workspace-wide) instead of a reviewer-attention concern.Three commits (bisectable)
feat(hal): MapperFlush flush-token + VMSAv8 descriptor encoders(8c3c166)Mmu::map→Result<MapperFlush, MmuError>;Mmu::unmap→Result<(MapperFlush, PhysFrame), MmuError>tyrne_hal::mmu::vmsav8module: pureconst fndescriptor encoders +MAIR_EL1_VALUE/TCR_EL1_VALUE/SCTLR_EL1_MMU_ENABLE_MASKtyrne-test-hal::FakeMmuupdated to mint tokens; existing round-trip tests discharge themhal/src/mmu.rs→hal/src/mmu/mod.rs(rename) +hal/src/mmu/vmsav8.rs(new)feat(bsp-qemu-virt): activate MMU with identity layout + MapperFlush(b573d83)bsp-qemu-virt/src/mmu.rs(new):QemuVirtMmuimpl with L0 → L1 → L2 → L3 walker,activate,invalidate_tlb_*bsp-qemu-virt/src/mmu_bootstrap.rs(new):unsafe fn mmu_bootstrap()— once-per-boot routine running ADR-0027 §Simulation Steps 1–3bsp-qemu-virt/linker.ld:.boot_ptreservation (4 × 4 KiB inside.bss)bsp-qemu-virt/src/main.rs:kernel_entrywired (vector install →boot_ns→mmu_bootstrap→tyrne: mmu activated→ GIC + DAIF unmask → timer banner)docs(roadmap): T-016 Done; B2 implementation-complete; post-MMU baseline(cdb2717)current.md+phase-b.mdupdated; T-016 task status flips Draft → In Progress → Donehal.md§Mmu tense hedge flipped to past tense;boot.mdStage 3 + sequence diagram refreshedperf-baseline-2026-05-08-post-t-016-mmu-activated.md(20-iter harness band)ADR-0027 §Simulation table (the load-bearing design checklist)
SCTLR_EL1.M = 0, MMU off, caches off, all bootstrap frames zeroL0[0],L1[0],L1[1],L2_low[64..73](9 device blocks),L2_high[0..64](64 RAM blocks)MSR MAIR_EL1 + TCR_EL1 + TTBR0_EL1 + TTBR1_EL1=xzr+ISBTLBI VMALLE1+DSB ISH+IC IALLU+DSB ISH+ISB+ RMWSCTLR_EL1.{M,I,C}=1+ISBkernel_entrytyrne: mmu activated; demo runsThe critical correctness moment is Step 3's final
ISB: it drains the pipeline so the next instruction-fetch goes through the freshly-installed regime. Any error in Step 1 (typoed descriptor, missing AF, wrong attribute index) produces a Translation Fault on that next fetch.Smoke trace (post-T-016)
Every line other than the new
tyrne: mmu activatedinsertion is byte-stable from the post-T-015 baseline.Test plan
cargo fmt --all -- --check— cleancargo host-clippy(-D warnings) — cleancargo kernel-clippy(-D warnings) — cleancargo host-test— 182/182 pass (was 159 pre-T-016; +23 = 12 vmsav8 encoder tests + 6 MapperFlush semantics tests + 5 existing FakeMmu round-trip tests updated for the token return type)cargo kernel-build— cleantyrne: all tasks complete;tyrne: mmu activatedline printed in the expected position-d int,unimp,guest_errors— only the pre-existing PL011 "data written to disabled UART" warning (358 instances onmain, 379 on T-016; the +21 delta is exactly one byte per character of the new UART line). No new fault classes.tools/perf-harness.sh --report=...(20 iter, debug, QEMU TCG) — p10 / p50 / p90 = 5.397 / 6.153 / 6.652 ms. Pre-T-016 baseline was 3.884 / 4.642 / 5.584 ms. The Δ ≈ +1.5 ms is QEMU TCG translation-cache overhead at MMU bring-up; on real hardware the activation cost is sub-100 µs per ADR-0027 §Consequences.What's deferred
Everything in T-016 §Out of scope: high-half kernel migration (ADR-0033 placeholder), physical-frame allocator (PMM —
Mmu::map'sFrameProvideris wired but no kernel caller yet), block-split-on-collision, per-section kernel-image permissions (.textRX vs.rodataR vs.bss/.dataRW — ADR-0034 placeholder), multi-core TLB shootdown, page-fault routing into the capability system, ASID assignment per task,MappingFlags::USERruntime exercise,Mmu::lookup.Refs: ADR-0009, ADR-0027, T-016
Audit: UNSAFE-2026-0022 / 0023 / 0024 / 0025
Summary by CodeRabbit
New Features
Documentation
Tests