Artemis Milestone 2f: Configuration descriptor read + Mass Storage/BOT class confirmation
Chains off the device descriptor request via a new deferred-action mechanism on xhci_dev_t (next_action/next_action_slot_id/next_action_length): a short 9-byte Configuration descriptor read learns wTotalLength, then a full read retrieves Config+Interface+Endpoint descriptors, walked for the Interface descriptor to confirm bInterfaceClass/SubClass/Protocol == Mass Storage/ SCSI/Bulk-Only Transport. The deferral exists because ringing the next doorbell synchronously inside xhci_poll_events()'s event-processing loop -- before the current event's ERDP write -- hung the guest outright (confirmed live via checkpoint logging, amd64). Fixed by moving the actual control-transfer submission to a small dispatch at the end of xhci_poll_events(), after ERDP is updated. A debug hack that shipped mid-session (forcing a repeated 9-byte read instead of chaining into the real 44-byte length, to isolate whether the hang was doorbell-ordering or length-specific) has been reverted: restored the real length and re-verified live. The doorbell-ordering fix was the whole story -- the 44-byte read completes cleanly. Verified live via QMP hotplug, all three architectures, byte-identical results: wTotalLength=0x2c, bInterfaceClass=0x08, bInterfaceSubClass=0x06, bInterfaceProtocol=0x50 -- confirmed Mass Storage/SCSI/BOT. Disconnect confirmed clean on every arch, no wedge. FABRIC-2.md Section X Milestone 2f updated with the full writeup. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPfdtaXs9ay1nbwuMnrscu
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
2c34e45d05
commit
b9c540a78b
+43
-4
@@ -3328,12 +3328,14 @@ command, DCBAA entry clear), and the callback surface into Section U's higher-le
|
||||
`usb-storage`), meaning class is defined at the interface level, not device level, for
|
||||
standard Mass Storage devices — confirms the "is this a USB drive" check has to read the
|
||||
Configuration/Interface descriptor (next item below), not the device descriptor
|
||||
- [ ] Request and parse the configuration descriptor
|
||||
- [ ] Confirm the device reports the Mass Storage class / Bulk-Only Transport subclass/
|
||||
- [x] Request and parse the configuration descriptor — **done 2026-08-25**, see writeup below
|
||||
- [x] Confirm the device reports the Mass Storage class / Bulk-Only Transport subclass/
|
||||
protocol (this is the actual "is this a USB drive" check — separate from, and prior
|
||||
to, Section U item 7's foreign-drive/home-blocks-signature check, which happens one
|
||||
layer up, after the drive is already known to be USB mass storage)
|
||||
- [ ] Set the device configuration (SET_CONFIGURATION control transfer)
|
||||
layer up, after the drive is already known to be USB mass storage) — **done
|
||||
2026-08-25**, see writeup below
|
||||
- [ ] Set the device configuration (SET_CONFIGURATION control transfer) — still open, last
|
||||
item before 2g's bulk transfers are possible
|
||||
|
||||
**Device descriptor request, done 2026-08-22.** Extends the EP0 Transfer Ring built in 2e's
|
||||
Address Device work with real control-transfer submission: a new `xhci_ep0_enqueue_trb()`
|
||||
@@ -3352,6 +3354,43 @@ results everywhere: `idVendor=0x46f4` (QEMU's own USB vendor ID), `idProduct=0x0
|
||||
`bDeviceClass=0x00`. `logs/20260822-125515/amd64/`, `logs/20260822-125624/aarch64/`,
|
||||
`logs/20260822-125800/riscv64/`.
|
||||
|
||||
**Configuration descriptor request/parse + Mass Storage/BOT class confirmation, done
|
||||
2026-08-25.** Chains off the device descriptor success via a new `dev->next_action`/
|
||||
`next_action_slot_id`/`next_action_length` deferral mechanism on `xhci_dev_t`, rather than
|
||||
issuing the next control transfer synchronously from inside the Transfer Event handling in
|
||||
`xhci_poll_events()`. That deferral is load-bearing, not stylistic: an earlier same-session
|
||||
attempt that rang the next doorbell synchronously, still inside the event-processing loop and
|
||||
before the current event's ERDP write, hung the guest outright — confirmed live on amd64 via
|
||||
checkpoint logging (execution stopped exactly at the doorbell MMIO write). The fix moves the
|
||||
actual `xhci_ep0_get_config_descriptor()` call to a small dispatch at the end of
|
||||
`xhci_poll_events()`, after the main event loop and the ERDP update.
|
||||
|
||||
Sequencing is two control transfers, chained: a short 9-byte read (just the Configuration
|
||||
descriptor header) to learn `wTotalLength`, then a second read for the full length —
|
||||
`xhci_ep0_get_config_descriptor()` shares its control-read submission plumbing with the device
|
||||
descriptor request via a new static `xhci_ep0_control_read()` helper. The full read (Config +
|
||||
Interface + Endpoint descriptors, concatenated per USB spec) is then walked byte-by-byte
|
||||
looking for `bDescriptorType == USB_DESC_TYPE_INTERFACE` — not read at a fixed offset, since an
|
||||
Interface descriptor's position within the stream depends on the device's own interface/
|
||||
endpoint layout — and `bInterfaceClass`/`bInterfaceSubClass`/`bInterfaceProtocol` checked
|
||||
against Mass Storage (`0x08`) / SCSI transparent (`0x06`) / Bulk-Only Transport (`0x50`).
|
||||
|
||||
**A real debugging false lead, caught and corrected before commit.** While chasing the
|
||||
doorbell-ordering hang above, a `/* DEBUG: force short length to isolate 3rd-transfer-vs-44-
|
||||
byte */` hack shipped in an uncommitted, mid-session state: instead of chaining into the real
|
||||
`wTotalLength` (0x2c = 44 bytes) once learned, it kept re-requesting the 9-byte short read
|
||||
indefinitely, to test whether *any* 3rd control transfer would hang, independent of length.
|
||||
That test never got run against the real path before the session ended. Restored the real
|
||||
`total_len` and re-verified live: the full 44-byte read completes cleanly, no hang, on all
|
||||
three architectures — the doorbell-ordering fix above was the whole story; the 44-byte length
|
||||
was never actually implicated.
|
||||
|
||||
Verified live via QMP hotplug, all three architectures, byte-identical results everywhere:
|
||||
`wTotalLength=0x2c`, `bInterfaceClass=0x08`, `bInterfaceSubClass=0x06`,
|
||||
`bInterfaceProtocol=0x50` → `confirmed Mass Storage / SCSI / BOT device`. Disconnect (device_del)
|
||||
also confirmed clean on every arch — no wedge, matches 2e's existing disconnect discipline.
|
||||
`logs/20260825-071717/amd64/`, `logs/20260825-072116/aarch64/`, `logs/20260825-072329/riscv64/`.
|
||||
|
||||
**2g. Bulk-Only Transport (BOT) — the actual read/write path**
|
||||
- [ ] Identify and configure the device's bulk IN and bulk OUT endpoints
|
||||
- [ ] Implement CBW (Command Block Wrapper) construction and send, for a SCSI READ(10)
|
||||
|
||||
Reference in New Issue
Block a user