usb: hub-child enumeration proven end-to-end in QEMU + tighten hub test

The usb-kbd behind the QEMU usb-hub now enumerates fully: debounce ->
port reset -> enable -> second debounce -> pre-attach -> attach ->
ATTACH 5.2 -> slot 3 -> addressed -> descriptors read -> HID class match
-> 'Loading subdriver "USB HID" for port 5.2' (class 3.1 proto 1,
keyboard boot protocol).

Root cause of the final stall: an unconditional SetPortFeature
(PORT_INDICATOR) for every connected+enabled port NAK-hung on QEMU's
indicator-less hub (no control-transfer timeout), blocking the flow
before the post-enable debounce. Now gated on
wHubCharacteristics.HUB_CHAR_PORTIND like Linux has_indicators.

test-usb-hub-qemu.sh: check #5 now requires the hub-child subdriver
line ('Loading subdriver "USB HID" for port <root>.<child>') so a
root-port HID (tablet) can no longer satisfy it — closes a false-
positive hole. Timeout raised to 360s for slow TT-path driver bring-up.

Docs (plan P3-A runtime validation + matrix): full hub + hub-child
chain recorded as QEMU-proven; remaining items (intermittent reactor
startup race, slow TT-path Evaluate Context) noted.
This commit is contained in:
2026-07-19 19:55:27 +09:00
parent 5fcb03c99b
commit 6446d440bf
3 changed files with 30 additions and 26 deletions
+1 -1
View File
@@ -20,7 +20,7 @@
| xHCI host controller | ✅ | 🔲 | Interrupt-driven mode proven (test-xhci-irq-qemu.sh); 51-flag quirk table + HCCPARAMS2 gating (P2-A/B); 36-code error recovery (P2-C); not HW-validated |
| EHCI/UHCI/OHCI controllers | 🔲 | 🔲 | Compile clean; runtime enumeration proofs planned (P8-B) |
| USB storage (BOT) | ✅ | 🔲 | usbscsid autospawn + bounded sector readback proven (test-usb-storage-qemu.sh); guest-side write verify open |
| USB hub (non-root) | ✅ | 🔲 | Hub enumeration chain proven in QEMU (test-usb-hub-qemu.sh): descriptor, EP1 change detection, initial scan, debounce, reset→enable; hub-child device enumeration (kbd→HID) blocked on xhcid TT/debug path |
| USB hub (non-root) | ✅ | 🔲 | Hub + hub-child enumeration proven in QEMU (test-usb-hub-qemu.sh): hub descriptor/EP1/scan/debounce/reset→enable, and usb-kbd behind hub enumerates to HID subdriver (class 3.1 keyboard); intermittent xhcid reactor startup race noted |
| **GPU/Display** | | | |
| VESA | ✅ | 🔲 | QEMU framebuffer works |
| virtio-gpu | ✅ | N/A | 2D only, QEMU |
+21 -19
View File
@@ -372,25 +372,27 @@ U1/U2 LPM on hub ports (P7); interrupt-EP usage on USB 3 hubs without one
handling, and TT accumulation.
**Runtime validation (2026-07-19, `test-usb-hub-qemu.sh` on QEMU
`usb-hub` + `usb-kbd` behind it):** the full usbhubd chain is proven at
runtime — hub descriptor read (8 ports), EP1 interrupt-driven change
detection, initial full port scan, connection debounce, port reset with
completion wait, and port enable all observed in the boot log. The proof
also surfaced and we fixed five latent bugs: the xhcid scheme refused
port-dir opens for unenumerated hub children (attach bootstrap
deadlock), a status-bitmap off-by-one (bit N1 vs spec's bit N), a
missing initial scan (daemon could block on EP1 before ever polling),
an unbounded EP1 wait (state changes during synchronous waits produce
no new interrupt), and a debounce that could deadlock on a hung
C_PORT_CONNECTION clear (stability is now judged on connection state,
with the change bit cleared post-attach). QEMU's hub stalls
ClearPortFeature on change indicators — handled gracefully by the P2-C
stall recovery (best-effort, no crash). **Remaining gap:** the hub-child
device (usb-kbd) reaches `enabled: true` but does not complete
enumeration to a HID driver — xhcid's hub-child attach path progresses
only at debug level and needs TT/route investigation with debug
logging. Also observed: an intermittent xhcid IRQ-reactor startup race
(one boot showed no reactor start; retry passed).
`usb-hub` + `usb-kbd` behind it):** the full chain is proven at runtime,
for the hub AND for a device behind it. Hub: descriptor read (8 ports),
EP1 interrupt-driven change detection, initial full port scan. Hub-child
(usb-kbd): connection debounce → port reset with completion wait → port
enable → attach → xHCI slot/address/descriptor enumeration → HID class
match → `Loading subdriver "USB HID" for port 5.2` (class 3.1 proto 1,
keyboard boot protocol). The proof surfaced and we fixed seven latent
bugs: the xhcid scheme refused port-dir opens for unenumerated hub
children (attach bootstrap deadlock), a status-bitmap off-by-one
(bit N1 vs spec's bit N), a missing initial scan (daemon could block
on EP1 before ever polling), an unbounded EP1 wait (state changes during
synchronous waits produce no new interrupt), a debounce that could
deadlock on a hung C_PORT_CONNECTION clear (stability now judged on
connection state, change bit cleared post-attach), an unconditional
PORT_INDICATOR SetPortFeature that NAK-hung on indicator-less hubs
(now gated on wHubCharacteristics.HUB_CHAR_PORTIND like Linux
has_indicators), and QEMU's ClearPortFeature stall on change indicators
(handled gracefully by P2-C stall recovery). **Remaining:** an
intermittent xhcid IRQ-reactor startup race (some boots lose it; retry
passes), and hub-child class-driver bring-up is slow on QEMU's emulated
TT path (~13s for the hub-child Evaluate Context).
### P3-B: Hub interrupt-driven change detection ✅ COMPLETE (verified 2026-07-18)
+8 -6
View File
@@ -161,12 +161,14 @@ if [[ "$check_mode" -eq 1 ]]; then
exit 1
fi
# 5. HID class driver spawned — the usb-kbd sits BEHIND the hub, so
# a registered producer proves the hub port enumeration chain
# (power-on → debounce → reset-wait → attach → xhcid enumerate).
if ! grep -q "registered producer" "$log_file"; then
echo "ERROR: usbhidd did not register — child enumeration behind hub failed" >&2
echo " Expected fragment: 'registered producer'" >&2
# 5. Hub-child enumeration: the usb-kbd sits BEHIND the hub (QEMU port
# 1.2, a dotted PortId). Requiring the hub-child subdriver line proves
# the full chain (power-on → debounce → reset-wait → enable → attach →
# xhcid enumerate → class match). A root-port HID (the tablet on port 2)
# must NOT satisfy this — the match is on the hub-child "5.2" form.
if ! grep -Eq 'Loading subdriver "USB HID" for port [0-9]+\.[0-9]+' "$log_file"; then
echo "ERROR: hub-child (usb-kbd behind hub) did not enumerate to a HID driver" >&2
echo " Expected: 'Loading subdriver \"USB HID\" for port <root>.<child>'" >&2
echo " See $log_file" >&2
exit 1
fi