Skip to content

T-016: activate MMU with identity-mapped kernel + MapperFlush flush-token discipline#23

Merged
cemililik merged 6 commits into
mainfrom
t-016-mmu-activation
May 9, 2026
Merged

T-016: activate MMU with identity-mapped kernel + MapperFlush flush-token discipline#23
cemililik merged 6 commits into
mainfrom
t-016-mmu-activation

Conversation

@cemililik
Copy link
Copy Markdown
Collaborator

@cemililik cemililik commented May 8, 2026

Summary

Implements T-016 (B2 milestone) per ADR-0027: turns the MMU on with an identity-mapped layout, lights up TTBR0_EL1 with kernel + GIC + UART regions, configures MAIR_EL1 for device-nGnRnE / normal-cached, and lands the #[must_use] MapperFlush flush-token discipline at the Mmu trait 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_use lint failure (denied workspace-wide) instead of a reviewer-attention concern.

Three commits (bisectable)

  1. feat(hal): MapperFlush flush-token + VMSAv8 descriptor encoders (8c3c166)
    • HAL trait: Mmu::mapResult<MapperFlush, MmuError>; Mmu::unmapResult<(MapperFlush, PhysFrame), MmuError>
    • New tyrne_hal::mmu::vmsav8 module: pure const fn descriptor encoders + MAIR_EL1_VALUE / TCR_EL1_VALUE / SCTLR_EL1_MMU_ENABLE_MASK
    • tyrne-test-hal::FakeMmu updated to mint tokens; existing round-trip tests discharge them
    • Module restructure: hal/src/mmu.rshal/src/mmu/mod.rs (rename) + hal/src/mmu/vmsav8.rs (new)
  2. feat(bsp-qemu-virt): activate MMU with identity layout + MapperFlush (b573d83)
    • bsp-qemu-virt/src/mmu.rs (new): QemuVirtMmu impl 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–3
    • bsp-qemu-virt/linker.ld: .boot_pt reservation (4 × 4 KiB inside .bss)
    • bsp-qemu-virt/src/main.rs: kernel_entry wired (vector install → boot_nsmmu_bootstraptyrne: mmu activated → GIC + DAIF unmask → timer banner)
    • Audit log: UNSAFE-2026-0022 / 0023 / 0024 / 0025 + bootstrap-Amendments on 0023 / 0024
  3. docs(roadmap): T-016 Done; B2 implementation-complete; post-MMU baseline (cdb2717)
    • current.md + phase-b.md updated; T-016 task status flips Draft → In Progress → Done
    • hal.md §Mmu tense hedge flipped to past tense; boot.md Stage 3 + sequence diagram refreshed
    • New perf-baseline-2026-05-08-post-t-016-mmu-activated.md (20-iter harness band)
    • Smoke-verification Amendments dated 2026-05-09 on UNSAFE-2026-0022 / 0023 / 0024

ADR-0027 §Simulation table (the load-bearing design checklist)

Step Pre Action Post
0 SCTLR_EL1.M = 0, MMU off, caches off, all bootstrap frames zero (entry) unchanged
1 bootstrap frames zero populate L0[0], L1[0], L1[1], L2_low[64..73] (9 device blocks), L2_high[0..64] (64 RAM blocks) all 4 frames populated; PC still at PA
2 frames populated, MMU off MSR MAIR_EL1 + TCR_EL1 + TTBR0_EL1 + TTBR1_EL1=xzr + ISB system regs configured; MMU still off
3 regs configured TLBI VMALLE1 + DSB ISH + IC IALLU + DSB ISH + ISB + RMW SCTLR_EL1.{M,I,C}=1 + ISB MMU ON; PC continues at identity-mapped VA
4 MMU on return to kernel_entry print tyrne: mmu activated; demo runs

The 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)

tyrne: hello from kernel_main
tyrne: mmu activated                ← NEW
tyrne: timer ready (62500000 Hz, resolution 16 ns)
tyrne: starting cooperative scheduler
tyrne: task B — waiting for IPC
tyrne: task A -- sending IPC
tyrne: task B — received IPC (label=0xaaaa); replying
tyrne: task A — received reply (label=0xbbbb); done
tyrne: all tasks complete
tyrne: boot-to-end elapsed = X ns

Every line other than the new tyrne: mmu activated insertion is byte-stable from the post-T-015 baseline.

Test plan

  • cargo fmt --all -- --check — clean
  • cargo host-clippy (-D warnings) — clean
  • cargo kernel-clippy (-D warnings) — clean
  • cargo host-test182/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 — clean
  • QEMU smoke — full demo through tyrne: all tasks complete; tyrne: mmu activated line printed in the expected position
  • -d int,unimp,guest_errors — only the pre-existing PL011 "data written to disabled UART" warning (358 instances on main, 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.
  • B2 closure-trio (business + security + performance) — landed as a follow-on per the B1 closure precedent (NOT required for T-016 Done flip).

What's deferred

Everything in T-016 §Out of scope: high-half kernel migration (ADR-0033 placeholder), physical-frame allocator (PMM — Mmu::map's FrameProvider is wired but no kernel caller yet), block-split-on-collision, per-section kernel-image permissions (.text RX vs .rodata R vs .bss/.data RW — ADR-0034 placeholder), multi-core TLB shootdown, page-fault routing into the capability system, ASID assignment per task, MappingFlags::USER runtime exercise, Mmu::lookup.

Refs: ADR-0009, ADR-0027, T-016
Audit: UNSAFE-2026-0022 / 0023 / 0024 / 0025

Summary by CodeRabbit

  • New Features

    • MMU now activates during kernel boot; boot ordering updated (vector install, timestamp baseline before MMU, interrupt controller init/unmask).
    • New MMU implementation and a mapping API that returns a flush-token to control TLB invalidation.
  • Documentation

    • Boot, HAL/MMU, roadmap, audit, and performance baseline docs updated to reflect MMU activation and design decisions.
  • Tests

    • Host-side MMU tests updated to exercise the flush-token discipline and alignment/flag rules.

cemililik added 3 commits May 9, 2026 00:21
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
@qodo-code-review
Copy link
Copy Markdown

ⓘ 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.

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @cemililik, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Review Change Stack

Warning

Rate limit exceeded

@cemililik has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 51 minutes and 39 seconds before requesting another review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b65580c3-eaa7-4903-915e-c4aa6f083f97

📥 Commits

Reviewing files that changed from the base of the PR and between d5d88d4 and b66d743.

📒 Files selected for processing (1)
  • bsp-qemu-virt/src/mmu_bootstrap.rs
📝 Walkthrough

Walkthrough

This 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.

Changes

MMU Activation (T-016)

Layer / File(s) Summary
HAL Contracts & Token
hal/src/lib.rs, hal/src/mmu/mod.rs
Expose mmu and add MapperFlush with constructor/accessor and flush/ignore; Mmu::mapResult<MapperFlush, MmuError>; Mmu::unmapResult<(MapperFlush, PhysFrame), MmuError>.
VMSAv8 Descriptor Encoders
hal/src/mmu/vmsav8.rs
Pure const fn encoders and constants (MAIR/TCR/SCTLR/AP/SH), flags_to_descriptor_bits, block_descriptor, page_descriptor, table_descriptor, and unit tests.
QemuVirtMmu Implementation
bsp-qemu-virt/src/mmu.rs
QemuVirtAddressSpace wrapper and QemuVirtMmu; activate programs TTBR0_EL1 with ISB/DSB/TLBI barriers; map/unmap perform L0→L3 walks, install/clear L3 leaf descriptors, validate flags/alignment, and return MapperFlush (plus PhysFrame on unmap); TLB invalidate helpers added.
Bootstrap & Linker Reservation
bsp-qemu-virt/linker.ld, bsp-qemu-virt/src/mmu_bootstrap.rs
Linker reserves four 4KiB bootstrap page-table frames in .bss (L0/L1/L2_low/L2_high). mmu_bootstrap() populates descriptor chain with identity layout (2MiB L2 blocks for MMIO and RAM/kernel), programs MAIR/TCR/TTBR0 (clears TTBR1), invalidates TLB/I-cache, and enables the MMU via SCTLR_EL1 RMW.
Kernel Entry Wiring
bsp-qemu-virt/src/main.rs
Add mmu and mmu_bootstrap modules; install EL1 vector table, snapshot cpu.now_ns() into BOOT_NS before MMU activation, call mmu_bootstrap(), print activation trace, initialize GIC and unmask IRQs after MMU, and reorder timer-banner emission.
Test Implementation & Validation
test-hal/src/mmu.rs
FakeMmu updated to return MapperFlush tokens; tests added/updated to assert token VA, flush() per-address invalidation, ignore() no-op, alignment and invalid-flag rejections, and bulk mapping behavior with .ignore().
Documentation & Audit
docs/architecture/boot.md, docs/architecture/hal.md, docs/audits/unsafe-log.md, docs/analysis/*, docs/roadmap/*
Boot-flow and boot-time diagram updated, HAL Mmu docs updated to reflect MapperFlush discipline, four UNSAFE audit entries (UNSAFE-2026-0022..0025) appended, perf baseline added, and T-016/T-016 milestone status updated.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 Hop, tiny rabbit, through page-table rows,

Four frames in .boot_pt where the cold zero grows.
TTBR wakes, barriers chant their rhymes,
TLBs hush, and the kernel counts its times.
MMU blooms — the bunny dances in mapped clover.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly summarizes the main change: MMU activation with identity-mapped kernel and the new MapperFlush flush-token discipline, which are the core features of this T-016 implementation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t-016-mmu-activation

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread bsp-qemu-virt/src/mmu.rs
Comment on lines +398 to +400
if !is_table {
return Err(MmuError::AlreadyMapped);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Keep FakeMmu aligned with the real Mmu contract.

map / unmap currently accept unaligned VAs, while the trait requires MmuError::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 win

Update the stale hal/src/mmu.rs source 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6494ed2 and d9dc53d.

📒 Files selected for processing (15)
  • bsp-qemu-virt/linker.ld
  • bsp-qemu-virt/src/main.rs
  • bsp-qemu-virt/src/mmu.rs
  • bsp-qemu-virt/src/mmu_bootstrap.rs
  • docs/analysis/reports/perf-baseline-2026-05-08-post-t-016-mmu-activated.md
  • docs/analysis/tasks/phase-b/T-016-mmu-activation.md
  • docs/architecture/boot.md
  • docs/architecture/hal.md
  • docs/audits/unsafe-log.md
  • docs/roadmap/current.md
  • docs/roadmap/phases/phase-b.md
  • hal/src/lib.rs
  • hal/src/mmu/mod.rs
  • hal/src/mmu/vmsav8.rs
  • test-hal/src/mmu.rs

Comment thread bsp-qemu-virt/src/mmu_bootstrap.rs
Comment thread bsp-qemu-virt/src/mmu.rs
Comment thread bsp-qemu-virt/src/mmu.rs
Comment thread bsp-qemu-virt/src/mmu.rs
Comment thread hal/src/mmu/mod.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
@cemililik
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
test-hal/src/mmu.rs (1)

156-162: 💤 Low value

Consider extracting VA alignment validation to a helper.

The alignment check logic is duplicated between map (lines 156-162) and unmap (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 value

Consider making "why safer alternatives rejected" explicit in unsafe justifications.

The coding guideline requires each unsafe block to document:
(a) why it is needed
(b) what invariants it upholds
(c) why safer alternatives were rejected

All 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 unsafe block 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

📥 Commits

Reviewing files that changed from the base of the PR and between d9dc53d and d5d88d4.

📒 Files selected for processing (5)
  • bsp-qemu-virt/src/mmu.rs
  • bsp-qemu-virt/src/mmu_bootstrap.rs
  • docs/analysis/tasks/phase-b/T-016-mmu-activation.md
  • hal/src/mmu/mod.rs
  • test-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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant