Files
LithosAnanake/include/starkernel/xhci.h
T
Robert Allan JamesandClaude Sonnet 5 b9c540a78b 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
2026-08-25 07:27:07 -04:00

343 lines
17 KiB
C

/*
* xhci.h — xHCI (Extensible Host Controller Interface, USB 3.x) register
* layout and shared constants for StarKernel's USB host controller driver.
*
* Register layout from the xHCI 1.2 specification. Four MMIO regions, each
* reached via an offset from PCI BAR0:
* Capability Registers — at BAR0 + 0, fixed layout, CAPLENGTH gives the
* offset to Operational Registers
* Operational Registers — at BAR0 + CAPLENGTH
* Runtime Registers — at BAR0 + RTSOFF (read from Capability Registers)
* Doorbell Array — at BAR0 + DBOFF (read from Capability Registers)
*
* Struct fields are `volatile`, naturally aligned, NOT __attribute__((packed))
* — matching virtio_blk.c's precedent and its documented riscv64 lesson:
* packed structs force byte-wise loads/stores on strict-alignment targets,
* and QEMU's MMIO handlers for exact-width registers can misbehave under
* byte-wise access. The xHCI spec's register layout is naturally aligned at
* every offset, so this translates directly without padding tricks.
*
* QEMU's `qemu-xhci` device identifies as PCI vendor 0x1B36 (Red Hat, Inc.),
* device 0x000D — confirmed live via QMP `query-pci` against a real running
* instance (not assumed from memory), 2026-08-22.
*/
#ifndef STARKERNEL_XHCI_H
#define STARKERNEL_XHCI_H
#include <stdint.h>
/* -------------------------------------------------------------------------
* PCI identification
* ------------------------------------------------------------------------- */
#define XHCI_PCI_VENDOR_ID 0x1B36u /* Red Hat, Inc. (QEMU qemu-xhci) */
#define XHCI_PCI_DEVICE_ID 0x000Du
/* -------------------------------------------------------------------------
* Capability Registers (BAR0 + 0)
* ------------------------------------------------------------------------- */
typedef struct {
volatile uint8_t cap_length; /* offset to Operational Registers */
volatile uint8_t reserved0;
volatile uint16_t hci_version; /* BCD xHCI spec version */
volatile uint32_t hcs_params1; /* MaxSlots[7:0], MaxIntrs[18:8], MaxPorts[31:24] */
volatile uint32_t hcs_params2; /* IST, ERST Max, scratchpad buffer counts */
volatile uint32_t hcs_params3; /* U1/U2 device exit latencies */
volatile uint32_t hcc_params1; /* AC64, BNC, CSZ, xECP pointer[31:16], etc. */
volatile uint32_t db_off; /* Doorbell Array offset (low 2 bits reserved) */
volatile uint32_t rts_off; /* Runtime Register Space offset (low 5 bits reserved) */
volatile uint32_t hcc_params2;
} xhci_cap_regs_t;
#define XHCI_HCSPARAMS1_MAX_SLOTS(v) ((uint32_t)(v) & 0xFFu)
#define XHCI_HCSPARAMS1_MAX_INTRS(v) (((uint32_t)(v) >> 8) & 0x7FFu)
#define XHCI_HCSPARAMS1_MAX_PORTS(v) (((uint32_t)(v) >> 24) & 0xFFu)
/* HCSPARAMS2: Max Scratchpad Buffers is a 10-bit field split across two
* non-adjacent locations (xHCI 1.2 spec table 5-13) — Hi bits[25:21],
* Lo bits[31:27]. Zero means the controller needs no scratchpad buffers
* (common for simple emulated controllers, but verify live, not assumed). */
#define XHCI_HCSPARAMS2_MAX_SCRATCHPAD_BUFS(v) \
((((uint32_t)(v) >> 21) & 0x1Fu) << 5 | (((uint32_t)(v) >> 27) & 0x1Fu))
/* HCCPARAMS1.CSZ (bit 2): 0 = 32-byte Slot/Endpoint/Input Contexts,
* 1 = 64-byte. Every context field offset shifts with this bit -- must be
* read live, never assumed, before laying out any context structure. */
#define XHCI_HCCPARAMS1_CSZ(v) (((uint32_t)(v) >> 2) & 0x1u)
/* -------------------------------------------------------------------------
* Operational Registers (BAR0 + cap_length)
* ------------------------------------------------------------------------- */
typedef struct {
volatile uint32_t usb_cmd; /* Run/Stop, HC Reset, Interrupter Enable, ... */
volatile uint32_t usb_sts; /* HCHalted, HSE, EINT, PCD, CNR, HCE */
volatile uint32_t page_size; /* bit N set => 2^(N+12)-byte pages supported */
volatile uint32_t reserved0[2];
volatile uint32_t dn_ctrl; /* Device Notification Control */
volatile uint64_t crcr; /* Command Ring Control Register */
volatile uint32_t reserved1[4];
volatile uint64_t dcbaap; /* Device Context Base Address Array Pointer */
volatile uint32_t config; /* MaxSlotsEn[7:0] */
/* Port Register Sets follow at a fixed offset (0x400 from Operational
* base), not contiguous with the fields above — accessed via
* xhci_port_regs() below, not as a struct member. */
} xhci_op_regs_t;
/* USBCMD bits */
#define XHCI_USBCMD_RUN (1u << 0) /* Run/Stop: 1 = run */
#define XHCI_USBCMD_HCRST (1u << 1) /* HC Reset */
#define XHCI_USBCMD_INTE (1u << 2) /* Interrupter Enable */
#define XHCI_USBCMD_HSEE (1u << 3) /* Host System Error Enable */
/* USBSTS bits */
#define XHCI_USBSTS_HCH (1u << 0) /* HC Halted */
#define XHCI_USBSTS_HSE (1u << 2) /* Host System Error */
#define XHCI_USBSTS_EINT (1u << 3) /* Event Interrupt */
#define XHCI_USBSTS_PCD (1u << 4) /* Port Change Detect */
#define XHCI_USBSTS_CNR (1u << 11) /* Controller Not Ready */
#define XHCI_USBSTS_HCE (1u << 12) /* Host Controller Error */
/* CRCR bits (low bits of the 64-bit register; pointer occupies bits[63:6]) */
#define XHCI_CRCR_RCS (1ull << 0) /* Ring Cycle State */
#define XHCI_CRCR_CS (1ull << 1) /* Command Stop */
#define XHCI_CRCR_CA (1ull << 2) /* Command Abort */
#define XHCI_CRCR_CRR (1ull << 3) /* Command Ring Running (read-only) */
#define XHCI_CRCR_PTR_MASK (~0x3Full) /* pointer must be 64-byte aligned */
/* CONFIG */
#define XHCI_CONFIG_MAX_SLOTS_EN(n) ((uint32_t)(n) & 0xFFu)
/* Port Register Set — array at Operational base + 0x400, 0x10 bytes each,
* indexed 0..(MaxPorts-1) for ports numbered 1..MaxPorts. */
typedef struct {
volatile uint32_t portsc; /* Port Status and Control */
volatile uint32_t portpmsc; /* Port Power Management Status and Control */
volatile uint32_t portli; /* Port Link Info */
volatile uint32_t porthlpmc; /* Port Hardware LPM Control */
} xhci_port_regs_t;
#define XHCI_PORT_REGS_OFFSET 0x400u
/* PORTSC bits (subset needed for hotplug + reset) */
#define XHCI_PORTSC_CCS (1u << 0) /* Current Connect Status */
#define XHCI_PORTSC_PED (1u << 1) /* Port Enabled/Disabled */
#define XHCI_PORTSC_PR (1u << 4) /* Port Reset */
#define XHCI_PORTSC_PLS_MASK (0xFu << 5) /* Port Link State */
#define XHCI_PORTSC_PP (1u << 9) /* Port Power */
#define XHCI_PORTSC_SPEED_MASK (0xFu << 10)
#define XHCI_PORTSC_SPEED(v) (((uint32_t)(v) >> 10) & 0xFu) /* xHCI 1.2 spec table 7-13 speed IDs */
#define XHCI_PORTSC_CSC (1u << 17) /* Connect Status Change */
#define XHCI_PORTSC_PEC (1u << 18) /* Port Enabled/Disabled Change */
#define XHCI_PORTSC_PRC (1u << 21) /* Port Reset Change */
/* Writing 1 to a _C (change) bit clears it (RW1CS) — writing 0 has no effect.
* PORTSC also has RW1CS bits interleaved with RW bits; always read-modify-
* write with the change bits masked to 0 unless intentionally clearing one,
* to avoid accidentally acknowledging an event by a stray read-modify-write. */
/* -------------------------------------------------------------------------
* Runtime Registers (BAR0 + rts_off)
* ------------------------------------------------------------------------- */
typedef struct {
volatile uint32_t iman; /* Interrupt Management: bit0=IP, bit1=IE */
volatile uint32_t imod; /* Interrupt Moderation */
volatile uint32_t erstsz; /* Event Ring Segment Table Size */
volatile uint32_t reserved0;
volatile uint64_t erstba; /* Event Ring Segment Table Base Address */
volatile uint64_t erdp; /* Event Ring Dequeue Pointer; bit3=EHB */
} xhci_intr_regs_t;
typedef struct {
volatile uint32_t mf_index; /* Microframe Index */
volatile uint32_t reserved0[7];
/* Interrupter Register Sets follow, one xhci_intr_regs_t per interrupter,
* starting immediately after this 0x20-byte header. Interrupter 0 is
* accessed via xhci_intr_regs_t at (runtime_base + 0x20). */
} xhci_runtime_regs_t;
#define XHCI_IMAN_IP (1u << 0) /* Interrupt Pending */
#define XHCI_IMAN_IE (1u << 1) /* Interrupt Enable */
#define XHCI_ERDP_EHB (1ull << 3) /* Event Handler Busy */
#define XHCI_ERDP_PTR_MASK (~0xFull) /* pointer occupies bits[63:4] */
/* -------------------------------------------------------------------------
* Doorbell Array (BAR0 + db_off) — array of uint32_t, one per device slot
* plus doorbell 0 for the Command Ring. Write-only.
* ------------------------------------------------------------------------- */
typedef volatile uint32_t xhci_doorbell_t;
#define XHCI_DB_TARGET(ep) ((uint32_t)(ep) & 0xFFu) /* 0 = command ring */
#define XHCI_DB_STREAM_ID(sid) (((uint32_t)(sid) & 0xFFFFu) << 16)
/* -------------------------------------------------------------------------
* TRB (Transfer Request Block) — 16 bytes, the unit of both Command Ring
* and Event Ring entries (and Transfer Rings, used later for BOT I/O).
* ------------------------------------------------------------------------- */
typedef struct {
volatile uint64_t parameter;
volatile uint32_t status;
volatile uint32_t control;
} xhci_trb_t;
#define XHCI_TRB_CONTROL_CYCLE (1u << 0) /* Cycle bit */
#define XHCI_TRB_CONTROL_TYPE_SHIFT 10
#define XHCI_TRB_CONTROL_TYPE_MASK (0x3Fu << XHCI_TRB_CONTROL_TYPE_SHIFT)
#define XHCI_TRB_TYPE(ctrl) (((ctrl) & XHCI_TRB_CONTROL_TYPE_MASK) >> XHCI_TRB_CONTROL_TYPE_SHIFT)
/* TRB types used by this driver (subset — xHCI defines many more) */
#define XHCI_TRB_TYPE_LINK 6 /* ring-wraparound marker, Command/Transfer Rings only */
#define XHCI_TRB_TYPE_ENABLE_SLOT_CMD 9
#define XHCI_TRB_TYPE_ADDRESS_DEVICE_CMD 11
#define XHCI_TRB_TYPE_SETUP_STAGE 2 /* Transfer Ring, control transfers only */
#define XHCI_TRB_TYPE_DATA_STAGE 3
#define XHCI_TRB_TYPE_STATUS_STAGE 4
#define XHCI_TRB_TYPE_TRANSFER_EVENT 32
#define XHCI_TRB_TYPE_COMMAND_COMPLETION_EVT 33
#define XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVT 34
/* Control bits used only by Link TRBs */
#define XHCI_TRB_CONTROL_TC (1u << 1) /* Toggle Cycle */
/* Control bits for control-transfer TRBs (xHCI 1.2 spec section 4.11.2.2 /
* table 6-23..6-25). IDT (Immediate Data) tells the controller the Setup
* Stage TRB's parameter field IS the 8-byte setup packet, not a pointer
* to one -- required for every Setup Stage TRB. TRT/DIR select data
* direction: TRT=3 (IN Data Stage) for the standard "read a descriptor"
* case this driver needs first; DIR must match TRT's direction on the
* Data Stage TRB, and the Status Stage TRB's DIR is the OPPOSITE
* direction of the Data Stage (status is always the reverse handshake). */
#define XHCI_TRB_CONTROL_IDT (1u << 6)
#define XHCI_TRB_CONTROL_IOC (1u << 5) /* Interrupt On Completion */
#define XHCI_TRB_CONTROL_DIR_IN (1u << 16) /* Data/Status Stage: 1=IN, 0=OUT */
#define XHCI_SETUP_TRT_NO_DATA 0u
#define XHCI_SETUP_TRT_OUT_DATA 2u
#define XHCI_SETUP_TRT_IN_DATA 3u
#define XHCI_TRB_CONTROL_TRT_SHIFT 16 /* Setup Stage TRB only; Data/Status Stage overlays DIR at the same bit */
/* Standard USB Setup packet (8 bytes) -- the exact bytes placed in a
* Setup Stage TRB's parameter field via IDT. */
typedef struct {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} usb_setup_packet_t;
#define USB_REQ_GET_DESCRIPTOR 6u
#define USB_DESC_TYPE_DEVICE 1u
#define USB_DESC_TYPE_CONFIG 2u
#define USB_DIR_DEVICE_TO_HOST 0x80u
/* Standard USB Interface descriptor field offsets (9 bytes, USB 2.0 spec
* table 9-12) -- Mass Storage class detection reads these three fields.
* Not decoded via a struct like usb_setup_packet_t: the Interface
* descriptor's exact position within a Configuration descriptor's byte
* stream isn't fixed (depends on the device's actual interface/endpoint
* layout), so it's found by walking the byte stream looking for
* bDescriptorType == USB_DESC_TYPE_INTERFACE, not by a fixed struct
* offset into the whole buffer. */
/* Every standard USB descriptor starts with these two bytes (bLength,
* bDescriptorType) -- used to walk the concatenated descriptor stream a
* full Configuration descriptor read returns (Config + Interface +
* Endpoint descriptors back to back), not just the Interface one. */
#define USB_DESC_OFF_LENGTH 0u
#define USB_DESC_OFF_TYPE 1u
#define USB_CONFIG_OFF_TOTAL_LENGTH 2u /* wTotalLength, 2 bytes, Configuration descriptor only */
#define USB_DESC_TYPE_INTERFACE 4u
#define USB_IFACE_OFF_CLASS 5u
#define USB_IFACE_OFF_SUBCLASS 6u
#define USB_IFACE_OFF_PROTOCOL 7u
#define USB_CLASS_MASS_STORAGE 0x08u
#define USB_SUBCLASS_SCSI 0x06u /* SCSI transparent command set */
#define USB_PROTOCOL_BOT 0x50u /* Bulk-Only Transport */
/* Command Completion Event TRB layout (xHCI 1.2 spec table 6-32):
* parameter[63:4] = Command TRB Pointer, status[31:24] = Completion Code,
* status[23:0] = unused here, control[31:24] = Slot ID (Enable Slot's
* result, also present on Address Device completions). */
#define XHCI_EVT_COMPLETION_CODE(status) (((uint32_t)(status) >> 24) & 0xFFu)
#define XHCI_EVT_SLOT_ID(control) (((uint32_t)(control) >> 24) & 0xFFu)
#define XHCI_COMPLETION_CODE_SUCCESS 1u
/* Port Status Change Event TRB layout (xHCI 1.2 spec table 6-34):
* parameter[31:24] = Port ID (1-based, matches PORTSC array indexing
* 1..MaxPorts); parameter[23:0] and the rest of the TRB are reserved. */
#define XHCI_PSC_EVT_PORT_ID(parameter) (((uint32_t)(parameter) >> 24) & 0xFFu)
/* -------------------------------------------------------------------------
* Slot Context, Endpoint Context, Input Control Context — 32-byte layout
* only (xHCI 1.2 spec tables 6-6, 6-9, 6-5). HCCPARAMS1.CSZ selects 32- vs
* 64-byte contexts; confirmed live (CSZ=0) against this driver's target
* QEMU qemu-xhci controller (2026-08-22) before writing these -- 64-byte
* contexts (CSZ=1) are NOT implemented here. xhci_cmd_address_device()
* checks CSZ itself and refuses rather than silently mis-laying-out a
* 64-byte-context controller as 32-byte.
* ------------------------------------------------------------------------- */
typedef struct {
volatile uint32_t dword0; /* Route String[19:0] Speed[23:20] MTT[25] Hub[26] Context Entries[31:27] */
volatile uint32_t dword1; /* Max Exit Latency[15:0] Root Hub Port Number[23:16] Number of Ports[31:24] */
volatile uint32_t dword2; /* Parent Hub Slot ID[7:0] Parent Port Number[15:8] TTT[17:16] Interrupter Target[31:22] */
volatile uint32_t dword3; /* USB Device Address[7:0] Slot State[31:27] */
volatile uint32_t reserved[4];
} xhci_slot_ctx32_t;
#define XHCI_SLOT_CTX_SPEED_SHIFT 20
#define XHCI_SLOT_CTX_CONTEXT_ENTRIES_SHIFT 27
#define XHCI_SLOT_CTX_ROOT_PORT_SHIFT 16
#define XHCI_SLOT_CTX_INTR_TARGET_SHIFT 22
typedef struct {
volatile uint32_t dword0; /* EP State[2:0] Interval[23:16] */
volatile uint32_t dword1; /* CErr[2:1] EP Type[5:3] Max Packet Size[31:16] */
volatile uint64_t tr_dequeue_ptr; /* [0]=DCS (Dequeue Cycle State), [63:4]=pointer */
volatile uint32_t dword4; /* Average TRB Length[15:0] */
volatile uint32_t reserved[3];
} xhci_ep_ctx32_t;
#define XHCI_EP_CTX_TYPE_SHIFT 3
#define XHCI_EP_CTX_TYPE_CONTROL_BIDI 4u /* the only EP type this driver uses so far (EP0) */
#define XHCI_EP_CTX_CERR_SHIFT 1
#define XHCI_EP_CTX_MAX_PACKET_SHIFT 16
typedef struct {
volatile uint32_t drop_flags; /* D0..D31 -- unused for Address Device (nothing to drop) */
volatile uint32_t add_flags; /* A0..A31 -- bit0=Slot, bit1=EP0 for Address Device */
volatile uint32_t reserved[5];
volatile uint32_t config_word; /* Configuration Value/Interface/AltSetting -- unused here */
} xhci_input_ctrl_ctx32_t;
#define XHCI_INPUT_CTRL_ADD_SLOT (1u << 0)
#define XHCI_INPUT_CTRL_ADD_EP0 (1u << 1)
/* -------------------------------------------------------------------------
* Ring sizing — decided up front per Milestone 2's punch list (2a).
*
* Fixed, single-page rings: 256 TRBs x 16 bytes = 4096 bytes = one page.
* This project's usage (MSC hotplug detection + read/write to one drive at
* a time) does not need a high-throughput, dynamically-growable ring —
* matches this codebase's existing preference for fixed, page-sized
* allocations over dynamic growth (e.g. KRD_MAX_BLOCKS's fixed 1024-block
* RAMDRIVE). One Command Ring, one Event Ring (Interrupter 0 only — this
* driver does not use multiple interrupters).
* ------------------------------------------------------------------------- */
#define XHCI_RING_TRB_COUNT 256u
#define XHCI_RING_BYTES (XHCI_RING_TRB_COUNT * sizeof(xhci_trb_t))
/* Milestone 2e: upper bound on ports tracked for connect/disconnect ->
* Enable Slot correlation (xhci_dev_t.port_slot_id). PORTSC's own field
* width allows up to 255 ports (XHCI_HCSPARAMS1_MAX_PORTS is 8 bits), but
* no real or QEMU-emulated root hub this driver targets comes close to
* that; 32 is comfortably generous and keeps this a fixed, not
* heap-allocated, array. */
#define XHCI_MAX_TRACKED_PORTS 32u
#endif /* STARKERNEL_XHCI_H */