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:
co-authored by
Claude Opus 5.5
parent
0e2c60dac3
commit
080b7012ba
+87
-1
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user