/* * xhci.c — xHCI USB host controller driver for StarKernel: PCI discovery * (Milestone 2b), controller bring-up (2c), polled Event Ring servicing * (2d), and Command Ring submission + PORTSC connect/disconnect detection * (2e, in progress). Enumeration/BOT read-write (2f-2g) follow in later * increments. * * Memory model: BAR0 is mapped identity (virtual address == physical * address), matching virtio_blk.c's precedent and pci_map_bar()'s own * documented behavior (amd64: vmm_map_range(phys, phys, ...); other * arches: no-op, UEFI identity map already covers it). */ #ifndef __STARKERNEL__ #error "xhci.c is kernel-only" #endif #include #include #include "starkernel/pci.h" #include "starkernel/xhci.h" #include "starkernel/xhci_driver.h" #include "starkernel/kmalloc.h" #include "starkernel/timer.h" #include "console.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 int xhci_find_and_map(xhci_dev_t *dev) { if (!dev) return -1; if (pci_find_first(XHCI_PCI_VENDOR_ID, XHCI_PCI_DEVICE_ID, &dev->pci) != 0) { console_println("xhci: no controller found on PCI bus 0"); return -1; } pci_enable(&dev->pci); dev->bar0_phys = pci_bar(&dev->pci, 0); if (!dev->bar0_phys) { console_println("xhci: BAR0 read failed (zero or I/O BAR)"); return -1; } if (pci_map_bar(dev->bar0_phys, XHCI_BAR0_MAP_SIZE) != 0) { console_println("xhci: BAR0 mapping failed"); return -2; } dev->cap = (xhci_cap_regs_t *)(uintptr_t)dev->bar0_phys; dev->op = (xhci_op_regs_t *)((uint8_t *)dev->cap + dev->cap->cap_length); dev->runtime = (xhci_runtime_regs_t *)((uint8_t *)dev->cap + (dev->cap->rts_off & ~0x1Fu)); dev->doorbell = (xhci_doorbell_t *)((uint8_t *)dev->cap + (dev->cap->db_off & ~0x3u)); uint32_t hcs1 = dev->cap->hcs_params1; dev->max_slots = XHCI_HCSPARAMS1_MAX_SLOTS(hcs1); dev->max_intrs = XHCI_HCSPARAMS1_MAX_INTRS(hcs1); dev->max_ports = XHCI_HCSPARAMS1_MAX_PORTS(hcs1); dev->max_scratchpad_bufs = XHCI_HCSPARAMS2_MAX_SCRATCHPAD_BUFS(dev->cap->hcs_params2); console_println("xhci: controller found, BAR0 mapped"); return 0; } /* Spin-count-bounded busy-wait, matching kernel_main.c's established * pattern (heartbeat_ticks() elapsed + a hard spin-count safety cap, not * just one or the other). Polls *reg for (val & mask) to equal want_set * (0 or 1), re-reading the register itself each iteration. */ static int xhci_wait_bit(volatile uint32_t *reg, uint32_t mask, int want_set, uint64_t max_ticks) { uint64_t start = heartbeat_ticks(); uint64_t spins = 0; for (;;) { uint32_t val = *reg; int is_set = (val & mask) != 0; if (is_set == want_set) return 0; spins++; if (heartbeat_ticks() - start >= max_ticks || spins >= 100000000ULL) { return -1; } } } /* Only one controller is supported (matches xhci_dev_t's own doc comment); * latched at the end of a successful xhci_bringup() for * xhci_poll_events()'s use. */ static xhci_dev_t *g_xhci_dev = NULL; /* Forward declaration -- xhci_bringup() below issues one Enable Slot as a * command-ring smoke test; the implementation lives after xhci_bringup() * (see the "Command Ring submission" section) so it can stay close to * xhci_poll_events(), the read side of the same ring pair. */ int xhci_cmd_enable_slot(xhci_dev_t *dev); int xhci_bringup(xhci_dev_t *dev) { if (!dev || !dev->op) return -2; /* 1. If running, stop first (Run/Stop must be cleared before HCRST is * guaranteed to behave per spec on some implementations). */ if (dev->op->usb_cmd & XHCI_USBCMD_RUN) { dev->op->usb_cmd &= ~XHCI_USBCMD_RUN; if (xhci_wait_bit(&dev->op->usb_sts, XHCI_USBSTS_HCH, 1, 10000) != 0) { console_println("xhci: timeout waiting for halt before reset"); return -1; } } /* 2. Host Controller Reset. HCRST self-clears; CNR (Controller Not * Ready) must also clear before touching any other operational * register. */ dev->op->usb_cmd |= XHCI_USBCMD_HCRST; if (xhci_wait_bit(&dev->op->usb_cmd, XHCI_USBCMD_HCRST, 0, 10000) != 0) { console_println("xhci: timeout waiting for HCRST to self-clear"); return -1; } if (xhci_wait_bit(&dev->op->usb_sts, XHCI_USBSTS_CNR, 0, 10000) != 0) { console_println("xhci: timeout waiting for CNR to clear"); return -1; } /* 3. Device Context Base Address Array — (max_slots+1) x 8-byte * pointers, 64-byte aligned, zeroed (DCBAAP requires 64-byte * alignment per spec; kmalloc_aligned enforces it). */ size_t dcbaa_bytes = (size_t)(dev->max_slots + 1) * sizeof(uint64_t); dev->dcbaa = kmalloc_aligned(dcbaa_bytes, 64); if (!dev->dcbaa) { console_println("xhci: DCBAA allocation failed"); return -2; } for (size_t i = 0; i < dcbaa_bytes / sizeof(uint64_t); i++) { ((uint64_t *)dev->dcbaa)[i] = 0; } /* 3b. Scratchpad buffers, only if the controller asks for them (slot 0 * of the DCBAA points at the scratchpad buffer array, not a device * context, when max_scratchpad_bufs > 0). PAGESIZE register: bit N * set means 2^(N+12)-byte pages; use the lowest set bit found. */ if (dev->max_scratchpad_bufs > 0) { uint32_t pagesize_bits = dev->op->page_size; uint32_t page_bytes = 4096u; for (uint32_t b = 0; b < 16; b++) { if (pagesize_bits & (1u << b)) { page_bytes = 1u << (b + 12); break; } } size_t arr_bytes = (size_t)dev->max_scratchpad_bufs * sizeof(uint64_t); dev->scratchpad_arr = kmalloc_aligned(arr_bytes, 64); if (!dev->scratchpad_arr) { console_println("xhci: scratchpad array allocation failed"); return -2; } for (uint32_t i = 0; i < dev->max_scratchpad_bufs; i++) { void *buf = kmalloc_aligned(page_bytes, page_bytes); if (!buf) { console_println("xhci: scratchpad buffer allocation failed"); return -2; } ((uint64_t *)dev->scratchpad_arr)[i] = (uint64_t)(uintptr_t)buf; } ((uint64_t *)dev->dcbaa)[0] = (uint64_t)(uintptr_t)dev->scratchpad_arr; } dev->op->dcbaap = (uint64_t)(uintptr_t)dev->dcbaa; /* 4. Command Ring — XHCI_RING_TRB_COUNT TRBs, 64-byte aligned, zeroed. * Initial Ring Cycle State = 1 (software convention; the ring is * "owned" by software until the first TRB with a matching cycle bit * is consumed). CRCR low bits carry RCS, not the TRBs themselves. */ dev->cmd_ring = (xhci_trb_t *)kmalloc_aligned(XHCI_RING_BYTES, 64); if (!dev->cmd_ring) { console_println("xhci: command ring allocation failed"); return -2; } for (uint32_t i = 0; i < XHCI_RING_TRB_COUNT; i++) { dev->cmd_ring[i].parameter = 0; dev->cmd_ring[i].status = 0; dev->cmd_ring[i].control = 0; } dev->cmd_ring_cycle = 1; dev->cmd_ring_enq = 0; /* Last slot is a permanent Link TRB back to index 0 (xHCI 1.2 spec * §4.9.2 — software must terminate every ring segment with one; without * it the controller reads uninitialised memory past the segment instead * of wrapping). Toggle Cycle (TC) tells the controller to flip its own * consumer cycle state when it processes this TRB, matching the * producer-side toggle xhci_submit_command() below performs on wrap. */ dev->cmd_ring[XHCI_RING_TRB_COUNT - 1].parameter = (uint64_t)(uintptr_t)dev->cmd_ring; dev->cmd_ring[XHCI_RING_TRB_COUNT - 1].control = (XHCI_TRB_TYPE_LINK << XHCI_TRB_CONTROL_TYPE_SHIFT) | XHCI_TRB_CONTROL_TC | XHCI_TRB_CONTROL_CYCLE; dev->op->crcr = ((uint64_t)(uintptr_t)dev->cmd_ring & XHCI_CRCR_PTR_MASK) | XHCI_CRCR_RCS; /* 5. Event Ring — one segment (Event Ring Segment Table with a single * 16-byte entry: base address + size), wired to Interrupter 0. * Interrupter Register Sets start at runtime_base + 0x20; each is * sizeof(xhci_intr_regs_t) apart, but only Interrupter 0 is used * (single-interrupter design decided in 2a). */ dev->evt_ring = (xhci_trb_t *)kmalloc_aligned(XHCI_RING_BYTES, 64); if (!dev->evt_ring) { console_println("xhci: event ring allocation failed"); return -2; } for (uint32_t i = 0; i < XHCI_RING_TRB_COUNT; i++) { dev->evt_ring[i].parameter = 0; dev->evt_ring[i].status = 0; dev->evt_ring[i].control = 0; } dev->evt_ring_cycle = 1; dev->evt_ring_deq = 0; /* Event Ring Segment Table entry layout: u64 base + u32 size + u32 * reserved = 16 bytes. One segment is enough (ERST Max >= 1 always). */ dev->evt_ring_seg_table = kmalloc_aligned(16, 64); if (!dev->evt_ring_seg_table) { console_println("xhci: event ring segment table allocation failed"); return -2; } uint64_t *erst = (uint64_t *)dev->evt_ring_seg_table; erst[0] = (uint64_t)(uintptr_t)dev->evt_ring; /* base address */ erst[1] = (uint64_t)XHCI_RING_TRB_COUNT; /* size, low 32 bits used */ dev->intr0 = (xhci_intr_regs_t *)((uint8_t *)dev->runtime + 0x20); xhci_intr_regs_t *intr0 = dev->intr0; intr0->erstsz = 1; intr0->erstba = (uint64_t)(uintptr_t)dev->evt_ring_seg_table; intr0->erdp = ((uint64_t)(uintptr_t)dev->evt_ring & XHCI_ERDP_PTR_MASK); /* 6. Enable device slots (all of them — no reason to restrict for a * single-drive-at-a-time driver) and start the controller. */ dev->op->config = XHCI_CONFIG_MAX_SLOTS_EN(dev->max_slots); dev->op->usb_cmd |= XHCI_USBCMD_RUN; if (xhci_wait_bit(&dev->op->usb_sts, XHCI_USBSTS_HCH, 0, 10000) != 0) { console_println("xhci: controller did not leave halted state after RUN"); return -3; } console_println("xhci: controller running"); g_xhci_dev = dev; /* Milestone 2e smoke test: prove the write path (TRB enqueue, cycle * bit, doorbell ring) before building slot allocation on top of it. * A real Enable Slot is harmless to issue speculatively -- it just * reserves a Device Slot Context the driver doesn't use yet -- and its * Command Completion Event is the only live proof that a TRB written by * software was actually consumed by the controller. Real connect-driven * Enable Slot calls (Milestone 2e proper) replace/reuse this call site * once Port Status Change handling exists. */ xhci_cmd_enable_slot(dev); return 0; } /* ------------------------------------------------------------------------- * Command Ring submission -- Milestone 2e. Shared by Enable Slot now and * Address Device next; xhci_poll_events() above is the read side of this * same ring pair, already verified live for Port Status Change events. * ------------------------------------------------------------------------- */ static void xhci_submit_command(xhci_dev_t *dev, uint64_t parameter, uint32_t status, uint32_t trb_type) { xhci_trb_t *trb = &dev->cmd_ring[dev->cmd_ring_enq]; trb->parameter = parameter; trb->status = status; trb->control = (trb_type << XHCI_TRB_CONTROL_TYPE_SHIFT) | (dev->cmd_ring_cycle ? XHCI_TRB_CONTROL_CYCLE : 0); dev->cmd_ring_enq++; if (dev->cmd_ring_enq == XHCI_RING_TRB_COUNT - 1) { /* About to hand the Link TRB to the controller -- its cycle bit * must match the producer cycle state at the moment of handoff, * and the producer state flips here too (this is the wrap). */ dev->cmd_ring[XHCI_RING_TRB_COUNT - 1].control = (XHCI_TRB_TYPE_LINK << XHCI_TRB_CONTROL_TYPE_SHIFT) | XHCI_TRB_CONTROL_TC | (dev->cmd_ring_cycle ? XHCI_TRB_CONTROL_CYCLE : 0); dev->cmd_ring_enq = 0; dev->cmd_ring_cycle ^= 1u; } /* Doorbell 0 targets the Command Ring (XHCI_DB_TARGET(0)); write-only, * one write per new TRB posted -- xhci.h's own doc comment on the * Doorbell Array. */ dev->doorbell[0] = XHCI_DB_TARGET(0); } int xhci_cmd_enable_slot(xhci_dev_t *dev) { if (!dev || !dev->cmd_ring) return -1; xhci_submit_command(dev, 0, 0, XHCI_TRB_TYPE_ENABLE_SLOT_CMD); console_println("xhci: enable slot command submitted"); return 0; } /* ------------------------------------------------------------------------- * Milestone 2d: Event Ring servicing, polled from sk_repl_idle(). * * Only one controller is supported (matches xhci_dev_t's own doc comment), * so xhci_poll_events() is a self-contained singleton call, no argument * needed -- it recovers the device pointer latched by xhci_bringup() above * rather than taking one. See xhci_driver.h's own doc comment for why this * is polled rather than interrupt-driven (a real, checked-live finding, * not a shortcut: the amd64 PCI INTx routing formula tried first turned * out to be simply wrong). * ------------------------------------------------------------------------- */ /* Port Register Set array lives at Operational base + 0x400 (xhci.h's own * doc comment on xhci_port_regs_t) -- not reachable through xhci_op_regs_t * itself since it isn't a contiguous struct member. port_id is 1-based, * matching XHCI_PSC_EVT_PORT_ID()'s decode and the spec's own numbering. */ static xhci_port_regs_t *xhci_port_regs(xhci_dev_t *dev, uint32_t port_id) { if (port_id < 1 || port_id > dev->max_ports) return NULL; return (xhci_port_regs_t *)((uint8_t *)dev->op + XHCI_PORT_REGS_OFFSET + (port_id - 1) * sizeof(xhci_port_regs_t)); } void xhci_poll_events(void) { xhci_dev_t *dev = g_xhci_dev; if (!dev) return; while (((dev->evt_ring[dev->evt_ring_deq].control & XHCI_TRB_CONTROL_CYCLE) != 0) == (dev->evt_ring_cycle != 0)) { xhci_trb_t *trb = &dev->evt_ring[dev->evt_ring_deq]; uint32_t type = XHCI_TRB_TYPE(trb->control); switch (type) { case XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVT: { /* Milestone 2e: identify which port changed and whether it * now reads connected or disconnected. Slot allocation/ * addressing on connect is the next increment -- this only * detects and acknowledges the change for now. */ uint32_t port_id = XHCI_PSC_EVT_PORT_ID(trb->parameter); xhci_port_regs_t *port = xhci_port_regs(dev, port_id); if (!port) { console_println("xhci: port status change event (bad port id)"); break; } uint32_t portsc = port->portsc; if (portsc & XHCI_PORTSC_CCS) { console_println("xhci: port status change -- device connected"); } else { console_println("xhci: port status change -- device disconnected"); } /* Acknowledge only CSC (RW1CS): preserve PP, write 0 for * PED/PR (writing 1 there disables the port / starts a new * reset -- not intended here) and for every other _C bit * (writing 0 leaves them untouched, not cleared) -- the * same discipline this driver already applies to ERDP.EHB. */ port->portsc = (portsc & XHCI_PORTSC_PP) | XHCI_PORTSC_CSC; break; } case XHCI_TRB_TYPE_COMMAND_COMPLETION_EVT: /* No commands are issued yet (Milestone 2e is the first * command-ring user) -- logged for the same reason. */ console_println("xhci: command completion event"); break; case XHCI_TRB_TYPE_TRANSFER_EVENT: /* No transfer rings exist yet (Milestone 2g) -- logged. */ console_println("xhci: transfer event"); break; default: break; } dev->evt_ring_deq++; if (dev->evt_ring_deq == XHCI_RING_TRB_COUNT) { dev->evt_ring_deq = 0; dev->evt_ring_cycle ^= 1u; } } /* Event Ring dequeue-pointer update (xHCI 1.2 spec §4.9.4): write the * new dequeue pointer back to ERDP with bit3 (EHB, Event Handler Busy, * RW1C) set -- writing 1 to EHB is what clears it, per spec, not a * read-modify-write of the current value. Skipping this leaves the * controller believing the event handler is still busy and it will * not post further events on this interrupter. IMAN.IP/USBSTS.EINT * are deliberately not touched here: this driver never sets * USBCMD.INTE/IMAN.IE (polled, not interrupt-driven -- see this * function's own doc comment), so those RW1C bits never latch and * have nothing to clear. */ dev->intr0->erdp = ((uint64_t)(uintptr_t)&dev->evt_ring[dev->evt_ring_deq] & XHCI_ERDP_PTR_MASK) | XHCI_ERDP_EHB; }