summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-01 16:33:09 +0100
committersalmonik <salmonik@tuta.io>2026-11-01 16:33:09 +0100
commit363e24bcd18a28948f63fa78ac8a4eaa8784fe59 (patch)
tree6b74ab993ff42e3bf15b01f4cfd09bcb3f0d5ea6
parentusb: real-hardware rework — hubs, both EHCI controllers, keyboard, xHCI har... (diff)
downloadrs-dos-363e24bcd18a28948f63fa78ac8a4eaa8784fe59.tar.gz
rs-dos-363e24bcd18a28948f63fa78ac8a4eaa8784fe59.zip
scripts: add test-usb; fix duplicate -bios in the test harness
- test-usb boots an EHCI and an xHCI controller with a mouse + keyboard and asserts enumeration, report decoding, and input reaching the shell. The EHCI case asserts one key end-to-end (QEMU's EHCI model delivers only the first report through root-port split transactions); the xHCI case asserts a whole typed command. - lib.sh: qemu_args() already supplies -bios, so the fresh OVMF varstore copy was silently overridden by the system firmware. Pass the copy last (QEMU honours the last -bios) so tests really boot clean NVRAM. - DEBUG_NOTES: document why USB never worked on the OptiPlex (EHCI shipped with DEBUG_MATRIX enabled, no RMH/hub support, only the first controller, no boot protocol, unbounded DMA), the success/failure log to look for on hardware, and the QEMU coverage limits. - Makefile: add test-usb and include it in `make test`.
-rw-r--r--DEBUG_NOTES.md90
-rw-r--r--Makefile8
-rw-r--r--scripts/lib.sh6
-rwxr-xr-xscripts/tests/test-usb.sh77
4 files changed, 178 insertions, 3 deletions
diff --git a/DEBUG_NOTES.md b/DEBUG_NOTES.md
index 5730ca5..b7c0e08 100644
--- a/DEBUG_NOTES.md
+++ b/DEBUG_NOTES.md
@@ -541,3 +541,93 @@ ehci: HID boot mouse armed (device 1)
541``` 541```
542 542
543Periodic schedule: 2200+ EXECUTEs/frame with 0 qtd halts; idle mouse NAKs. 543Periodic schedule: 2200+ EXECUTEs/frame with 0 qtd halts; idle mouse NAKs.
544
545---
546
547## 🔌 USB real-hardware status (OptiPlex 7010 / Q77) — v0.6.x rework
548
549### Why USB "straight out did not work"
550
5511. **EHCI was disabled in the shipped build.** `ehci.rs` had
552 `static DEBUG_MATRIX: bool = true` (since `be868a4`): every boot ran the
553 one-shot transfer-variant bisection matrix and then `return false` before
554 enumerating. No committed build had ever brought an EHCI device up.
5552. **No hub support.** On 7-series PCH the EHCI root port presents the
556 **integrated Rate Matching Hub** (8087:0024, device class 9); FS/LS mice
557 and keyboards sit *behind* it. The driver read the RMH's descriptors,
558 found no HID endpoint and gave up. Split transactions were hardcoded to
559 hub address 0 + root port (the QEMU topology).
5603. **Only the first EHCI was initialized** (`find_ehci()` returned the first
561 match). The 7010 has two: `00:1a.0` and `00:1d.0`.
5624. **No `SET_PROTOCOL(boot)`** on EHCI/xHCI; UHCI asked for report protocol
563 (`wValue=1`). Report protocol layouts are device-defined.
5645. **EHCI DMA had no <4 GiB bound** (32-bit DMA pointers), while the page
565 bitmap spans all RAM. A buffer above 4 GiB silently breaks transfers.
5666. **xHCI**: no scratchpad buffers (HCSPARAMS2), `MaxSlotsEn=1`, and DMA
567 from the heap allocator rather than physical pages.
568
569### What the drivers do now
570
571- `ehci.rs`: both controllers; hub class requests (power / reset / status);
572 TT splits with the hub address + child port; HID mouse **and** keyboard;
573 boot protocol; per-device periodic QH (chained into the frame list);
574 DMA from `alloc_pages_range(16 MiB, 4 GiB)`; 2 ms recovery after
575 `SET_ADDRESS`.
576- `usb.rs` (xHCI): scratchpad buffers, `MaxSlotsEn = min(MaxSlots, 4)`,
577 per-slot EP0 + interrupt rings, page-allocator DMA, boot protocol,
578 mouse + keyboard.
579- `hid.rs`: shared HID boot-keyboard decoding (one scancode FIFO feeding
580 `ps2::feed_scancode`), used by UHCI, EHCI and xHCI.
581- First-report lines are logged (`ehci: mouse report …`, `usb: kbd report …`)
582 and the `usb` shell command lists controller(s) and device(s) with
583 armed/fails/reports counters.
584
585### OptiPlex 7010 — expected serial log (success path)
586
587```
588ehci: 00:1a.0 mmio=… ports=… 64-bit-caps=…
589ehci: USB legacy handed over by BIOS
590ehci: controller up (… ports)
591ehci: port N connected (high-speed)
592ehci: dev 8087:0024 class=0x9 … addr=1 ← rate matching hub
593ehci: hub addr=1 ports=8
594ehci: hub port K: full-speed device (split)
595ehci: dev …:… class=0x0 … addr=2 sp=0 (split)
596ehci: HID mouse armed (addr 2 ep0x81 mps … )
597ehci: periodic schedule enabled (1 device(s) on ctrl 0)
598ehci: mouse report dx=… dy=… b=0x…
599```
600
601Then the same on `00:1d.0` for the keyboard, and `> ` followed by typed keys.
602Failure signatures to report:
603
604- `ehci: no EHCI controller` — PCI probe/class mismatch (should not happen).
605- `ehci: USB legacy claim timed out` — BIOS still owns the controller.
606- `ehci: hub descriptor failed` / `hub port K reset timeout` — hub class
607 request stage; note the code.
608- `ehci: get-dev-desc failed (addr 0 sp 0 tt Some(…))` — split transaction
609 did not reach the child; note the TT tuple.
610- `ehci: no HID mouse/keyboard endpoint in config` — descriptor walk.
611- `ehci: xfer fail stage=…` — transfer engine; the QR band has the state.
612
613### QEMU coverage (make test-usb)
614
615- EHCI root-port FS mouse/keyboard (split path), two devices chained.
616- xHCI mouse/keyboard on two slots.
617- Keyboard report → scancode → WM → shell echo is asserted in both cases.
618- **Not coverable in QEMU**: an EHCI high-speed hub (`usb-hub` is FS-only and
619 refuses a HS EHCI root port), so the RMH code path is hardware-only.
620- **QEMU EHCI split limit**: for full/low-speed devices behind root-port
621 split transactions QEMU delivers only the *first* report (125 NAKs after
622 it, even though the qTD is re-armed). The re-arm shape is the standard one
623 (`qh->next_qtd = qtd`, qTD re-activated; the HC owns the status/toggle
624 overlay), so `test-usb` asserts one key end-to-end on EHCI and a full
625 command on xHCI.
626
627### Known gaps
628
629- EHCI: hub support is one level deep; no hotplug once a device of that kind
630 is armed; no isochronous/other transfer types.
631- xHCI: no hub support; no USB3 warm-reset subtleties beyond the current
632 port-reset sequence.
633- AHCI/PS2 behaviour unchanged (PS/2 remains the fallback input path).
diff --git a/Makefile b/Makefile
index ee9755f..ead8c20 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@
6SYSTEM_IMG ?= /tmp/system.img 6SYSTEM_IMG ?= /tmp/system.img
7 7
8.PHONY: all build run run-headless debug system system-fresh inspect \ 8.PHONY: all build run run-headless debug system system-fresh inspect \
9 test test-disk test-fault test-gfx test-mouse test-msg fmt clean help 9 test test-disk test-fault test-gfx test-mouse test-msg test-usb fmt clean help
10 10
11# ---- build ---------------------------------------------------------------- 11# ---- build ----------------------------------------------------------------
12 12
@@ -45,7 +45,7 @@ inspect:
45 45
46# ---- tests ---------------------------------------------------------------- 46# ---- tests ----------------------------------------------------------------
47 47
48test: test-disk test-gfx test-mouse test-msg test-fault 48test: test-disk test-gfx test-mouse test-msg test-fault test-usb
49 49
50test-disk: 50test-disk:
51 ./scripts/tests/test-disk.sh 51 ./scripts/tests/test-disk.sh
@@ -62,6 +62,9 @@ test-mouse:
62test-msg: 62test-msg:
63 ./scripts/tests/test-msg.sh 63 ./scripts/tests/test-msg.sh
64 64
65test-usb:
66 ./scripts/tests/test-usb.sh
67
65# ---- utilities ------------------------------------------------------------ 68# ---- utilities ------------------------------------------------------------
66 69
67fmt: 70fmt:
@@ -86,6 +89,7 @@ help:
86 @echo " make test-mouse PS/2 mouse + cursor tracking" 89 @echo " make test-mouse PS/2 mouse + cursor tracking"
87 @echo " make test-msg scheduler message queues" 90 @echo " make test-msg scheduler message queues"
88 @echo " make test-fault fault dump (screen + serial)" 91 @echo " make test-fault fault dump (screen + serial)"
92 @echo " make test-usb USB HID enumeration (EHCI + xHCI)"
89 @echo " make fmt format Rust source" 93 @echo " make fmt format Rust source"
90 @echo " make clean remove build artifacts" 94 @echo " make clean remove build artifacts"
91 @echo "" 95 @echo ""
diff --git a/scripts/lib.sh b/scripts/lib.sh
index d5aecac..6ca3e92 100644
--- a/scripts/lib.sh
+++ b/scripts/lib.sh
@@ -157,8 +157,12 @@ test_boot() {
157 cp "$OVMF" "$nv" 157 cp "$OVMF" "$nv"
158 local -a qargs 158 local -a qargs
159 mapfile -t qargs < <(qemu_args) 159 mapfile -t qargs < <(qemu_args)
160 "$QEMU" -bios "$nv" \ 160 # qemu_args() already carries -bios $OVMF; put the fresh varstore copy
161 # last so it wins (QEMU uses the last -bios) without duplicating the
162 # whole argument set.
163 "$QEMU" \
161 "${qargs[@]}" \ 164 "${qargs[@]}" \
165 -bios "$nv" \
162 -serial "file:$LOG" \ 166 -serial "file:$LOG" \
163 -monitor "unix:$SOCK,server,nowait" \ 167 -monitor "unix:$SOCK,server,nowait" \
164 -display none \ 168 -display none \
diff --git a/scripts/tests/test-usb.sh b/scripts/tests/test-usb.sh
new file mode 100755
index 0000000..55a3f2b
--- /dev/null
+++ b/scripts/tests/test-usb.sh
@@ -0,0 +1,77 @@
1#!/usr/bin/env bash
2#
3# scripts/tests/test-usb.sh — USB host-controller + HID regression.
4#
5# Boots twice, once with an EHCI (USB2) controller and once with an xHCI
6# (USB3) controller, each with a HID mouse and keyboard:
7#
8# • both devices must enumerate and arm their interrupt-IN endpoints
9# • a keyboard report must be decoded (scancode path)
10# • the key must reach the shell (WM input routing)
11#
12# QEMU limitation: its EHCI model delivers only the *first* report for a
13# full/low-speed device serviced by root-port split transactions, so the EHCI
14# case asserts one key end-to-end (a regression in enumeration/arming fails
15# earlier). The xHCI case has no splits and asserts a whole typed command.
16# Mouse report delivery cannot be injected into usb-mouse headless; the
17# hardware run (OptiPlex) is the arbiter for both.
18#
19# Usage: ./scripts/tests/test-usb.sh
20# Env: SYSTEM_IMG, OVMF, QEMU (see scripts/lib.sh)
21#
22set -euo pipefail
23source "$(dirname "$0")/../lib.sh"
24
25echo "==> 1/3 build (kernel + bins)"
26build_all
27
28echo "==> 2/3 stage system image"
29stage_image --force
30
31# Per-controller log/socket files; test_boot() reads these globals.
32case_boot() {
33 local name="$1"
34 shift
35 LOG="/tmp/rsdos-usb-$name-serial.log"
36 SOCK="/tmp/rsdos-usb-$name-qmon.sock"
37 QPID="/tmp/rsdos-usb-$name-qpid"
38 QLOG="/tmp/rsdos-usb-$name-qemu.log"
39 test_boot "$@"
40}
41
42case_arms() {
43 wait_log "$LOG" "HID mouse armed" 20 || fail "$1: mouse not enumerated"
44 wait_log "$LOG" "HID keyboard armed" 20 || fail "$1: keyboard not enumerated"
45 sleep 1
46}
47
48case_done() {
49 kill "$(cat "$QPID" 2>/dev/null)" 2>/dev/null || true
50 sleep 0.5
51 echo " $1: mouse + keyboard OK"
52}
53
54echo "==> 3/3 EHCI (USB2) enumeration"
55case_boot ehci \
56 -device usb-ehci,id=ehci \
57 -device usb-mouse,bus=ehci.0,port=1 \
58 -device usb-kbd,bus=ehci.0,port=2
59case_arms ehci
60sendkeys "h"
61wait_log "$LOG" "kbd report" 10 || fail "ehci: no keyboard report decoded"
62tr -d '\r' < "$LOG" | grep -qx "h" || fail "ehci: typed key did not reach the shell"
63case_done ehci
64
65echo "==> 3/3 xHCI (USB3) enumeration"
66case_boot xhci \
67 -device qemu-xhci,id=xhci \
68 -device usb-mouse,bus=xhci.0 \
69 -device usb-kbd,bus=xhci.0
70case_arms xhci
71sendkeys "he"
72wait_log "$LOG" "kbd report" 10 || fail "xhci: no keyboard report decoded"
73wait_log "$LOG" "unknown command: he" 15 || fail "xhci: typed text did not reach the shell"
74case_done xhci
75
76echo ""
77echo "PASS — USB enumeration + HID input (EHCI and xHCI)"