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
+87 -1
View File
@@ -12,7 +12,9 @@
* ARCH_AMD64 : vmm_map_range(phys, phys, size, WR|CD) before first access.
* others : no-op; the UEFI identity map covers PCI MMIO windows.
*
* Only bus 0 is scanned (sufficient for all QEMU virtio devices).
* pci_find_first() scans bus 0 only (sufficient for all QEMU virtio
* devices); pci_find_class() scans every bus (2026-10-14, for real USB
* controllers, which sit behind bridges).
*/
#ifndef __STARKERNEL__
@@ -75,6 +77,11 @@ typedef struct {
* ------------------------------------------------------------------------- */
static uint64_t g_ecam_base = 0;
/* Buses the configuration space reaches: through ECAM, those the MCFG
* entry names (its window covers no others -- reading past it is reading
* whatever lies beyond); through port I/O (amd64), all 256. */
static uint8_t g_bus_first = 0;
static uint8_t g_bus_last = 255;
/* -------------------------------------------------------------------------
* Port I/O helpers (amd64 only)
@@ -276,6 +283,8 @@ static int parse_mcfg(void *rsdp) {
for (j = 0; j < n_alloc; j++) {
if (ents[j].segment_group == 0) {
g_ecam_base = ents[j].base_address;
g_bus_first = ents[j].start_bus;
g_bus_last = ents[j].end_bus;
return 0;
}
}
@@ -341,6 +350,53 @@ int pci_find_first(uint16_t vendor_id, uint16_t device_id, PciDevice *out) {
return -1;
}
/* Every function on every bus the configuration space reaches, in order,
* for the index-th whose class, subclass and programming interface match
* (2026-10-14, boards/BRINGUP.md A1). Every bus is read, not only those a
* bridge is found to lead to: simple, and the same answer, since an
* unassigned bus reads as nothing there (vendor 0xFFFF). Function 0 says
* whether functions 1-7 exist (header type bit 7). */
int pci_find_class(uint8_t class_code, uint8_t subclass, uint8_t prog_if,
unsigned index, PciDevice *out) {
unsigned bus;
unsigned seen = 0;
#ifdef ARCH_AMD64
unsigned first = 0, last = 255; /* port I/O: every bus */
#else
unsigned first = g_bus_first, last = g_bus_last;
if (!g_ecam_base) return -1;
#endif
for (bus = first; bus <= last; bus++) {
uint8_t slot;
for (slot = 0; slot < 32; slot++) {
uint8_t fn, fns;
uint32_t id0 = cfg_read32((uint8_t)bus, slot, 0, PCI_CFG_VENDOR_ID);
if ((id0 & 0xFFFFu) == 0xFFFFu) continue;
fns = (cfg_read8((uint8_t)bus, slot, 0, PCI_CFG_HEADER_TYPE) & 0x80u) ? 8 : 1;
for (fn = 0; fn < fns; fn++) {
uint32_t id = fn ? cfg_read32((uint8_t)bus, slot, fn, PCI_CFG_VENDOR_ID) : id0;
uint32_t cr;
if ((id & 0xFFFFu) == 0xFFFFu) continue;
cr = cfg_read32((uint8_t)bus, slot, fn, PCI_CFG_CLASS_REV);
if ((uint8_t)(cr >> 24) != class_code || (uint8_t)(cr >> 16) != subclass ||
(uint8_t)(cr >> 8) != prog_if)
continue;
if (seen++ != index) continue;
if (out) {
out->bus = (uint8_t)bus;
out->device = slot;
out->function = fn;
out->vendor_id = (uint16_t)(id & 0xFFFFu);
out->device_id = (uint16_t)(id >> 16);
}
return 0;
}
}
}
return -1;
}
uint32_t pci_read32(const PciDevice *d, uint16_t off) {
return cfg_read32(d->bus, d->device, d->function, off);
}
@@ -379,6 +435,36 @@ uint64_t pci_bar(const PciDevice *d, int bar_idx) {
return lo & ~0xFu;
}
/* A memory BAR's size, by the standard probe: all ones written, what
* stays writable read back, the original restored -- with memory decoding
* off meanwhile, so that the BAR never answers at the probe's address.
* 0 for an I/O BAR or one that implements nothing. (2026-10-14, A1.) */
uint64_t pci_bar_size(const PciDevice *d, int bar_idx) {
uint16_t off = (uint16_t)(PCI_CFG_BAR0 + bar_idx * 4);
uint16_t cmd = pci_read16(d, PCI_CFG_COMMAND);
uint32_t lo = pci_read32(d, off), hi = 0, slo, shi = 0xFFFFFFFFu;
int is64 = !(lo & PCI_BAR_TYPE_MASK) && (lo & 0x6u) == PCI_BAR_MEM_64;
uint64_t mask;
if (lo & PCI_BAR_TYPE_MASK) return 0;
pci_write16(d, PCI_CFG_COMMAND, (uint16_t)(cmd & ~PCI_CMD_MEM_SPACE));
pci_write32(d, off, 0xFFFFFFFFu);
slo = pci_read32(d, off);
pci_write32(d, off, lo);
if (is64) {
hi = pci_read32(d, (uint16_t)(off + 4));
pci_write32(d, (uint16_t)(off + 4), 0xFFFFFFFFu);
shi = pci_read32(d, (uint16_t)(off + 4));
pci_write32(d, (uint16_t)(off + 4), hi);
}
pci_write16(d, PCI_CFG_COMMAND, cmd);
mask = ((uint64_t)shi << 32) | (slo & ~0xFu);
if (!is64) mask |= 0xFFFFFFFF00000000ull;
if ((mask & 0xFFFFFFFFFFFFFFF0ull) == 0) return 0;
return ~mask + 1;
}
int pci_map_bar(uint64_t phys_addr, uint64_t size) {
#ifdef ARCH_AMD64
if (!phys_addr || !size) return -1;