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:
co-authored by
Claude Sonnet 5
parent
3f74ff0f78
commit
849b83b727
@@ -101,6 +101,27 @@ DOE_LATEST_DIR := experiments/bare_metal/latest
|
||||
|
||||
CAPSULES_DIR ?= capsules
|
||||
ARTDISK ?= disk/artemis.img
|
||||
# ZUSEDISK -- Zuse's own minted USB thumbdrive, attached by default so a
|
||||
# plain `make qemu` lands already authenticated into the Zuse identity
|
||||
# (Captain Bob, 2026-08-28). Attached at QEMU launch time via -drive/
|
||||
# -device on the qemu-xhci controller each arch's qemu target already
|
||||
# creates; xhci_bringup()'s initial port scan (src/starkernel/usb/xhci.c)
|
||||
# is what makes an already-connected-at-launch device visible at all --
|
||||
# xhci_poll_events() alone is purely hotplug-event-driven and would never
|
||||
# see a device present before controller reset. Override to "" (empty) to
|
||||
# boot without Zuse attached, e.g. `make -f Makefile.starkernel qemu ZUSEDISK=`.
|
||||
ZUSEDISK ?= disk/zuse.img
|
||||
# Precomputed (not inlined as $(if ...,...)) because the drive/device specs
|
||||
# below are comma-heavy and GNU make's $(if) function splits its own
|
||||
# arguments on every top-level comma -- inlining it silently truncated the
|
||||
# flags to just "-drive if=none" with everything past the next comma
|
||||
# dropped, confirmed live via `ps aux` showing the truncated qemu command
|
||||
# line (2026-08-28).
|
||||
ifneq ($(strip $(ZUSEDISK)),)
|
||||
ZUSEDISK_QEMU_ARGS := -drive if=none,id=zusedrv,file=$(ZUSEDISK),format=raw -device usb-storage,bus=xhci0.0,drive=zusedrv
|
||||
else
|
||||
ZUSEDISK_QEMU_ARGS :=
|
||||
endif
|
||||
MKCAPSULE_SRC = tools/mkcapsule.c tools/pkcs8_ed25519.c \
|
||||
src/starkernel/crypto/ed25519.c \
|
||||
src/starkernel/crypto/fe25519.c \
|
||||
@@ -785,6 +806,7 @@ ifeq ($(ARCH),amd64)
|
||||
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
|
||||
-device virtio-blk-pci,drive=artdisk \
|
||||
-device qemu-xhci,id=xhci0 \
|
||||
$(ZUSEDISK_QEMU_ARGS) \
|
||||
-object rng-random,id=rng0,filename=/dev/urandom \
|
||||
-device virtio-rng-pci,rng=rng0 \
|
||||
-chardev socket,id=cserial,path=$$SERIAL_SOCK,server=on,wait=off,logfile=$$LOG \
|
||||
@@ -868,6 +890,7 @@ else ifeq ($(ARCH),aarch64)
|
||||
-device virtio-blk-pci,drive=artdisk \
|
||||
-device virtio-keyboard-pci,addr=0x3 \
|
||||
-device qemu-xhci,id=xhci0,addr=0x4 \
|
||||
$(ZUSEDISK_QEMU_ARGS) \
|
||||
-object rng-random,id=rng0,filename=/dev/urandom \
|
||||
-device virtio-rng-pci,rng=rng0,addr=0x5 \
|
||||
-device ramfb \
|
||||
@@ -957,6 +980,7 @@ else ifeq ($(ARCH),riscv64)
|
||||
-device virtio-blk-pci,drive=artdisk,addr=0x1 \
|
||||
-device virtio-keyboard-pci,addr=0x3 \
|
||||
-device qemu-xhci,id=xhci0,addr=0x4 \
|
||||
$(ZUSEDISK_QEMU_ARGS) \
|
||||
-object rng-random,id=rng0,filename=/dev/urandom \
|
||||
-device virtio-rng-pci,rng=rng0,addr=0x5 \
|
||||
-device ramfb \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-28T22:27:23Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-29T01:39:10Z -->
|
||||
<!-- 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.
Binary file not shown.
@@ -79,12 +79,28 @@ typedef enum {
|
||||
* install the cert into Hera's own VM immediately
|
||||
* (vm_zuse_cert_install()) -- the seed otherwise only
|
||||
* ever lives on the minted thumbdrive.
|
||||
* @param drive_known_blank Pass 1 when the caller has *already* just run
|
||||
* homeblocks_sig_check() on dev and confirmed
|
||||
* HOMEBLOCKS_SIG_BLANK (e.g. capsule_zuse_boot_try_
|
||||
* attach(), which must check sig_rc before it can even
|
||||
* decide to call this) -- skips this function's own
|
||||
* internal "refuse to overwrite" re-check, which
|
||||
* otherwise repeats the exact same full BOT read
|
||||
* sequence a second time for no reason (found live,
|
||||
* FABRIC-3.md §F.25/§F.26: the redundant check was
|
||||
* mistaken for a hang before the real cause -- leaked
|
||||
* `tail -f` processes from repeated hard kills during
|
||||
* the same debugging session -- was found). Pass 0 from
|
||||
* any caller (like MINT, mama_forth_words.c) that has
|
||||
* not already checked -- the safety check still applies
|
||||
* there.
|
||||
* @return MINT_OK on success, an error code otherwise.
|
||||
*/
|
||||
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);
|
||||
|
||||
#endif /* __STARKERNEL__ */
|
||||
|
||||
|
||||
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
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
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
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
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
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
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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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 &&
|
||||
|
||||
Reference in New Issue
Block a user