Zuse default-attach: xHCI initial-port-scan fix, ZUSEDISK wiring, mismatched-marker resync

xhci_bringup() now scans for already-connected ports at bring-up
(xhci_scan_ports_for_already_connected()), not just later hotplug events,
so a USB device present on the QEMU command line at launch is detected.
Makefile.starkernel attaches disk/zuse.img on the xhci0 bus by default in
all three arch qemu targets (ZUSEDISK=, empties for a bare boot).

capsule_mint_identity() gained a drive_known_blank param to skip a fully
redundant second homeblocks_sig_check() when the caller already confirmed
HOMEBLOCKS_SIG_BLANK itself.

Root-caused what looked like a hang after the drive attached: Artemis's
fence still carried a genesis marker from before a mid-session reformat,
while the reformatted disk/zuse.img read back BLANK -- a mismatched pair
capsule_zuse_boot_try_attach() correctly declined to act on, leaving the
boot idling at a plain ok> with nothing left to log (indistinguishable
from a hang under slow TCG). Fixed by zeroing both disk/artemis.img and
disk/zuse.img at their original sizes, giving a matched blank pair.
Verified full three-architecture acceptance: amd64 fresh genesis-mint,
aarch64/riscv64 clean reload against the same now-minted images.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KBjfeLPo71sUQ8zC7V7P5m
This commit is contained in:
Robert Allan James
2026-08-28 21:42:14 -04:00
co-authored by Claude Sonnet 5
parent 3f74ff0f78
commit 849b83b727
34 changed files with 210921 additions and 36 deletions
+7 -3
View File
@@ -59,7 +59,8 @@ static int write_devblock(struct blkio_dev *dev, uint32_t devblock,
MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm,
const char *full_name, const char *username,
const char *email, const char *phone,
uint8_t out_pubkey[32], uint8_t out_seed[32]) {
uint8_t out_pubkey[32], uint8_t out_seed[32],
int drive_known_blank) {
if (!dev || !full_name || !username) return MINT_ERR_WRITE_FAIL;
/* full_name/username required and must fit; email/phone may be NULL
@@ -75,8 +76,11 @@ MintResult capsule_mint_identity(struct blkio_dev *dev, VM *issuer_vm,
/* Refuse to overwrite an already-recognized home-blocks drive --
* mirrors WRITE(10)'s own refuse-on-non-blank-media posture (§F.8,
* decided 2026-08-28: "reasonable by analogy" there, now decided). */
{
* decided 2026-08-28: "reasonable by analogy" there, now decided).
* Skipped when the caller already just confirmed HOMEBLOCKS_SIG_BLANK
* itself (drive_known_blank) -- otherwise this repeats the exact same
* full BOT read sequence a second time immediately after the first. */
if (!drive_known_blank) {
homeblocks_sig_t existing;
if (homeblocks_sig_check(dev, HOMEBLOCKS_SIG_START_FBLOCK, &existing)
== HOMEBLOCKS_SIG_OK) {
+2 -1
View File
@@ -73,7 +73,8 @@ void capsule_zuse_boot_try_attach(struct blkio_dev *dev,
uint8_t seed[32], pubkey[32];
MintResult r = capsule_mint_identity(dev, (VM *)0, "Zuse", "zuse",
(const char *)0, (const char *)0,
pubkey, seed);
pubkey, seed,
1 /* sig_rc already confirmed BLANK above */);
if (r != MINT_OK) {
console_println("Zuse: genesis mint failed");
return;
+2 -1
View File
@@ -868,7 +868,8 @@ static void mama_word_mint(VM *vm)
}
MintResult r = capsule_mint_identity(dev, vm, full_name, username, email, phone,
(uint8_t *)0, (uint8_t *)0);
(uint8_t *)0, (uint8_t *)0,
0 /* not pre-checked -- keep the safety check */);
switch (r) {
case MINT_OK:
console_println("MINT: identity minted");
+88 -29
View File
@@ -144,6 +144,14 @@ int xhci_ep0_get_device_descriptor(xhci_dev_t *dev, uint32_t slot_id);
int xhci_ep0_get_config_descriptor(xhci_dev_t *dev, uint32_t slot_id, uint16_t length);
int xhci_ep0_set_configuration(xhci_dev_t *dev, uint32_t slot_id, uint8_t config_value);
/* Forward declaration -- xhci_bringup() below calls this once the
* controller is running, to catch a device that was already connected
* (present on the QEMU command line at launch, not hot-plugged after boot).
* The implementation lives after xhci_poll_events() since it shares
* xhci_port_regs() and the same connect-handling logic. See xhci_scan_
* ports_for_already_connected()'s own doc comment for why this exists. */
static void xhci_scan_ports_for_already_connected(xhci_dev_t *dev);
int xhci_bringup(xhci_dev_t *dev)
{
if (!dev || !dev->op) return -2;
@@ -347,6 +355,16 @@ int xhci_bringup(xhci_dev_t *dev)
: "xhci: context size = 32 bytes");
g_xhci_dev = dev;
/* Catch a device already connected at launch -- xhci_poll_events() is
* purely event-ring-driven (Port Status Change events only), and a
* device present on the QEMU command line before this controller reset
* never generates one (nothing "changed" from the controller's
* perspective once it starts looking). g_xhci_dev must be set first --
* this reuses the same connect-handling path xhci_poll_events() uses,
* which reads it as a singleton rather than taking dev as an arg. */
xhci_scan_ports_for_already_connected(dev);
return 0;
}
@@ -1161,6 +1179,75 @@ static xhci_port_regs_t *xhci_port_regs(xhci_dev_t *dev, uint32_t port_id)
(port_id - 1) * sizeof(xhci_port_regs_t));
}
/* Shared "device connected" handling -- factored out of xhci_poll_events()'s
* PORT_STATUS_CHANGE_EVT case so xhci_scan_ports_for_already_connected()
* (called once from xhci_bringup(), see its own doc comment) can drive the
* exact same Enable Slot sequence for a device that was already attached at
* controller bring-up, not just one detected via a later hotplug event. */
static void xhci_handle_port_connected(xhci_dev_t *dev, uint32_t port_id, uint32_t portsc)
{
console_println("xhci: port status change -- device connected");
/* Milestone 2e prep: Address Device requires the port in Default
* state. USB3 links train and enable themselves; USB2 needs software
* to drive PORTSC.PR and wait for PRC/PED before the device will
* respond to addressing -- not yet known which this driver's ports
* need, so log raw PORTSC and PED rather than assume. */
xhci_log_hex32("xhci: portsc=", portsc);
console_println((portsc & XHCI_PORTSC_PED)
? "xhci: port enabled (PED set)"
: "xhci: port not yet enabled (PED clear)");
/* 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->connect_state == XHCI_CONN_IDLE) {
dev->pending_connect_port_id = port_id;
dev->pending_connect_speed = XHCI_PORTSC_SPEED(portsc);
dev->connect_state = XHCI_CONN_AWAIT_ENABLE_SLOT;
xhci_cmd_enable_slot(dev);
} else {
console_println("xhci: enable slot already pending -- dropped");
}
}
/* xhci_scan_ports_for_already_connected -- called once from xhci_bringup(),
* right after the controller starts running. xhci_poll_events() only reacts
* to Port Status Change *events*, and a device present on the QEMU command
* line before this controller reset never generates one (nothing "changed"
* from the controller's perspective once it starts looking) -- confirmed by
* reading the spec's event model, not assumed. Without this scan, such a
* device stays invisible to the guest forever, since no later event will
* ever announce it either.
*
* Scans tracked ports for CCS (Current Connect Status) directly, and drives
* the first connected one found through the same Enable Slot path
* xhci_poll_events() uses -- matching that path's own single-outstanding-
* connect limitation, which is fine here too: this driver's real use case
* is exactly one thumbdrive already attached at boot, not several. */
static void xhci_scan_ports_for_already_connected(xhci_dev_t *dev)
{
uint32_t max_port = dev->max_ports;
if (max_port > XHCI_MAX_TRACKED_PORTS) max_port = XHCI_MAX_TRACKED_PORTS;
for (uint32_t port_id = 1; port_id <= max_port; port_id++) {
xhci_port_regs_t *port = xhci_port_regs(dev, port_id);
if (!port) continue;
uint32_t portsc = port->portsc;
if (!(portsc & XHCI_PORTSC_CCS)) continue;
console_println("xhci: device already connected at bring-up");
xhci_handle_port_connected(dev, port_id, portsc);
/* Acknowledge CSC the same way xhci_poll_events() does, in case
* the controller latched it during reset -- harmless if it was
* never set. */
port->portsc = (portsc & XHCI_PORTSC_PP) | XHCI_PORTSC_CSC;
break;
}
}
void xhci_poll_events(void)
{
xhci_dev_t *dev = g_xhci_dev;
@@ -1185,35 +1272,7 @@ void xhci_poll_events(void)
}
uint32_t portsc = port->portsc;
if (portsc & XHCI_PORTSC_CCS) {
console_println("xhci: port status change -- device connected");
/* Milestone 2e prep: Address Device requires the port
* in Default state. USB3 links train and enable
* themselves; USB2 needs software to drive PORTSC.PR
* and wait for PRC/PED before the device will respond
* to addressing -- not yet known which this driver's
* ports need, so log raw PORTSC and PED rather than
* assume. */
xhci_log_hex32("xhci: portsc=", portsc);
console_println((portsc & XHCI_PORTSC_PED)
? "xhci: port enabled (PED set)"
: "xhci: port not yet enabled (PED clear)");
/* 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->connect_state == XHCI_CONN_IDLE) {
dev->pending_connect_port_id = port_id;
dev->pending_connect_speed = XHCI_PORTSC_SPEED(portsc);
dev->connect_state = XHCI_CONN_AWAIT_ENABLE_SLOT;
xhci_cmd_enable_slot(dev);
} else {
console_println("xhci: enable slot already pending -- dropped");
}
xhci_handle_port_connected(dev, port_id, portsc);
} else {
console_println("xhci: port status change -- device disconnected");
if (port_id >= 1 && port_id <= XHCI_MAX_TRACKED_PORTS &&