Files
LithosAnanake/include/starkernel/xhci_driver.h
T
Robert Allan JamesandClaude Sonnet 5 6d330efdd8 Artemis Milestone 2e: real connect drives Enable Slot, slot ID correlated
xhci_poll_events()'s Port Status Change connect branch now calls
xhci_cmd_enable_slot() directly (the earlier boot-time smoke test call is
gone), tracked via a new dev->pending_connect_port_id -- since this
driver only ever has one command outstanding at a time, that alone
identifies which port a later Command Completion Event answers, without
needing to match the Command TRB Pointer yet. On success the returned
Slot ID is recorded in a new dev->port_slot_id[], a fixed
uint32_t[XHCI_MAX_TRACKED_PORTS] (32) indexed by port. Disconnect clears
the port's tracked slot (real teardown -- Disable Slot, DCBAA clear,
Section U callback -- is still a later increment).

Fixed array, not heap-allocated: a first attempt sized port_slot_id
dynamically via kmalloc_aligned(dev->max_ports * sizeof(uint32_t), 64)
inside xhci_bringup() and it crashed amd64 with a page fault (IFETCH at
RIP=CR2=0xA0000, the legacy VGA hole) during the unrelated Mama-VM-birth
phase afterward -- a heap-corruption signature, not chased to root cause.
Switching to a fixed array (matching this driver's existing preference
for fixed over dynamic allocation) made the crash go away; the crashing
boot's log is kept (logs/20260822-102516/) as the evidence trail.

Verified live via QMP hotplug, all three architectures: connect ->
"enable slot command submitted" -> "enable slot succeeded", with a
disconnect/reconnect cycle repeating cleanly and no port wedge.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HZ8kNoTuP63pbQtro4qvrm
2026-08-22 10:33:37 -04:00

141 lines
6.8 KiB
C

/*
* xhci_driver.h — xHCI USB host controller driver public API for StarKernel
*
* Register-layout definitions live in xhci.h; this header is the driver's
* own state and public entry points, matching virtio_blk.h's split.
*/
#ifndef STARKERNEL_XHCI_DRIVER_H
#define STARKERNEL_XHCI_DRIVER_H
#include <stdint.h>
#include "starkernel/pci.h"
#include "starkernel/xhci.h"
/* Driver state for one xHCI controller instance. Only one controller is
* supported (matches virtio_blk's single-device precedent). */
typedef struct {
PciDevice pci;
uint64_t bar0_phys; /* physical MMIO base, BAR0 */
xhci_cap_regs_t *cap; /* BAR0 + 0 */
xhci_op_regs_t *op; /* BAR0 + cap->cap_length */
xhci_runtime_regs_t *runtime; /* BAR0 + cap->rts_off */
xhci_doorbell_t *doorbell; /* BAR0 + cap->db_off */
uint32_t max_slots;
uint32_t max_ports;
uint32_t max_intrs;
uint32_t max_scratchpad_bufs;
/* Set up by xhci_bringup(); NULL/0 until then. */
void *dcbaa; /* Device Context Base Address Array */
void *scratchpad_arr; /* array of scratchpad buffer pointers, if any */
xhci_trb_t *cmd_ring; /* Command Ring, XHCI_RING_TRB_COUNT TRBs;
* index XHCI_RING_TRB_COUNT-1 is a
* permanent Link TRB back to index 0 */
uint32_t cmd_ring_cycle; /* current Command Ring Cycle State (RCS) */
uint32_t cmd_ring_enq; /* next free Command Ring index (0..COUNT-2) */
xhci_trb_t *evt_ring; /* Event Ring, XHCI_RING_TRB_COUNT TRBs */
void *evt_ring_seg_table; /* Event Ring Segment Table (1 entry) */
uint32_t evt_ring_cycle; /* current Event Ring Cycle State */
uint32_t evt_ring_deq; /* current Event Ring dequeue index */
xhci_intr_regs_t *intr0; /* Interrupter 0 register set, cached
* by xhci_bringup() for
* xhci_poll_events() */
/* Milestone 2e: connect -> Enable Slot correlation. port_slot_id is
* indexed by port_id - 1 (1-based port IDs, matching PORTSC/Port
* Status Change Event numbering); 0 means no slot allocated for that
* port yet. Fixed-size, not heap-allocated -- XHCI_MAX_TRACKED_PORTS
* comfortably covers any real or emulated root hub's port count
* without adding a new kmalloc_aligned() call to xhci_bringup(); ports
* beyond this bound (checked against both this array and max_ports)
* are simply not tracked, matching this driver's existing preference
* for fixed allocations over dynamic growth (xhci.h's own ring-sizing
* rationale). Only one Enable Slot is ever in flight at a time (this
* driver issues commands synchronously with respect to connect events,
* not a queue) -- pending_connect_port_id is 0 when idle, or the
* port_id whose Command Completion Event is still outstanding. */
uint32_t port_slot_id[XHCI_MAX_TRACKED_PORTS];
uint32_t pending_connect_port_id;
} xhci_dev_t;
/*
* xhci_find_and_map — locate the xHCI controller on PCI bus 0, enable it
* (I/O+MEM+bus-master), map its BAR0 MMIO region, and
* fill in the four register-region pointers in *dev.
*
* dev must point to a zero-initialised xhci_dev_t.
*
* Returns 0 on success.
* Returns -1 if no xHCI device was found on the PCI bus.
* Returns -2 if the BAR0 mapping failed.
*/
int xhci_find_and_map(xhci_dev_t *dev);
/*
* xhci_bringup — reset the controller, allocate and program the DCBAA,
* Command Ring, and Event Ring (Interrupter 0), then start
* the controller (RUN/STOP=1) and confirm it left the
* halted state.
*
* Must be called after a successful xhci_find_and_map(). Does not enable
* interrupts (USBCMD.INTE / IMAN.IE) -- this driver is polled, not
* interrupt-driven (see xhci_poll_events()'s own doc comment for why).
*
* Returns 0 on success.
* Returns -1 on reset timeout.
* Returns -2 on allocation failure.
* Returns -3 if the controller failed to leave the halted state after RUN.
* On success, latches dev into the module-static pointer xhci_poll_events()
* reads -- only one controller is supported, matching virtio_blk's
* single-device precedent.
*/
int xhci_bringup(xhci_dev_t *dev);
/*
* xhci_poll_events — read Interrupter 0's Event Ring, dispatching each TRB
* by type: Port Status Change reads PORTSC to log
* connect/disconnect and acknowledges CSC; Command
* Completion and Transfer Event are logged only (slot
* allocation and BOT transfers are later increments).
* Advances the Event Ring dequeue pointer and clears
* ERDP.EHB when done.
*
* Polled, not interrupt-driven: an initial attempt at IRQ delivery
* (Milestone 2d's first draft) found the amd64 PCI INTx routing formula
* gives a demonstrably wrong GSI (checked live via QMP query-pci: xHCI at
* PCI slot 4 reports IRQ 10, the formula predicted 16), and the
* aarch64/riscv64 slot/pin-derived source IDs were unverified at the new
* slot this controller occupies. Rather than guess further at chipset
* PIRQ routing, this matches Section U item 6's own design intent
* (Captain Bob: "interrupt-driven, coarse cadence, cheap early-exit...
* quick check blocks... done") via sk_repl_idle()'s existing coarse-cadence
* hook instead of a per-arch IRQ path -- USB insertion is a human-timescale
* event, not a hot path, so polling costs nothing meaningful here.
*
* No arguments and no return value -- only one xHCI controller is
* supported, so the caller needs no device handle. A no-op if
* xhci_bringup() has not completed successfully (dev pointer not yet
* latched).
*/
void xhci_poll_events(void);
/*
* xhci_cmd_enable_slot — submit an Enable Slot command TRB to the Command
* Ring and ring doorbell 0. Does not wait for or
* read the resulting Command Completion Event -- it
* arrives asynchronously via xhci_poll_events(),
* which correlates the returned Slot ID back to
* dev->pending_connect_port_id and records it in
* dev->port_slot_id[].
*
* Called from xhci_poll_events()'s own Port Status Change handling on a
* real connect event -- not called directly by other code.
*
* Returns 0 if the command was posted, -1 if dev/dev->cmd_ring is not set
* up (xhci_bringup() has not completed).
*/
int xhci_cmd_enable_slot(xhci_dev_t *dev);
#endif /* STARKERNEL_XHCI_DRIVER_H */