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
This commit is contained in:
Robert Allan James
2026-08-22 10:33:37 -04:00
co-authored by Claude Sonnet 5
parent dd043bbfeb
commit 6d330efdd8
12 changed files with 36185 additions and 27 deletions
+54 -15
View File
@@ -251,20 +251,14 @@ int xhci_bringup(xhci_dev_t *dev)
return -3;
}
/* Milestone 2e: per-port Enable Slot correlation state -- fixed array,
* see xhci_dev_t's own doc comment; no allocation needed. */
for (uint32_t i = 0; i < XHCI_MAX_TRACKED_PORTS; i++) dev->port_slot_id[i] = 0;
dev->pending_connect_port_id = 0;
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;
}
@@ -358,8 +352,32 @@ void xhci_poll_events(void)
uint32_t portsc = port->portsc;
if (portsc & XHCI_PORTSC_CCS) {
console_println("xhci: port status change -- device connected");
/* Only one Enable Slot in flight at a time (see
* xhci_dev_t's doc comment) -- if another connect's
* slot request is still outstanding, this one is
* dropped rather than queued. Acceptable for this
* milestone's single-device testing scope; revisit if
* multi-port simultaneous connects become a real
* scenario. */
if (port_id > XHCI_MAX_TRACKED_PORTS) {
console_println("xhci: port beyond tracked range -- enable slot skipped");
} else if (dev->pending_connect_port_id == 0) {
dev->pending_connect_port_id = port_id;
xhci_cmd_enable_slot(dev);
} else {
console_println("xhci: enable slot already pending -- dropped");
}
} else {
console_println("xhci: port status change -- device disconnected");
if (port_id >= 1 && port_id <= XHCI_MAX_TRACKED_PORTS &&
dev->port_slot_id[port_id - 1] != 0) {
/* Real teardown (Disable Slot command, DCBAA entry
* clear, callback to Section U's code) is a later
* increment -- for now just stop tracking the slot
* so a future connect on this port isn't confused
* for one already in progress. */
dev->port_slot_id[port_id - 1] = 0;
}
}
/* Acknowledge only CSC (RW1CS): preserve PP, write 0 for
* PED/PR (writing 1 there disables the port / starts a new
@@ -369,11 +387,32 @@ void xhci_poll_events(void)
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");
case XHCI_TRB_TYPE_COMMAND_COMPLETION_EVT: {
uint32_t code = XHCI_EVT_COMPLETION_CODE(trb->status);
uint32_t slot_id = XHCI_EVT_SLOT_ID(trb->control);
/* Correlates to the single in-flight Enable Slot, not to
* the Command TRB Pointer in trb->parameter -- this driver
* only ever has one command outstanding (see
* xhci_dev_t's doc comment), so pending_connect_port_id
* alone is enough to identify which port this completion
* belongs to; a real Command TRB Pointer match becomes
* necessary once Address Device commands can also be
* in flight concurrently with Enable Slot. */
if (dev->pending_connect_port_id != 0) {
uint32_t port_id = dev->pending_connect_port_id;
dev->pending_connect_port_id = 0;
if (code == XHCI_COMPLETION_CODE_SUCCESS &&
port_id >= 1 && port_id <= XHCI_MAX_TRACKED_PORTS) {
dev->port_slot_id[port_id - 1] = slot_id;
console_println("xhci: enable slot succeeded");
} else {
console_println("xhci: enable slot failed");
}
} else {
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");