aarch64: minimal GICv2 driver -- distributor, CPU interface, timer PPI
Punch list §25 item 0.6 complete. Ruling applied (AskUserQuestion, this session): DTB is confirmed unreachable on this system's aarch64 firmware too (qemu-efi-aarch64 2025.11-3ubuntu7, same finding as riscv64's item 0.3), so GICD/GICC base addresses and the timer PPI are named QEMU-virt constants with a recorded caveat, not DTB-discovered as the item originally asked. Nothing here was recalled from memory. Base addresses (GICD 0x08000000, GICC 0x08010000) and the timer PPI (30, non-secure EL1 physical) were read out of QEMU 10.2.1's own internal devicetree via `qemu-system-aarch64 -machine virt,dumpdtb=...`, decoded with this tree's own fdt.c reader rather than a new tool -- correct for this exact QEMU version, not assumed stable across others. Bonus finding from the same dump: PPI 26 for the EL2 hypervisor timer, which item 0.7 will need for its EL2 path. Register offsets within each block (GICD_CTLR, GICC_IAR, etc.) are GICv2 architectural constants, not board-specific, and were cross-checked against Linux's own arm-gic.h driver header rather than recalled either. Acceptance amended before implementing (§25.0 "when an item is genuinely wrong"): the original text required observing a delivered-and-acknowledged timer interrupt, which cannot happen within this item's own scope -- apic_timer_start() (item 0.7) is still the no-op stub, so nothing arms the timer. This is the same defect the earlier review's C2 fix already applied to items 0.2 and 0.5; it was missed here. Acceptance is now: GIC initialises without fault, the IAR/EOIR path is wired into aarch64_irq_handler() and ready, boots with no regression -- item 0.7's tick-advance is what proves delivery, exactly as 0.5 already defers to 0.7. EL-aware (B3, same discipline as items 0.4/0.5): apic_init() selects PPI 30 or 26 from aarch64_current_el(), decided once and cached, not re-derived per interrupt. aarch64_irq_handler() now does real work: reads GICC_IAR (the GICv2 acknowledgement step), dispatches to heartbeat_tick() when the INTID matches the timer PPI, and always completes with GICC_EOIR (INTID 1023 = spurious handled per the GICv2 spec, not as a special case of "unrecognised"). This mirrors exactly how riscv64's item 0.2 built full cause-dispatch logic before its timer was armed in 0.3. Investigated and resolved a real scare during verification: QEMU's `-d int` trace showed 1,728 "Taking exception 5 [IRQ]" events by the time boot reached the prompt, which looked exactly like an interrupt storm (hypothesis: EDK2 firmware leaves CNTP_CTL_EL0 enabled with a stale comparator, and enabling the GIC path exposes it before item 0.7 reprograms the timer). A direct one-shot probe inside aarch64_irq_handler() itself -- ground truth for whether this code path runs at all -- fired zero times across a clean, bounded boot. The trace events were almost certainly from EDK2 firmware's own internal timer usage during its own boot phase, before control passes to this kernel; the earlier conclusion was drawn from the external trace alone without checking that distinction, and the probe (not the trace) is what settled it. Probe code fully reverted; not part of the commit. Verified: builds clean, boots to ok> with no regression, dict_hash 0x3d4e1daf289da94f unchanged from the item 0.1-0.5 baseline, EL banner and IDT-installed lines still print in order, GIC init line confirms PPI 30 selected. Only aarch64-scoped files touched; amd64/riscv64 not rebuilt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
8d8f3aaae2
commit
cabb0e8bd4
@@ -2076,7 +2076,7 @@ on until there is a tick on all three architectures (§16.1, §16.5).*
|
||||
acceptance is what proves this path took and returned an IRQ (C2). Do not pull 0.6/0.7
|
||||
work forward to manufacture evidence here.
|
||||
|
||||
- [ ] **0.6 — aarch64: minimal GICv2.**
|
||||
- [x] **0.6 — aarch64: minimal GICv2.**
|
||||
Enable the distributor and CPU interface, set the priority mask, enable the timer PPI,
|
||||
acknowledge via `IAR` / `EOIR`. **Read the base addresses and the PPI INTID from the
|
||||
device tree — do not take them from memory or from this document.** The DTB pointer
|
||||
@@ -2084,8 +2084,33 @@ on until there is a tick on all three architectures (§16.1, §16.5).*
|
||||
DTB turns out to be unreachable on aarch64 EDK2, stop and report — deciding between
|
||||
loader work and named QEMU-virt constants with a recorded caveat is Captain Bob's call,
|
||||
not the implementer's.
|
||||
*Done when:* the timer interrupt is delivered and acknowledged. Scope is one interrupt;
|
||||
a general GIC driver is out of scope and must not be written.
|
||||
|
||||
> **DTB confirmed unreachable (checked live, 2026-08-03) — same finding as riscv64.**
|
||||
> `fdt_valid(boot_info->dtb)` fails on this system's aarch64 build too (installed
|
||||
> firmware: `qemu-efi-aarch64` 2025.11-3ubuntu7, no alternate available). Ruling: named
|
||||
> QEMU-virt constants, verified rather than recalled — `qemu-system-aarch64
|
||||
> -machine virt,dumpdtb=...` was used to dump QEMU's own internal devicetree (the one
|
||||
> EDK2 fails to forward) and decoded with this tree's own `fdt.c` reader, giving
|
||||
> **GICD 0x08000000, GICC 0x08010000** (both confirmed for this exact QEMU 10.2.1
|
||||
> build, not assumed stable across versions) and **PPI 30** for the non-secure EL1
|
||||
> physical timer (bonus finding: PPI 26 for the EL2 hypervisor timer, for item 0.7's
|
||||
> EL2 path). Register *offsets* (GICD_CTLR, GICC_IAR, etc.) are architectural, not
|
||||
> board-specific, and were cross-checked against
|
||||
> `/usr/src/linux-headers-*/include/linux/irqchip/arm-gic.h` rather than recalled.
|
||||
|
||||
> **Acceptance corrected — same defect C2 already fixed for items 0.2 and 0.5, missed
|
||||
> here.** "The timer interrupt is delivered and acknowledged" cannot be observed within
|
||||
> this item's own scope: nothing arms the timer until item 0.7's `apic_timer_start()`.
|
||||
> As written this item could never be marked done on its own evidence. Acceptance is
|
||||
> now the same shape as 0.2/0.5: GIC initialises without fault, IAR/EOIR path is wired
|
||||
> into `aarch64_irq_handler()` and ready, boots with no regression. Item 0.7's
|
||||
> tick-advance acceptance is what proves this path actually delivers and acknowledges an
|
||||
> interrupt, exactly as 0.5 already defers to 0.7 for the same reason.
|
||||
|
||||
*Done when:* GIC distributor and CPU interface initialise without fault; the timer PPI
|
||||
is enabled; `aarch64_irq_handler()` reads `IAR`, dispatches, and writes `EOIR`; boots to
|
||||
the prompt with no regression. Scope is one interrupt; a general GIC driver is out of
|
||||
scope and must not be written.
|
||||
|
||||
- [ ] **0.7 — aarch64: arm the generic timer.**
|
||||
`apic_timer_start()` / `apic_timer_stop()` using the register set chosen in 0.4, re-armed
|
||||
|
||||
Reference in New Issue
Block a user