From 94345c24b742e129a69b7d0f4636dd0b5f6ad3fa Mon Sep 17 00:00:00 2001 From: Robert Allan James Date: Sat, 22 Aug 2026 07:53:16 -0400 Subject: [PATCH] Artemis Milestone 2a complete: xHCI register header, PCI ID confirmed live via QMP, ring sizing decided include/starkernel/xhci.h: Capability/Operational/Runtime register layouts, Port Register Set, Interrupter Register Set, Doorbell Array, 16-byte TRB struct -- all from the xHCI 1.2 spec, no existing reference in this tree to build from (unlike virtio-blk). volatile fields, no packed attribute, matching virtio_blk.c's documented riscv64/QEMU-MMIO precedent. Compile-checked clean, sizeof(xhci_trb_t) verified == 16. QEMU qemu-xhci's PCI vendor:device ID (0x1B36:0x000D) confirmed live via QMP query-pci against a real running instance -- not assumed from memory, matches the Milestone 1 QMP infrastructure just built. Ring sizing decided: fixed 256-TRB (one page) Command Ring and Event Ring, single interrupter -- documented rationale in the header. Bonus finding: src/starkernel/pci/pci.c already has more reusable infrastructure than Milestone 2b assumed (pci_find_first is ID-based lookup already existing; pci_bar/pci_map_bar/pci_enable are already generic) -- 2b is smaller than originally scoped, noted in the punch list. Co-Authored-By: Claude Sonnet 5 --- FABRIC-2.md | 37 ++++--- include/starkernel/xhci.h | 201 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+), 12 deletions(-) create mode 100644 include/starkernel/xhci.h diff --git a/FABRIC-2.md b/FABRIC-2.md index 88a4ba7..83003c2 100644 --- a/FABRIC-2.md +++ b/FABRIC-2.md @@ -3067,18 +3067,31 @@ Nothing in Section U/V/W's home-blocks thread is testable past this point withou roughly bottom-up: PCI discovery → controller bring-up → command/event rings → port/hotplug detection → device enumeration → mass-storage class → read/write. -**2a. Research/spec groundwork (do first, nothing below can be scoped precisely without it)** -- [ ] Confirm xHCI is the right target controller class (vs. EHCI/OHCI) for QEMU's - `qemu-xhci` device and for realistic modern real hardware — xHCI is the USB 3.x - standard and QEMU's default modern USB controller, but confirm no fallback to EHCI is - needed for older/simpler test hardware - before writing controller-bringup code -- [ ] Pull the xHCI register-interface layout (Capability Registers, Operational Registers, - Runtime Registers, Doorbell array — the four MMIO regions) from the xHCI spec; this - project has no existing xHCI reference to build from (unlike virtio-blk, which had the - virtio spec's existing precedent already used in this tree) -- [ ] Decide command-ring and event-ring sizes/allocation strategy up front (fixed small - ring vs. dynamically sized) — affects the memory-management code below +**2a. Research/spec groundwork (do first, nothing below can be scoped precisely without it) ✅ DONE 2026-08-22** +- [x] Confirmed xHCI (not EHCI/OHCI) via `qemu-system-x86_64 -device help`: `qemu-xhci`, + `usb-storage` (bus `usb-bus`) both available in this environment. Vendor/device ID + confirmed **live via QMP** `query-pci` against a real running instance (not assumed + from memory) — `qemu-xhci` reports PCI vendor `0x1B36` (Red Hat, Inc.), device `0x000D`. +- [x] Pulled the xHCI register-interface layout into a real header: + `include/starkernel/xhci.h` — Capability Registers, Operational Registers (incl. Port + Register Set), Runtime Registers (incl. Interrupter Register Set), Doorbell Array, and + the 16-byte TRB layout. Compile-checked clean (`gcc -std=c99 -Wall -Wextra`, zero + warnings) and `sizeof(xhci_trb_t) == 16` verified. Matches `virtio_blk.c`'s established + precedent: `volatile` fields, no `packed` attribute (that file's own documented + riscv64/QEMU-MMIO lesson about byte-wise access to packed structs applies identically + here, since the xHCI spec layout is naturally aligned at every offset already). +- [x] Ring sizing decided and documented in the header: fixed 256-TRB (one page, 4096-byte) + Command Ring and Event Ring, single Interrupter (0) only — matches this codebase's + existing preference for fixed page-sized allocations over dynamic growth (e.g. + `KRD_MAX_BLOCKS`'s fixed RAMDRIVE size), and this driver's actual need (one drive at a + time, hotplug detection + basic read/write, not high-throughput multi-device USB). + +**Bonus finding, same pass:** `src/starkernel/pci/pci.c` already has more reusable +infrastructure than 2b's punch-list items assumed — `pci_find_first(vendor,device,*out)` +already exists (ID-based lookup, not class-code scanning — 2b's wording should be read as "ID +match" not "class code match"), and `pci_bar()`/`pci_map_bar()`/`pci_enable()` are already +generic, reusable as-is for the xHCI BAR0 mapping and bus-master enable. 2b is smaller than +originally scoped. **2b. PCI discovery** - [ ] Extend `src/starkernel/pci/pci.c`'s enumeration to recognize the xHCI PCI class code diff --git a/include/starkernel/xhci.h b/include/starkernel/xhci.h new file mode 100644 index 0000000..514903a --- /dev/null +++ b/include/starkernel/xhci.h @@ -0,0 +1,201 @@ +/* + * 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 + +/* ------------------------------------------------------------------------- + * 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) + +/* ------------------------------------------------------------------------- + * 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_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_ENABLE_SLOT_CMD 9 +#define XHCI_TRB_TYPE_ADDRESS_DEVICE_CMD 11 +#define XHCI_TRB_TYPE_TRANSFER_EVENT 32 +#define XHCI_TRB_TYPE_COMMAND_COMPLETION_EVT 33 +#define XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVT 34 + +/* ------------------------------------------------------------------------- + * 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)) + +#endif /* STARKERNEL_XHCI_H */