Give each xHCI usb-storage device its own port; fix stale ZUSEDISK default
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run

FABRIC-3.md §IX.5's "4th-device enumeration failure" was never a driver
bug: QEMU's default qemu-xhci controller (p2=4,p3=4) exposes only 4 real
dual-role ports, not 8 as the parameter names suggest. Attaching more
devices than that on bus=xhci0.0 without an explicit port= makes QEMU
silently auto-insert a USB2 hub past the 4th slot; the xHCI/BOT driver
correctly reports that hub as "not a Mass Storage/SCSI/BOT device" because
it genuinely isn't one, and every drive behind it is unreachable (no hub
descent in this driver). Confirmed live via QEMU's own `info usb` before
touching any kernel code.

Fix is entirely in the QEMU test harness, not the kernel:
- New XHCI_PORTS Make variable (default 16, overridable) sizes p2/p3 on
  all three arches' qemu-xhci controller with real headroom above the
  current 9-device identity roster, per Bob's standing ruling against
  hardcoding a bound to today's scale (FABRIC-3.md §VII.4).
- ZUSEDISK_QEMU_ARGS now gives Zuse's drive an explicit port=1.
- QEMU_EXTRA's own doc comment shows the port= pattern for additional
  devices.

Also fixed in passing: ZUSEDISK's default path (disk/zuse.img) was stale
-- that file was deleted from git at c3db963, superseded by
disk/thumbdrives/zuse-thumb-ident.img, but the Makefile default was never
updated, so a plain `make qemu` silently failed to attach Zuse at all.
Now defaults to the real minted image.

Not addressed here, flagged for later: scripts/bleach_zuse_img.sh and
disk/README.md still reference the deleted disk/zuse.img path. The
9-device concurrent enumeration itself (the scenario that originally
surfaced §IX.5) remains unverified against the real kernel -- this change
only confirms single-device boots are unaffected on all three arches.

Three-arch clean qemu acceptance passed (single Zuse device, port 1):
amd64, aarch64, riscv64 all reached POST: PASSED / PARITY:OK / "Zuse:
identity confirmed from attached thumbdrive" / (zuse) ok>.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Ec88YKxxhZGG1RNnune78
This commit is contained in:
Robert Allan James
2026-09-06 20:24:05 -04:00
co-authored by Claude Sonnet 5
parent 2c1b3cd695
commit 30c26ade3d
14 changed files with 71885 additions and 7 deletions
+31 -6
View File
@@ -105,6 +105,20 @@ DOE_LATEST_DIR := experiments/bare_metal/latest
CAPSULES_DIR ?= capsules
ARTDISK ?= disk/artemis.img
# XHCI_PORTS -- real top-level port count the qemu-xhci controller exposes on the
# xhci0.0 bus (p2 == p3 == this value; these are the SAME physical dual-role ports
# counted from the USB2 and USB3 register sets respectively, NOT additive to p2+p3 --
# confirmed live 2026-09-06, the stock default p2=4,p3=4 exposes exactly 4 real ports,
# not 8). Every usb-storage device attached below or via QEMU_EXTRA must get its own
# explicit port=N less than this value: exceed it and QEMU silently drops the overflow
# onto an auto-inserted internal USB2 hub, which the xHCI/BOT driver correctly refuses
# to treat as Mass Storage (root cause of the FABRIC-3.md §IX.5 "4th-device enumeration
# failure" -- misread as a driver bug at the time, it never was one). Sized with real
# headroom above the current 9-device identity roster (Zuse + 8 minted identities)
# rather than matching it exactly, per Bob's standing ruling against hardcoding a bound
# to today's scale (FABRIC-3.md §VII.4). Override if a future test needs more, e.g.
# `make -f Makefile.starkernel qemu XHCI_PORTS=32`.
XHCI_PORTS ?= 16
# ZUSEDISK -- Zuse's own minted USB thumbdrive, attached by default so a
# plain `make qemu` lands already authenticated into the Zuse identity
# (Captain Bob, 2026-08-28). Attached at QEMU launch time via -drive/
@@ -114,7 +128,7 @@ ARTDISK ?= disk/artemis.img
# xhci_poll_events() alone is purely hotplug-event-driven and would never
# see a device present before controller reset. Override to "" (empty) to
# boot without Zuse attached, e.g. `make -f Makefile.starkernel qemu ZUSEDISK=`.
ZUSEDISK ?= disk/zuse.img
ZUSEDISK ?= disk/thumbdrives/zuse-thumb-ident.img
# Precomputed (not inlined as $(if ...,...)) because the drive/device specs
# below are comma-heavy and GNU make's $(if) function splits its own
# arguments on every top-level comma -- inlining it silently truncated the
@@ -122,7 +136,7 @@ ZUSEDISK ?= disk/zuse.img
# dropped, confirmed live via `ps aux` showing the truncated qemu command
# line (2026-08-28).
ifneq ($(strip $(ZUSEDISK)),)
ZUSEDISK_QEMU_ARGS := -drive if=none,id=zusedrv,file=$(ZUSEDISK),format=raw -device usb-storage,bus=xhci0.0,drive=zusedrv
ZUSEDISK_QEMU_ARGS := -drive if=none,id=zusedrv,file=$(ZUSEDISK),format=raw -device usb-storage,bus=xhci0.0,port=1,drive=zusedrv
else
ZUSEDISK_QEMU_ARGS :=
endif
@@ -768,7 +782,18 @@ QEMU_DISPLAY ?= gtk
# boot). Existing use: attaching a disposable USB test image for WRITE(10)
# validation, e.g.
# QEMU_EXTRA="-drive if=none,id=usbtest,file=disk/usbwrite-test.img,format=raw \
# -device usb-storage,bus=xhci0.0,drive=usbtest"
# -device usb-storage,bus=xhci0.0,port=2,drive=usbtest"
# Give every extra usb-storage device its own explicit port= (ZUSEDISK_QEMU_ARGS
# above already claims port 1), distinct from every other device attached on
# bus=xhci0.0 -- without it, QEMU's own port auto-assignment fills the
# controller's real top-level ports in device order and then silently drops
# any further device behind an auto-inserted internal USB2 hub, which the
# xHCI/BOT driver correctly refuses to treat as Mass Storage (confirmed live,
# 2026-09-06, root cause of the FABRIC-3.md §IX.5 "4th-device enumeration
# failure" -- it was never a driver bug, the emulated controller only exposes
# 4 real ports for p2=4,p3=4 despite the port count parameters implying 8).
# The qemu-xhci controller below is sized via XHCI_PORTS (see its own doc comment
# above) to give real ports to Zuse plus every identity drive attached this way.
QEMU_EXTRA ?=
qemu: all
@@ -816,7 +841,7 @@ ifeq ($(ARCH),amd64)
-cdrom $(QEMU_ISO) -boot d \
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
-device virtio-blk-pci,drive=artdisk \
-device qemu-xhci,id=xhci0 \
-device qemu-xhci,id=xhci0,p2=$(XHCI_PORTS),p3=$(XHCI_PORTS) \
$(ZUSEDISK_QEMU_ARGS) \
-object rng-random,id=rng0,filename=/dev/urandom \
-device virtio-rng-pci,rng=rng0 \
@@ -900,7 +925,7 @@ else ifeq ($(ARCH),aarch64)
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
-device virtio-blk-pci,drive=artdisk \
-device virtio-keyboard-pci,addr=0x3 \
-device qemu-xhci,id=xhci0,addr=0x4 \
-device qemu-xhci,id=xhci0,addr=0x4,p2=$(XHCI_PORTS),p3=$(XHCI_PORTS) \
$(ZUSEDISK_QEMU_ARGS) \
-object rng-random,id=rng0,filename=/dev/urandom \
-device virtio-rng-pci,rng=rng0,addr=0x5 \
@@ -990,7 +1015,7 @@ else ifeq ($(ARCH),riscv64)
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
-device virtio-blk-pci,drive=artdisk,addr=0x1 \
-device virtio-keyboard-pci,addr=0x3 \
-device qemu-xhci,id=xhci0,addr=0x4 \
-device qemu-xhci,id=xhci0,addr=0x4,p2=$(XHCI_PORTS),p3=$(XHCI_PORTS) \
$(ZUSEDISK_QEMU_ARGS) \
-object rng-random,id=rng0,filename=/dev/urandom \
-device virtio-rng-pci,rng=rng0,addr=0x5 \
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-09-06T04:51:32Z -->
<!-- Generated by mkcapsule --manifest 2026-09-07T00:13:26Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. -->
BIN
View File
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,15 @@
[=3hBdsDxe: loading Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
[=3hStarKernel UEFI Loader
Loading kernel from ESP...
[CKPT 001] Entered efi_main - ConOut live
RAW SERIAL UP
[CKPT 002] Serial (COM1) initialized
Monolithic build - kernel linked directly
Collecting boot information...
CmdLine: parsed OK
[CKPT 004] Command line parsed
[CKPT 005] Kernel stack allocation decided
[CKPT 006] Boot info collected (ACPI table located)
GOP: linear framebuffer found
[CKPT 007] GOP: linear framebuffer found
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff