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.
This commit is contained in:
Robert Allan James
2026-08-27 14:56:11 -04:00
parent 6a3eb5997f
commit f84e679a88
+72 -2
View File
@@ -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<br/>(Milestone 2 — biggest single blocker)"]
HOTPLUG["✅ Hotplug event surfacing<br/>CLOSED 2026-08-27, M2 (§F.3)"]
STALL["❌ USB error/stall recovery (M2)"]
STALL["❌ USB error/stall recovery (M2)<br/>SCOPED 2026-08-27 (§F.14) — full BOT-spec recovery designed"]
M6["✅ Milestone 6 — capsule PKI<br/>DONE 2026-08-26"]
PH8["✅ Phase 8 — Zuse identity<br/>+ 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