Skip to content

Development #10 — T-012 (B1 IRQ infrastructure) + B0 closure retros#10

Merged
cemililik merged 11 commits into
mainfrom
development
Apr 29, 2026
Merged

Development #10 — T-012 (B1 IRQ infrastructure) + B0 closure retros#10
cemililik merged 11 commits into
mainfrom
development

Conversation

@cemililik
Copy link
Copy Markdown
Collaborator

@cemililik cemililik commented Apr 28, 2026

Summary

  • B1 substantially landed. T-012 — exception vector table at VBAR_EL1, GIC v2 driver for QEMU virt, generic-timer IRQ wiring, and idle's WFI activation — promoted to In Review. Pairs with T-013's EL drop (already on main from PR Development #9 — T-011 + T-008 + T-013 (B0 hygiene close + B1 EL drop) #9) to give Phase B its full kernel-takes-an-interrupt foundation.
  • B0 closure retros. Business + security retros for the B0-via-PR-Development #9 — T-011 + T-008 + T-013 (B0 hygiene close + B1 EL drop) #9 milestone, plus the four post-merge Adjustment items applied (post-fix-sweep DoD rule, conduct-approval-review skill canonised, T-012 design-first DoD update, security-review consolidated artefact).
  • Aliasing discipline extended. ADR-0021's no-&mut-across-cpu.context_switch rule extends to the IRQ-handler frame via a §Revision notes Amendment; v1's irq_entry is ack-and-ignore and vacuously satisfies the discipline; the rule is in writing before the first scheduler-touching IRQ handler lands.

What landed (commit-by-commit)

Commit Theme Notes
b60978d docs(roadmap) Close B0 + promote T-008 / T-011 / T-013 to Done.
f9c145c docs B0 closure business + security reviews + 4 retro Adjustment items.
8800b0b fix(analysis) Apply approval-review findings on B0 closure reviews (Orta #1: Sign-off → Threat-model impact + separate Verdict; Düşük #1: Appendix sourcing line).
c366200 docs(arch) T-012 design-first — docs/architecture/exceptions.md lands ahead of code (per B0 sec-review §8).
a043079 feat(bsp) T-012 step 1+2+3 — GIC v2 driver ( bsp-qemu-virt/src/gic.rs), 16-entry vector table at tyrne_vectors ( bsp-qemu-virt/src/vectors.s), MSR VBAR_EL1 install + MSR DAIFClr, #0x2 unmask.
b4ed68c feat(bsp,hal) T-012 step 4+5 — <QemuVirtCpu as Timer>::arm_deadline / cancel_deadline real impls; irq_entry ack-and-ignore on PPI 27; idle_entry body uses cpu.wait_for_interrupt(); tyrne_hal::timer::ns_to_ticks helper + 5 host tests (148/148 host suite green).
28c5ce9 docs T-012 step 6+7 — ADR-0021 §Revision notes Amendment (IRQ-handler aliasing discipline) + UNSAFE-2026-0014 Amendment (irq_entry future site) + ADR-0010 §Revision notes (arm_deadline / cancel_deadline live) + ADR-0022 first rider Sub-rider closure + T-012 task file → In Review with all AC/DoD checkboxes flipped.
6d2f725 docs Back-fill <TBD-commit-3> 28c5ce9 SHA across 8 files (matches the UNSAFE-2026-0011 SHA back-fill pattern).
67ff867 docs Review-fix sweep — Düşük #1 (idle_entry doc-comment was stale; rewrote to describe current WFI behaviour) + Düşük #2 (ADR-0021 Amendment header date 2026-04-27 → 2026-04-28).

New audit-log entries

Three new entries; one Amendment on an existing entry. All append-only per unsafe-policy.md §3.

  • UNSAFE-2026-0019QemuVirtGic MMIO surface (distributor + CPU interface; init sequence + IrqController trait impl). Introduced a043079.
  • UNSAFE-2026-0020 — EL1 exception vector table install + asm trampolines (MSR VBAR_EL1, vectors.s 16-entry table, IRQ trampoline saving 192-byte TrapFrame, panic-class trampolines for unhandled vectors). Introduced a043079.
  • UNSAFE-2026-0021 — EL1 virtual generic-timer compare-register writes (CNTV_CVAL_EL0, CNTV_CTL_EL0 enable / mask). Introduced b4ed68c.
  • UNSAFE-2026-0014 Amendment — names irq_entry as a future site of the same momentary-&mut pattern; v1 has no live citation yet (irq_entry is ack-and-ignore). Introduced 28c5ce9.

ADR amendments (no new ADRs)

  • ADR-0010 §Revision notes — arm_deadline / cancel_deadline deferred halves are now live on QemuVirtCpu; pointer to UNSAFE-2026-0021. Trait surface unchanged.
  • ADR-0021 §Revision notes — Amendment extending the no-&mut-across-context-switch rule to the IRQ-handler frame. Three numbered future-call-site rules; rejected alternatives recorded.
  • ADR-0022 §Revision notes — first-rider Sub-rider closure paragraph: T-009 (time source) ✅ + T-012 (IRQ delivery) ✅ ⇒ idle_entry switches from core::hint::spin_loop() + yield to cpu.wait_for_interrupt() + yield. ADR's chosen option matches shipped code from this commit forward.

Architecture documentation

  • docs/architecture/exceptions.md — new (~262 lines) — vector table layout (Mermaid 4×4 grid), IRQ dispatch flow (Mermaid sequence), GIC v2 driver shape (per-register table + init sequence), generic-timer IRQ wiring, idle WFI activation, IRQ-handler aliasing discipline. Implementation map with per-step status flags.
  • docs/architecture/README.mdexceptions.md row promoted from Design to Accepted (v0.0.1).

Reviews included in this PR

  • B0 closure business retro — Phase A vs B0 cost-of-arc (~30 commits, 2 days vs ~50 commits, 6 days; 109 → 143 host tests); ADR-0025 §Rule 1 + §Rule 2 first empirical run; audit-log Amendment discipline codified; 8 Adjustment items (4 done, 4 pending — one of which, T-012 promotion, is closed by this PR).
  • B0 closure consolidated security review — 8-axis adversarial review of UNSAFE-2026-0006..0018 + 4 Amendments. Verdict: Approve. No Yüksek / Orta findings. Two threat-model shifts named (ADR-0024 EL1 structural property; ADR-0021 aliasing model with Stacked-Borrows compliance).
  • T-009 mini-retro — already on main; cited by the B0 retro as the agenda-loader.

New skill

  • .claude/skills/conduct-approval-review/SKILL.md — distinct from perform-code-review / perform-security-review / conduct-review. 9-step procedure for the "you've already done the work, now confirm it's coherent and ready" review pattern. Three validation runs cited as the canonisation justification.

DoD status

Gate Status
cargo fmt --all -- --check ✅ clean
cargo host-clippy ✅ clean (-D warnings)
cargo kernel-clippy ✅ clean
cargo kernel-build ✅ clean
cargo host-test ✅ 148 / 148 (24 hal + 90 kernel + 34 test-hal; was 143 pre-T-012)
cargo +nightly miri test (host) ⏳ deferred to CI (R6 GitHub Actions skeleton already pinned for Miri)
QEMU smoke (default + -machine virtualization=on) ⏳ deferred to maintainer-side verification — same disclaimer T-013 used

Test plan

  • CI: fmt, host-clippy, kernel-clippy, kernel-build, host-test, Miri pass on the R6 workflow.
  • QEMU smoke (default config): kernel boots; A6 cooperative-IPC trace reproduces (eight lines + T-009 timer banner + T-009 boot-to-end measurement). The kernel can take an interrupt but none fires today (no caller arms a deadline in v1 IPC demo).
  • QEMU smoke (-machine virtualization=on, EL2 entry): kernel boots; T-013's EL drop still works post-T-012; identical post-post_eret trace.
  • (Optional) Deliberate-deadline test path: a debug task body arms arm_deadline(now + 100ms); the IRQ fires; irq_entry masks the timer + EOIs; kernel returns to interrupted code. (No caller in v1 demo; this is the first real exercise of the IRQ path.)
  • Independent review pass of T-012's three implementation commits + the documentation sweep, with the T-012-arc review prompt (the one I drafted in chat) as the briefing.

What this PR does NOT do

  • No sched::on_timer_irq hook. v1's IPC demo arms no deadlines; irq_entry is ack-and-ignore by design. The hook is added at the same time as the first scheduler-touching IRQ arc (preemption / time_sleep_until / IPI cross-CPU wake). The ADR-0021 §Revision-notes Amendment lands the discipline now so the implementation conforms when the hook is wired.
  • No FakeIrqController in tyrne-test-hal. Reasoned-deferred — no host-testable kernel logic touches the GIC trait surface in v1 (the tyrne_hal::timer::ns_to_ticks math is the new host-testable surface, covered by 5 new tests). Added when there's something to test against it.
  • No splitting of T-012 into T-012a / T-012b. The retro's cost-of-arc anticipation (1–5 commits) was met at 3 commits; the bundled-by-default approach landed cleanly.
  • No B2 / MMU work. That decision (when to draft ADR-0027) is now unblocked by T-012's In Review status — left as the next session's call.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Exception/interrupt framework with EL1 vector table, IRQ delivery, and GICv2 support
    • Real generic-timer deadline arming/canceling; idle now sleeps on interrupts (WFI)
  • Documentation

    • New architecture, audits/unsafe-log, security & business review artifacts, and roadmap/task/decision updates
    • New "conduct-approval-review" skill and tightened code-review standards
  • Tests

    • Added timer conversion unit tests

cemililik and others added 9 commits April 27, 2026 18:03
Maintainer merged PR #9 to main (merge commit 9a66e8b). Closes the
three In-Review tasks, marks the milestone, and updates the active
state on `development`.

T-008 / T-011 / T-013 promoted In Review → Done
- All AC and DoD checkboxes reconciled with delivery (T-013's two
  QEMU smoke gates flipped to [x] with "Verified by maintainer
  2026-04-27 prior to PR #9 merge"). Each task gains a final
  review-history row crediting the PR-merge promotion event and
  summarising the outcome of the two independent review passes (24
  findings closed across `9a8e312` + `39dd978` + `9680e41`).

phase-b/README.md status column flipped to Done for all three.

phase-b.md
- §B0 §"Tasks under B0": T-008 / T-011 status updated; T-010
  (optional split) annotated "explicitly not opened — T-007 closed
  without needing the split".
- §B0 §"Acceptance criteria": every line now carries a ✅ with a
  delivery cite or a deliberate-deferral note. ADR-0023 explicitly
  on the accept-deferred path; the "three architecture docs"
  expectation reduced to two with the kernel-core.md / scheduling.md
  pair subsumed into scheduler.md + ipc.md per T-008's scope-
  discipline call. Final test count 143 (vs the 109+ target).
  B0 declared **closed 2026-04-27** at the end of the AC list.
- §B1 sub-breakdown items 1-4 marked ✅ delivered (ADR-0024,
  asm extension + K3-12, current_el helper, two QEMU smoke gates).
  Item 5 (T-012 exception infrastructure) remains the open B1
  thread.

current.md
- Active phase B unchanged; active milestone shifted from "B0 in
  progress" to "B1 with T-013 done, T-012 the remaining work".
- Active task: None.
- "In review" cleared (was three; all three promoted).
- "Last completed milestone" updated from A6 to B0 with the headline
  numbers (143 host tests, 93.97% sched / 96.33% workspace coverage,
  miri clean, two new architecture docs, three new audit entries
  plus four Amendments).
- "Last completed tasks" updated to T-008 / T-011 / T-013, with a
  one-line note on the rule-validation outcome (zero post-Accept
  ADR riders during PR #9 — empirical first run of ADR-0025's
  §Rule 1 / §Rule 2).
- "Active decisions": ADR-0024 description updated to "Implemented
  by T-013 (Done)" rather than "now unblocked".
- "Next task to open": T-012 named as the next natural pickup.
- "Next review trigger": B0 closure retrospective (with the cost-
  of-arc analysis the T-009 mini-retro's second-follow-up agenda
  pre-loaded for it) + the optional consolidated B0 security review.
- "unsafe audit status" line refreshed to reflect the post-PR-#9
  shape: UNSAFE-2026-0014 Amendment, UNSAFE-2026-0016 Amendment,
  UNSAFE-2026-0017 + 0018 active with the introducing-commit-
  boundary discipline note.

Verification: cargo fmt clean; host-test 143 / 143 green
(no functional changes — doc-only).

Refs: ADR-0021, ADR-0022, ADR-0024, ADR-0025

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ment items

Closes the B0 milestone with two review artefacts and acts on four of
the six adjustment items the retros surfaced. T-012 (B1's remaining
task) and the future-session items wait for QEMU access; tracked in
the retro Adjustments and current.md as the next-trigger.

Two new review artefacts:
- docs/analysis/reviews/business-reviews/2026-04-27-B0-closure.md
  Full milestone retrospective. Cost-of-arc analysis (Phase A: ~2
  days / ~30 commits / 109 tests; B0: ~6 days / 50+ commits / 143
  tests; 3× ratio explained by ADR-0025 emergence + review
  fix-rounds + audit-log discipline establishment, all one-time
  costs). Records ADR-0025 §Rule 1's first empirical run (zero
  post-Accept ADR riders during PR #9 — rule fired correctly).
  Lessons from the audit-log Amendment-vs-in-place asymmetry
  resolution. Closes 4 of 6 pre-loaded T-009 mini-retro items.
- docs/analysis/reviews/security-reviews/2026-04-27-B0-closure.md
  8-axis adversarial pass over UNSAFE-2026-0006..0018 plus the
  four post-introduction Amendments. Verdict: clean, no Yüksek
  findings. Two pre-existing items (cross-table revocation,
  generation overflow) re-stated at original severity. Two new
  positive observations: Amendment discipline codified;
  architecture docs identified as a security multiplier
  (recommendation: T-012 ships exceptions.md as a hard
  deliverable, not a follow-up).

Both review-folder README indices updated with the new rows.

A1: .claude/skills/conduct-approval-review/SKILL.md
The independent verification pass that PR #9 ran three times (T-006/
T-007/T-009 promotion; ADR-0024/0025 Accept; T-008/T-011/T-013
promotion) is now canonised as a skill. Distinct from
perform-code-review (style + correctness) and perform-security-
review (adversarial axis): this skill is the promotion gate that
verifies an artefact's claims about its own state match reality.
Procedure: settled-decisions context → verification list (V1..VN) →
reproduce gates locally → audit-log discipline spot-check → review
dimensions → per-artefact AC audit → verdict (✅/🟡/❌). Anti-
patterns explicitly cover "LGTM without verification list" and
"re-litigating settled decisions" — the two failure modes the prior
review rounds surfaced. .claude/skills/README.md index updated.

A2: docs/standards/code-review.md Author's responsibilities item 7
Three data points (T-009 review-fix cascade, cool-down withdrawal
ripple in PR #9 first round, ipc.md broken-link sweep in PR #9
second round) tip the post-fix sweep rule into codification.
"After fixing a doc typo, renaming a symbol, or changing a literal
string that may appear in multiple places, the author runs git grep
-F '<old-token>' before staging." Specific failure modes named;
reviewer-side verification step ("reviewers verify by running the
same grep on the change's tip") added.

A3: T-012 task file's Documentation AC tightened
docs/architecture/exceptions.md is now a *required* deliverable
(was conditional / optional in the original task file). Citation:
B0 closure security review §8 ("Architecture docs are a security
multiplier"). The doc must explain vector-table layout, dispatch
flow, GIC programming sequence, and how IRQ delivery interacts
with ADR-0021's raw-pointer scheduler bridge (likely an ADR-0021
Amendment when wired). exceptions.md lands alongside T-012's
code, not as a follow-up.

A4: B0 retro Adjustments section reflects the new state
Three items flipped to [x] (post-fix sweep DoD codified; approval-
review skill canonised; T-012 DoD updated for exceptions.md);
the consolidated security review item flipped to [x] (delivered
as the parallel security-review artefact); T-012 In Progress and
B2 prep stay [ ] (deferred to a session with QEMU access).
Performance baseline re-run stays [ ] (post-T-012). New
[ ] item added: 2026-05-04 ADR-0024 rider-rate re-measurement
per ADR-0025 §Rule 2 calibration.

current.md updated:
- "Last reviews" section gains the two new B0-closure reviews
  at the top.
- "Next review trigger" replaced with B1 closure (T-012 Done
  + consolidated B1 security review + performance baseline
  re-run); separate scheduled trigger for the 2026-05-04
  ADR-0024 rider-rate measurement.

Future-session items (tracked in todo list, not closed in this
commit):
- B1: T-012 full implementation (needs QEMU access).
- B2: docs/architecture/exceptions.md (bundled with T-012).
- C1: performance baseline re-run (post-T-012).
- C2: B1 closure retrospective (post-T-012).
- C3: B1 consolidated security review (post-T-012, GIC focus).
- C4: ADR-0024 first-week rider rate measurement (2026-05-04).

Verification: cargo fmt clean; host-clippy / kernel-clippy /
kernel-build / host-test (143 / 143) all green. No code changes
in this commit — pure docs + skill addition.

Refs: ADR-0021, ADR-0022, ADR-0024, ADR-0025

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two findings from the parallel approval review of f9c145c. One Orta
(must-fix this round) + one Düşük (cheap to bundle).

Orta #1 — B0 security review §8 + Verdict structural alignment with master plan
The 8-axis structure followed master-plan §1..§7 names but §8 was
named "Sign-off" rather than "Threat-model impact" (the master
plan's defined axis 8). The verdict was folded into §8 instead of
sitting in a separate ## Verdict section using the master plan's
Approve / Changes requested / Escalate vocabulary. A reviewer
walking the master plan template-by-template would not find their
checklist.

Substantive fix:
- Rename ## 8. Sign-off → ## 8. Threat-model impact.
- Answer the master plan's threat-model question explicitly. B0's
  arc does have two real threat-model shifts that the prior shape
  buried:
  • ADR-0024 promotes EL1-only execution from defense-in-depth to
    a structural property. Pre-T-013, the kernel relied on
    firmware/emulator delivering at EL1 with UNSAFE-2026-0016 as
    a last-line guard. Post-T-013, boot.s actively drives EL1
    regardless of entry EL; UNSAFE-2026-0016 is the post-condition,
    not the only defence. Threat-model implication: the "we run
    at EL1" assumption is now an enforced invariant, not a hoped-
    for one.
  • ADR-0021 reshapes the aliasing model the kernel proves
    correctness against. Pre-T-006, &mut Scheduler<C> aliasing
    across context switches was latent UB (UNSAFE-2026-0012)
    relying on the optimiser. Post-T-006, the raw-pointer bridge
    plus momentary-&mut discipline is Stacked-Borrows-checked by
    Miri on every test run. Threat-model implication: memory-
    safety story shifted from "hope the compiler doesn't exploit
    our UB" to "checked invariant the test gate enforces."
- Move two pre-existing items (cross-table revocation gap,
  generation overflow) under a "Threat-model items inherited from
  Phase A (unchanged severity)" sub-bullet of §8 — they are
  genuinely threat-model items, not sign-off bullets, so this is
  the right home for them post-rename.
- Keep the two positive observations (Audit-log Amendment
  discipline; arch-docs as security multiplier) under §8 as
  "Positive observations about the review surface itself" — the
  review-surface framing makes them sub-bullets of §8 honestly,
  not orphaned sign-off prose.
- Add separate ## Verdict section ending with **Approve.** keyword
  per master-plan vocabulary. Verdict prose preserved from the
  original §8 closing paragraph.

Citation preservation: T-012:44 cites "B0 closure consolidated
security review §8 recommendation ('Architecture docs are a
security multiplier')". The §8 recommendation sub-bullet is
preserved verbatim under the new heading; the citation continues
to resolve. No T-012 edit needed.

Düşük #1 — B0 retro Appendix sourcing line tightened
The Appendix introduction read "The T-009 mini-retro's second-
follow-up note pre-loaded six agenda items for this retro." More
accurate: items 1, 2, 4, 5 originate in the second follow-up's
substantive paragraphs; item 3 (ADR-0024 first-week rider rate)
originates in the first follow-up; item 6 (stale-link CI check)
was a routing decision in the second follow-up's Adjustments-
status snapshot rather than a fresh preloading. Rephrased the
introduction to acknowledge "two follow-up notes plus the
original Adjustments section" with a per-item provenance
parenthetical. The six rows themselves are unchanged; net delta
("six items in → four closed + two routed forward") still holds.

Out of scope for this fix-round (per the approval review):
- Düşük #2 (UNSAFE-2026-0006 §1/§8 placement) — reviewer noted
  this is a prompt-expectation mismatch, not a delivery gap;
  "note logged for completeness" was the verdict. Skipped.
- Open questions — none from this approval review.

Verification: cargo fmt clean; host-clippy / kernel-clippy /
kernel-build / host-test (143 / 143) all green; doc-only changes
confirmed via git diff --stat (29 lines net).

Refs: ADR-0021, ADR-0024
Approval-Review: independent agent, 2026-04-27, f9c145c tip

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…the code

Promotes T-012 from Draft to In Progress with a design-first arc.
The B0 closure security review §8 ("Architecture docs are a security
multiplier") tightened T-012's Documentation AC to require
exceptions.md as a hard deliverable; this commit lands that doc
ahead of the code, not as a follow-up — the lesson the B0 cost-of-
arc analysis flagged about T-008 ("the architecture-doc-after-code
shape costs a session of redo if the code shape doesn't match the
doc you eventually write").

docs/architecture/exceptions.md (new, ~250 lines)
- Status banner explicit: T-012 In Progress, doc lands ahead of
  code per B0 security review §8.
- Context section connects the five Accepted ADRs the design rests
  on (0010 Timer, 0011 IrqController, 0021 raw-pointer bridge, 0022
  idle/Deadlock, 0024 EL drop).
- Design covers:
  • Vector table at VBAR_EL1 — Mermaid 4×4 grid; only the Current-
    EL-SP_ELx IRQ entry fires in v1, others trampoline to a
    panic-class handler.
  • Dispatch flow — Mermaid sequence diagram for the IRQ path
    (vector → trampoline → save frame → Rust handler → ack →
    dispatch → EOI → eret).
  • GIC v2 driver — distributor (0x0800_0000) + CPU interface
    (0x0801_0000) Mermaid layout; per-register table; init
    sequence (disable distributor → disable all SPIs → priorities
    → targets → enable distributor → PMR → enable CPU interface);
    per-method MMIO mapping.
  • Generic-timer IRQ wiring — PPI 27, CNTV_CVAL_EL0 / CNTV_CTL_EL0
    programming, handler hook to sched::on_timer_irq.
  • Idle WFI activation — pre/post code blocks showing the body
    swap; the wake source dependency on the timer-IRQ re-arm.
  • IRQ-handler interaction with ADR-0021's raw-pointer bridge —
    same momentary-&mut discipline; T-012 will likely ship an
    ADR-0021 Amendment when the handler is wired (UNSAFE-2026-
    0014 will gain a corresponding Amendment for the new call
    site).
- Implementation map: seven Approach steps with status flags
  (🔵 next, 🟡 follows, 🟢 in-progress / partially done by this
  doc) so the next implementation commit knows exactly where to
  land.
- Invariants: DAIF.I masked from reset until step 3 lands; vector
  table installed once; ISR is constant-time; no userspace in IRQ
  context; GICv2 single-core; no nested interrupts.
- Trade-offs: GICv2-only, single-core, all-or-nothing masking,
  timer-IRQ-only handling in v1, hand-written vector trampolines.
- Open questions: ADR-0021 Amendment shape; generic-timer IRQ ID
  confirmation; SError handler shape; vector-table assembly idiom;
  sched::on_timer_irq signature.

docs/architecture/README.md
- Index gains an `exceptions.md` row (Design status — T-012
  implementation in progress).
- boot.md status note corrected: was "EL drop pending T-013";
  T-013 has been Done since 2026-04-27. Now reads "T-013 EL drop
  landed".

T-012 task file
- Status: Draft → In Progress.
- Review-history row: design-first arc rationale; what the doc
  covers; explicit "no code in this commit" note so the next
  implementation commit's diff is unambiguous; the seven-step
  implementation map referenced.

phase-b/README.md status column reflects T-012's new state.

current.md
- "Active task" now points at T-012 (was None).
- "In progress" line surfaced (was unused) with the design-first
  detail.
- "Active milestone" + "Active phase" lines updated for T-012's
  In Progress state.

Verification: cargo fmt clean; host-test 143 / 143 unchanged
(doc-only commit). No code changes; bsp-qemu-virt and
kernel/src/sched/mod.rs untouched; the `unimplemented!()` panics
in <QemuVirtCpu as Timer> still in place.

Refs: ADR-0010, ADR-0011, ADR-0021, ADR-0022, ADR-0024

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… unmask

Lands the kernel-can-take-an-interrupt foundation for B1. Wires GIC,
vector table, and DAIF unmask in `kernel_entry` in the order documented
in `docs/architecture/exceptions.md` §"Implementation map" (vector
table → GIC init → DAIF unmask). No IRQ source is enabled at the GIC
yet, so no IRQ fires; smoke trace expected unchanged.

bsp-qemu-virt/src/gic.rs (new, ~280 lines)
- QemuVirtGic struct holding distributor (0x0800_0000) and CPU
  interface (0x0801_0000) MMIO bases.
- `unsafe const fn new` stores bases verbatim — no MMIO.
- `unsafe fn init` performs the 7-step GICv2 boot programming
  sequence per ARM IHI 0048B §4: disable distributor → mask all
  SPIs (ICENABLER<n>=0xFFFF_FFFF for n=1..) → priorities 0xA0
  (mid) → route SPIs to CPU 0 → enable distributor → PMR=0xFF
  (wide-open) → enable CPU interface.
- IrqController trait impl: enable / disable / acknowledge /
  end_of_interrupt as one-MMIO-write-each. acknowledge folds GIC
  spurious INTID 1023 to None per the trait contract.
- All MMIO via core::ptr::read_volatile / write_volatile against
  typed offsets — no compiler reordering, no &mut materialised.
- unsafe impl Send + Sync covered by the same audit entry.

bsp-qemu-virt/src/vectors.s (new, ~150 lines)
- 16-entry EL1 exception vector table at `tyrne_vectors`, 2 KiB-
  aligned via `.balign 2048` in `.text.vectors` section.
- Each entry is one `b <trampoline>` instruction; the rest of the
  0x80 slot is filled by the next `.balign 0x80`.
- Only Current-EL-with-SP_ELx IRQ (offset +0x280) routes to the
  real handler (`tyrne_irq_curr_el_trampoline`). Sync, FIQ,
  SError on any mode and IRQ on non-curr_el_spx modes route to
  panic-class trampolines.
- IRQ trampoline saves AAPCS64-caller-saved regs (x0..x18 + x30 +
  ELR_EL1 + SPSR_EL1) as a 192-byte TrapFrame, calls
  `irq_entry(*mut TrapFrame)`, restores, eret. AAPCS64
  callee-saved (x19..x29) preserved by Rust naturally.
- panic-class trampolines pass a small integer class id +
  ESR_EL1 to `panic_entry(class, esr) -> !`.

bsp-qemu-virt/src/exceptions.rs (new, ~190 lines)
- TrapFrame #[repr(C)] mirrors the asm `stp` sequence byte-for-
  byte: 12 × [u64; 2] = 192 bytes, 16-byte SP-aligned.
- `extern "C" fn irq_entry(*mut TrapFrame)` — acknowledges via
  the published GIC static, dispatches (currently empty match —
  any IRQ that fires here panics; T-012 step 4 lights up the
  timer recogniser at IRQ 27), end_of_interrupt, returns to
  trampoline.
- `extern "C" fn panic_entry(class: u64, esr: u64) -> !` — class
  id 0 = generic, 1 = unhandled-IRQ-mode; panic message includes
  raw ESR_EL1.

bsp-qemu-virt/linker.ld
- `.text` section gains a `. = ALIGN(2048)` plus
  `KEEP(*(.text.vectors))` so `tyrne_vectors` lands at a 2 KiB
  boundary (VBAR_EL1's bottom-11-bit-zero requirement).

bsp-qemu-virt/build.rs
- Adds `cargo:rerun-if-changed=src/vectors.s` so the vector-table
  asm triggers a rebuild on edit.

bsp-qemu-virt/src/main.rs
- mod gic + mod exceptions added; QemuVirtGic + bases re-exported
  for kernel_entry use.
- Second global_asm!(include_str!("vectors.s")) declares the
  vector table; extern "C" { static tyrne_vectors: u8; } makes
  the symbol address visible to Rust.
- New static GIC: StaticCell<QemuVirtGic> alongside existing
  CPU / CONSOLE statics.
- kernel_entry gains the three-step boot sequence between the
  console banner and the timer banner: (1) MSR VBAR_EL1, (2)
  QemuVirtGic construction + GIC.init(), (3) MSR DAIFClr, #0x2.
  Each block carries a SAFETY comment with audit citation
  (UNSAFE-2026-0019 for GIC, UNSAFE-2026-0020 for vector table +
  DAIF).

docs/audits/unsafe-log.md
- UNSAFE-2026-0019 — QemuVirtGic MMIO surface. Covers new() +
  init() + IrqController trait methods + Send/Sync markers.
  Five rejected alternatives enumerated (combine new+init,
  higher-level crate, RMW-on-CTLR, Mutex/RwLock, ...).
- UNSAFE-2026-0020 — EL1 exception vector table install + asm
  trampolines + DAIF unmask. Covers vectors.s asm, the
  MSR VBAR_EL1 + MSR DAIFClr blocks in kernel_entry, the Rust
  irq_entry / panic_entry handlers. Eight invariants relied on
  including the 2 KiB alignment, AAPCS64 callee-save
  preservation, the trap-frame layout match, the boot ordering
  (vector table → GIC init → DAIF unmask). Five rejected
  alternatives.
- Both entries' Status note that QEMU smoke verification is
  pending at the maintainer's first opportunity — kernel-build
  is clean but actual exception-take + GIC-MMIO behaviour is
  observable only in QEMU smoke.

What this commit does NOT do (T-012 step 4+):
- No IRQ source enabled at the GIC. acknowledge() can return
  Some only if a future commit calls gic.enable(IrqNumber(...)).
- Timer arm_deadline / cancel_deadline still panic with
  unimplemented!() — the time-source half (T-009) is unchanged.
- idle_entry's body is still core::hint::spin_loop + yield_now
  (per ADR-0022 first rider). WFI activation lands with the
  timer-IRQ wiring.
- ADR-0021 Amendment for IRQ-handler aliasing discipline lands
  in T-012's documentation-sweep commit.

Verification: cargo fmt clean; cargo host-clippy clean; cargo
kernel-clippy clean; cargo kernel-build clean; cargo host-test
143/143 unchanged (host suite does not exercise BSP code).
QEMU smoke pending — running `qemu-system-aarch64 -M virt
-kernel <binary>` with this commit should reproduce the A6 + T-009
trace bytes verbatim because no IRQ fires.

Refs: ADR-0011, ADR-0024
Audit: UNSAFE-2026-0019, UNSAFE-2026-0020

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lights up the IRQ-armed half of the Timer trait. arm_deadline /
cancel_deadline are real impls; idle's body is wait_for_interrupt
+ yield_now per ADR-0022 first-rider sub-rider. v1 cooperative IPC
demo does not arm any deadline, so smoke trace is expected unchanged
from commit a043079.

hal/src/timer.rs — ns_to_ticks helper
- Inverse of ticks_to_ns: ns × frequency_hz / 1e9 with u128
  intermediate + saturating cast. Matches ticks_to_ns's saturation
  discipline so the round-trip is monotonic at the ~584-year
  extreme.
- Five new tests bring hal host-test count 19 → 24:
  ns_to_ticks_zero_ns_is_zero, _round_trips_at_qemu_frequency,
  _one_second_yields_frequency_at_any_freq, _saturates_at_u64_max,
  _panics_on_zero_frequency.

bsp-qemu-virt/src/cpu.rs — real Timer impl
- New constant TIMER_IRQ = IrqNumber(27) — the EL1 virtual generic-
  timer's PPI on QEMU virt's GICv2.
- arm_deadline(deadline_ns):
    1. ns_to_ticks → CNTV_CVAL_EL0 (compare register)
    2. CNTV_CTL_EL0 = 0b01 (ENABLE=1, IMASK=0)
    3. gic.enable(TIMER_IRQ)
- cancel_deadline():
    1. CNTV_CTL_EL0 = 0b10 (ENABLE=0, IMASK=1) — masks at source
    2. gic.disable(TIMER_IRQ)
- Both methods carry SAFETY comments citing UNSAFE-2026-0019 (GIC
  MMIO) and UNSAFE-2026-0021 (CNTV_CTL/CVAL writes).
- Imports: ns_to_ticks from tyrne_hal::timer, IrqController +
  IrqNumber from tyrne_hal, GIC static from crate root.

bsp-qemu-virt/src/exceptions.rs — irq_entry timer dispatch
- TIMER_IRQ_ID = 27 (mirrors cpu.rs constant; recogniser-side).
- irq_entry now dispatches on irq.0:
    if irq.0 == TIMER_IRQ_ID:
        MSR CNTV_CTL_EL0, 0b10  (mask at source — prevent re-fire)
        gic.end_of_interrupt(irq); return
    else:
        gic.end_of_interrupt(irq); panic!("unhandled IRQ {n}")
- v1 has no scheduler-side wake-on-deadline; the timer-IRQ branch
  is the "ack and ignore" minimum that proves the IRQ infrastructure
  works. Future preemption + time_sleep_until syscalls add a
  sched::on_timer_irq hook here.
- Adds use core::arch::asm; for the CTL mask write.

bsp-qemu-virt/src/main.rs — idle_entry body change
- Imports tyrne_hal::Cpu (was missing — `wait_for_interrupt` is
  a Cpu trait method).
- idle_entry's body changes from
    core::hint::spin_loop();
    yield_now(...);
  to
    cpu.wait_for_interrupt();   // WFI — sleeps until any unmasked IRQ
    yield_now(...);
- Closes ADR-0022 first rider's *Sub-rider* gate ("WFI activation
  requires *two* tasks, not one"): time-source half (T-009) +
  IRQ-delivery half (T-012 commit a043079) are now both live.
- v1 cooperative IPC demo never reaches idle (both application
  tasks have permanent tail-spin-yield loops), so this change is
  structurally invisible in the demo's smoke trace.

docs/audits/unsafe-log.md — UNSAFE-2026-0021
- Three architected MSR writes: CNTV_CVAL_EL0, CNTV_CTL_EL0
  (ENABLE / IMASK).
- Six invariants relied on (EL1 + non-VHE; counter-family
  alignment with UNSAFE-2026-0015's CNTVCT choice; PPI 27
  identity; ns_to_ticks inverse correctness; nostack/nomem; GIC
  interaction via IrqController).
- Four rejected alternatives (CNTV_TVAL relative timer; RMW on
  CTL; skip-mask-at-source; inline ns_to_ticks math).

What this commit does NOT do (T-012 step 6+):
- No sched::on_timer_irq hook yet — v1 has no waiters that need
  waking; the irq_entry timer branch is "ack and ignore". Adds in
  the next preemption / time_sleep_until task.
- No ADR-0021 Amendment for IRQ-handler aliasing discipline (lands
  in T-012 step 7 / commit 3 of this arc).
- No ADR-0010 / ADR-0022 §Revision notes riders yet.
- T-012 status stays In Progress; flips to In Review with the
  documentation-sweep commit.

Verification: cargo fmt clean; host-clippy / kernel-clippy /
kernel-build clean; cargo host-test 148/148 (was 143; +5 from
ns_to_ticks tests). QEMU smoke pending — running with this commit
should reproduce the A6 + T-009 trace bytes verbatim because no
caller arms a deadline in the cooperative IPC demo and idle is
not reached.

Refs: ADR-0010, ADR-0022
Audit: UNSAFE-2026-0021

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes T-012's documentation surface; In Review now reflected on all
roadmap and review artefacts. No code changes.

Audit-log + ADR amendments (append-only per unsafe-policy.md §3 +
ADR-0025 §Rule 2):

- ADR-0021 §Revision notes — Amendment recording that the
  no-`&mut Scheduler<C>`-across-`cpu.context_switch` rule extends to
  the IRQ-handler frame. v1's `irq_entry` is *ack-and-ignore* and
  vacuously satisfies the discipline; future scheduler-touching arcs
  (preemption, `time_sleep_until` wake-on-deadline, IPI cross-CPU
  wake) follow the same momentary-`&mut` pattern. Rejected
  alternatives recorded for completeness.
- UNSAFE-2026-0014 — Amendment naming `irq_entry` as a future site of
  the same momentary-`&mut`-from-`*mut`-Scheduler pattern. v1 has no
  call site yet (ack-and-ignore body); first scheduler-touching IRQ
  handler will add the citation.
- ADR-0010 §Revision notes — recording that the deferred
  `arm_deadline` / `cancel_deadline` halves are now live on
  `QemuVirtCpu`; pointer to UNSAFE-2026-0021. Trait surface unchanged.
- ADR-0022 §Revision notes — first rider's *Sub-rider* closure
  paragraph: T-009 (time source) + T-012 (IRQ delivery) both now met,
  so `idle_entry` switches from `core::hint::spin_loop` + yield to
  `wait_for_interrupt` + yield. ADR's chosen option matches the
  shipped code from this commit forward.

Task / roadmap surfaces:

- T-012 task file: status `In Progress` → `In Review`. All seven
  Acceptance criteria + DoD checkboxes flipped (148/148 host tests;
  fmt / host-clippy / kernel-clippy / kernel-build clean; QEMU smoke +
  Miri pass deferred to maintainer-side verification per same
  disclaimer T-013 used). Review-history row appended for the
  `In Progress → In Review` promotion across the three implementation
  commits (`a043079`, `b4ed68c`, this).
- phase-b §B1 sub-breakdown item 5 promoted to ✅ with full closure
  paragraph; AC item gains *(2026-04-28)* status note.
- B0 closure retro Adjustment "Open T-012 as In Progress" flipped to
  ✅; B2-prep trigger fired (T-012 In Review). Active-task pointer
  updated.
- `docs/roadmap/current.md`: active milestone, active task, in-review
  fields all updated.
- `docs/architecture/README.md`: exceptions.md row promoted from
  Design to Accepted (v0.0.1).
- `docs/architecture/exceptions.md`: implementation-map status flags
  flipped from 🔵/🟡/🟢 to ✅ (per-step) with introducing-commit SHAs;
  ADR-0021-Amendment paragraph updated to past-tense ("ships" →
  "ships, recorded at ...").

Refs: T-012, ADR-0010, ADR-0021, ADR-0022, ADR-0025
Audit: UNSAFE-2026-0014 (Amendment, irq_entry future site)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces eight `<TBD-commit-3>` placeholders with the actual SHA
landed for the documentation sweep. Also flips the `exceptions.md`
status banner from "In Progress" to "In Review" with the three
implementation commits cited.

Same back-fill pattern as commit 330e998 (UNSAFE-2026-0011 Amendment
SHA back-fill from PR #9). The amendments themselves were correctly
written in the introducing commit (28c5ce9) per unsafe-policy.md §3
+ ADR-0025 §Rule 2; this is the SHA-resolution follow-up.

Refs: T-012, ADR-0021 (Amendment), ADR-0022 (Sub-rider closure),
ADR-0010 (Revision notes)
Audit: UNSAFE-2026-0014 (Amendment SHA back-fill)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two Düşük findings from the T-012-arc approval review:

1. `bsp-qemu-virt/src/main.rs::idle_entry` — function-level doc-comment
   was stale: still described the body as `spin_loop` + `yield_now`
   ("not `wfi` + `yield_now` — until a timer IRQ source lands (T-009)")
   and projected `wait_for_interrupt` as future work. The body itself
   landed in commit b4ed68c using `cpu.wait_for_interrupt()`; only the
   doc-comment lagged. The PR-#9 post-fix-sweep DoD rule
   (code-review.md item 7 — `git grep -F` after a behaviour change)
   would have caught this. Rewritten to describe the now-current
   behaviour: WFI + yield, ADR-0022 first-rider Sub-rider closed,
   v1's IPC demo never reaching idle in practice (so WFI is
   structurally unreachable in the demo path).

2. `docs/decisions/0021-raw-pointer-scheduler-ipc-bridge.md:123` —
   Amendment header read "2026-04-27" but the cited commit `28c5ce9`
   landed 2026-04-28; the matching UNSAFE-2026-0014 Amendment in
   `docs/audits/unsafe-log.md:225` already had the correct
   2026-04-28 date. One-character fix bringing the two Amendments
   into agreement on when the discipline-extension landed.

Bilgi #3 (TrapFrame `_reserved` never written by trampoline) and
Bilgi #4 (`compiler_fence` on spurious-acknowledge path is defensive)
are noted but not actioned — observation-grade only.

Gates: cargo fmt / host-clippy / kernel-clippy / kernel-build clean.
148/148 host tests unchanged (no code paths touched).

Refs: T-012, ADR-0021 (Amendment), ADR-0022 (Sub-rider closure)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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, your pull request is larger than the review limit of 150000 diff characters

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

📝 Walkthrough

Walkthrough

Installs AArch64 EL1 exception/IRQ infrastructure for the QEMU virt BSP: vector table and trampolines, GICv2 driver and init, timer arm/cancel wiring, BSP startup changes (VBAR_EL1, DAIF unmask, WFI idle), plus extensive documentation, audits, and task/status updates.

Changes

Cohort / File(s) Summary
Skills & Standards
\.claude/skills/README.md, \.claude/skills/conduct-approval-review/SKILL.md, docs/standards/code-review.md
Added conduct-approval-review skill and a code-review checklist requiring fixed-string grep verification after doc-relevant token changes.
Exception & IRQ Core
bsp-qemu-virt/src/vectors.s, bsp-qemu-virt/src/exceptions.rs, bsp-qemu-virt/src/gic.rs
Added EL1 vector table and trampolines, TrapFrame, panic/irq entry points, and a QEMU-virt GICv2 MMIO driver with init, enable/disable, acknowledge, and EOI semantics (spurious handling included).
Timer & CPU
bsp-qemu-virt/src/cpu.rs, hal/src/timer.rs
Implemented QemuVirtCpu deadline arm/cancel (CNTV_CVAL/CNTV_CTL_EL0 + GIC PPI enable/disable) and added ns_to_ticks (const, saturating, ceiling) with tests.
BSP Init & Idle
bsp-qemu-virt/src/main.rs
Installs tyrne_vectors via VBAR_EL1, initializes GIC with MMIO bases, clears DAIF.I, switches idle loop to wait_for_interrupt() then yield_now, and introduces a global GIC handle.
Build & Linker
bsp-qemu-virt/build.rs, bsp-qemu-virt/linker.ld
Added src/vectors.s to cargo rerun-if-changed; ensured .text.vectors is placed/KEPT in .text with 2048-byte alignment.
Docs — Architecture & Tasks
docs/architecture/exceptions.md, docs/architecture/README.md, docs/analysis/tasks/phase-b/*, docs/analysis/reviews/*, docs/roadmap/*, docs/roadmap/phases/phase-b.md
New exceptions architecture doc; updated architecture index, task statuses (T-008/T-011/T-013 → Done, T-012 → In Review), B0 closure retrospectives, and roadmap/phase docs reflecting PR merges and test counts.
Docs — Audits & Decisions
docs/audits/unsafe-log.md, docs/decisions/0010-timer-trait.md, docs/decisions/0021-raw-pointer-scheduler-ipc-bridge.md, docs/decisions/0022-idle-task-and-typed-scheduler-deadlock.md
Appended UNSAFE amendments for IRQ/timer/GIC/vector-table; updated ADRs for timer trait semantics, IRQ aliasing discipline (no live &mut across irq entry), and idle WFI activation.
Miscellaneous
docs/analysis/reviews/business-reviews/*.md, docs/analysis/reviews/security-reviews/*.md, \.claude/skills/*
Added B0 closure business/security reviews and related index entries; small README/index updates.

Sequence Diagram(s)

sequenceDiagram
    participant CPU as CPU (kernel)
    participant VT as Vector Table
    participant Trampoline as IRQ Trampoline (asm)
    participant IRQEntry as irq_entry() (Rust)
    participant GIC as QemuVirtGic
    participant Timer as Generic Timer
    participant Scheduler as Scheduler (timer hook)

    Timer->>CPU: PPI 27 fires (deadline reached)
    CPU->>VT: Exception taken to EL1 (IRQ slot)
    VT->>Trampoline: branch to irq trampoline
    Trampoline->>Trampoline: save registers + ELR/SPSR
    Trampoline->>IRQEntry: call irq_entry(frame)
    IRQEntry->>GIC: acknowledge() (read GICC_IAR)
    GIC-->>IRQEntry: returns IRQ id (27 or spurious)
    alt Timer IRQ (valid)
        IRQEntry->>Timer: mask CNTV_CTL_EL0
        IRQEntry->>GIC: end_of_interrupt(27) (write GICC_EOIR)
        IRQEntry->>Scheduler: invoke timer hook
    else Spurious
        IRQEntry->>GIC: end_of_interrupt(spurious)
    end
    IRQEntry-->>Trampoline: return from irq_entry
    Trampoline->>CPU: restore registers
    Trampoline->>CPU: eret (resume)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • Development #8 — Continues the timer/HAL work; closely related to ns_to_ticks and the QemuVirtCpu timer implementation.
  • Development #6 — Related BSP/idle-loop and scheduler-bridge changes touching similar idle behavior and kernel entry sites.

Poem

🐇 I nibbled vectors, neat and aligned,
GICs hum softly, interrupts find,
Timers tick, deadlines no longer dread,
Trampolines save, then leap overhead,
Hooray — the kernel wakes from its bed!

🚥 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 accurately describes the main changes: T-012 IRQ infrastructure work and B0 closure retrospectives. It is specific, clear, and reflects the primary deliverables.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch development

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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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 the exception infrastructure and interrupt delivery for the QEMU virt BSP, including a GIC v2 driver, an aarch64 exception vector table, and Rust handlers for IRQs and panics. The kernel now configures VBAR_EL1 at boot, enables the generic timer's deadline functionality, and updates the idle task to use the WFI instruction. Feedback primarily addressed inaccuracies in the exceptions.md architecture documentation, where Mermaid diagrams and descriptive text incorrectly described the register saving/restoring logic and included a scheduler hook not yet present in the implementation.

Comment thread docs/architecture/exceptions.md Outdated
participant Sched as Scheduler hook
HW->>Vec: IRQ taken; PC = VBAR_EL1 + 0x280
Vec->>Tramp: branch
Tramp->>Frame: stp x0..x29; mrs ELR_EL1, SPSR_EL1
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

The Mermaid sequence diagram indicates that the IRQ trampoline saves registers x0..x29. However, the implementation in bsp-qemu-virt/src/vectors.s (lines 116-125) only saves x0..x18 and x30 (LR), skipping the callee-saved registers x19..x29. The diagram should be updated to accurately reflect the saved register set.

Suggested change
Tramp->>Frame: stp x0..x29; mrs ELR_EL1, SPSR_EL1
Tramp->>Frame: stp x0..x18, x30; mrs ELR_EL1, SPSR_EL1

Comment thread docs/architecture/exceptions.md Outdated
Tramp->>Handler: call irq_entry(&mut Frame)
Handler->>GIC: gic.acknowledge() → Some(IrqNumber)
alt timer IRQ (id 27)
Handler->>Sched: sched::on_timer_irq(&Frame)
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

The Mermaid diagram shows a call to sched::on_timer_irq(&Frame). However, the current implementation of irq_entry in bsp-qemu-virt/src/exceptions.rs (lines 156-183) does not yet include this hook, as noted in the source comments. The diagram should match the actual implementation state for v1, or clearly mark this as a future extension.

Suggested change
Handler->>Sched: sched::on_timer_irq(&Frame)
alt timer IRQ (id 27)
Handler->>Handler: mask timer + EOI (v1)

Comment thread docs/architecture/exceptions.md Outdated
end
Handler->>GIC: gic.end_of_interrupt(irq)
Handler-->>Tramp: returns
Tramp->>Frame: ldp x0..x29; msr ELR_EL1, SPSR_EL1
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

Similar to the save sequence, the restore sequence in the Mermaid diagram mentions ldp x0..x29, which does not match the implementation in vectors.s (lines 133-145).

Suggested change
Tramp->>Frame: ldp x0..x29; msr ELR_EL1, SPSR_EL1
Tramp->>Frame: ldp x0..x18, x30; msr ELR_EL1, SPSR_EL1

Comment thread docs/architecture/exceptions.md Outdated
Tramp-->>HW: eret
```

The trampoline saves the AAPCS64-caller-saved registers (`x0..x18`) plus enough of `x19..x29` that the Rust handler can use callee-saved registers freely; `ELR_EL1` and `SPSR_EL1` are saved so the eret restores the interrupted PSTATE exactly. The exact saved set is fixed by the trampoline's asm and exposed to Rust as a `#[repr(C)]` struct passed by `&mut`.
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

The text states that the trampoline saves "enough of x19..x29 that the Rust handler can use callee-saved registers freely". This is inaccurate; the trampoline in vectors.s does not save any registers in the x19..x29 range. Instead, it relies on the Rust compiler's adherence to the AAPCS64 ABI, where the callee (irq_entry) is responsible for preserving those registers if it uses them.

Suggested change
The trampoline saves the AAPCS64-caller-saved registers (`x0..x18`) plus enough of `x19..x29` that the Rust handler can use callee-saved registers freely; `ELR_EL1` and `SPSR_EL1` are saved so the eret restores the interrupted PSTATE exactly. The exact saved set is fixed by the trampoline's asm and exposed to Rust as a `#[repr(C)]` struct passed by `&mut`.
The trampoline saves the AAPCS64-caller-saved registers (`x0..x18`) plus `x30` (LR); `ELR_EL1` and `SPSR_EL1` are saved so the eret restores the interrupted PSTATE exactly. Callee-saved registers (`x19..x29`) are preserved by the Rust handler per the C ABI. The exact saved set is fixed by the trampoline's asm and exposed to Rust as a `#[repr(C)]` struct passed by pointer.

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: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/analysis/tasks/phase-b/T-012-exception-and-irq-infrastructure.md (1)

8-10: ⚠️ Potential issue | 🟡 Minor

Dependency status here needs to be updated.

T-009 is still described as In Review, but the other roadmap/closure docs in this PR already mark it Done on 2026-04-27. Leaving it stale makes T-012’s gating ambiguous.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/analysis/tasks/phase-b/T-012-exception-and-irq-infrastructure.md` around
lines 8 - 10, Update the dependency status text in T-012 to reflect that T-009
is Done (not "In Review")—replace the T-009 status string in the Dependencies
paragraph (reference symbol T-009) with "Done" and, if known, add the completion
date (2026-04-27) for clarity; also verify and update the ADR-0024 mention only
if ADR-0024 has actually been Accepted so the gating sentence about VBAR_EL1
installation is accurate (referenced symbols: T-009, ADR-0024, T-012).
🧹 Nitpick comments (1)
hal/src/timer.rs (1)

431-438: The “saturates” test does not currently exercise the saturation branch.

ns=u64::MAX, freq=1_000_000_000 evaluates to exactly u64::MAX, so this checks the boundary value, not overflow-to-saturation behavior. Add one case that forces ticks > u64::MAX pre-cast (e.g., very high frequency) to validate the branch directly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hal/src/timer.rs` around lines 431 - 438, The test
ns_to_ticks_saturates_at_u64_max currently only hits the exact boundary; add an
additional assertion that forces the internal u128 calculation to exceed
u64::MAX so the saturation branch in ns_to_ticks is exercised (for example set
high_freq to a value larger than 1_000_000_000 such as 1_000_000_001 or u64::MAX
and assert ns_to_ticks(huge_ns, high_freq) == u64::MAX); update the test body of
ns_to_ticks_saturates_at_u64_max to include this extra case referencing
ns_to_ticks and the existing huge_ns/high_freq variables.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.claude/skills/conduct-approval-review/SKILL.md:
- Around line 93-114: The fenced code block in the SKILL.md example is missing a
language tag which triggers markdownlint; update the opening fence from ``` to
include a language (e.g., ```md or ```text or ```markdown) so the block is
annotated—modify the fenced block in the SKILL.md content (the triple-backtick
block under the "Verdict" / "Verification audit" section) to include one of
those language tags.
- Line 30: Replace the non-English strings in SKILL.md with English equivalents:
translate the Turkish quote "kabul edilen bulguların tamamı düzeltildi mi?" to
an English sentence (e.g., "Have all accepted findings been fixed?") and change
the severity labels `Yüksek` / `Orta` / `Düşük` to `High` / `Medium` / `Low`;
update every occurrence noted in the diff (including the quoted sentence and the
labels around the lines referenced, e.g., the quoted review example and the
severity list) so the whole document remains English-only.

In `@bsp-qemu-virt/src/exceptions.rs`:
- Around line 137-138: The assembly entry functions are currently declared as
safe extern "C" functions; change both irq_entry and panic_entry to be unsafe
extern "C" fn to reflect their caller-side safety preconditions and prevent
accidental Rust calls. Update the function signatures for irq_entry and
panic_entry (keeping #[no_mangle] and extern "C") to: declare them with the
unsafe qualifier (e.g., #[no_mangle] pub unsafe extern "C" fn irq_entry(_frame:
*mut TrapFrame) { ... } and similarly for panic_entry) so the type system
enforces the unsafe contract at call sites.

In `@bsp-qemu-virt/src/gic.rs`:
- Around line 308-338: The enable() and disable() methods compute distributor
MMIO offsets from irq.0 and then perform unsafe writes without validating the
IRQ, which can overflow/saturate the offset math; add a validation step at the
start of both enable() and disable() (before computing reg_offset or calling
write_distributor) that ensures IrqNumber is in the allowed range (e.g. less
than the maximum IRQs handled by the distributor / that (n/32).saturating_mul(4)
+ base stays within the GIC window), and reject/abort on invalid values
(panic/debug_assert/return an Err depending on your API choice) so
write_distributor is never invoked with an out-of-range offset; reference the
enable/disable methods, IrqNumber, write_distributor, and the
GICD_ISENABLER_BASE / GICD_ICENABLER_BASE constants when adding the checks.

In `@docs/analysis/reviews/business-reviews/2026-04-27-B0-closure.md`:
- Around line 31-44: The header "Five `unsafe` regions introduced or amended
during B0" is inconsistent with the table listing UNSAFE-2026-0012 through
UNSAFE-2026-0018 (seven entries); update the scope label to "Seven `unsafe`
regions" to match the table, or if the intent was to list only five, remove or
collapse two of the entries (for example drop UNSAFE-2026-0012 and
UNSAFE-2026-0018) and adjust any accompanying text accordingly; ensure
references to UNSAFE-2026-0012, UNSAFE-2026-0013, UNSAFE-2026-0014,
UNSAFE-2026-0015, UNSAFE-2026-0016, UNSAFE-2026-0017, and UNSAFE-2026-0018
remain consistent after the change.

In `@docs/analysis/tasks/phase-b/README.md`:
- Line 14: Update the index row for T-012 in
docs/analysis/tasks/phase-b/README.md to match the task file status by changing
the status cell for [T-012](T-012-exception-and-irq-infrastructure.md) from "In
Progress" to "In Review"; ensure the table row for T-012 exactly matches the
status used in T-012-exception-and-irq-infrastructure.md so the index and task
file are synchronized.

In `@docs/architecture/exceptions.md`:
- Around line 79-88: The docs currently show the timer IRQ path calling
sched::on_timer_irq, but the shipped implementation (irq_entry) masks
CNTV_CTL_EL0, calls gic.end_of_interrupt / EOIs the interrupt and returns
without borrowing scheduler state; update the sequence diagram and the timer
section to reflect this shipped flow (remove the immediate sched::on_timer_irq
call from the "current" path), and instead document the deferred wake hook as a
future/optional path; reference the irq_entry handler and CNTV_CTL_EL0 masking
behavior and ensure the alt/else branches and end_of_interrupt lines match the
actual gic.end_of_interrupt behavior.
- Around line 66-92: The docs claim the trampoline saves x0..x29, but the actual
assembly in vectors.s saves x0..x18 and x30 plus ELR_EL1 and SPSR_EL1 into the
192-byte frame; update the mermaid diagram (replace "stp x0..x29" with the
correct saved registers: x0..x18 and x30) and change the prose to state that
TrapFrame (the #[repr(C)] struct exposed to Rust) contains x0..x18, x30, ELR_EL1
and SPSR_EL1, while x19..x29 are not pushed by the trampoline but are preserved
by Rust callee conventions (AAPCS64), and reference the trampoline entry
(irq_entry) and TrapFrame struct names so readers can correlate docs with
vectors.s and the Rust handler.

In `@docs/decisions/0021-raw-pointer-scheduler-ipc-bridge.md`:
- Around line 132-134: Reword the paragraph so it no longer claims that the
first scheduler-touching IRQ handler will create the UNSAFE-2026-0014 naming
amendment; instead state that the audits log already records UNSAFE-2026-0014
and that when a future scheduler-touching IRQ handler (irq_entry) is introduced
it should add a follow-up Amendment that records activation of that
already-named site rather than creating a parallel audit entry, retaining the
single-pattern mental model referenced alongside UNSAFE-2026-0020.

In `@docs/roadmap/current.md`:
- Around line 13-14: The roadmap's headline metrics for sched/mod.rs and
workspace coverage conflict with the B0 closure retro; choose the canonical
values recorded in the B0 closure retro (sched/mod.rs 84.16% and workspace
96.37%) and update the milestone summary in current.md to match (replace 83.93%
→ 84.16% and 96.33% → 96.37%), then re-run any small consistency check to ensure
the PR/merge references and test counts in the same paragraph remain unchanged
and accurate (verify the host tests and PR/merge commit text for T-011 and B0
closure notes).

In `@docs/roadmap/phases/phase-b.md`:
- Line 94: The bullet incorrectly states "T-012 closed" while its parenthetical
notes verification is pending; update the line mentioning T-012 so it does not
assert closure—e.g., change "T-012 closed" to "T-012 implemented (pending
verification)" or "T-012 delivered — verification pending" and keep the existing
details about arm_deadline, idle_entry, and ADR-0022 intact so the note reflects
that the implementation is present but final closure awaits maintainer-side QEMU
verification.

In `@hal/src/timer.rs`:
- Around line 192-194: The ns_to_ticks conversion currently floors deadlines by
using simple division, which can arm IRQs early; change the conversion to round
up (ceiling) so any sub-tick ns maps to the next tick. In ns_to_ticks, compute
ticks with a ceiling formula using the existing intermediate (ns as u128 *
frequency_hz as u128) and NANOS_PER_SECOND so that ticks = ceil(intermediate /
NANOS_PER_SECOND), then perform the same overflow check against u64::MAX before
returning; ensure you update the ticks calculation where variables intermediate,
frequency_hz, NANOS_PER_SECOND, and ticks are used.

---

Outside diff comments:
In `@docs/analysis/tasks/phase-b/T-012-exception-and-irq-infrastructure.md`:
- Around line 8-10: Update the dependency status text in T-012 to reflect that
T-009 is Done (not "In Review")—replace the T-009 status string in the
Dependencies paragraph (reference symbol T-009) with "Done" and, if known, add
the completion date (2026-04-27) for clarity; also verify and update the
ADR-0024 mention only if ADR-0024 has actually been Accepted so the gating
sentence about VBAR_EL1 installation is accurate (referenced symbols: T-009,
ADR-0024, T-012).

---

Nitpick comments:
In `@hal/src/timer.rs`:
- Around line 431-438: The test ns_to_ticks_saturates_at_u64_max currently only
hits the exact boundary; add an additional assertion that forces the internal
u128 calculation to exceed u64::MAX so the saturation branch in ns_to_ticks is
exercised (for example set high_freq to a value larger than 1_000_000_000 such
as 1_000_000_001 or u64::MAX and assert ns_to_ticks(huge_ns, high_freq) ==
u64::MAX); update the test body of ns_to_ticks_saturates_at_u64_max to include
this extra case referencing ns_to_ticks and the existing huge_ns/high_freq
variables.
🪄 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: 9f98bf09-ead3-482f-852c-b0ca6ef1c3a9

📥 Commits

Reviewing files that changed from the base of the PR and between 9a66e8b and 67ff867.

📒 Files selected for processing (28)
  • .claude/skills/README.md
  • .claude/skills/conduct-approval-review/SKILL.md
  • bsp-qemu-virt/build.rs
  • bsp-qemu-virt/linker.ld
  • bsp-qemu-virt/src/cpu.rs
  • bsp-qemu-virt/src/exceptions.rs
  • bsp-qemu-virt/src/gic.rs
  • bsp-qemu-virt/src/main.rs
  • bsp-qemu-virt/src/vectors.s
  • docs/analysis/reviews/business-reviews/2026-04-27-B0-closure.md
  • docs/analysis/reviews/business-reviews/README.md
  • docs/analysis/reviews/security-reviews/2026-04-27-B0-closure.md
  • docs/analysis/reviews/security-reviews/README.md
  • docs/analysis/tasks/phase-b/README.md
  • docs/analysis/tasks/phase-b/T-008-architecture-docs.md
  • docs/analysis/tasks/phase-b/T-011-missing-tests-bundle.md
  • docs/analysis/tasks/phase-b/T-012-exception-and-irq-infrastructure.md
  • docs/analysis/tasks/phase-b/T-013-el-drop-to-el1.md
  • docs/architecture/README.md
  • docs/architecture/exceptions.md
  • docs/audits/unsafe-log.md
  • docs/decisions/0010-timer-trait.md
  • docs/decisions/0021-raw-pointer-scheduler-ipc-bridge.md
  • docs/decisions/0022-idle-task-and-typed-scheduler-deadlock.md
  • docs/roadmap/current.md
  • docs/roadmap/phases/phase-b.md
  • docs/standards/code-review.md
  • hal/src/timer.rs

Comment thread .claude/skills/conduct-approval-review/SKILL.md Outdated
Comment thread .claude/skills/conduct-approval-review/SKILL.md Outdated
Comment thread bsp-qemu-virt/src/exceptions.rs Outdated
Comment thread bsp-qemu-virt/src/gic.rs
Comment thread docs/analysis/reviews/business-reviews/2026-04-27-B0-closure.md
Comment thread docs/architecture/exceptions.md Outdated
Comment thread docs/decisions/0021-raw-pointer-scheduler-ipc-bridge.md Outdated
Comment thread docs/roadmap/current.md Outdated
Comment thread docs/roadmap/phases/phase-b.md Outdated
Comment thread hal/src/timer.rs
Code (5 fixes):

1. `bsp-qemu-virt/src/exceptions.rs` — irq_entry and panic_entry are now
   `unsafe extern "C" fn` (was `extern "C" fn`). Both have caller-side
   safety preconditions (frame validity for irq_entry; class validity
   for panic_entry) already documented in `# Safety` doc-comments;
   making them `unsafe fn` enforces the contract at the type level for
   any future Rust caller. The asm trampolines `bl` them unchanged.

2. `bsp-qemu-virt/src/gic.rs` — enable/disable now assert
   `irq.0 < GIC_MAX_IRQ` (1020) per ARM IHI 0048B §4.3.2 before
   computing distributor offsets. Without the check, an out-of-range
   IrqNumber would compute an MMIO offset outside the distributor
   window even with saturating arithmetic — writing to reserved or
   wrong-window addresses. The IrqController trait returns no Result,
   so panic is the right mechanism for kernel-internal contract
   violations. SAFETY comments updated to cite the range invariant.

3. `hal/src/timer.rs` — ns_to_ticks switched from floor to ceiling
   division (via `u128::div_ceil`). ADR-0010 §Decision outcome
   specifies `arm_deadline` semantics as "When `now_ns()` reaches or
   exceeds `deadline_ns`, the hardware timer IRQ fires"; flooring
   could arm the comparator at a tick whose time-equivalent is
   sub-tick *before* deadline_ns, violating the contract. Ceiling
   guarantees the comparator's tick-equivalent is ≥ deadline_ns. New
   `# Rounding` doc section explains the choice. Existing tests
   (round-trip at 62.5 MHz divisor frequency, one-second-yields-
   frequency at multiple frequencies, zero-ns, panic-on-zero-frequency)
   all still pass — they exercise frequencies that divide evenly into
   1e9 ns/s, where ceiling and floor agree.

4. `hal/src/timer.rs::ns_to_ticks_saturates_at_u64_max` — added two
   over-boundary cases (`freq = 1_000_000_001` and `freq = u64::MAX`)
   so the saturation *branch* (not just the boundary) is exercised.
   The original test hit only the exact boundary
   (`huge_ns * 1e9 = u64::MAX * 1e9` divides exactly).

5. `bsp-qemu-virt/src/exceptions.rs::TrapFrame` — no code change;
   the doc-comment's "Padding" claim about `_reserved` was already
   correct. Reviewer's Bilgi #3 was an observation, not a fix
   request.

Documentation (8 fixes):

6. `.claude/skills/conduct-approval-review/SKILL.md` — Turkish quote
   in line 30 translated to English ("kabul edilen bulguların tamamı
   düzeltildi mi?" → "Have all accepted findings been fixed?");
   severity labels in the output-format template translated
   (`Yüksek`/`Orta`/`Düşük` → `High`/`Medium`/`Low`); the line-53
   "Yüksek finding" prose translated; the example fenced code block
   gained a `markdown` language tag. CLAUDE.md §3 mandates English in
   all committed artefacts.

7. `docs/analysis/reviews/business-reviews/2026-04-27-B0-closure.md` —
   "Five `unsafe` regions" → "Seven" to match the actual table count
   (UNSAFE-2026-0012 through 0018 inclusive = 7 entries).

8. `docs/analysis/tasks/phase-b/README.md` — T-012 row status changed
   from `In Progress (design-first 2026-04-28)` to
   `In Review (2026-04-28)` to match the task file's header.

9. `docs/architecture/exceptions.md` — IRQ dispatch flow Mermaid
   diagram updated to reflect shipped behaviour: timer-IRQ branch
   now shows "msr CNTV_CTL_EL0 → gic.end_of_interrupt → return"
   (ack-and-ignore for v1) instead of the placeholder
   sched::on_timer_irq call. Spurious-INTID arm added (returns
   without EOI per GICv2 architecture spec). Saved-register list
   corrected: trampoline saves `x0..x18, x30` (not `x0..x29`); the
   prose now explicitly states that `x19..x29` are AAPCS64
   callee-saved and preserved by the Rust handler, not pushed by
   the trampoline.

10. `docs/decisions/0021-raw-pointer-scheduler-ipc-bridge.md` — the
    Amendment paragraph that said "When the first scheduler-touching
    IRQ handler lands, ... UNSAFE-2026-0014 ... gains an Amendment"
    reworded to reflect that the Amendment **already exists** (commit
    `28c5ce9`) and v1's body has no live citation yet because
    `irq_entry` is ack-and-ignore. Future scheduler-touching arcs
    add a follow-up Amendment recording the activation.

11. `docs/roadmap/current.md` — pre-T-011 baseline coverage numbers
    reconciled with the canonical B0 closure retro: sched/mod.rs was
    `83.93%` → `84.16%`; workspace was `96.33%` → `96.37%`. The post-
    T-011 numbers (93.97% / 96.37%) were already correct.

12. `docs/roadmap/phases/phase-b.md` — "T-012 closed" → "T-012
    delivered (pending verification)". The bullet's existing
    parenthetical already explained that closure waits on QEMU
    verification; the lead label is now consistent with that.

13. `docs/analysis/tasks/phase-b/T-012-exception-and-irq-infrastructure.md`
    — Dependencies field: T-009 status `In Review` → `Done` (closed
    2026-04-27 via PR #9); ADR-0024 mention sharpened to record its
    Accept date (2026-04-27).

Bilgi findings #3 (`TrapFrame._reserved` never written) and #4
(spurious-acknowledge `compiler_fence` is defensive-not-load-bearing)
intentionally not actioned per their "no action today" tags.

Gates: cargo fmt / host-clippy / kernel-clippy / kernel-build all
clean. host-test 148 / 148 unchanged (24 hal + 90 kernel + 34
test-hal); the new ns_to_ticks_saturates_at_u64_max assertions
extend an existing test rather than add a new one.

Refs: T-012, ADR-0010 (rounding clarification rider candidate),
ADR-0021 (Amendment reword), ADR-0024 (precondition language)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@HodeTech HodeTech deleted a comment from coderabbitai Bot Apr 28, 2026
@cemililik
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 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.

Actionable comments posted: 4

♻️ Duplicate comments (1)
docs/architecture/exceptions.md (1)

160-165: ⚠️ Potential issue | 🟠 Major

Document the shipped timer IRQ path here as well.

This section still says irq_entry calls sched::on_timer_irq(...), but bsp-qemu-virt/src/exceptions.rs currently masks CNTV_CTL_EL0, EOIs the interrupt, and returns. Keeping the future wake hook in the current flow blurs the v1 guarantee that IRQ entry does not borrow scheduler state yet.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/architecture/exceptions.md` around lines 160 - 165, Update the
documentation to reflect the actual shipped timer IRQ path: describe that
irq_entry (in the QEMU-virt BSP) masks CNTV_CTL_EL0, calls gic.end_of_interrupt
for IrqNumber(27), and returns without invoking sched::on_timer_irq; remove or
revise the sentence claiming irq_entry calls sched::on_timer_irq so the docs
preserve the v1 guarantee that IRQ entry does not borrow scheduler state and
instead note that any wake/scheduling hook happens later outside the IRQ entry
path.
🧹 Nitpick comments (2)
hal/src/timer.rs (1)

417-467: Add a direct sub-tick rounding regression test.

Current ns_to_ticks tests mostly use values where floor and ceil produce the same result. A small remainder-case test would lock in the ceiling policy and prevent silent regressions.

✅ Proposed test addition
 #[test]
 fn ns_to_ticks_one_second_yields_frequency_at_any_freq() {
@@
 }
 
+#[test]
+fn ns_to_ticks_rounds_up_sub_tick_deadlines() {
+    // 62.5 MHz => 16 ns per tick.
+    let freq = 62_500_000;
+    assert_eq!(ns_to_ticks(1, freq), 1);
+    assert_eq!(ns_to_ticks(15, freq), 1);
+    assert_eq!(ns_to_ticks(16, freq), 1);
+    assert_eq!(ns_to_ticks(17, freq), 2);
+}
+
 #[test]
 fn ns_to_ticks_saturates_at_u64_max() {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hal/src/timer.rs` around lines 417 - 467, Add a unit test (e.g., fn
ns_to_ticks_rounds_up_on_subtick) that asserts ns_to_ticks uses ceiling on
sub-tick remainders: call ns_to_ticks with a frequency and an ns value that is
just 1ns greater than an exact tick boundary (for example freq = 3 and ns =
333_333_334) and assert the result is the ceiling tick count (2); place the test
near the other ns_to_ticks tests and reference ns_to_ticks and the
NANOS_PER_SECOND logic so the rounding policy is locked in.
bsp-qemu-virt/src/exceptions.rs (1)

43-70: Add a compile-time size guard for TrapFrame.

TrapFrame is part of a byte-for-byte ABI with bsp-qemu-virt/src/vectors.s, but right now a future field edit only fails on the first live IRQ. A const size assertion here would turn that drift into a build-time failure.

Suggested guard
 #[repr(C)]
 #[derive(Debug)]
 pub struct TrapFrame {
     /// `x0` and `x1` saved at frame offset 0x00.
     pub x0_x1: [u64; 2],
@@
     /// Padding — keeps the frame at 192 bytes total (16-byte SP-aligned).
     pub _reserved: [u64; 2],
 }
+
+const _: [(); 0xC0] = [(); core::mem::size_of::<TrapFrame>()];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bsp-qemu-virt/src/exceptions.rs` around lines 43 - 70, The TrapFrame struct
is part of a byte-for-byte ABI and needs a compile-time size assertion to catch
accidental layout changes; add a const size guard after the TrapFrame definition
that uses core::mem::size_of::<TrapFrame>() (or a const assertion pattern) to
assert its size equals 192 bytes (or the expected byte size matching vectors.s),
so any future edits will fail at compile time if the struct size drifts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bsp-qemu-virt/src/exceptions.rs`:
- Around line 139-143: The SAFETY comment for the unsafe block using
(*GIC.0.get()).assume_init_ref() must be expanded to the full triplet: state why
unsafe is needed (to create a stable &QemuVirtGic reference from a static
MaybeUninit in IRQ context where Rust cannot prove initialization), what
invariants are relied on (kernel_entry initialized GIC before unmasking
interrupts, GIC is never moved or reinitialized, no mutable aliases during ISR
so the returned reference remains valid for ISR lifetime), and why safer
alternatives were rejected (e.g., Mutex/RefCell/Option would either require
heap/locking unavailable in early IRQ path or impose unacceptable
overhead/latency for ISR). Do the same for the other unsafe block around lines
163–169 (identify the function/usage that dereferences the same static or uses
assume_init/_mut), ensuring each comment names the symbol (GIC, QemuVirtGic,
kernel_entry, ISR, assume_init_ref) and asserts the exact invariants and
rationale.

In `@docs/architecture/exceptions.md`:
- Around line 15-16: Update every mention of ADR-0021 in this document to
reflect that the IRQ-handler amendment now exists and is dated 2026-04-28:
replace language that describes the amendment as future/open (e.g., "will likely
gain an Amendment", "dated 2026-04-27") with a statement that the amendment is
present and dated 2026-04-28, and ensure any reference to the IRQ-handler
amendment mentions ADR-0021 and the 2026-04-28 amendment date; apply the same
change to the other occurrences noted in the file (the remaining ADR-0021
references).

In `@docs/decisions/0021-raw-pointer-scheduler-ipc-bridge.md`:
- Around line 127-130: The IRQ entry function's signature must be declared
unsafe to preserve the caller-side contract: change the shipped handler back to
pub unsafe extern "C" fn irq_entry(_frame: *mut TrapFrame) (or restore the
unsafe on the existing irq_entry symbol), and ensure any callers/registrations
expect an unsafe extern "C" entry; also add a short comment next to irq_entry
and/or TrapFrame noting that changes touching capabilities/IPC/memory/crypto
must be flagged for explicit security review as described in the ADR.

In `@docs/roadmap/current.md`:
- Line 31: The roadmap's "Next review trigger" paragraph still references an
intermediate gate: "a code review on the GIC + vector-table commit before WFI
activation lands"; remove that clause (or change it to a past-tense note that
the gate is complete) since WFI activation already landed in commit b4ed68c, so
update the "Next review trigger" sentence under the Next review trigger header
to either delete the intermediate gate mention or mark it as completed.

---

Duplicate comments:
In `@docs/architecture/exceptions.md`:
- Around line 160-165: Update the documentation to reflect the actual shipped
timer IRQ path: describe that irq_entry (in the QEMU-virt BSP) masks
CNTV_CTL_EL0, calls gic.end_of_interrupt for IrqNumber(27), and returns without
invoking sched::on_timer_irq; remove or revise the sentence claiming irq_entry
calls sched::on_timer_irq so the docs preserve the v1 guarantee that IRQ entry
does not borrow scheduler state and instead note that any wake/scheduling hook
happens later outside the IRQ entry path.

---

Nitpick comments:
In `@bsp-qemu-virt/src/exceptions.rs`:
- Around line 43-70: The TrapFrame struct is part of a byte-for-byte ABI and
needs a compile-time size assertion to catch accidental layout changes; add a
const size guard after the TrapFrame definition that uses
core::mem::size_of::<TrapFrame>() (or a const assertion pattern) to assert its
size equals 192 bytes (or the expected byte size matching vectors.s), so any
future edits will fail at compile time if the struct size drifts.

In `@hal/src/timer.rs`:
- Around line 417-467: Add a unit test (e.g., fn
ns_to_ticks_rounds_up_on_subtick) that asserts ns_to_ticks uses ceiling on
sub-tick remainders: call ns_to_ticks with a frequency and an ns value that is
just 1ns greater than an exact tick boundary (for example freq = 3 and ns =
333_333_334) and assert the result is the ceiling tick count (2); place the test
near the other ns_to_ticks tests and reference ns_to_ticks and the
NANOS_PER_SECOND logic so the rounding policy is locked in.
🪄 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: 56f32c6c-98f5-48df-8b5f-6e373606778e

📥 Commits

Reviewing files that changed from the base of the PR and between 67ff867 and d820a88.

📒 Files selected for processing (11)
  • .claude/skills/conduct-approval-review/SKILL.md
  • bsp-qemu-virt/src/exceptions.rs
  • bsp-qemu-virt/src/gic.rs
  • docs/analysis/reviews/business-reviews/2026-04-27-B0-closure.md
  • docs/analysis/tasks/phase-b/README.md
  • docs/analysis/tasks/phase-b/T-012-exception-and-irq-infrastructure.md
  • docs/architecture/exceptions.md
  • docs/decisions/0021-raw-pointer-scheduler-ipc-bridge.md
  • docs/roadmap/current.md
  • docs/roadmap/phases/phase-b.md
  • hal/src/timer.rs
✅ Files skipped from review due to trivial changes (1)
  • docs/analysis/reviews/business-reviews/2026-04-27-B0-closure.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/analysis/tasks/phase-b/README.md
  • docs/analysis/tasks/phase-b/T-012-exception-and-irq-infrastructure.md
  • bsp-qemu-virt/src/gic.rs

Comment thread bsp-qemu-virt/src/exceptions.rs Outdated
Comment thread docs/architecture/exceptions.md Outdated
Comment thread docs/decisions/0021-raw-pointer-scheduler-ipc-bridge.md
Comment thread docs/roadmap/current.md Outdated
Code (3 fixes):

1. `bsp-qemu-virt/src/exceptions.rs::irq_entry` — both inline SAFETY
   blocks (`(*GIC.0.get()).assume_init_ref()` at line 139 and the
   `msr cntv_ctl_el0` write at line 169) expanded to the full triplet
   required by `unsafe-policy.md` §1: why `unsafe` is needed, the
   exact invariants relied on (named symbols: `GIC`, `QemuVirtGic`,
   `kernel_entry`, `assume_init_ref`, `CNTV_CTL_EL0`, EL1 + non-VHE
   per ADR-0024 + UNSAFE-2026-0017), and why safer alternatives were
   rejected (`Mutex`/`RefCell`/`OnceCell`-`Option` for the GIC borrow;
   typed-register wrapper crates for the system-register write). The
   previous comments covered invariants but elided rejected
   alternatives, leaving them non-conforming under `§1`.

2. `bsp-qemu-virt/src/exceptions.rs::TrapFrame` — added a const
   compile-time guard `const _: () = assert!(size_of::<TrapFrame>() ==
   192);` immediately after the struct. The asm `stp` sequence in
   `vectors.s` writes through fixed offsets and reserves exactly 192
   bytes; a drift between the asm and the `repr(C)` would corrupt
   saved registers on every IRQ. The guard fails the build before
   that can ship. (Nitpick.)

3. `hal/src/timer.rs::ns_to_ticks_rounds_up_on_subtick` — new unit
   test exercising freq = 3 Hz, ns = 333_333_334 → expected 2 ticks
   (one tick = 333_333_333.333… ns; ns is 1 ns past tick 1, so the
   ceiling tick count is 2). Pairs with a boundary check at ns =
   333_333_333 → 1 tick. Existing `ns_to_ticks` tests use frequencies
   that divide evenly into 1e9 ns/s, where ceiling and floor agree —
   without this test a regression from `div_ceil` to floor division
   would silently violate ADR-0010's "reaches or exceeds deadline_ns"
   contract and the existing suite would not catch it. (Nitpick.)

Documentation (5 fixes):

4. `docs/architecture/exceptions.md` line 15 — replaced "ADR-0021 will
   likely gain an Amendment when the handler is wired" with a
   present-tense statement that ADR-0021 carries the 2026-04-28
   Amendment recording the IRQ-frame extension, including a deep-link
   to the ADR's §Revision notes anchor.

5. `docs/architecture/exceptions.md` line 205 — corrected the
   ADR-0021 Amendment date from `(2026-04-27)` to `(2026-04-28)` to
   match the actual amendment block in
   `docs/decisions/0021-raw-pointer-scheduler-ipc-bridge.md`.

6. `docs/architecture/exceptions.md` lines 160-167 — the prose at
   §"Generic-timer IRQ wiring" still listed step 3 as
   `Calls sched::on_timer_irq(...)`, contradicting the shipped
   ack-and-ignore body in `irq_entry`. Re-numbered to match the code:
   ack → mask `CNTV_CTL_EL0` → `gic.end_of_interrupt(IrqNumber(27))`
   → return; added a paragraph explicitly preserving the v1 guarantee
   that IRQ entry does not borrow scheduler state, with a link to the
   ADR-0021 2026-04-28 Amendment that lays out the discipline for any
   future scheduler-touching IRQ handler.

7. `docs/architecture/exceptions.md` line 244 — open-question bullet
   for the ADR-0021 Amendment shape rephrased to past tense ("T-012
   shipped the 2026-04-28 Amendment"); the residual open question is
   now whether the first scheduler-touching IRQ handler should add a
   follow-up Amendment recording the activation site, per the
   discipline note already in the existing Amendment body.

8. `docs/decisions/0021-raw-pointer-scheduler-ipc-bridge.md` Amendment
   §Revision notes — the cited IRQ-handler signature
   `extern "C" fn(_frame: *mut TrapFrame)` updated to
   `unsafe extern "C" fn(_frame: *mut TrapFrame)` to match the shipped
   handler (PR #10 fix #1 already made `irq_entry` an `unsafe fn`;
   the ADR text had drifted). New parenthetical explains that the
   `unsafe fn` qualifier is part of the contract — the function has
   caller-side preconditions documented in its `# Safety` section that
   are now visible at the type level, with a link to
   `bsp-qemu-virt/src/exceptions.rs::irq_entry`.

9. `docs/roadmap/current.md` line 31 — removed the "Mid-T-012 a code
   review on the GIC + vector-table commit before WFI activation
   lands" intermediate-gate clause from the "Next review trigger"
   sentence; WFI activation already landed in `b4ed68c`, so the
   intermediate gate is moot. The B1-closure trigger and the
   2026-05-04 ADR-0024 rider-rate measurement remain.

Findings deliberately not actioned:

  - Reviewer's request to "add a short comment next to irq_entry
    and/or TrapFrame noting that changes touching capabilities/IPC/
    memory/crypto must be flagged for explicit security review as
    described in the ADR" — that contract lives in CLAUDE.md (§"Before
    starting work" item 4) and `unsafe-policy.md` §"Review", and the
    function-level doc-comment of `irq_entry` already cites the
    relevant audits and ADR-0021. Adding an inline reminder would
    duplicate policy that already lives in the canonical locations
    and contradicts CLAUDE.md's "Default to writing no comments" rule
    for non-load-bearing prose.

Gates: cargo fmt / host-clippy / kernel-clippy / kernel-build all
clean. host-test 149 / 149 (was 148; +1 new
`ns_to_ticks_rounds_up_on_subtick`). The TrapFrame compile-time size
guard is exercised by every kernel build.

Refs: T-012, ADR-0010 (rounding test lock-in), ADR-0021 (Amendment
date + signature consistency), `unsafe-policy.md` §1 (SAFETY-comment
triplet)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@cemililik
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/architecture/exceptions.md`:
- Around line 171-195: The documentation still claims idle remains a spin-loop
until T-012, but the code and implementation map show idle_entry now calls
cpu.wait_for_interrupt() (WFI) and the WFI path is shipped; update the prose in
the idle section to reflect that idle now uses the WFI path (idle_entry ->
cpu.wait_for_interrupt()) as the default wake strategy, remove or reword the
stale sentence about staying in spin_loop form, and reference ADR-0022/T-012
appropriately (e.g., note that the first rider/Sub-rider gate from ADR-0022 is
closed and that remaining T-012 items are separate follow-ups) so the text
aligns with the shown code block.
🪄 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: d26f348e-41d6-42a1-ba25-7129ecfc2226

📥 Commits

Reviewing files that changed from the base of the PR and between d820a88 and 5c9cf06.

📒 Files selected for processing (5)
  • bsp-qemu-virt/src/exceptions.rs
  • docs/architecture/exceptions.md
  • docs/decisions/0021-raw-pointer-scheduler-ipc-bridge.md
  • docs/roadmap/current.md
  • hal/src/timer.rs
✅ Files skipped from review due to trivial changes (1)
  • docs/roadmap/current.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • hal/src/timer.rs

Comment on lines +171 to +195
Once the timer IRQ is wired, idle's body changes from:

```rust
fn idle_entry() -> ! {
let cpu = unsafe { (*CPU.0.get()).assume_init_ref() };
loop {
core::hint::spin_loop();
unsafe { yield_now(SCHED.as_mut_ptr(), cpu).expect("idle: yield_now failed"); }
}
}
```

to:

```rust
fn idle_entry() -> ! {
let cpu = unsafe { (*CPU.0.get()).assume_init_ref() };
loop {
cpu.wait_for_interrupt(); // WFI; sleeps until any IRQ wakes us
unsafe { yield_now(SCHED.as_mut_ptr(), cpu).expect("idle: yield_now failed"); }
}
}
```

This closes ADR-0022 first rider's *Sub-rider* gate. The timer IRQ is the wake source: a periodic re-arm (or a one-shot from a sleeping task's `time_sleep_until` syscall, when those land in B5) keeps idle's `wfi` from blocking indefinitely. Until T-012's full set lands, idle stays in the `spin_loop` form.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Update the idle prose to reflect the shipped wfi path.

This paragraph still says idle remains in the spin-loop form until T-012 lands, but the implementation map marks the WFI activation complete. That makes the sentence read stale next to the new code block.

Proposed fix
- Until T-012's full set lands, idle stays in the spin_loop form.
+ T-012 now activates the `wfi` idle path; the spin-loop form above is shown only as the pre-change baseline.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/architecture/exceptions.md` around lines 171 - 195, The documentation
still claims idle remains a spin-loop until T-012, but the code and
implementation map show idle_entry now calls cpu.wait_for_interrupt() (WFI) and
the WFI path is shipped; update the prose in the idle section to reflect that
idle now uses the WFI path (idle_entry -> cpu.wait_for_interrupt()) as the
default wake strategy, remove or reword the stale sentence about staying in
spin_loop form, and reference ADR-0022/T-012 appropriately (e.g., note that the
first rider/Sub-rider gate from ADR-0022 is closed and that remaining T-012
items are separate follow-ups) so the text aligns with the shown code block.

@cemililik cemililik merged commit 7b42bbe into main Apr 29, 2026
10 checks passed
cemililik added a commit that referenced this pull request Apr 29, 2026
T-012 promoted `In Review → Done` via PR #10 merge to `main`. The
substantive implementation arc — three commits + one doc-sweep + two
review-fix sweeps — is now closed; the next work-item is the B1
closure review trio (business + consolidated security + performance-
baseline), not a new implementation task.

Files updated:

1. `docs/analysis/tasks/phase-b/T-012-exception-and-irq-infrastructure.md` —
   Status field flipped to `Done`. Two new review-history rows: one
   summarising the two PR #10 review-fix sweeps (round-1 `d820a88`
   11/12 findings + round-2 `5c9cf06` 8 findings + 2 nitpicks; what
   they covered code-side and doc-side), and the `In Review → Done`
   promotion row recording the PR #10 merge with merge-SHA back-fill
   pending (same precedent as `6d2f725` for T-012 step-3 SHA). The
   row makes the two maintainer-side DoD gates explicit: `cargo
   +nightly miri test` + QEMU smoke verification of the deliberate-
   deadline path stay pinned for the maintainer's CI / hardware run,
   and the audit-log entries UNSAFE-2026-0019 / 0020 / 0021 keep
   their `Pending QEMU smoke verification` status notes intact until
   the maintainer lifts them via append-only Amendment after running
   the smoke (this is *not* a merge-time mechanical update; it
   tracks an actual QEMU run).

2. `docs/analysis/tasks/phase-b/README.md` — T-012 row status `In
   Review (2026-04-28)` → `Done (2026-04-28)`.

3. `docs/roadmap/phases/phase-b.md` — B1 sub-item 5 (T-012) flipped
   from "In Review 2026-04-28" to "Done 2026-04-28 via PR #10 merge"
   with summary of the implementation + review arc; B1 acceptance-
   criteria T-012 line flipped from "delivered (pending verification)"
   to "delivered (Done 2026-04-28)" with the QEMU-smoke + Miri
   maintainer-side disclaimer preserved. New **Status** line added
   below the criteria: "B1 implementation complete 2026-04-28 with
   PR #10's merge to main"; B1 closure review trio explicitly named
   as pre-closure work (mirrors B0's "Status: B0 closed 2026-04-27"
   convention).

4. `docs/roadmap/current.md` — substantial sweep:
   - Active phase / milestone / task lines: B1 implementation
     complete; T-012 Done; no active task (next is B1 closure review
     trio, not a new implementation task).
   - "In review" + "In progress" both now `none`.
   - Last-completed-tasks line restructured around T-012 with
     headline numbers (149 host tests, 148 → 149 via the sub-tick
     rounding test; three new audit entries; two review-fix sweeps
     closing 19 findings + 2 nitpicks).
   - Active-decisions block updated: ADR-0010 IRQ-delivery half is
     live (no longer "deferred to T-012"); ADR-0021 references the
     2026-04-28 Amendment; ADR-0022 first rider's *Sub-rider* is now
     closed by T-012's WFI activation.
   - Next-task-to-open: explicit "none until B1 closure review
     lands"; B2 (MMU activation) is the next implementation arc
     after closure.
   - Next-review-trigger: B1 closure trio explicitly named with
     deep-links to the three review folders; QEMU smoke + Miri
     surfaced as pre-closure work-items.
   - `unsafe` audit-status note updated to 2026-04-28 / post-PR-#10:
     adds UNSAFE-2026-0019 / 0020 / 0021 with their pending-smoke
     status notes; UNSAFE-2026-0014 second Amendment (commit
     `28c5ce9`, IRQ-handler future-site) recorded.

5. `docs/architecture/README.md` — `exceptions.md` row status:
   "T-012 In Review 2026-04-28; pending maintainer-side QEMU smoke
   verification" → "T-012 Done 2026-04-28 via PR #10 merge; ...
   maintainer-side QEMU smoke verification of the deliberate-
   deadline path remains pre-B1-closure work".

6. `docs/architecture/exceptions.md` — top-of-doc Status banner +
   bottom-of-implementation-map paragraph both updated to "Done
   2026-04-28 via PR #10 merge"; mentions the two review-fix
   sweeps explicitly so a reader chasing commits has the full
   chain (`a043079` → `b4ed68c` → `28c5ce9` → `d820a88` →
   `5c9cf06`); QEMU smoke + Miri framed as pre-B1-closure work.

7. `README.md` (top-level) — phase-status sentence freshened:
   B0 closed 2026-04-27, B1 implementation complete 2026-04-28
   (kernel runs at EL1 unconditionally, 16-entry vector table,
   GIC v2 driver, generic-timer IRQ wiring, idle WFI), B2 MMU
   activation next.

Audit-log entries (UNSAFE-2026-0019 / 0020 / 0021) intentionally
not edited in this commit. Their `Pending QEMU smoke verification`
status notes are factual claims about an unobserved verification
step; they stay until the maintainer actually runs the smoke and
appends an Amendment recording the result. Lifting them at merge
time would be a body-edit on an append-only log.

Gates (no code changed in this commit; documentation only):
cargo fmt / kernel-build clean; host-test 149 / 149 (25 hal +
90 kernel + 34 test-hal) — same as `5c9cf06`.

Refs: T-012, ADR-0010 (deferred halves now live), ADR-0021
(2026-04-28 Amendment), ADR-0022 (first rider Sub-rider closed),
ADR-0024 (B1 EL drop), `docs/roadmap/current.md` next-review-trigger
(B1 closure trio)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Apr 29, 2026
…prep

Lands the three review artifacts that close out B1 implementation
(T-013 + T-012) at the milestone level — business retrospective,
consolidated security pass, performance baseline re-run — and pivots
the roadmap from "B1 closure review trio is the next work-item" to
"B2 prep — ADR-0027 drafting is unblocked" per the business retro's
Pathfinder output.

Three new artifacts:

1. `docs/analysis/reviews/security-reviews/2026-04-28-B1-closure.md`
   — eight-axis adversarial pass over the post-PR-#10 surface. Covers
   the new GIC v2 MMIO surface (UNSAFE-2026-0019), the EL1 vector
   table install + asm trampolines (UNSAFE-2026-0020), the EL1
   virtual generic-timer compare-register writes (UNSAFE-2026-0021),
   and the UNSAFE-2026-0014 commit-`28c5ce9` Amendment naming
   `irq_entry` as a future site of the same momentary-`&mut` pattern.
   Verdict: Approve. No Yüksek findings. One forward-flagged
   non-blocking item (`arm_deadline` CVAL+CTL race only relevant
   when a future caller exists). Three threat-model shifts
   identified (IRQ delivery as structural property; expanded unsafe
   surface with proportionally expanded discipline; IRQ-handler
   aliasing as a unified second-axis rule). Three positive review-
   surface observations (design-first as security multiplier, two-
   round review-fix cadence catching 19+2 findings pre-merge,
   audit-log Pending-QEMU-smoke status notes as explicit
   verification-pending markers).

2. `docs/analysis/reviews/performance-optimization-reviews/`
   `2026-04-28-B1-closure.md` — per-section size delta vs A6
   baseline; `.text` 13.6 → 21.4 KiB (+57%; T-012 ~5.3 KiB of the
   ~8 KiB delta — vector table 2 KiB, gic.rs ~1.6 KiB, exceptions
   ~0.4 KiB, Timer arm/cancel ~0.3 KiB, hal::timer ~0.5 KiB), `.bss`
   17.5 → 21.7 KiB (+24% almost entirely from T-007's 4 KiB idle
   stack; T-012's `.bss` cost is ~32 bytes for the GIC StaticCell).
   New Metric 6: IRQ delivery cost (~52 instructions per timer-IRQ
   round trip — 31 trampoline + 20 handler body + ~25 cycles
   microcode; not exercised in v1 because the IPC demo arms no
   deadline). Three concerns flagged for future hypothesis-driven
   cycles (IPC round-trip wall-clock measurement now infrastructure-
   unblocked via T-009's `now_ns()`; stack high-water-mark probes;
   `TrapFrame` slimming for ack-and-ignore handlers). Verdict:
   baseline recorded; no proposal made.

3. `docs/analysis/reviews/business-reviews/2026-04-28-B1-closure.md`
   — five-section retrospective (chronicler / plan-diff / learning
   / adjuster / pathfinder). Cost-of-arc: B1 ran ~7 commits/day
   between Phase A's 15/day and B0's 8/day; meta-process cost paid
   forward, review-fix-round cadence is now load-bearing (not
   free). Two new design patterns empirically validated: (a)
   design-first arc — `exceptions.md` landed before code in commit
   `c366200`, zero "doc disagrees with code" findings during
   PR #10's two rounds (with two minor diagram/prose mismatches
   round-1+2 caught); (b) explicit `Pending QEMU smoke verification`
   audit-log status notes as a forward-discipline pattern (three
   data points; recommend canonising as `unsafe-policy.md` §3 sub-
   rule once a third arc reproduces it). Pathfinder pivots to B2
   prep — ADR-0027 drafting unblocked, parallelizable with
   maintainer-side QEMU smoke + Miri.

Index updates:

4. `docs/analysis/reviews/business-reviews/README.md` — new row for
   the B1 closure retro.
5. `docs/analysis/reviews/security-reviews/README.md` — new row for
   the B1 closure security pass.
6. `docs/analysis/reviews/performance-optimization-reviews/`
   `README.md` — new row for the B1 closure baseline; the
   "blocked-on-timer-init" disclaimer prose updated to reflect that
   T-009 + T-012 have lit up the measurement primitive.

Roadmap pivot:

7. `docs/roadmap/current.md` —
   - Active-phase headline: B1 closure review trio "landed
     2026-04-28; milestone closure waits on maintainer-side QEMU
     smoke + Miri pass" (was "is the next work-item").
   - Active-task block: pivots from "none — next is the B1 closure
     review trio" to "none — next implementation work is B2 prep —
     drafting ADR-0027". By ADR-0025 §Rule 1 the implementation
     T-NNN gets opened in the same commit that lands ADR-0027's
     *Dependency chain* section; current.md does not pre-allocate
     the number.
   - Last-reviews list: the three B1 closure artifacts added at
     the top.
   - Next-task-to-open: B2 prep with the design-first pattern
     applied — `docs/architecture/memory-management.md` lands
     alongside (ideally before) the implementation, per the B1
     retro's Adjustments item that adopts design-first as a B2
     hard-requirement.
   - Next-review-trigger: B2 closure (whenever T-014 / whichever
     number lands), with the 2026-05-04 ADR-0024 rider-rate
     measurement and the maintainer-side QEMU smoke + Miri runs
     remaining as separate-but-tracked open work-items.

Methodology notes for the artifacts:

- The security review explicitly cross-references the [B0 closure
  security review's §2](docs/analysis/reviews/security-reviews/
  2026-04-27-B0-closure.md) which named this artifact as the venue
  for T-012's GIC MMIO surface coverage — closing that forward-
  reference cleanly.
- The performance baseline notes the A6 80-KiB-flat-binary number
  is not directly comparable to the new 24.1-KiB measurement
  because the linker LOAD segment shape is different; the section-
  level breakdown is the canonical comparison and is byte-accurate
  for both readings. Recommendation: drop the flat-binary line
  from future baseline reports unless an audit step recomputes
  the A6 number under the current linker script.
- The business retro's planned-vs-unplanned-Amendment distinction
  for ADR-0025 §Rule 2 is a recommended rule clarification, not a
  rule change; defer to the maintainer at the 2026-05-04 first-
  week measurement of ADR-0024.

Gates (no code changed in this commit; documentation only):
cargo fmt / kernel-build / kernel-clippy / host-clippy clean;
host-test 149 / 149 (25 hal + 90 kernel + 34 test-hal) — same
as `5c9cf06` post-PR-#10-round-2.

Refs: T-012 (Done 2026-04-28), T-013 (Done 2026-04-27), ADR-0010
(deferred halves now live), ADR-0021 (2026-04-28 Amendment),
ADR-0022 (first rider Sub-rider closed), ADR-0024 (B1 EL drop),
ADR-0025 (§Rule 1 + §Rule 2 calibration ongoing), UNSAFE-2026-0019
/ 0020 / 0021 (Pending QEMU smoke verification status notes
maintained).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Apr 29, 2026
…sure trio + exceptions.md idle prose drift

Two parallel inputs land here as one cohesive doc-only commit:

(A) Independent approval-review on commit 9678d6c (the B1 closure
    trio) returned Approve verdict with 1 Medium + 7 Low + 2 Nit
    findings. The Approve verdict means the originating commit's
    substantive conclusions stand; the findings are prose / numerical
    accuracy / framing improvements.

(B) A separate inline-comment finding flagged that
    `docs/architecture/exceptions.md` §"Idle's wfi activation" still
    contained the pre-T-012 framing ("Until T-012's full set lands,
    idle stays in the spin_loop form") even though `idle_entry`'s
    body uses `cpu.wait_for_interrupt()` since commit `b4ed68c`. The
    fix here aligns the prose with the shipped code.

(A) — review-trio fixes:

`docs/analysis/reviews/business-reviews/2026-04-28-B1-closure.md`:
- (M1) "Codify verification-pending status notes" Adjustments item
  reconciled with Appendix item #2 — the canonisation criterion is
  now consistently "the next IRQ-related arc reproduces the
  pattern", not "three same-arc entries". UNSAFE-2026-0019 / 0020 /
  0021 share one arc and one shape; they are *one* cross-arc data
  point. The §"Audit-log Pending QEMU smoke verification status
  notes" Learning section, the Adjustments item, and the Appendix
  item now all carry the same threshold language.
- (L6) Cost-of-arc commit-count breakdown corrected from
  "3 implementation + 1 design-first + 1 doc-sweep + 2 review-fix +
  1 Done = 8 (claimed 9)" to a 9-commit categorization that actually
  sums correctly: 2 implementation (a043079, b4ed68c) + 1 design-
  first doc (c366200) + 1 doc-sweep (28c5ce9 — T-012 step 6+7) +
  1 SHA-backfill (6d2f725) + 1 review-fix-prep (67ff867) + 2 PR-
  review-round (d820a88, 5c9cf06) + 1 Done-sweep (ffefb76) = 9.
  Commit `28c5ce9` is now classified as the doc-sweep role rather
  than double-counted as both step 6+7 and the documentation-sweep
  commit.
- (N1) "Result: zero 'doc and code disagree' findings" softened to
  "only two diagram/prose mismatches caught — no structural
  'doc disagrees with code' findings"; the original phrasing self-
  contradicted the immediately-following "Two diagram/prose
  corrections" paragraph.
- (N2) Stale-link drift section qualified: "stale-link drift
  suppressed" → "stale-link drift suppressed for new T-012 changes",
  with explicit acknowledgement that pre-existing repo drift
  (umbrix → tyrne rename leftovers) is a separate concern outside
  PR #10's scope. New Adjustments item ("Repo-wide
  umbrix-to-phase-a.md → tyrne-to-phase-a.md link sweep") opens the
  separate `docs(refs):` commit that handles the pre-existing
  drift.

`docs/analysis/reviews/security-reviews/2026-04-28-B1-closure.md`:
- (L1) New §3 forward-flagged item: cancel_deadline ordering race
  window between Step 1 (MSR CNTV_CTL_EL0=0b10 source-mask) and
  Step 2 (gic.disable(TIMER_IRQ)). Symmetric to the arm_deadline
  CVAL+CTL race that the original review flagged; the original
  review missed this symmetric case. v1 still benign (no caller).
- (L4) "Kernel crate is still zero-unsafe" claim corrected. The
  `grep -r "unsafe " kernel/src/` description was misleading — the
  raw-pointer scheduler bridge `unsafe fn` bodies contain 15 inner
  `unsafe { ... }` blocks (mandatory under
  `#![deny(unsafe_op_in_unsafe_fn)]`). The substantive sentence
  ("T-012 added zero kernel-crate unsafe blocks; the new surface
  is BSP-internal") is preserved; only the misleading claim about
  the absence of `unsafe { ... }` blocks is rewritten.
- (L8) GIC ITARGETSR loop-bound math precise now: last byte_idx is
  1020 (loop condition `byte_idx < irq_count` exits at 1024); max
  offset = 0x800 + 0x3FC = 0xBFC. The original "0xC00" was the
  one-past-end byte. Conclusion ("well within the 4 KiB window")
  unchanged.
- Verdict block: "One forward-flagged item" → "Two forward-flagged
  items" reflecting (L1)'s new entry; the cross-references in the
  README index and the business retro's Adjustments are kept
  consistent.

`docs/analysis/reviews/performance-optimization-reviews/`
`2026-04-28-B1-closure.md`:
- (L7) §Metric 1 decomposition table:
  `exceptions.rs::irq_entry + panic_entry ~400 bytes` →
  `~244 bytes (irq_entry 116 B + panic_entry 128 B per
  rust-objdump)`. Verified by disassembling the release ELF.
- (L5) "Roughly two-thirds of the A6→B1 .text delta is T-012 alone"
  → "Roughly 58 % of the A6→B1 .text delta is T-013 + T-012
  specifically" with explicit T-013 / T-012 / B0 split. The
  arithmetic now uses the corrected 244-byte exceptions.rs figure;
  total estimate sums to ~7,810 bytes (~2 % residual to LLVM
  optimization shifts; matches empirical +7,968 within rounding).
- (L2) §Metric 6 IRQ delivery cost trampoline line-items corrected.
  The `mov sp, x0` typo (which would have corrupted SP) → `mov x0,
  sp` (correct: writes the frame pointer to x0 for `bl irq_entry`).
  Save phase enumeration now verified line-by-line against
  vectors.s:115-130 (15 instructions: sub sp + 10 stp + mrs elr +
  mrs spsr + 1 stp + mov x0,sp). Restore phase corrected from "16"
  to 15 (the original "11 ldp" double-counted the elr/spsr ldp
  pair). New approximate dispatch cost: 51 instructions + ~25
  cycles of microcode (down from the original 52). The §Hotspot
  observation (~60 % trampoline) becomes ~59 % (30/51).

`docs/analysis/reviews/security-reviews/README.md`:
- Index row's verdict description updated to match the artifact's
  two forward-flagged items.

Substantive verdicts unchanged: security Approve, performance
Baseline-recorded, business B2-prep pivot all stand. The findings
are consistency / accuracy / framing improvements, not corrections
to the underlying conclusions.

(B) — exceptions.md idle prose drift:

`docs/architecture/exceptions.md` §"Idle's wfi activation"
restructured. The original "from spin_loop → to WFI" historical
comparison framed the WFI form as a future change ("Until T-012's
full set lands, idle stays in the spin_loop form"), which was
accurate when the doc was drafted design-first in commit `c366200`
but is now stale — `b4ed68c` flipped idle_entry to WFI.

The new prose:
- Leads with "Idle's body uses WFI as the default wake strategy —
  the form ADR-0022's Decision outcome originally specified, now
  shipped" + the WFI-form code block.
- Records the historical flip as past tense: "T-012's commit
  `b4ed68c` flipped `idle_entry`'s body from the interim
  core::hint::spin_loop() shape ... to the wait_for_interrupt()
  shape above. This closes ADR-0022 first rider's Sub-rider gate."
- Names the wake source explicitly (timer IRQ; arm_deadline
  callers from B5+ time_sleep_until or B-late preemption tick).
- Notes that v1's IPC demo path makes WFI structurally unreachable
  (both application tasks tail-`spin_loop`-yield permanently); the
  shipped change is observable as a code-shape change, not a
  behavioural change in the v1 trace.
- Flags remaining T-012 items (maintainer-side QEMU smoke + Miri)
  as separate from this section's scope, with cross-link to the
  B1 closure retro's Adjustments and to the audit-log
  Pending-QEMU-smoke status notes.

The spin_loop code block is removed because preserving an obsolete
pre-shipping form serves no current reader; the historical
transition is captured in the prose plus the cross-reference to
commit `b4ed68c`.

Audit-log entries (UNSAFE-2026-0019 / 0020 / 0021) and
ADR-0021 §Revision notes Amendment intentionally not edited — the
findings here are prose/numerical fixes in review and architecture
artifacts, not in append-only audit or ADR bodies. Introducing-
commit-boundary discipline preserved.

Pre-existing N2 (`umbrix-to-phase-a.md` → `tyrne-to-phase-a.md`
repo-wide rename) deliberately out of scope for this commit; will
land in a separate `docs(refs):` commit so the scope is clear.

Gates (no code changed in this commit; documentation only):
cargo fmt / kernel-build / host-clippy / kernel-clippy clean;
host-test 149 / 149 (25 hal + 90 kernel + 34 test-hal) — same
as `9678d6c` post-trio.

Refs: T-012 (Done 2026-04-28; commit b4ed68c flipped idle to
WFI), ADR-0021 (2026-04-28 Amendment unchanged), ADR-0022 (first
rider Sub-rider closure paragraph unchanged), ADR-0025 (§Rule 2
planned-vs-unplanned-Amendment distinction observation
unchanged), UNSAFE-2026-0019 / 0020 / 0021 (Pending QEMU smoke
verification status notes maintained, untouched).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Apr 29, 2026
Approval review on PR #11 returned a punch-list: 5 inline comments
+ 1 nitpick + 3 PR-level comments + 2 overall comments. This commit
applies 9 of the 10 actionable findings; 1 finding (security review
§4 irq_entry "loop" claim) was verified false against the source
and is rejected with explanation below. The 2 overall comments
(pattern-deduplication, narrative-tightening) are noted as known
follow-up concerns but are out-of-scope for this commit.

Findings applied:

(F1) `docs/analysis/reviews/performance-optimization-reviews/`
     `2026-04-28-B1-closure.md` — security cross-reference line:
     "one forward-flagged non-blocking item" → "two forward-flagged
     non-blocking items" with both names (`arm_deadline` CVAL+CTL
     race + `cancel_deadline` CTL+GIC race). Synchronises with the
     security review (which gained the second item in commit
     `a5d4f9b`).

(F3) `docs/analysis/reviews/security-reviews/2026-04-28-B1-closure.md`
     §Verdict — "no Yüksek findings" → "no high-severity findings".
     Brings the B1 trio's verdict prose under CLAUDE.md §3 (English
     in repository) without disrupting the established
     conduct-approval-review SKILL template's Turkish severity-label
     vocabulary (which the user explicitly approved earlier as
     "established pattern"). Scope kept narrow: this commit
     translates only the B1 trio's instances; the B0 closure
     security review's index entry retains "Yüksek" as a
     pre-existing committed artifact (unsafe-policy.md §3 Amendment
     discipline applies to audit-log entries, not review artifacts,
     but a broader sweep is a separate scope question and not
     trigger-driven by this review round).

(F4) `docs/analysis/reviews/security-reviews/README.md` line 36 —
     same Yüksek → high-severity translation in the index entry's
     Verdict cell.

(F5) `docs/analysis/tasks/phase-b/T-012-exception-and-irq-`
     `infrastructure.md` Definition-of-done checklist line 82 —
     "Task status updated to `In Review`" → "Task status updated to
     `Done`". The Status field at line 5 was already `Done` from
     ffefb76; the DoD checklist line was a stale residual from the
     `In Review` promotion. Now consistent.

(F6) `docs/roadmap/phases/phase-b.md` B1 milestone status block —
     "B1 closure review trio is pending" → "B1 closure review trio
     landed 2026-04-28 via PR #11" with deep-links to the three
     artefacts. Only QEMU smoke + Miri remain pending. The B1
     milestone-level `Done` flip happens after the maintainer-side
     verification.

(N1) `README.md` Status: line — split from a dense single-paragraph
     summary into a four-bullet status list (Phase A / Phase B B0 /
     Phase B B1 / Path forward) plus a final pointer to
     `docs/roadmap/current.md`. Each bullet is a concise summary;
     dates retained; "Status:" label retained.

(P1) `docs/analysis/reviews/business-reviews/2026-04-28-B1-closure.md`
     §"Design-first arc" — math `19 + 2 + 2 = 23 total findings` →
     `21 total review items (19 substantive findings — 11 round-1 +
     8 round-2 — plus 2 round-2 nitpicks; the round-1 nitpick
     disposition is folded into the 11 applied)`. The `19+2+2`
     arithmetic was wrong (sums to 23, but the breakdown didn't
     match either round's commit-message numbers). The new text
     gives a clear decomposition matching `d820a88` ("apply 11 of
     12 findings") and `5c9cf06` ("8 findings + 2 nitpicks") commit
     messages.

(P2) `docs/analysis/reviews/business-reviews/2026-04-28-B1-closure.md`
     §"File renames and stale-link drift" — paragraph that said the
     `umbrix → tyrne` rename would land in "a follow-up `docs(refs):`
     commit (the immediate next commit after this retro lands)" was
     stale: the rename DID land in commit `10e3351`, which is one
     of PR #11's four commits — the same PR that lands this retro.
     Updated to "the `docs(refs):` commit `10e3351` (one of the
     four commits in PR #11, the same PR that lands this retro)"
     with deep-link. The Adjustments item already correctly says
     ✅ Done; this paragraph now matches.

(P3) Merge-commit-SHA back-fill across 3 documents. PR #10's merge
     into `main` is commit `7b42bbe` (verified via
     `git log --oneline 9a66e8b..origin/main`); the documents that
     said "merge commit SHA pending back-fill in a follow-up
     `docs(audits)`-style commit" are now updated to reference
     `7b42bbe` directly with a clickable PR #10 link. Affected
     files:
     - `docs/analysis/tasks/phase-b/T-012-exception-and-irq-`
       `infrastructure.md` (review-history Done promotion row).
     - `docs/analysis/reviews/security-reviews/`
       `2026-04-28-B1-closure.md` (Change frontmatter).
     - `docs/roadmap/current.md` (Last-completed-tasks line; also
       added merge-commit `9a66e8b` cross-reference for T-013/PR
       #9 to match PR #10's back-fill shape).
     The earlier framing was process-detail noise (`docs(audits)-`
     style follow-up commit) that made each document feel dated
     immediately after merge; the new framing names the merge
     commit directly and is evergreen.

Finding rejected:

(F2) Security review §4 "Kernel-mode discipline" — claim that
     `irq_entry` is "straight-line / loop-free / constant-time".
     The reviewer asserted this is incorrect because "irq_entry
     uses a loop to acknowledge pending IRQs via repeated
     GICC_IAR reads".

     Verified against the source ([`bsp-qemu-virt/src/exceptions.rs`
     `irq_entry`](docs/architecture/exceptions.md) lines ~145-193):
     `irq_entry`'s body is straight-line. There is no `loop`, no
     `while`, no `for`. The dispatch shape is:
       1. `let gic = ...` (single static deref)
       2. `let Some(irq) = gic.acknowledge() else { return };`
          (single GICC_IAR read; spurious → early return)
       3. `if irq.0 == TIMER_IRQ_ID { ... return; }`
          (single dispatch arm; mask + EOI + return)
       4. `panic!("unhandled IRQ")` (structurally unreachable in v1)

     `gic.acknowledge()` returns `Option<IrqNumber>` — a single
     IRQ ID, not an iterator. The function does NOT drain pending
     IRQs in a loop; the GICv2 architecture actually delivers one
     IRQ per acknowledge cycle. To process additional pending
     IRQs, the CPU re-takes the exception after `eret` (if more
     are pending and DAIF.I is unmasked), which goes through the
     trampoline + `irq_entry` again — but each `irq_entry`
     invocation handles exactly one IRQ.

     The "constant-time / straight-line" claim is therefore
     correct and remains in the security review unchanged. The
     reviewer's mental model may have come from a typical
     edge-triggered GIC handler that DOES loop on IAR until
     spurious — but Tyrne's choice (per [docs/architecture/
     exceptions.md](docs/architecture/exceptions.md) §"Dispatch
     flow") is one-IRQ-per-entry; the documentation accurately
     reflects the implementation.

Overall comments not addressed (noted as known concerns):

- **`Pending QEMU smoke verification` pattern repetition.** The
  pattern appears in `current.md`, `phase-b.md`, the three review
  artefacts, and the T-012 task file. Factoring into a single
  canonical explanation (in `unsafe-policy.md` or a short standard)
  is the right shape; per the [B1 closure retro Adjustments item +
  Appendix #2](docs/analysis/reviews/business-reviews/2026-04-28-B1-closure.md),
  the canonisation trigger is "the next IRQ-related arc reproduces
  the pattern" — cross-arc reproducibility threshold. Not actionable
  in this commit.
- **B1 narrative repetition across files.** Real concern but
  addressing it via cross-linking would substantially restructure
  multiple committed review artefacts (which are append-only-style
  per the project convention). Better handled at the next milestone
  retro's plan-diff section, when the retro can decide whether to
  pivot to a cross-link-heavy style going forward. Recorded here
  for visibility.

Gates (no code changed; documentation only): cargo fmt /
kernel-build / kernel-clippy / host-clippy clean; host-test 149 /
149 unchanged.

Refs: PR #11 approval review (the conversation-driven punch-list);
T-012 (Done 2026-04-28; merge commit `7b42bbe`); ADR-0021 (2026-04-28
Amendment unchanged); ADR-0022 (first rider Sub-rider closed,
unchanged); CLAUDE.md §3 (English-in-repository rule applied to
verdict prose).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cemililik added a commit that referenced this pull request May 9, 2026
Doc-only fixup commit on the B3-prep PR. Reviewer Approve-with-nits
verdict; all 10 actionable findings applied. No merge-blocker;
cargo gates unchanged from main (185/185 host; clippy / fmt /
kernel-build clean).

Applied (Minor):

1. **Finding 3.1 — §Simulation row 3 wrap-scan rationale.** The
   "two-pass scan ensures correctness even under hint-past-last-
   free" claim describes a state that cannot occur in v1 (the
   unconditional rewind in row 2 keeps `hint ≤ lowest-free-index`).
   Reworded row 3 to honestly frame the wrap as forward-compat
   scaffolding for the future SMP extension where per-core caches
   may leave the global hint stale.

2. **Finding 4.1 — UNSAFE-2026-0001 umbrella adjudication-deferred.**
   The reviewer correctly noted that UNSAFE-2026-0001's actual
   scope (PL011 MMIO base blessing) is semantically distant from
   PMM frame-zeroing (raw write_bytes over normal-cached RAM).
   Added an explicit "adjudication-deferred to T-017's security
   review" soft-flag in §Dependency chain step 5, propagated to
   §Decision drivers + §Decision outcome + Option A pros + T-017
   §Approach commit 2. The Amendment-vs-new-entry call is now
   honestly framed as a borderline judgment for T-017's review;
   either outcome is bounded.

3. **Finding 8.1 — repurposed T-017 test #10.** The original
   `free_frame_rejects_pa_in_reserved_range` overlapped with test
   #4 (which already pinned `free_frame(reserved)` → `DoubleFree`).
   Repurposed to `free_frame_reserved_check_iterates_only_populated_slots`
   to pin the populated-slots-only contract on the defensive scan
   (genuinely O(populated-entries), not O(R)). Combined coverage
   is now: test #4 = "reserved-PA correctly rejected"; test #10
   = "non-reserved-PA correctly accepted under partially-populated
   array".

4. **Finding 8.2 — `MAX_RESERVED_RANGES` per-BSP const generic.**
   The hardcoded `8` was unjustified and risked TooManyReservedRanges
   on future BSPs whose typical aarch64 boot-stack reservations
   (DTB + ATF + ACPI + initrd + framebuffer) could land 7–9
   ranges. Promoted to a per-BSP const generic `R` consistent with
   the existing `PMM_BITMAP_BYTES` `N` parameterisation: the kernel
   crate exposes `Pmm<const N: usize, const R: usize>`; v1's
   `bsp-qemu-virt` picks `R = 8`; future BSPs pick what they need.
   The `expect("...")` BSP panic is structurally unreachable when
   `R` is sized per the BSP's static reservation list.

5. **Finding 10.1 — Option A regime-independence qualifier.**
   The bare "regime-independent (high-half-portable)" claim in
   §Pros didn't carry the qualifier from §Decision outcome / §Positive
   ("metadata addressing"). Added the same qualifier to §Pros and
   noted that the frame-zeroing step's `*mut u8` from PA is
   identity-mapping-dependent today — a `phys_to_virt` helper is
   needed post-high-half (but the same helper would be needed for
   the rejected Option C, so Option A's regime-independence
   advantage at the algorithm-and-metadata level still stands).

Applied (Nit):

6. **Finding 3.2 — §Simulation row 0 `.bss` zero-fill prose.** The
   "bitmap zero-initialised by `.bss` zero-fill" precondition was
   technically true but not load-bearing — `Pmm::new` constructs
   a fully-initialised value. Reworded row 0 to acknowledge this:
   "the zero-init is precondition-only, not load-bearing".

7. **Finding 5.1 — Option B u32/u16 indices math.** The 128 KiB
   metadata claim assumed `u32` indices; `u16` halves to 64 KiB
   for v1's 32 K frames. Tightened the math with a parenthetical
   ("64 KiB with `u16` since 32 K fits in 15 bits — but `u32`
   is forward-portable to BSPs with > 64 K frames such as Pi 4
   with 1 M frames at 4 GiB"). Qualitative judgment unchanged
   (still 16–32× the bitmap).

8. **Finding 8.3 — §Approach commit 1 test set.** Moved
   `new_rejects_too_many_reserved_ranges` from "implicit later
   commit" to commit 1 alongside `new_marks_reserved_ranges_and_initialises_counters`
   (both pin `Pmm::new`, so they belong in the constructor commit).

9. **Finding 9 — UNSAFE-2026-0001 umbrella name consistency.** The
   ADR's prose drifted between "kernel-static-buffer raw-pointer
   write to identity-mapped memory" (L35) and "MMIO + kernel-static-
   buffer raw-pointer" (L58). Picked the former as the canonical
   phrasing and aligned both surfaces.

10. **Finding 12.1 — T-016 §"Why one bundled task" disambiguation.**
    The line "instead of T-016 + T-017 + T-018?" was written when
    those numbers were hypothetical sub-task splits of T-016 itself.
    Post-T-017 (PMM) + T-018 (AddressSpace, future) numbering, the
    line risked confusion. Added a parenthetical clarifying it's
    about a hypothetical 3-way split of T-016, not the actual
    post-T-016 numbering.

Bonus — Finding 12.2 (kernel-internal framing) — slight cosmetic
qualifier added: "kernel-internal at runtime" + a parenthetical
clarifying that the BSP touches the PMM only at construction time
(linker-symbol → Pmm::new → StaticCell publication) and never
afterwards.

Verification:
  - cargo fmt --check           clean
  - cargo test                  185/185 host (unchanged — docs only)
  - cargo kernel-build          clean

Files touched:
  - docs/decisions/0035-physical-memory-manager.md (+30 / -20)
  - docs/analysis/tasks/phase-b/T-017-physical-memory-manager.md (+18 / -10)
  - docs/analysis/tasks/phase-b/T-016-mmu-activation.md (+1 / -1)

This commit completes the PR #25 review-round; all reviewer-flagged
findings closed. The Accept commit (2de67f1) carrying ADR-0035 →
Accepted stays as the load-bearing flip; this fixup commit is the
honesty / consistency polish pass on top.

Refs: ADR-0035, T-017
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