diff --git a/FABRIC.md b/FABRIC.md index 0a1c2c7..8932f51 100644 --- a/FABRIC.md +++ b/FABRIC.md @@ -3843,6 +3843,73 @@ document and committing that amendment as its own item.* > **This is the checkpoint** — 4.3.x groundwork stops here for review per this item's own > acceptance criterion, before scoping whatever comes next. +- [ ] **4.3.5 — amd64: I/O APIC bring-up + i8042 keyboard, interrupt-driven.** No I/O APIC + driver exists in this tree today (checked: `apic.c` is Local-APIC-only, and the timer + needs no routing because it self-interrupts) and `pic_disable()` masks the legacy 8259 + permanently — so no legacy IRQ, IRQ1 included, currently has any path to the CPU. This + item stands up a minimal I/O APIC driver (MMIO base, redirection table entry for IRQ1 + targeting a chosen vector and the boot CPU's LAPIC ID), programs the i8042 controller for + interrupt mode, and installs an ISR that reads the scancode from port `0x60` and issues + `apic_eoi()`. **No polling of the i8042 status port (`0x64`) anywhere in this path** — + that is the whole point of doing this before the REPL keyboard work, not after. + *Done when:* a keypress in QEMU on amd64 produces a captured scancode via the interrupt + path with no polling loop in the code, three-arch QEMU boot unaffected, log committed. + *Refs:* §27.5. + +- [ ] **4.3.5a — riscv64: PLIC bring-up (external interrupt controller).** Does not exist + anywhere in this tree — Phase 0 (0.2/0.3) only ever enabled the S-mode *timer* interrupt + (`sie.STIE`); external interrupts (`sie.SEIE`, bit 9) were never touched, and + `interrupts.c`'s own comment ("the PLIC replaces both") describes the architecture, not + anything implemented. This item enables `sie.SEIE`, programs the PLIC's per-source + priority, the hart context's threshold and enable bits, and wires the trap handler's + external-interrupt case to `PLIC claim` → dispatch → `PLIC complete`. Prerequisite for + 4.3.5b; no keyboard code in this item. + *Done when:* a synthetic/known external interrupt source claims and completes correctly + through the PLIC on riscv64, three-arch QEMU boot unaffected, log committed. + *Refs:* §27.5. + +- [ ] **4.3.5b — riscv64: virtio-keyboard-device, interrupt-driven.** Add + `virtio-keyboard-device` (MMIO virtio, matching the existing MMIO virtio-blk pattern on + this board) to the riscv64 `qemu`/`qemu-esp` targets in `Makefile.starkernel`. New driver + under `src/starkernel/virtio/` for the virtio-input device class — decode `EV_KEY` events + off the input event virtqueue. Unlike `virtio_blk.c`'s synchronous poll-the-used-ring + pattern, this device must be interrupt-driven end to end: the used-ring notification + arrives via the PLIC path from 4.3.5a, not a poll loop. Built on 4.3.5a. + *Done when:* a keypress in QEMU on riscv64 produces a captured `EV_KEY` event via the + interrupt path with no polling loop, three-arch QEMU boot unaffected, log committed. + *Refs:* §27.5. + +- [ ] **4.3.5c — aarch64: GIC SPI wiring for virtio-input.** Item 0.6 scoped the GIC to + "one interrupt" (the timer PPI) on purpose and explicitly called a general GIC driver + out of scope. A PCI-attached `virtio-keyboard-pci` device signals via legacy INTx, which + is an SPI, not a PPI — a different GIC distributor path (SPI target-CPU and priority + registers) that today's minimal init never touches. This item extends the GIC init from + 0.6 just far enough to enable and route one SPI, still not a general driver — scope stays + as narrow as 0.6's did. + *Done when:* aarch64 can take and acknowledge (`IAR`/`EOIR`) an SPI from the virtio-input + PCI device, three-arch QEMU boot unaffected, log committed. + *Refs:* §27.5. + +- [ ] **4.3.5d — aarch64: virtio-keyboard-pci, interrupt-driven.** Add `virtio-keyboard-pci` + to the aarch64 `qemu`/`qemu-esp` targets — the existing `virtio-blk-pci` device proves the + PCI bus is already enumerated (`pci.c`) on this board, so this rides the same bus, a new + device class. Same virtio-input driver shape as 4.3.5b (shared code where the transport + allows — MMIO vs. PCI config-space discovery differs, the event-queue/`EV_KEY` decode + should not). Built on 4.3.5c. + *Done when:* a keypress in QEMU on aarch64 produces a captured `EV_KEY` event via the + interrupt path with no polling loop, three-arch QEMU boot unaffected, log committed. + *Refs:* §27.5. + +- [ ] **4.3.5e — Checkpoint: one keyboard abstraction, three architectures, no polling.** + Converge 4.3.5/4.3.5b/4.3.5d behind a single scancode/keycode interface so the REPL + keyboard-input work (M8, outside Stadium) has one thing to call, not three. Stop and + review here — same posture as 4.3.4 — before scoping REPL wiring. + *Done when:* a keypress on all three architectures produces the same shape of event at + the shared interface, confirmed with a live keypress test per architecture (not a + synthetic/injected one — this is the first item in this slice where a real key matters), + three-arch QEMU boot, logs committed. + *Refs:* §27.5. + ⋯ *(4.3.x is open-ended — more items get appended here as Console work is scoped item by* *item, developed on the fly per §25.0. 4.4 below is unaffected by anything added above* *this marker.)* @@ -4215,3 +4282,47 @@ projection machinery — the first real exercise of that math, expected to take not a quick add. Stop and review here before scoping the next 4.3.x item. Out-of-scope list for 4.3.1–4.3.4 is recorded once, at the 4.3 punch-list entry itself (§25.5), not repeated per sub-item. + +### 27.5 Keyboard input, interrupt-driven (4.3.5–4.3.5e) + +Scoped 2026-08-07, next after the 4.3.4 checkpoint. Purpose: get a real keypress into the +kernel with no polling anywhere in the path, so the REPL keyboard-input work (M8, tracked +outside Stadium — see `.claude/CLAUDE.md`'s roadmap section) has something to call. This +section is groundwork only, same as 27.1–27.4 was for the framebuffer — it stops at "a +keypress produces an event at a shared interface," not REPL line-editing, not a scancode- +to-ASCII layout, not repeat/modifier-key semantics. Those are later items, scoped once +4.3.5e is reviewed. + +**Why this split is bigger than 4.3.3's three-way split.** 4.3.3/4.3.3a/4.3.3b all built on +interrupt/timer substrate Phase 0 already finished. Keyboard input does not have that +luxury on two of three architectures: + +- **amd64** has a Local APIC (for the self-interrupting timer) but no I/O APIC driver at + all, and `pic_disable()` (`interrupts.c:228`) permanently masks the legacy 8259 — so today + there is no path for *any* legacy IRQ, keyboard's IRQ1 included, to reach the CPU. 4.3.5 + stands up the minimum I/O APIC needed for one redirection entry. +- **riscv64** has never enabled external interrupts at all — Phase 0 (0.2/0.3) scoped + strictly to the S-mode timer interrupt (`sie.STIE`). The PLIC (`sie.SEIE`, bit 9) is + unimplemented; `interrupts.c`'s existing comment naming the PLIC describes the + architecture, not working code. 4.3.5a is that bring-up, split out as its own item because + it is substrate, not keyboard-specific — same shape as Phase 0's per-ISA items. +- **aarch64**'s GIC (item 0.6) was deliberately scoped to one PPI (the timer) and explicitly + ruled a general GIC driver out of scope. A PCI `virtio-keyboard-pci` device signals via an + SPI, a distinct GIC path 0.6 never touched. 4.3.5c is the minimal SPI extension, held to + the same narrow-scope discipline 0.6 used. + +**Hardware path differs by architecture, same as the framebuffer did.** amd64's `q35` +machine has a real i8042 PS/2 controller (QEMU default, IRQ1) — 4.3.5. Neither `virt` board +(aarch64, riscv64) has PS/2 hardware; both get a `virtio-keyboard` device added to their +QEMU command lines and a new virtio-input driver (this tree has none — `virtio_blk.c` is +the only existing virtio driver, and it is fully synchronous/polled, so it is a transport +reference only, not an interrupt-handling one) — 4.3.5b (riscv64, MMIO transport) and 4.3.5d +(aarch64, PCI transport, riding the bus `pci.c` already enumerates for `virtio-blk-pci`). + +**No polling, anywhere, in any of 4.3.5/4.3.5b/4.3.5d.** This is the one hard constraint +carried through every sub-item — restated per item because it is the actual point of doing +interrupt bring-up first rather than reading `0x64`'s status bit or spinning on a used-ring +index, which would have been the fast/wrong way to get a key on screen quickly. + +4.3.5e is the checkpoint, same posture as 4.3.4: converge the three architecture-specific +paths behind one interface before anything scopes REPL line input on top of it.