Artemis Milestone 2e (in progress): xHCI Command Ring write path proven live
Enable Slot command TRB submitted via a new xhci_submit_command()/ xhci_cmd_enable_slot(), ring doorbell 0, confirmed by a real Command Completion Event on all three architectures -- the first time this driver has written a TRB rather than only reading the Event Ring (2d). Added the Command Ring's previously-missing Link TRB (xHCI 1.2 spec sec 4.9.2) for wraparound correctness. Port Register connect/disconnect handling, slot-ID/context bookkeeping, Address Device, and the callback surface into Section U's code are still open -- this is the discriminating first step, not full 2e. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HZ8kNoTuP63pbQtro4qvrm
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
2b16daba16
commit
9c8ad8f6ff
+23
@@ -3217,6 +3217,29 @@ Final three-arch acceptance (probe-free, `clean qemu`): all three boot to `ok>`
|
||||
calling into block_subsystem.c directly (matching the existing "kernel/Artemis
|
||||
decoupling boundary" pattern already documented in `block_subsystem.c`)
|
||||
|
||||
**In progress, 2026-08-22 — Command Ring write path proven live before building the rest of
|
||||
2e on top of it.** Before touching the Port Register / connect-vs-disconnect logic above, the
|
||||
driver had never once written a TRB or rung a doorbell — `xhci_poll_events()` was read-only.
|
||||
Closed that gap first as its own discriminating step:
|
||||
- Command Ring now carries a permanent Link TRB at index `XHCI_RING_TRB_COUNT-1` (xHCI 1.2
|
||||
spec §4.9.2 requires one per segment for wraparound; previously absent since nothing had
|
||||
ever posted enough commands to reach it) — Toggle Cycle bit set, producer/consumer cycle
|
||||
state kept in sync on wrap by `xhci_submit_command()`.
|
||||
- Added `xhci_submit_command()` (enqueue + doorbell ring, shared plumbing for Enable Slot and
|
||||
the future Address Device) and `xhci_cmd_enable_slot()`, called once as a smoke test right
|
||||
after a successful `xhci_bringup()`.
|
||||
- Verified live, all three architectures: each boot logs `xhci: enable slot command
|
||||
submitted` during bring-up, then `xhci: command completion event` once `xhci_poll_events()`
|
||||
starts running from `ok>`'s idle loop — proof the controller actually consumed a
|
||||
software-written TRB via doorbell 0, not just that software believes it did.
|
||||
`logs/20260822-093311/amd64/`, `logs/20260822-093412/aarch64/`, `logs/20260822-093549/riscv64/`.
|
||||
- Still open, unchanged from the checklist above: reading PORTSC to distinguish connect from
|
||||
disconnect, correlating the Command Completion Event back to its issuing command (currently
|
||||
only logged, slot ID/context bookkeeping not read from the event TRB), Address Device, and
|
||||
the callback surface into Section U's higher-level code. The Enable Slot call above is a
|
||||
speculative smoke test, not yet triggered by a real connect event — that wiring is the next
|
||||
increment.
|
||||
|
||||
**2f. USB device enumeration (post-connect, before it's usable as storage)**
|
||||
- [ ] Request and parse the device descriptor (confirm vendor/product IDs are even needed,
|
||||
or if class-only detection suffices for this project's purposes)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-22T13:22:00Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-22T13:35:47Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
Binary file not shown.
@@ -184,12 +184,24 @@ typedef struct {
|
||||
#define XHCI_TRB_TYPE(ctrl) (((ctrl) & XHCI_TRB_CONTROL_TYPE_MASK) >> XHCI_TRB_CONTROL_TYPE_SHIFT)
|
||||
|
||||
/* TRB types used by this driver (subset — xHCI defines many more) */
|
||||
#define XHCI_TRB_TYPE_LINK 6 /* ring-wraparound marker, Command/Transfer Rings only */
|
||||
#define XHCI_TRB_TYPE_ENABLE_SLOT_CMD 9
|
||||
#define XHCI_TRB_TYPE_ADDRESS_DEVICE_CMD 11
|
||||
#define XHCI_TRB_TYPE_TRANSFER_EVENT 32
|
||||
#define XHCI_TRB_TYPE_COMMAND_COMPLETION_EVT 33
|
||||
#define XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVT 34
|
||||
|
||||
/* Control bits used only by Link TRBs */
|
||||
#define XHCI_TRB_CONTROL_TC (1u << 1) /* Toggle Cycle */
|
||||
|
||||
/* Command Completion Event TRB layout (xHCI 1.2 spec table 6-32):
|
||||
* parameter[63:4] = Command TRB Pointer, status[31:24] = Completion Code,
|
||||
* status[23:0] = unused here, control[31:24] = Slot ID (Enable Slot's
|
||||
* result, also present on Address Device completions). */
|
||||
#define XHCI_EVT_COMPLETION_CODE(status) (((uint32_t)(status) >> 24) & 0xFFu)
|
||||
#define XHCI_EVT_SLOT_ID(control) (((uint32_t)(control) >> 24) & 0xFFu)
|
||||
#define XHCI_COMPLETION_CODE_SUCCESS 1u
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Ring sizing — decided up front per Milestone 2's punch list (2a).
|
||||
*
|
||||
|
||||
@@ -29,8 +29,11 @@ typedef struct {
|
||||
/* 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 */
|
||||
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 */
|
||||
@@ -99,4 +102,17 @@ int xhci_bringup(xhci_dev_t *dev);
|
||||
*/
|
||||
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 --
|
||||
* that arrives asynchronously via xhci_poll_events(),
|
||||
* which currently only logs it (Milestone 2e's slot-
|
||||
* ID/context bookkeeping is not wired up yet).
|
||||
*
|
||||
* 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 */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -98,6 +98,12 @@ static int xhci_wait_bit(volatile uint32_t *reg, uint32_t mask, int want_set,
|
||||
* 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;
|
||||
@@ -182,6 +188,20 @@ int xhci_bringup(xhci_dev_t *dev)
|
||||
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;
|
||||
|
||||
@@ -233,6 +253,59 @@ int xhci_bringup(xhci_dev_t *dev)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user