From f84e679a887a1e6a9dcf9405b3c7c5ba8050886f Mon Sep 17 00:00:00 2001 From: Robert Allan James Date: Thu, 27 Aug 2026 14:56:11 -0400 Subject: [PATCH] FABRIC-3.md: scope STALL -- full BOT-spec recovery designed, closing Milestone 2 Traced the completion-code handling before designing anything: every transfer completion (control or bulk) shares one gate that logs and bails on any non-success code -- STALL isn't distinguished from any other failure, and no recovery exists (no xHCI Reset Endpoint, no CLEAR_FEATURE(ENDPOINT_HALT)). A bounded-timeout safety net in xhci_bot_wait_for_idle() prevents a hang, but the endpoint stays wedged for everything after it. Per direct instruction, designed full recovery now rather than deferring to Milestone 8: new STALL_ERROR completion code, new xHCI Reset Endpoint + Set TR Dequeue Pointer command TRB types (neither exists today), CLEAR_FEATURE(ENDPOINT_HALT) reusing the existing control-transfer plumbing, escalating to a full Bulk-Only Mass Storage Reset on a second stall, bounded via a new retry counter mirroring the existing bot_tur_retries/XHCI_BOT_TUR_MAX_RETRIES precedent exactly. This closes the last open Milestone 2 item. --- FABRIC-3.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/FABRIC-3.md b/FABRIC-3.md index a26f129..1f277c1 100644 --- a/FABRIC-3.md +++ b/FABRIC-3.md @@ -121,7 +121,13 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR read-only today specifically because of this). - [ ] Implement basic error/stall recovery (CSW failure status, endpoint stall clear) — at - minimum enough to not wedge the controller on a single bad transfer. + minimum enough to not wedge the controller on a single bad transfer. **SCOPED 2026-08-27 + (`FABRIC-3.md` §F.14)**: full BOT-spec recovery designed — new `STALL_ERROR` completion code, + new xHCI Reset Endpoint + Set TR Dequeue Pointer commands, `CLEAR_FEATURE(ENDPOINT_HALT)` via + existing control-transfer plumbing, escalating to a full Bulk-Only Mass Storage Reset on a + second stall, bounded via a new retry counter mirroring `bot_tur_retries`'s own precedent. + Real gap closed today: CSW status handling (PASS/FAILED/PHASE ERROR) already existed; nothing + distinguished a STALL specifically, and no recovery of any kind existed before this pass. ### From FABRIC-2.md §X, Milestone 3 — Block subsystem extensions @@ -1435,7 +1441,7 @@ punch list hides that; a graph doesn't. graph TD W10["❌ WRITE(10) SCSI support
(Milestone 2 — biggest single blocker)"] HOTPLUG["✅ Hotplug event surfacing
CLOSED 2026-08-27, M2 (§F.3)"] - STALL["❌ USB error/stall recovery (M2)"] + STALL["❌ USB error/stall recovery (M2)
SCOPED 2026-08-27 (§F.14) — full BOT-spec recovery designed"] M6["✅ Milestone 6 — capsule PKI
DONE 2026-08-26"] PH8["✅ Phase 8 — Zuse identity
+ block-fence, DONE 2026-08-26"] @@ -1573,6 +1579,10 @@ finished). Dashed arrows = softer "gates/informs" relationships. noticing, because each only traced `homeblocks_sig_check()` itself, never the ordinary attach path running alongside it. Fixed now (relocate to devblock 1) rather than left to surface later as a real, confusing runtime failure. +- **`STALL` closes out Milestone 2 entirely** (§F.14) — the last item on that punch list. + Unlike most nodes this session, this one designs genuinely new protocol machinery rather than + finding existing infrastructure already covers it: no completion-code distinction and no + recovery of any kind existed before this pass, only a bounded-timeout safety net. **Not yet done:** an ordered plan (which node to attack first, given the graph). Per Captain Bob's own framing, that's the next pass — "start asking and answering questions iteratively @@ -2153,6 +2163,66 @@ power is lost between the two writes) — real question, not addressed here; the updating from their currently-recorded devblock-0-relative assumption to devblock-1, a mechanical follow-up once any of this is actually coded, not re-litigated here. +### F.14 — `STALL` (the last M2 item) — full BOT-spec recovery designed + +Traced the actual completion-code handling before designing anything. **Real finding: every +transfer completion, control or bulk, shares one gate** — `if (code != XHCI_COMPLETION_CODE_SUCCESS) +{ console_println("xhci: control transfer failed"); break; }` (`xhci.c:1267-1270`). A STALL +isn't distinguished from any other failure today, and there is no recovery of any kind — no +xHCI Reset Endpoint command, no USB `CLEAR_FEATURE(ENDPOINT_HALT)`. There is a real safety net +though: `xhci_bot_wait_for_idle()` (`xhci.c:831-839`) has a bounded iteration count and returns +`BOT_STATUS_TIMEOUT` rather than hanging forever, since the generic bail-out never resets +`bot_cmd_kind`. So today's failure mode is "clean timeout, endpoint left permanently wedged for +everything after it" — not a crash, but not recoverable either. + +Also confirmed by inspection: none of the pieces real recovery needs exist yet — +`XHCI_COMPLETION_CODE_STALL_ERROR` isn't defined (only `_SUCCESS`), nor are the `RESET_ENDPOINT` +(xHCI spec value 14) or `SET_TR_DEQUEUE_POINTER` (value 16) command TRB types. Per direct +instruction, full recovery is designed now rather than deferred to Milestone 8. + +**Decisions made 2026-08-27 — the recovery sequence, mirroring the existing bounded-retry +precedent `bot_tur_retries`/`XHCI_BOT_TUR_MAX_RETRIES` already establishes for TUR:** + +1. **Distinguish the completion code.** Add `XHCI_COMPLETION_CODE_STALL_ERROR` (6, per xHCI + spec) alongside the existing `_SUCCESS` (1). The shared completion-code gate branches: a + plain non-success/non-stall code keeps today's behavior (log and bail); a stall specifically + enters the new recovery path below instead of falling straight to the generic message. +2. **xHCI-level reset (two new command TRB types + two new command functions, mirroring the + existing `xhci_cmd_disable_slot()`/`xhci_cmd_configure_endpoint()` shape exactly):** + - `xhci_cmd_reset_endpoint(dev, slot_id, ep_id)` — new `XHCI_TRB_TYPE_RESET_ENDPOINT_CMD` + (14), transitions the stalled endpoint from Halted back to Stopped in the xHC's internal + context. Which endpoint is "the stalled one" is already known — it's whichever of + `bulk_in_ep_addr`/`bulk_out_ep_addr` the in-flight transfer purpose + (`BOT_DATA_IN`/`BOT_DATA_OUT`/`CBW_SENT`/`CSW_RECEIVE`) was using, both already tracked + fields. + - `xhci_cmd_set_tr_dequeue_pointer(dev, slot_id, ep_id, new_dequeue, dcs)` — new + `XHCI_TRB_TYPE_SET_TR_DEQUEUE_POINTER_CMD` (16), repositions the ring's dequeue pointer + past the failed TRB so the next enqueued transfer resumes cleanly. +3. **USB-level clear (reuses the existing control-transfer infrastructure already built for + `GET_DESCRIPTOR`/`SET_CONFIGURATION` — no new transfer-stage machinery, just a new request + payload):** send `CLEAR_FEATURE(ENDPOINT_HALT)` (standard request, `wValue=0`, `wIndex=` + the stalled endpoint address) to clear the device's own halt condition and reset its data + toggle. +4. **Escalation on a second stall (or if step 3 itself stalls):** issue Bulk-Only Mass Storage + Reset (BOT class request, `bmRequestType=0x21`, `bRequest=0xFF`, no data stage — again the + existing control-transfer machinery, a different request payload) followed by + `CLEAR_FEATURE(ENDPOINT_HALT)` on *both* bulk endpoints (BOT spec 5.3.4's full procedure), + then retry the original command from scratch — the same "fresh target" restart shape the + existing TUR-chain retry already uses. +5. **Bounded, not infinite:** a new `bot_stall_recoveries` counter capped at a new + `XHCI_BOT_STALL_MAX_RECOVERIES`, exact value TBD but same shape as `XHCI_BOT_TUR_MAX_RETRIES`. + Exhausting it sets `bot_last_status = BOT_STATUS_FAILED` and `bot_cmd_kind = BOT_CMD_NONE` + explicitly — a clean terminal failure signaled to `xhci_bot_wait_for_idle()`'s caller, + rather than relying purely on the outer timeout the way today's un-recovered stall does. + +**Not yet scoped (deferred within this node):** the exact `XHCI_BOT_STALL_MAX_RECOVERIES` value; +whether a `WRITE(10)`-specific residual-data concern exists on a stalled Data-Out stage (a +partially-written SCSI command's recovery semantics may differ subtly from a stalled Data-In +read — not analyzed here, `WRITE(10)` itself still doesn't exist); real-hardware validation is +explicitly out of reach until Milestone 8, same caveat `WRITE(10)`'s own CSW-residue question +carries (§F.1) — this design is believed spec-correct but has only QEMU (which rarely if ever +issues real stalls) to test against for now. + ### D.5 — Scope expansion (2026-08-27): identity is common to every VM, not just users Surfaced while scoping `ACLKEY`, stated directly: *"the whole object is to deliver a