xHCI/BOT driver: genuine multi-device support (FABRIC-3.md §VII)
Per-slot registry (xhci_msc_slot_t/dev->msc_slots, sized off the controller's own reported max_slots) replaces the single-device scalar fields the driver carried since Milestones 2e-2h. Boot-time port scan no longer stops at the first connected device; a connect/disconnect that arrives while the Command Ring is busy is now queued and drained instead of dropped. blkio_usb.c and repl.c's own single-device state (device descriptor buffers, blkio_dev_t, attach bookkeeping) became per-slot registries the same way. Live multi-device testing (not just compiling) surfaced a second, more severe bug outside the original plan: transfer_purpose and next_action were also single scalars shared across the whole controller. Two devices enumerating concurrently could have one's completion silently overwrite the other's still-outstanding one, permanently stalling it with no error. Fixed by moving both per-slot and, critically, reading the Transfer Event TRB's own real Slot ID field instead of trusting external bookkeeping. Verified live, all three architectures, mandatory clean-qemu acceptance: existing single-device path unchanged, and two devices attached simultaneously (amd64) both progress independently through enumeration without corrupting or stalling each other. Also in this pass (implemented and verified in earlier turns this session, committed together per direct instruction): - Headless-until-login console policy: no prompt/banner until a real identity logs in via an attached thumbdrive (WIREBIND or Zuse, neither special), reusing EMERGENCY_CONSOLE_ENABLED as the debug/recovery escape hatch (now default-off). - KILL/g_repl_active_vm dangling-pointer fix: killing the VM the console is currently USE'd onto now detaches back to Hera first, matching the existing EJECT/UNCLEAN precedent. FABRIC-3.md §VII/§VIII carry full closure notes for all three. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018EjXFo7mPXjUMjfJeuUUz4
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
cc6fcb6a0b
commit
9e81de3f43
+103
-20
@@ -37,6 +37,7 @@
|
||||
#include "starkernel/arch.h"
|
||||
#include "starkernel/xhci_driver.h"
|
||||
#include "starkernel/blkio_usb.h"
|
||||
#include "starkernel/kmalloc.h"
|
||||
#include "starkernel/homeblocks_sig.h"
|
||||
#include "starkernel/capsule_birth.h"
|
||||
#include "starkernel/capsule_zuse_boot.h"
|
||||
@@ -96,6 +97,19 @@ static VM *g_repl_active_vm = (void *)0;
|
||||
void sk_repl_set_active_vm(VM *vm) { g_repl_active_vm = vm; }
|
||||
VM *sk_repl_get_active_vm(void) { return g_repl_active_vm; }
|
||||
|
||||
/*===========================================================================
|
||||
* Headless-until-login gate, decided 2026-09-05: no console for the
|
||||
* running system unless a thumbdrive is present. One shared flag, set by
|
||||
* either login path (capsule_wirebind.c's regular-user console-VM birth,
|
||||
* capsule_zuse_boot.c's own attach/genesis-mint) -- neither is special,
|
||||
* per direct instruction. See repl.h's own doc comments.
|
||||
*===========================================================================*/
|
||||
|
||||
static int g_console_login_occurred = 0;
|
||||
|
||||
void sk_console_mark_login(void) { g_console_login_occurred = 1; }
|
||||
int sk_console_login_occurred(void) { return g_console_login_occurred; }
|
||||
|
||||
/*===========================================================================
|
||||
* Currently attached home-blocks device: mirrors g_repl_active_vm's own
|
||||
* shape (FABRIC-2.md §F.9's own precedent for this exact accessor). Set
|
||||
@@ -228,14 +242,33 @@ static void sk_repl_idle(VM *active_vm)
|
||||
* busy-wait -- which blkio_usb_open_msc() uses internally -- must
|
||||
* never run from inside xhci_poll_events()'s own call frame). */
|
||||
xhci_dev_t *xdev = xhci_get_dev();
|
||||
static blkio_dev_t usb_blk_dev; /* single-device scope, matching the xHCI
|
||||
* driver's own; referenced by both the
|
||||
* attach and detach handling below. */
|
||||
if (xdev && xdev->bot_msc_attach_pending) {
|
||||
xdev->bot_msc_attach_pending = 0;
|
||||
uint32_t slot_id = xdev->bot_msc_attach_slot_id;
|
||||
/* FABRIC-3.md §VII (2026-09-05): was `static blkio_dev_t usb_blk_dev`
|
||||
* ("single-device scope, matching the xHCI driver's own") -- now a
|
||||
* per-slot registry, same sizing/allocation precedent as xhci_dev_t's
|
||||
* own msc_slots[] (sized off xdev->max_slots, allocated once on first
|
||||
* idle tick after xdev is known, since this file has no bringup-time
|
||||
* hook of its own). Every slot with a pending attach/detach flag is
|
||||
* serviced this tick, not just one -- a single `if` here used to mean
|
||||
* a second device's pending flag would sit unnoticed until the first's
|
||||
* flag was consumed and cleared. */
|
||||
static blkio_dev_t *usb_blk_dev_slots = (void *)0;
|
||||
static uint32_t usb_blk_dev_slot_count = 0;
|
||||
if (xdev && (!usb_blk_dev_slots || usb_blk_dev_slot_count < xdev->max_slots + 1)) {
|
||||
size_t bytes = (size_t)(xdev->max_slots + 1) * sizeof(blkio_dev_t);
|
||||
blkio_dev_t *fresh = (blkio_dev_t *)kmalloc_aligned(bytes, 64);
|
||||
if (fresh) {
|
||||
memset(fresh, 0, bytes);
|
||||
usb_blk_dev_slots = fresh;
|
||||
usb_blk_dev_slot_count = xdev->max_slots + 1;
|
||||
}
|
||||
}
|
||||
for (uint32_t slot_id = 1; xdev && usb_blk_dev_slots && slot_id <= xdev->max_slots; slot_id++) {
|
||||
xhci_msc_slot_t *ms = xhci_msc_slot_for(xdev, slot_id);
|
||||
if (!ms || !ms->bot_msc_attach_pending) continue;
|
||||
ms->bot_msc_attach_pending = 0;
|
||||
blkio_dev_t *usb_blk_dev = &usb_blk_dev_slots[slot_id];
|
||||
|
||||
int rc = blkio_usb_open_msc(&usb_blk_dev, xdev, slot_id);
|
||||
int rc = blkio_usb_open_msc(usb_blk_dev, xdev, slot_id);
|
||||
if (rc == 0) {
|
||||
/* FABRIC-2.md Milestone 4: warn on blank/foreign/unrecognized
|
||||
* media -- the "warn" half. No "refuse" half yet: blkio_usb.c
|
||||
@@ -254,11 +287,22 @@ static void sk_repl_idle(VM *active_vm)
|
||||
* interim value (FABRIC-2.md §F.8/§F.13). */
|
||||
homeblocks_sig_t sig;
|
||||
homeblocks_sig_result_t sig_rc =
|
||||
homeblocks_sig_check(&usb_blk_dev, HOMEBLOCKS_SIG_START_FBLOCK, &sig);
|
||||
homeblocks_sig_check(usb_blk_dev, HOMEBLOCKS_SIG_START_FBLOCK, &sig);
|
||||
switch (sig_rc) {
|
||||
case HOMEBLOCKS_SIG_OK:
|
||||
log_message(LOG_DEBUG, "xhci: USB drive recognized as a home-blocks drive");
|
||||
g_homeblocks_dev = &usb_blk_dev;
|
||||
/* FABRIC-3.md §VII (2026-09-05): g_homeblocks_dev/
|
||||
* g_attached_blk_dev stay single "most recently
|
||||
* attached" pointers by deliberate, scoped choice --
|
||||
* the multi-device fix's target was the driver/backend
|
||||
* corrupting each other's live state when two devices
|
||||
* are attached at once (fixed above and in xhci.c/
|
||||
* blkio_usb.c), not making every console-facing FORTH
|
||||
* word (RUNCAP et al, mama_forth_words.c) multi-device
|
||||
* aware -- the console still interacts with one device
|
||||
* at a time, matching its own single-active-REPL
|
||||
* design. Revisit if a real use case needs otherwise. */
|
||||
g_homeblocks_dev = usb_blk_dev;
|
||||
g_homeblocks_sig = sig;
|
||||
g_homeblocks_sig_valid = 1;
|
||||
break;
|
||||
@@ -284,7 +328,7 @@ static void sk_repl_idle(VM *active_vm)
|
||||
* be detected before the REPL's own idle polling exists to
|
||||
* detect it). No-ops immediately if Zuse already has a real
|
||||
* identity this boot. */
|
||||
capsule_zuse_boot_try_attach(&usb_blk_dev, sig_rc, &sig, (VM *)sk_get_mama_vm());
|
||||
capsule_zuse_boot_try_attach(usb_blk_dev, sig_rc, &sig, (VM *)sk_get_mama_vm());
|
||||
|
||||
/* FABRIC-2.md §F.5/§F.23 (WIREBIND): the real thumbdrive-
|
||||
* attach call site for a regular (non-Zuse) identity --
|
||||
@@ -295,12 +339,12 @@ static void sk_repl_idle(VM *active_vm)
|
||||
* itself no-ops for a genesis-mode Zuse drive (no cert
|
||||
* region) or before Zuse has authenticated this boot. */
|
||||
if (sig_rc == HOMEBLOCKS_SIG_OK) {
|
||||
capsule_wirebind_try_attach(&usb_blk_dev, &sig, (VM *)sk_get_mama_vm());
|
||||
capsule_wirebind_try_attach(usb_blk_dev, &sig, (VM *)sk_get_mama_vm());
|
||||
}
|
||||
}
|
||||
if (rc == 0 && blk_subsys_attach_device(&usb_blk_dev) == BLK_OK) {
|
||||
xdev->bot_msc_attached = 1;
|
||||
g_attached_blk_dev = &usb_blk_dev;
|
||||
if (rc == 0 && blk_subsys_attach_device(usb_blk_dev) == BLK_OK) {
|
||||
ms->bot_msc_attached = 1;
|
||||
g_attached_blk_dev = usb_blk_dev;
|
||||
} else {
|
||||
log_message(LOG_ERROR, "xhci: USB MSC block-subsystem attach failed");
|
||||
}
|
||||
@@ -312,15 +356,21 @@ static void sk_repl_idle(VM *active_vm)
|
||||
* no device round-trip, so it wouldn't strictly need to run outside
|
||||
* xhci_poll_events()'s own call frame -- but handling it here anyway
|
||||
* matches the attach path's shape and keeps xhci.c decoupled from
|
||||
* block_subsystem.c (see bot_msc_detach_pending's own doc comment). */
|
||||
if (xdev && xdev->bot_msc_detach_pending) {
|
||||
xdev->bot_msc_detach_pending = 0;
|
||||
blk_subsys_detach_device(&usb_blk_dev);
|
||||
if (g_homeblocks_dev == &usb_blk_dev) {
|
||||
* block_subsystem.c (see bot_msc_detach_pending's own doc comment).
|
||||
* Per-slot loop now (FABRIC-3.md §VII, 2026-09-05), same reasoning as
|
||||
* the attach loop above. */
|
||||
for (uint32_t slot_id = 1; xdev && usb_blk_dev_slots && slot_id <= xdev->max_slots; slot_id++) {
|
||||
xhci_msc_slot_t *ms = xhci_msc_slot_for(xdev, slot_id);
|
||||
if (!ms || !ms->bot_msc_detach_pending) continue;
|
||||
ms->bot_msc_detach_pending = 0;
|
||||
blkio_dev_t *usb_blk_dev = &usb_blk_dev_slots[slot_id];
|
||||
|
||||
blk_subsys_detach_device(usb_blk_dev);
|
||||
if (g_homeblocks_dev == usb_blk_dev) {
|
||||
g_homeblocks_dev = (void *)0;
|
||||
g_homeblocks_sig_valid = 0;
|
||||
}
|
||||
if (g_attached_blk_dev == &usb_blk_dev) {
|
||||
if (g_attached_blk_dev == usb_blk_dev) {
|
||||
g_attached_blk_dev = (void *)0;
|
||||
}
|
||||
|
||||
@@ -576,6 +626,25 @@ int sk_console_getkey(VM *active_vm)
|
||||
}
|
||||
}
|
||||
|
||||
/* sk_repl_headless_wait - see repl.h's own doc comment. Same idle-service
|
||||
* shape as sk_console_getkey() above, minus the key-reading entirely: no
|
||||
* banner, no prompt, no console_getc()/readline of any kind -- this is
|
||||
* exactly the "no console for the running system unless a thumbdrive is
|
||||
* present" boundary, decided 2026-09-05. Exits the moment
|
||||
* sk_console_login_occurred() becomes true. */
|
||||
void sk_repl_headless_wait(VM *mama)
|
||||
{
|
||||
while (!sk_console_login_occurred()) {
|
||||
heartbeat_service();
|
||||
uint64_t now = heartbeat_ticks();
|
||||
if (now - g_last_beat_tick >= SK_IDLE_BEAT_INTERVAL) {
|
||||
g_last_beat_tick = now;
|
||||
sk_repl_idle(mama);
|
||||
}
|
||||
arch_relax();
|
||||
}
|
||||
}
|
||||
|
||||
/* ?TERMINAL's real body (sf_terminal_ready(), shim.c): non-blocking peek --
|
||||
* a single poll, no idle-servicing loop (a false result must return
|
||||
* immediately, not block). Buffers a found byte in g_console_pending_key so
|
||||
@@ -911,8 +980,16 @@ int sk_repl_step(VM *vm)
|
||||
vm->abort_requested = 0;
|
||||
|
||||
if (vm->error) {
|
||||
#if EMERGENCY_CONSOLE_ENABLED
|
||||
console_puts(" ERROR\n");
|
||||
vm->error = 0;
|
||||
#else
|
||||
/* Wired 2026-09-05: sk_fault_handler() existed but was never
|
||||
* called from here -- the "halts VM on error" half of this
|
||||
* function's own doc comment was aspirational, not real, until
|
||||
* now. Real for the headless-until-login default. */
|
||||
sk_fault_handler(vm);
|
||||
#endif
|
||||
} else {
|
||||
console_puts(" ok\n");
|
||||
}
|
||||
@@ -955,8 +1032,14 @@ void sk_repl_run(VM *vm)
|
||||
active->abort_requested = 0;
|
||||
|
||||
if (active->error) {
|
||||
#if EMERGENCY_CONSOLE_ENABLED
|
||||
console_puts(" ERROR\n");
|
||||
active->error = 0;
|
||||
#else
|
||||
/* Wired 2026-09-05, same as sk_repl_step()'s matching branch
|
||||
* above -- sk_fault_handler() existed but was never called. */
|
||||
sk_fault_handler(active);
|
||||
#endif
|
||||
} else {
|
||||
console_puts(" ok\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user