From 983b4ed05ada0eaa9a072a50937cd5062d86d25a Mon Sep 17 00:00:00 2001 From: Daniel Samson <12231216+daniel-samson@users.noreply.github.com> Date: Tue, 21 Jul 2026 21:26:56 +0100 Subject: [PATCH] =?UTF-8?q?usb:=20don't=20'correct'=20SuperSpeed=20EP0=20m?= =?UTF-8?q?ax=20packet=20size=20=E2=80=94=20it's=20an=20exponent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regression from the B3 MPS0 work, caught on real hardware: a SuperSpeed hub (and the SuperSpeed boot stick) failed to enumerate. bMaxPacketSize0 (device descriptor byte 7) is a LITERAL size for USB 2.0 and below (8/16/32/64) but an EXPONENT for SuperSpeed (9 = 2^9 = 512). The refresh read the exponent 9 as a size and issued Evaluate Context to set EP0 to 9 bytes, corrupting the control endpoint so every following transfer failed. SuperSpeed's EP0 is fixed at 512 and needs no correction, so the refresh is now skipped for speed >= 4; only full/low/high speed, where the field is a literal that can differ from the speed default, still run it. QEMU tolerated the wrong MPS0 — real silicon does not (the class of bug the harness can't reach, flagged real-HW-pending). This likely also explains intermittent no-storage/no-logs on a normal boot: the boot stick is SuperSpeed and hit the same corruption. Full suite green (orderly-shutdown's lone failure was its known QMP-timing flake — three clean re-runs). --- system/drivers/usb-xhci-bus/usb-xhci-library.zig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/system/drivers/usb-xhci-bus/usb-xhci-library.zig b/system/drivers/usb-xhci-bus/usb-xhci-library.zig index c69ecff..544e766 100644 --- a/system/drivers/usb-xhci-bus/usb-xhci-library.zig +++ b/system/drivers/usb-xhci-bus/usb-xhci-library.zig @@ -807,6 +807,15 @@ pub const Controller = struct { /// MPS0; QEMU forgives it — the classic full-speed-mouse-on-real-hardware /// failure (M20). fn refreshMaxPacketSize0(self: *Controller, device: *Device) bool { + // SuperSpeed (and above) fix EP0's max packet size at 512, and encode + // bMaxPacketSize0 as an EXPONENT (9 = 2^9 = 512), not a literal size — + // the default is already correct and byte 7 must NOT be read as a size. + // Only full/low/high speed carry a literal 8/16/32/64 that can differ + // from the speed default and need this correction. (Reading the SS + // exponent as a size set EP0 to 9 bytes and broke every following + // transfer — a SuperSpeed hub failing to enumerate on real hardware.) + if (device.speed >= 4) return true; + var head: [8]u8 = undefined; const request = usb_abi.getDescriptor(.device, 0, 0, 8); if (!self.controlTransfer(device, request, head[0..], true)) return false;