Development #10 — T-012 (B1 IRQ infrastructure) + B0 closure retros#10
Conversation
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>
There was a problem hiding this comment.
Sorry @cemililik, your pull request is larger than the review limit of 150000 diff characters
📝 WalkthroughWalkthroughInstalls 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
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)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
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.
| Tramp->>Frame: stp x0..x29; mrs ELR_EL1, SPSR_EL1 | |
| Tramp->>Frame: stp x0..x18, x30; mrs ELR_EL1, SPSR_EL1 |
| 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) |
There was a problem hiding this comment.
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.
| Handler->>Sched: sched::on_timer_irq(&Frame) | |
| alt timer IRQ (id 27) | |
| Handler->>Handler: mask timer + EOI (v1) |
| end | ||
| Handler->>GIC: gic.end_of_interrupt(irq) | ||
| Handler-->>Tramp: returns | ||
| Tramp->>Frame: ldp x0..x29; msr ELR_EL1, SPSR_EL1 |
There was a problem hiding this comment.
| 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`. |
There was a problem hiding this comment.
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.
| 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. |
There was a problem hiding this comment.
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 | 🟡 MinorDependency status here needs to be updated.
T-009is still described asIn Review, but the other roadmap/closure docs in this PR already mark itDoneon 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_000evaluates to exactlyu64::MAX, so this checks the boundary value, not overflow-to-saturation behavior. Add one case that forcesticks > u64::MAXpre-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
📒 Files selected for processing (28)
.claude/skills/README.md.claude/skills/conduct-approval-review/SKILL.mdbsp-qemu-virt/build.rsbsp-qemu-virt/linker.ldbsp-qemu-virt/src/cpu.rsbsp-qemu-virt/src/exceptions.rsbsp-qemu-virt/src/gic.rsbsp-qemu-virt/src/main.rsbsp-qemu-virt/src/vectors.sdocs/analysis/reviews/business-reviews/2026-04-27-B0-closure.mddocs/analysis/reviews/business-reviews/README.mddocs/analysis/reviews/security-reviews/2026-04-27-B0-closure.mddocs/analysis/reviews/security-reviews/README.mddocs/analysis/tasks/phase-b/README.mddocs/analysis/tasks/phase-b/T-008-architecture-docs.mddocs/analysis/tasks/phase-b/T-011-missing-tests-bundle.mddocs/analysis/tasks/phase-b/T-012-exception-and-irq-infrastructure.mddocs/analysis/tasks/phase-b/T-013-el-drop-to-el1.mddocs/architecture/README.mddocs/architecture/exceptions.mddocs/audits/unsafe-log.mddocs/decisions/0010-timer-trait.mddocs/decisions/0021-raw-pointer-scheduler-ipc-bridge.mddocs/decisions/0022-idle-task-and-typed-scheduler-deadlock.mddocs/roadmap/current.mddocs/roadmap/phases/phase-b.mddocs/standards/code-review.mdhal/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>
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
docs/architecture/exceptions.md (1)
160-165:⚠️ Potential issue | 🟠 MajorDocument the shipped timer IRQ path here as well.
This section still says
irq_entrycallssched::on_timer_irq(...), butbsp-qemu-virt/src/exceptions.rscurrently masksCNTV_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_tickstests 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 forTrapFrame.
TrapFrameis part of a byte-for-byte ABI withbsp-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
📒 Files selected for processing (11)
.claude/skills/conduct-approval-review/SKILL.mdbsp-qemu-virt/src/exceptions.rsbsp-qemu-virt/src/gic.rsdocs/analysis/reviews/business-reviews/2026-04-27-B0-closure.mddocs/analysis/tasks/phase-b/README.mddocs/analysis/tasks/phase-b/T-012-exception-and-irq-infrastructure.mddocs/architecture/exceptions.mddocs/decisions/0021-raw-pointer-scheduler-ipc-bridge.mddocs/roadmap/current.mddocs/roadmap/phases/phase-b.mdhal/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
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>
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
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
📒 Files selected for processing (5)
bsp-qemu-virt/src/exceptions.rsdocs/architecture/exceptions.mddocs/decisions/0021-raw-pointer-scheduler-ipc-bridge.mddocs/roadmap/current.mdhal/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
| 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. |
There was a problem hiding this comment.
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.
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>
…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>
…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>
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>
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
Summary
VBAR_EL1, GIC v2 driver for QEMU virt, generic-timer IRQ wiring, and idle's WFI activation — promoted toIn Review. Pairs with T-013's EL drop (already onmainfrom 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.&mut-across-cpu.context_switchrule extends to the IRQ-handler frame via a §Revision notes Amendment; v1'sirq_entryis 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)
b60978df9c145c8800b0bc366200docs/architecture/exceptions.mdlands ahead of code (per B0 sec-review §8).a043079bsp-qemu-virt/src/gic.rs), 16-entry vector table attyrne_vectors(bsp-qemu-virt/src/vectors.s),MSR VBAR_EL1install +MSR DAIFClr, #0x2unmask.b4ed68c<QemuVirtCpu as Timer>::arm_deadline/cancel_deadlinereal impls;irq_entryack-and-ignore on PPI 27;idle_entrybody usescpu.wait_for_interrupt();tyrne_hal::timer::ns_to_tickshelper + 5 host tests (148/148 host suite green).28c5ce9irq_entryfuture site) + ADR-0010 §Revision notes (arm_deadline/cancel_deadlinelive) + ADR-0022 first rider Sub-rider closure + T-012 task file →In Reviewwith all AC/DoD checkboxes flipped.6d2f725<TBD-commit-3>→28c5ce9SHA across 8 files (matches the UNSAFE-2026-0011 SHA back-fill pattern).67ff867idle_entrydoc-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.QemuVirtGicMMIO surface (distributor + CPU interface; init sequence +IrqControllertrait impl). Introduceda043079.MSR VBAR_EL1,vectors.s16-entry table, IRQ trampoline saving 192-byteTrapFrame, panic-class trampolines for unhandled vectors). Introduceda043079.CNTV_CVAL_EL0,CNTV_CTL_EL0enable / mask). Introducedb4ed68c.irq_entryas a future site of the same momentary-&mutpattern; v1 has no live citation yet (irq_entryis ack-and-ignore). Introduced28c5ce9.ADR amendments (no new ADRs)
arm_deadline/cancel_deadlinedeferred halves are now live onQemuVirtCpu; pointer to UNSAFE-2026-0021. Trait surface unchanged.&mut-across-context-switch rule to the IRQ-handler frame. Three numbered future-call-site rules; rejected alternatives recorded.idle_entryswitches fromcore::hint::spin_loop()+ yield tocpu.wait_for_interrupt()+ yield. ADR's chosen option matches shipped code from this commit forward.Architecture documentation
exceptions.mdrow promoted fromDesigntoAccepted (v0.0.1).Reviews included in this PR
main; cited by the B0 retro as the agenda-loader.New skill
.claude/skills/conduct-approval-review/SKILL.md— distinct fromperform-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
cargo fmt --all -- --checkcargo host-clippy-D warnings)cargo kernel-clippycargo kernel-buildcargo host-testcargo +nightly miri test(host)-machine virtualization=on)Test plan
fmt,host-clippy,kernel-clippy,kernel-build,host-test, Miri pass on the R6 workflow.-machine virtualization=on, EL2 entry): kernel boots; T-013's EL drop still works post-T-012; identical post-post_erettrace.arm_deadline(now + 100ms); the IRQ fires;irq_entrymasks the timer + EOIs; kernel returns to interrupted code. (No caller in v1 demo; this is the first real exercise of the IRQ path.)What this PR does NOT do
sched::on_timer_irqhook. v1's IPC demo arms no deadlines;irq_entryis 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.FakeIrqControllerintyrne-test-hal. Reasoned-deferred — no host-testable kernel logic touches the GIC trait surface in v1 (thetyrne_hal::timer::ns_to_ticksmath is the new host-testable surface, covered by 5 new tests). Added when there's something to test against it.In Reviewstatus — left as the next session's call.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Tests