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
+16 -4
View File
@@ -134,6 +134,18 @@ ARTDISK ?= disk/artemis.img
# to today's scale (FABRIC-3.md §VII.4). Override if a future test needs more, e.g.
# `make -f kernel/Makefile qemu XHCI_PORTS=32`.
XHCI_PORTS ?= 16
# XHCI_QEMU_ARGS -- the USB controller QEMU is given. Empty (the default) is
# qemu-xhci on the root bus, as always. Set it to rehearse a controller the
# way real hardware has one -- another vendor, behind a bridge (2026-10-14,
# boards/BRINGUP.md A1), e.g.
# XHCI_QEMU_ARGS="-device pcie-root-port,id=rp1,bus=pcie.0,chassis=1,addr=0x6 \
# -device nec-usb-xhci,id=xhci0,bus=rp1,p2=16,p3=16"
# Keep id=xhci0: ZUSEDISK and QEMU_EXTRA attach to bus xhci0.0.
XHCI_QEMU_ARGS ?=
XHCI_DEFAULT_ROOT := -device qemu-xhci,id=xhci0,p2=$(XHCI_PORTS),p3=$(XHCI_PORTS)
XHCI_DEFAULT_SLOT4 := -device qemu-xhci,id=xhci0,addr=0x4,p2=$(XHCI_PORTS),p3=$(XHCI_PORTS)
XHCI_ARGS_ROOT := $(if $(strip $(XHCI_QEMU_ARGS)),$(XHCI_QEMU_ARGS),$(XHCI_DEFAULT_ROOT))
XHCI_ARGS_SLOT4 := $(if $(strip $(XHCI_QEMU_ARGS)),$(XHCI_QEMU_ARGS),$(XHCI_DEFAULT_SLOT4))
# ZUSEDISK -- Zuse's own minted USB thumbdrive, attached by default so a
# plain `make qemu` lands already authenticated into the Zuse identity
# (Captain Bob, 2026-08-28). Attached at QEMU launch time via -drive/
@@ -961,7 +973,7 @@ ifeq ($(ARCH),amd64)
-cdrom $(QEMU_ISO) -boot d \
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
-device virtio-blk-pci,drive=artdisk \
-device qemu-xhci,id=xhci0,p2=$(XHCI_PORTS),p3=$(XHCI_PORTS) \
$(XHCI_ARGS_ROOT) \
$(ZUSEDISK_QEMU_ARGS) \
-object rng-random,id=rng0,filename=/dev/urandom \
-device virtio-rng-pci,rng=rng0 \
@@ -1045,7 +1057,7 @@ else ifeq ($(ARCH),aarch64)
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
-device virtio-blk-pci,drive=artdisk \
-device virtio-keyboard-pci,addr=0x3 \
-device qemu-xhci,id=xhci0,addr=0x4,p2=$(XHCI_PORTS),p3=$(XHCI_PORTS) \
$(XHCI_ARGS_SLOT4) \
$(ZUSEDISK_QEMU_ARGS) \
-object rng-random,id=rng0,filename=/dev/urandom \
-device virtio-rng-pci,rng=rng0,addr=0x5 \
@@ -1135,7 +1147,7 @@ else ifeq ($(ARCH),riscv64)
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
-device virtio-blk-pci,drive=artdisk,addr=0x1 \
-device virtio-keyboard-pci,addr=0x3 \
-device qemu-xhci,id=xhci0,addr=0x4,p2=$(XHCI_PORTS),p3=$(XHCI_PORTS) \
$(XHCI_ARGS_SLOT4) \
$(ZUSEDISK_QEMU_ARGS) \
-object rng-random,id=rng0,filename=/dev/urandom \
-device virtio-rng-pci,rng=rng0,addr=0x5 \
@@ -1246,7 +1258,7 @@ rpi5-rehearsal:
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
-device virtio-blk-pci,drive=artdisk \
-device virtio-keyboard-pci,addr=0x3 \
-device qemu-xhci,id=xhci0,addr=0x4,p2=$(XHCI_PORTS),p3=$(XHCI_PORTS) \
$(XHCI_ARGS_SLOT4) \
$(ZUSEDISK_QEMU_ARGS) \
-object rng-random,id=rng0,filename=/dev/urandom \
-device virtio-rng-pci,rng=rng0,addr=0x5 \
+15
View File
@@ -72,6 +72,15 @@ int pci_init(void *rsdp);
*/
int pci_find_first(uint16_t vendor_id, uint16_t device_id, PciDevice *out);
/*
* pci_find_class — the index-th function (0 = the first) on any bus,
* slot and function whose class code, subclass and programming interface
* match: e.g. 0x0C 0x03 0x30 is an xHCI controller of any vendor.
* Returns 0 and fills *out, or -1 if there are not that many.
*/
int pci_find_class(uint8_t class_code, uint8_t subclass, uint8_t prog_if,
unsigned index, PciDevice *out);
/* Config space accessors */
uint32_t pci_read32 (const PciDevice *d, uint16_t offset);
uint16_t pci_read16 (const PciDevice *d, uint16_t offset);
@@ -93,6 +102,12 @@ void pci_enable(const PciDevice *d);
*/
uint64_t pci_bar(const PciDevice *d, int bar_idx);
/*
* pci_bar_size — the size of memory BAR bar_idx, by the standard sizing
* probe (decoding off meanwhile). 0 for an I/O BAR or none.
*/
uint64_t pci_bar_size(const PciDevice *d, int bar_idx);
/*
* pci_map_bar — make the BAR MMIO region accessible.
* amd64: calls vmm_map_range(phys, phys, size, WR|CD).
+9 -16
View File
@@ -17,9 +17,9 @@
* 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.
* The controller is found by its PCI class (0x0C/0x03/0x30, xhci.c), on any
* bus, from any vendor. Until 2026-10-14 it was matched by QEMU's own
* qemu-xhci IDs, 1B36:000D, on bus 0 alone (boards/BRINGUP.md A1).
*/
#ifndef STARKERNEL_XHCI_H
@@ -27,12 +27,6 @@
#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)
@@ -413,13 +407,12 @@ typedef struct {
#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.
* Slot Context, Endpoint Context, Input Control Context — their fields
* (xHCI 1.2 spec tables 6-6, 6-9, 6-5). HCCPARAMS1.CSZ selects 32- vs
* 64-byte contexts; a 64-byte one holds these same fields in its first 32
* bytes, so these structs serve both, and xhci.c places each context 32 or
* 64 bytes apart (xhci_ctx_bytes(); since 2026-10-14 -- a CSZ=1 controller
* was refused before).
* ------------------------------------------------------------------------- */
typedef struct {
+2 -3
View File
@@ -538,9 +538,8 @@ int xhci_cmd_disable_slot(xhci_dev_t *dev, uint32_t slot_id);
* is servicing (xHCI 1.2 spec table 7-13 speed IDs) -- used to pick EP0's
* default Max Packet Size before any device descriptor has been read.
*
* Refuses (-2) if HCCPARAMS1.CSZ indicates 64-byte contexts -- only
* 32-byte contexts are implemented (see xhci.h's own doc comment on
* xhci_slot_ctx32_t).
* Lays the contexts out 32 or 64 bytes apart, as HCCPARAMS1.CSZ says
* (xhci.c's xhci_ctx_bytes(); 64-byte contexts refused until 2026-10-14).
*
* Called from xhci_poll_events()'s Command Completion handling once Enable
* Slot succeeds -- not called directly by other code.
+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;
+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);