fix(usb): the xHCI controller is found by class on any bus, sized, taken from the firmware, and 64-byte contexts laid out

boards/BRINGUP.md A1, FABRIC-3 SXL.  It was matched by QEMU's own IDs on
bus 0 alone, so no real board's was found.  Now pci_find_class() reads
every bus/slot/function; pci_bar_size() sizes BAR0; the USB Legacy Support
capability hands the controller over from the BIOS, SMIs off; CSZ=1
contexts are placed 64 bytes apart instead of refused.  XHCI_QEMU_ARGS
lets QEMU place the controller as real hardware does.

Test first: an NEC controller behind a PCIe root port -- no attach before,
Zuse minted after, on all three ISAs.  Standard acceptance unchanged; five
hot-plugged identities attach.  Not rehearsable: firmware handoff, CSZ=1.
Still open: only the first controller found is used.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-14 13:30:50 -04:00
co-authored by Claude Opus 5.5
parent 0e2c60dac3
commit 080b7012ba
24 changed files with 72536 additions and 67 deletions
+98 -42
View File
@@ -27,24 +27,77 @@
#include "starkernel/arch.h"
#include "log.h"
/* Conservative fixed BAR0 mapping size. xHCI has no self-describing
* capability-region length the way virtio PCI capabilities do (that's
* virtio_blk.c's approach, not available here) — 64 KiB comfortably
* covers Capability + Operational + Port registers + Runtime + Doorbell
* Array + typical extended-capability space for QEMU's qemu-xhci and for
* real hardware controllers with modest port counts. Revisit if a real
* device's actual BAR size (via PCI BAR-sizing probe, not yet
* implemented in pci.c) proves this insufficient. */
#define XHCI_BAR0_MAP_SIZE 0x10000ull
/* BAR0 is mapped at the size the BAR itself reports (pci_bar_size(), the
* standard probe), never less than 64 KiB: the Capability, Operational,
* Port, Runtime and Doorbell registers and the extended capabilities all
* live in it, and a real controller's BAR is commonly larger than QEMU's
* (2026-10-14, boards/BRINGUP.md A1). */
#define XHCI_BAR0_MIN_MAP_SIZE 0x10000ull
/* xHCI is PCI class 0x0C (serial bus), subclass 0x03 (USB), programming
* interface 0x30 (xHCI), whoever makes it. Until 2026-10-14 the controller
* was matched by QEMU's own vendor and device (1B36:000D) on bus 0 alone,
* so no real board's was ever found (A1). */
#define XHCI_PCI_CLASS 0x0Cu
#define XHCI_PCI_SUBCLASS 0x03u
#define XHCI_PCI_PROG_IF 0x30u
static int xhci_wait_bit(volatile uint32_t *reg, uint32_t mask, int want_set,
uint64_t max_ticks);
static void xhci_log_hex32(const char *prefix, uint32_t v);
/* Take the controller from the firmware (xHCI 1.2 section 4.22.1): a BIOS
* that drives USB itself (a keyboard in its setup screen, booting from a
* stick) owns the controller until the OS asks for it through the USB
* Legacy Support extended capability, and its SMIs keep firing behind the
* OS's back until they are switched off. QEMU's controller has no such
* capability; every PC's has. */
#define XHCI_XCAP_LEGACY 1u
#define XHCI_LEGSUP_BIOS_OWNED (1u << 16)
#define XHCI_LEGSUP_OS_OWNED (1u << 24)
static void xhci_take_from_bios(xhci_dev_t *dev)
{
uint32_t off = (dev->cap->hcc_params1 >> 16) & 0xFFFFu; /* in dwords */
unsigned guard = 0;
while (off && guard++ < 64) {
volatile uint32_t *cap = (volatile uint32_t *)((uint8_t *)dev->cap + (size_t)off * 4u);
uint32_t v = *cap;
if ((v & 0xFFu) == XHCI_XCAP_LEGACY) {
if (v & XHCI_LEGSUP_BIOS_OWNED) {
*cap = v | XHCI_LEGSUP_OS_OWNED;
if (xhci_wait_bit(cap, XHCI_LEGSUP_BIOS_OWNED, 0, 100) != 0) {
/* The firmware did not let go within a second: take
* it anyway, as Linux does. */
*cap = (*cap & ~XHCI_LEGSUP_BIOS_OWNED) | XHCI_LEGSUP_OS_OWNED;
log_message(LOG_WARN, "xhci: firmware did not hand the controller over -- taken");
}
} else {
*cap = v | XHCI_LEGSUP_OS_OWNED;
}
/* USBLEGCTLSTS: every SMI enable off (bits 0, 4, 13-15), every
* SMI status cleared (write-1-to-clear, bits 29-31). */
cap[1] = (cap[1] & ~((1u << 0) | (1u << 4) | (7u << 13))) | (7u << 29);
log_message(LOG_DEBUG, "xhci: controller taken from the firmware");
return;
}
if (((v >> 8) & 0xFFu) == 0) return;
off += (v >> 8) & 0xFFu;
}
}
int xhci_find_and_map(xhci_dev_t *dev)
{
uint64_t map_size;
if (!dev) return -1;
if (pci_find_first(XHCI_PCI_VENDOR_ID, XHCI_PCI_DEVICE_ID, &dev->pci) != 0) {
log_message(LOG_DEBUG, "xhci: no controller found on PCI bus 0");
if (pci_find_class(XHCI_PCI_CLASS, XHCI_PCI_SUBCLASS, XHCI_PCI_PROG_IF, 0, &dev->pci) != 0) {
log_message(LOG_DEBUG, "xhci: no xHCI controller on any PCI bus");
return -1;
}
xhci_log_hex32("xhci: controller vendor/device=",
((uint32_t)dev->pci.vendor_id << 16) | dev->pci.device_id);
pci_enable(&dev->pci);
@@ -54,7 +107,9 @@ int xhci_find_and_map(xhci_dev_t *dev)
return -1;
}
if (pci_map_bar(dev->bar0_phys, XHCI_BAR0_MAP_SIZE) != 0) {
map_size = pci_bar_size(&dev->pci, 0);
if (map_size < XHCI_BAR0_MIN_MAP_SIZE) map_size = XHCI_BAR0_MIN_MAP_SIZE;
if (pci_map_bar(dev->bar0_phys, map_size) != 0) {
log_message(LOG_ERROR, "xhci: BAR0 mapping failed");
return -2;
}
@@ -72,6 +127,8 @@ int xhci_find_and_map(xhci_dev_t *dev)
dev->max_ports = XHCI_HCSPARAMS1_MAX_PORTS(hcs1);
dev->max_scratchpad_bufs = XHCI_HCSPARAMS2_MAX_SCRATCHPAD_BUFS(dev->cap->hcs_params2);
xhci_take_from_bios(dev);
log_message(LOG_DEBUG, "xhci: controller found, BAR0 mapped");
return 0;
}
@@ -466,6 +523,18 @@ static uint32_t xhci_default_ep0_max_packet(uint32_t speed)
}
}
/* Every context -- Input Control, Slot, Endpoint -- is 32 bytes, or 64
* where HCCPARAMS1.CSZ says so (xHCI 1.2 section 6.2.1); a 64-byte
* context is the same fields in its first 32 bytes, then reserved space.
* So the 32-byte structs (xhci.h) stay right for every field, and only
* where each context begins depends on CSZ. Until 2026-10-14 a CSZ=1
* controller was refused outright; QEMU's has CSZ=0, many real ones have
* CSZ=1 (boards/BRINGUP.md A1). */
static size_t xhci_ctx_bytes(const xhci_dev_t *dev)
{
return XHCI_HCCPARAMS1_CSZ(dev->cap->hcc_params1) ? 64u : 32u;
}
int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
uint32_t port_id, uint32_t speed)
{
@@ -474,24 +543,18 @@ int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
xhci_msc_slot_t *ms = xhci_msc_slot_for(dev, slot_id);
if (!ms) return -1;
if (XHCI_HCCPARAMS1_CSZ(dev->cap->hcc_params1)) {
log_message(LOG_ERROR, "xhci: 64-byte contexts required, not implemented -- refusing");
return -2;
}
const size_t cs = xhci_ctx_bytes(dev); /* 32 or 64, by HCCPARAMS1.CSZ */
/* Lazily allocate once per slot; reused across a slot's own re-attach
* (FABRIC-3.md §VII, 2026-09-05: per-slot now, not single-device-wide).
* All three re-initialised fully below regardless of whether this is
* the first call for this slot. */
if (!ms->input_ctx) {
ms->input_ctx = kmalloc_aligned(
sizeof(xhci_input_ctrl_ctx32_t) + sizeof(xhci_slot_ctx32_t) +
sizeof(xhci_ep_ctx32_t), 64);
ms->input_ctx = kmalloc_aligned(3 * cs, 64); /* Input Control, Slot, EP0 */
if (!ms->input_ctx) return -1;
}
if (!ms->device_ctx) {
ms->device_ctx = kmalloc_aligned(
sizeof(xhci_slot_ctx32_t) + sizeof(xhci_ep_ctx32_t), 64);
ms->device_ctx = kmalloc_aligned(2 * cs, 64); /* Slot, EP0 */
if (!ms->device_ctx) return -1;
}
if (!ms->ep0_ring) {
@@ -522,7 +585,7 @@ int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
* controller writes this on Address Device success, software must not
* pre-fill it. */
uint8_t *dctx = (uint8_t *)ms->device_ctx;
for (size_t i = 0; i < sizeof(xhci_slot_ctx32_t) + sizeof(xhci_ep_ctx32_t); i++) dctx[i] = 0;
for (size_t i = 0; i < 2 * cs; i++) dctx[i] = 0;
((uint64_t *)dev->dcbaa)[slot_id] = (uint64_t)(uintptr_t)ms->device_ctx;
/* Input Context: Input Control Context, then Slot Context, then EP0
@@ -530,20 +593,18 @@ int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
* the Device Context; conflating the two is the standard mistake
* here). */
uint8_t *ictx = (uint8_t *)ms->input_ctx;
size_t total = sizeof(xhci_input_ctrl_ctx32_t) + sizeof(xhci_slot_ctx32_t) +
sizeof(xhci_ep_ctx32_t);
size_t total = 3 * cs;
for (size_t i = 0; i < total; i++) ictx[i] = 0;
xhci_input_ctrl_ctx32_t *ctrl = (xhci_input_ctrl_ctx32_t *)ictx;
ctrl->add_flags = XHCI_INPUT_CTRL_ADD_SLOT | XHCI_INPUT_CTRL_ADD_EP0;
xhci_slot_ctx32_t *slot = (xhci_slot_ctx32_t *)(ictx + sizeof(xhci_input_ctrl_ctx32_t));
xhci_slot_ctx32_t *slot = (xhci_slot_ctx32_t *)(ictx + cs);
slot->dword0 = (speed << XHCI_SLOT_CTX_SPEED_SHIFT) | (1u << XHCI_SLOT_CTX_CONTEXT_ENTRIES_SHIFT);
slot->dword1 = port_id << XHCI_SLOT_CTX_ROOT_PORT_SHIFT;
slot->dword2 = 0u << XHCI_SLOT_CTX_INTR_TARGET_SHIFT; /* Interrupter 0 */
xhci_ep_ctx32_t *ep0 = (xhci_ep_ctx32_t *)(ictx + sizeof(xhci_input_ctrl_ctx32_t) +
sizeof(xhci_slot_ctx32_t));
xhci_ep_ctx32_t *ep0 = (xhci_ep_ctx32_t *)(ictx + 2 * cs);
ep0->dword1 = (3u << XHCI_EP_CTX_CERR_SHIFT) |
(XHCI_EP_CTX_TYPE_CONTROL_BIDI << XHCI_EP_CTX_TYPE_SHIFT) |
(xhci_default_ep0_max_packet(speed) << XHCI_EP_CTX_MAX_PACKET_SHIFT);
@@ -578,10 +639,7 @@ int xhci_cmd_configure_endpoint(xhci_dev_t *dev, uint32_t slot_id)
if (!dev || !dev->cmd_ring || !ms || !ms->input_ctx || !ms->device_ctx) return -1;
if (ms->bulk_in_ep_addr == 0 || ms->bulk_out_ep_addr == 0) return -1;
if (XHCI_HCCPARAMS1_CSZ(dev->cap->hcc_params1)) {
log_message(LOG_ERROR, "xhci: 64-byte contexts required, not implemented -- refusing");
return -2;
}
const size_t cs = xhci_ctx_bytes(dev); /* 32 or 64, by HCCPARAMS1.CSZ */
if (!ms->bulk_in_ring) {
ms->bulk_in_ring = (xhci_trb_t *)kmalloc_aligned(XHCI_RING_BYTES, 64);
@@ -612,8 +670,8 @@ int xhci_cmd_configure_endpoint(xhci_dev_t *dev, uint32_t slot_id)
* Pointer in particular) still intact in the Device Context it reads
* -- swapping in a freshly zeroed buffer would hand the controller a
* blank EP0 out from under an endpoint it isn't being asked to touch. */
size_t old_device_ctx_bytes = sizeof(xhci_slot_ctx32_t) + sizeof(xhci_ep_ctx32_t);
size_t new_device_ctx_bytes = (1 + (size_t)max_dci) * sizeof(xhci_ep_ctx32_t);
size_t old_device_ctx_bytes = 2 * cs;
size_t new_device_ctx_bytes = (1 + (size_t)max_dci) * cs;
void *new_device_ctx = kmalloc_aligned(new_device_ctx_bytes, 64);
if (!new_device_ctx) return -1;
uint8_t *ndctx = (uint8_t *)new_device_ctx;
@@ -624,7 +682,7 @@ int xhci_cmd_configure_endpoint(xhci_dev_t *dev, uint32_t slot_id)
((uint64_t *)dev->dcbaa)[slot_id] = (uint64_t)(uintptr_t)ms->device_ctx;
/* ms->input_ctx is reused from Address Device -- same allocation
* (96 bytes: Input Control Ctx + Slot Ctx + one EP Ctx) is too small
* (three contexts: Input Control, Slot, EP0) is too small
* to also hold two more EP Contexts. Configure Endpoint's Input
* Context must span every DCI up to max_dci (xHCI 1.2 spec section
* 6.2.5.1: "the Input Context data structure shall contain output
@@ -633,9 +691,9 @@ int xhci_cmd_configure_endpoint(xhci_dev_t *dev, uint32_t slot_id)
* between EP0 (DCI 1) and the bulk endpoints are left zeroed
* (Add/Drop flags for those DCIs are 0, so the controller ignores
* their content). Reallocated here rather than growing the existing
* 96-byte block in place. */
size_t ctx_count = 1 /* Slot */ + max_dci; /* DCI 1..max_dci, one xhci_ep_ctx32_t each */
size_t total = sizeof(xhci_input_ctrl_ctx32_t) + ctx_count * sizeof(xhci_ep_ctx32_t);
* block in place. */
size_t ctx_count = 1 /* Slot */ + max_dci; /* DCI 1..max_dci, one context each */
size_t total = cs + ctx_count * cs;
void *new_input_ctx = kmalloc_aligned(total, 64);
if (!new_input_ctx) return -1;
ms->input_ctx = new_input_ctx;
@@ -654,7 +712,7 @@ int xhci_cmd_configure_endpoint(xhci_dev_t *dev, uint32_t slot_id)
* is an Output-only field and stays zero, matching Address Device's
* own Input Context handling. */
xhci_slot_ctx32_t *dev_slot = (xhci_slot_ctx32_t *)ms->device_ctx;
xhci_slot_ctx32_t *in_slot = (xhci_slot_ctx32_t *)(ictx + sizeof(xhci_input_ctrl_ctx32_t));
xhci_slot_ctx32_t *in_slot = (xhci_slot_ctx32_t *)(ictx + cs);
uint32_t entries_mask = ~((uint32_t)0x1Fu << XHCI_SLOT_CTX_CONTEXT_ENTRIES_SHIFT);
in_slot->dword0 = (dev_slot->dword0 & entries_mask) |
(max_dci << XHCI_SLOT_CTX_CONTEXT_ENTRIES_SHIFT);
@@ -673,16 +731,14 @@ int xhci_cmd_configure_endpoint(xhci_dev_t *dev, uint32_t slot_id)
* matches every real device this driver has been tested against
* under QEMU's emulation, revisit if a real high-throughput SS
* device needs it. */
xhci_ep_ctx32_t *in_ep = (xhci_ep_ctx32_t *)(ictx + sizeof(xhci_input_ctrl_ctx32_t) +
(size_t)in_dci * sizeof(xhci_ep_ctx32_t));
xhci_ep_ctx32_t *in_ep = (xhci_ep_ctx32_t *)(ictx + cs + (size_t)in_dci * cs);
in_ep->dword1 = (3u << XHCI_EP_CTX_CERR_SHIFT) |
(XHCI_EP_CTX_TYPE_BULK_IN << XHCI_EP_CTX_TYPE_SHIFT) |
((uint32_t)ms->bulk_in_max_packet << XHCI_EP_CTX_MAX_PACKET_SHIFT);
in_ep->tr_dequeue_ptr = ((uint64_t)(uintptr_t)ms->bulk_in_ring) | 1u; /* DCS = 1 */
in_ep->dword4 = 1024u;
xhci_ep_ctx32_t *out_ep = (xhci_ep_ctx32_t *)(ictx + sizeof(xhci_input_ctrl_ctx32_t) +
(size_t)out_dci * sizeof(xhci_ep_ctx32_t));
xhci_ep_ctx32_t *out_ep = (xhci_ep_ctx32_t *)(ictx + cs + (size_t)out_dci * cs);
out_ep->dword1 = (3u << XHCI_EP_CTX_CERR_SHIFT) |
(XHCI_EP_CTX_TYPE_BULK_OUT << XHCI_EP_CTX_TYPE_SHIFT) |
((uint32_t)ms->bulk_out_max_packet << XHCI_EP_CTX_MAX_PACKET_SHIFT);