riscv64: virtio-keyboard-pci, interrupt-driven keyboard input (item 4.3.5c)
Punch list §25 item 4.3.5c complete. Amended from a nonexistent MMIO transport to PCI (matching the board's actual virtio-blk-pci precedent). New virtio-input driver: eventq with pre-posted buffers, PLIC source computed at runtime from PCI slot/pin (derived live from this host's QEMU riscv64 DTB), mandatory ISR-status read, PCI interrupt-disable-bit check. New VKBD-EVENT/VKBD-DEBUG FORTH words. Verified with a real QEMU sendkey keypress: exact KEY_A/press match, two real interrupts serviced, zero exceptions. Three-arch acceptance boot clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5f4df673c1
commit
8251aebcf8
@@ -4027,17 +4027,97 @@ document and committing that amendment as its own item.*
|
||||
> (`logs/20260808-112015/amd64/`), aarch64 (`logs/20260808-112108/aarch64/`), riscv64
|
||||
> (`logs/20260808-112235/riscv64/`, `PLIC: init` line present, no fault).
|
||||
|
||||
- [ ] **4.3.5c — 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.5b, not a poll loop. Built on 4.3.5b.
|
||||
*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.
|
||||
- [x] **4.3.5c — riscv64: virtio-keyboard-pci, interrupt-driven.** 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.5b, not a poll loop. Built on 4.3.5b.
|
||||
|
||||
**Amended 2026-08-08 — transport was wrong.** Originally scoped as "MMIO virtio, matching
|
||||
the existing MMIO virtio-blk pattern on this board" — checked, not assumed, and that
|
||||
pattern doesn't exist: the Makefile's own riscv64 `qemu` target comment says
|
||||
"virtio-blk-pci GPT disk" and uses `-device virtio-blk-pci,...,addr=0x1`/`addr=0x2` — PCI
|
||||
transport, not MMIO. `virtio-mmio`/`VIRTIO_MMIO` appear nowhere in this tree. `pci.c`
|
||||
already supports riscv64 via ECAM with its own fallback base (`g_ecam_base =
|
||||
0x30000000ULL`). Corrected to PCI transport (`virtio-keyboard-pci`) — the same device
|
||||
class 4.3.5e uses on aarch64, which means this item and 4.3.5e now share most of the
|
||||
actual virtio-input driver logic (capability walk, feature negotiation, event-queue
|
||||
handling, ISR-status read), differing only in interrupt routing (PLIC vs. GIC).
|
||||
|
||||
Concrete steps:
|
||||
1. Add `-device virtio-keyboard-pci,addr=0x3` to the riscv64 `qemu`/`qemu-esp` targets in
|
||||
`Makefile.starkernel` — explicit `addr=`, matching this target's existing precedent for
|
||||
the two `virtio-blk-pci` drives (`addr=0x1`/`addr=0x2`), so the PCI slot (and therefore
|
||||
the PLIC source, see below) is deterministic rather than left to QEMU's auto-assignment.
|
||||
2. New `include/starkernel/virtio_input.h` / `src/starkernel/virtio/virtio_input.c` —
|
||||
same device identity, event structure, and design questions as 4.3.5e's own plan
|
||||
(vendor `0x1AF4`, device ID `0x1052`, `struct virtio_input_event`, `EV_KEY = 0x01`, all
|
||||
from this build host's `/usr/include/linux/virtio_ids.h`/`virtio_input.h`/
|
||||
`input-event-codes.h`). **Resolved here, carries forward to 4.3.5e:** don't refactor
|
||||
`virtio_blk.c`'s `static walk_virtio_caps()` into shared code — duplicate a small
|
||||
version into `virtio_input.c` instead. Keeps this item's diff to new files only and
|
||||
leaves the working, tested `virtio_blk.c` untouched.
|
||||
3. Compute the target PLIC source at **runtime**: `pin = pci_read8(dev,
|
||||
PCI_CFG_INT_PIN)` (needs the same new `PCI_CFG_INT_PIN` = `0x3D` constant 4.3.5d adds
|
||||
to `pci.h` — add it here if 4.3.5d hasn't landed first), `slot = dev->device`, `source
|
||||
= 32 + ((slot + pin - 1) % 4)` → PLIC sources 32–35. Derived live from this host's
|
||||
QEMU 10.2.1 riscv64 `virt` DTB, not recalled — full decode in §27.5.2. `plic_set_priority()`/
|
||||
`plic_enable()` take this source directly (PLIC's interrupt specifier is one bare cell,
|
||||
no separate type/flags field the way GIC's SPI encoding needs).
|
||||
4. Same `pci_enable()` interrupt-disable-bit gap 4.3.5e flags (`pci.c:363` never clears PCI
|
||||
COMMAND bit 10) — check it here too, first, since this item lands before 4.3.5e.
|
||||
5. Same mandatory ISR-status read as 4.3.5e (`VIRTIO_PCI_CAP_ISR_CFG`, level-triggered per
|
||||
the decoded `interrupt-map` — see §27.5.2) — skipping it leaves the PLIC source's
|
||||
pending condition latched.
|
||||
6. Wire `riscv64_interrupt_handler()`'s `SCAUSE_S_EXTERNAL` branch (`interrupts.c`) to
|
||||
dispatch the computed PLIC source to the new driver's ISR, extending the generic
|
||||
claim/complete dispatch 4.3.5b built — same shape as i8042's branch in amd64's
|
||||
`isr_common_handler()`, per that dispatch function's own doc comment.
|
||||
|
||||
*Done when:* a keypress in QEMU on riscv64 (via `sendkey`, same manual-injection technique
|
||||
as 4.3.5's amd64 verification) produces a captured `EV_KEY` event through the interrupt
|
||||
path with no polling loop anywhere in the path, three-arch QEMU boot unaffected, log
|
||||
committed.
|
||||
*Refs:* §27.5.
|
||||
|
||||
> **Done, 2026-08-08.** New `include/starkernel/virtio_input.h` / `src/starkernel/virtio/
|
||||
> virtio_input.c`: capability walk duplicated from `virtio_blk.c` (per the resolved design
|
||||
> question above), status/feature negotiation mirroring `vblk_init_device()`, eventq
|
||||
> (queue 0) with `EVENTQ_SIZE`=8 pre-posted device-writable buffers re-posted after each
|
||||
> drain — no synchronous wait anywhere, unlike `vblk_io()`. `enable_interrupt_route()` is
|
||||
> arch-guarded (`#if defined(ARCH_RISCV64)` computes and enables the PLIC source per
|
||||
> §27.5.2's formula; the `#else` path reports rather than silently no-ops, so a future arch
|
||||
> using this file without adding its own routing fails loudly instead of a permanently-
|
||||
> pending, never-enabled source). `PCI_CFG_INT_PIN` (`0x3D`) and `PCI_CMD_INTX_DISABLE`
|
||||
> (bit 10) added to `pci.h` — the latter checked and cleared explicitly, since
|
||||
> `pci_enable()` never touches it. `virtio_input_isr()` reads `VIRTIO_PCI_CAP_ISR_CFG`
|
||||
> before draining (mandatory, not optional — the routed source is level-triggered).
|
||||
> `arch/riscv64/interrupts.c`'s `SCAUSE_S_EXTERNAL` branch now dispatches to it when the
|
||||
> PLIC claim matches `g_virtio_input_plic_source`, the first real per-source consumer of
|
||||
> 4.3.5b's substrate. `kernel_main.c` calls `virtio_input_find_keyboard()` unconditionally
|
||||
> right after the Artemis `virtio_blk_find_artemis()` call, same pattern.
|
||||
>
|
||||
> New FORTH words in `src/word_source/keyboard_words.c` (extending the same file 4.3.5's
|
||||
> `KBD-SCAN`/`KBD-DEBUG` already use, riscv64-branch added alongside the existing amd64
|
||||
> one, no-op elsewhere): `VKBD-EVENT ( -- code value -1 | 0 )` pops one decoded `EV_KEY`
|
||||
> event off the interrupt-fed ring; `VKBD-DEBUG ( -- isr_count )` is the standing
|
||||
> diagnostic. Distinct words from `KBD-SCAN`/`KBD-DEBUG` rather than shared ones — different
|
||||
> device, different event shape; convergence is 4.3.5f's explicit job, not this item's.
|
||||
>
|
||||
> Verified live with a real keypress, not synthetic: QEMU HMP `sendkey a` (temporary
|
||||
> monitor socket added to the Makefile for this test, reverted after — same treatment as
|
||||
> every other one-shot verification harness this session) while a second connection typed
|
||||
> `VKBD-EVENT . . . CR VKBD-DEBUG . CR` over the serial socket. Result: `-1 1 30` (flag,
|
||||
> value, code — `code=30` is `KEY_A` exactly, `value=1` is a press, per this build host's
|
||||
> `/usr/include/linux/input-event-codes.h`) then `2` (`VKBD-DEBUG`, confirming two real
|
||||
> interrupts serviced, not a poll artifact). Zero exceptions
|
||||
> (`logs/20260808-114907/riscv64/qemu-riscv64-20260808-114907-sendkey-verify.log`).
|
||||
>
|
||||
> Three-architecture acceptance boot clean (standard `make qemu`, no monitor socket, no
|
||||
> regression from the new dispatch/driver code): amd64 (`logs/20260808-114435/amd64/`),
|
||||
> aarch64 (`logs/20260808-114603/aarch64/`), riscv64 (`logs/20260808-114720/riscv64/`,
|
||||
> `virtio-input: found device` / `driver ready` present, no fault).
|
||||
|
||||
- [ ] **4.3.5d — 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
|
||||
@@ -4118,11 +4198,11 @@ document and committing that amendment as its own item.*
|
||||
`/usr/include/linux/input-event-codes.h` (authoritative, not recalled): `struct
|
||||
virtio_input_event { __le16 type; __le16 code; __le32 value; }` (8 bytes), `EV_KEY =
|
||||
0x01`.
|
||||
4. **Design decision, flagged not resolved here:** `walk_virtio_caps()` (the
|
||||
capability-list walker `virtio_blk.c` uses for `COMMON_CFG`/`NOTIFY_CFG`) is `static`
|
||||
inside that file. This item needs the same walk plus, new, `ISR_CFG` (step 6). Promote
|
||||
it to a shared `virtio_pci.c`/`.h`, or duplicate it here? Get a ruling before writing
|
||||
code — don't default to one silently (§25.0 rule 3).
|
||||
4. **Resolved by 4.3.5c, 2026-08-08:** duplicate `walk_virtio_caps()` into
|
||||
`virtio_input.c` rather than promoting it out of `virtio_blk.c` into shared code —
|
||||
decided there since 4.3.5c lands first and needs the identical walk. This item's
|
||||
`virtio_input.c` is riscv64/4.3.5c's file plus the GIC-specific interrupt routing below;
|
||||
no separate capability-walker decision needed here.
|
||||
5. `pci_enable()` (`pci.c:363`) sets only IO/MEM/BUS_MASTER in the PCI COMMAND register —
|
||||
it never clears bit 10 (Interrupt Disable). If that bit is set at enumeration, INTx
|
||||
never asserts and every earlier step passes while producing zero real interrupts —
|
||||
@@ -4660,3 +4740,43 @@ intid = 32 + spi /* 35..38 */
|
||||
|
||||
Not yet decoded/needed: entries for PCI bridge-forwarded interrupts or multi-function
|
||||
devices beyond function 0 — out of scope for a single virtio-keyboard-pci device on bus 0.
|
||||
|
||||
### 27.5.2 riscv64 PLIC-source derivation for 4.3.5c (2026-08-08)
|
||||
|
||||
Same method as §27.5.1, applied to the riscv64 `virt` board's own DTB rather than assumed
|
||||
to match aarch64's — dumped with `qemu-system-riscv64 -machine virt,dumpdtb=<file> -cpu
|
||||
rv64` (this tree's exact `qemu` target flags, `Makefile.starkernel:921-923`) and decoded the
|
||||
same way (hand-parsed FDT struct block, no `dtc`).
|
||||
|
||||
**Transport correction that motivated this item's amendment.** The `soc/pci@30000000` node
|
||||
(`compatible = "pci-host-ecam-generic"`, `reg = 0x30000000 0x10000000`) confirms PCI/ECAM,
|
||||
matching `pci.c`'s existing riscv64 fallback base exactly — there is no MMIO virtio
|
||||
transport on this board, corrected at the 4.3.5c entry itself (§25.5).
|
||||
|
||||
**PLIC's interrupt binding is one cell, not three.** `soc/plic@c000000`'s own
|
||||
`#address-cells = 0`, `#interrupt-cells = 1` (phandle `0x3`) — simpler than GIC's
|
||||
`<type num flags>` triple, so the `interrupt-map` entry stride here is 6 cells (3 child-addr
|
||||
+ 1 child-irq + 1 phandle + 1 parent-irq), not GIC's 10. `interrupt-map-mask` is the
|
||||
identical `0x1800 0 0 0x7` (slot mod 4) pattern §27.5.1 found on aarch64 — same GPEX-family
|
||||
host bridge behaviour, confirmed independently rather than assumed carried over. Decoded
|
||||
table (phandle `0x3` on every entry, confirming it targets the PLIC node just read):
|
||||
|
||||
| slot mod 4 | INTA | INTB | INTC | INTD |
|
||||
|---|---|---|---|---|
|
||||
| 0 | PLIC 32 | PLIC 33 | PLIC 34 | PLIC 35 |
|
||||
| 1 | PLIC 33 | PLIC 34 | PLIC 35 | PLIC 32 |
|
||||
| 2 | PLIC 34 | PLIC 35 | PLIC 32 | PLIC 33 |
|
||||
| 3 | PLIC 35 | PLIC 32 | PLIC 33 | PLIC 34 |
|
||||
|
||||
Closed form:
|
||||
|
||||
```
|
||||
pin = pci_read8(dev, PCI_CFG_INT_PIN) /* config offset 0x3D; 1=INTA .. 4=INTD */
|
||||
slot = dev->device /* already populated by pci_find_first() */
|
||||
source = 32 + ((slot + pin - 1) % 4) /* 32..35 */
|
||||
```
|
||||
|
||||
4.3.5c pins the new device to `addr=0x3` (slot 3) explicitly in the Makefile rather than
|
||||
relying on QEMU's auto-assignment — deterministic, and keeps this table's slot-3 row as the
|
||||
one that actually matters for that item, though the runtime formula above holds regardless
|
||||
of slot.
|
||||
|
||||
Reference in New Issue
Block a user