diff --git a/docs/fabric/FABRIC-3.md b/docs/fabric/FABRIC-3.md index df1b9a56..f00b1bad 100644 --- a/docs/fabric/FABRIC-3.md +++ b/docs/fabric/FABRIC-3.md @@ -6565,3 +6565,19 @@ and memorize frequently so we can pick up where we left off"). removed, it can never take a job if it comes back. #140's "Done when" is met by this entry and the workflows' comments once it reaches `master`. +### 20. #168: MINT-ZUSE says what happened to the stick (2026-11-12) + +- **Found** (the code review of 2026-11-11): `failed()` printed "MINT-ZUSE: failed -- nothing changed" + on every failure after YES, though `gpt_write_one()` had by then rewritten the stick's partition + table and the identity partition's first blocks had been zeroed. +- **Tested first, seen to fail:** `qemu_session.py mint_part_fail` -- as `mint_fail`, but only writes + touching the identity partition's first sector (LBA 2048) fail, so the GPT is written and the mint + fails after it. Before: "nothing changed" (`logs/20261112-122132`). +- **Built:** `gpt_write_one()` tells the two apart -- `GPT_EIO` when its first write failed (nothing + written), a new `GPT_PART` when a later write or flush failed (the disk is changed). MINT-ZUSE says + "failed -- nothing changed" only then, and "failed -- the stick was erased; Zuse not minted" (58 + characters) for every failure after the table is written. The marker is still written last: the + old Zuse stick works either way. +- **Seen to pass:** `mint_part_fail` (the new message, never "nothing changed", the old stick logs + in); `mint_fail`, every write failing, still "nothing changed"; `replace`, the success path. + diff --git a/experiments/bare_metal/runs/doe-amd64-20261112-122002.csv b/experiments/bare_metal/runs/doe-amd64-20261112-122002.csv new file mode 100644 index 00000000..e69de29b diff --git a/experiments/bare_metal/runs/doe-amd64-20261112-122132.csv b/experiments/bare_metal/runs/doe-amd64-20261112-122132.csv new file mode 100644 index 00000000..e69de29b diff --git a/experiments/bare_metal/runs/doe-amd64-20261112-122925.csv b/experiments/bare_metal/runs/doe-amd64-20261112-122925.csv new file mode 100644 index 00000000..e69de29b diff --git a/experiments/bare_metal/runs/doe-amd64-20261112-123101.csv b/experiments/bare_metal/runs/doe-amd64-20261112-123101.csv new file mode 100644 index 00000000..e69de29b diff --git a/experiments/bare_metal/runs/doe-amd64-20261112-123325.csv b/experiments/bare_metal/runs/doe-amd64-20261112-123325.csv new file mode 100644 index 00000000..e69de29b diff --git a/experiments/bare_metal/runs/doe-amd64-20261112-123459.csv b/experiments/bare_metal/runs/doe-amd64-20261112-123459.csv new file mode 100644 index 00000000..e69de29b diff --git a/experiments/bare_metal/runs/doe-amd64-20261112-123722.csv b/experiments/bare_metal/runs/doe-amd64-20261112-123722.csv new file mode 100644 index 00000000..e69de29b diff --git a/experiments/bare_metal/runs/doe-amd64-20261112-123900.csv b/experiments/bare_metal/runs/doe-amd64-20261112-123900.csv new file mode 100644 index 00000000..e69de29b diff --git a/kernel/include/starkernel/gpt.h b/kernel/include/starkernel/gpt.h index 2cb315a6..e3f849ff 100644 --- a/kernel/include/starkernel/gpt.h +++ b/kernel/include/starkernel/gpt.h @@ -24,7 +24,8 @@ typedef struct { #define GPT_OK 0 /* a valid GPT */ #define GPT_NONE 1 /* not a GPT disk -- no protective MBR -- (or one whose sectors are not 512 bytes): read it raw */ #define GPT_BAD 2 /* a protective MBR, but its header or entry array does not check out: not used */ -#define GPT_EIO 3 /* its first block could not be read: not used */ +#define GPT_EIO 3 /* its first block could not be read: not used; gpt_write_one: nothing was written */ +#define GPT_PART 4 /* gpt_write_one only: a write failed after others were made -- the disk is changed (#168) */ /* Read `disk`'s protective MBR, primary GPT header and partition-entry array, * checking both CRC-32s, the revision, the sizes and the geometry. On GPT_OK, @@ -59,7 +60,10 @@ int gpt_grow_last(blkio_dev_t *disk, const uint8_t type[16], uint64_t max_part_s * entries, the backup header and entries at the disk's end, the protective * MBR written last (the old one zeroed first, so a write torn anywhere leaves * no table). Nothing inside the partition is written. GPT_OK; GPT_BAD for a - * disk whose sectors are not 512 bytes or that is too small; GPT_EIO. */ + * disk whose sectors are not 512 bytes or that is too small (nothing written); + * GPT_EIO when its first write failed (nothing written, the disk as it was); + * GPT_PART when a later write or a flush failed (the disk is changed: its old + * table is gone). The caller says which (#168). */ int gpt_write_one(blkio_dev_t *disk, const uint8_t type[16], uint64_t part_sectors, uint8_t disk_guid[16]); /* The index of the first partition of `type`, or -1. */ diff --git a/kernel/src/gpt.c b/kernel/src/gpt.c index c019a175..83eaf4ce 100644 --- a/kernel/src/gpt.c +++ b/kernel/src/gpt.c @@ -352,14 +352,16 @@ int gpt_write_one(blkio_dev_t *disk, const uint8_t type[16], uint64_t part_secto * partition not yet written -- a used stick's old table included */ { static const uint8_t zero[GPT_SECTOR]; - if (put_sectors(disk, 0u, zero, 1u) != 0 || disk->vt->flush(disk) != BLKIO_OK) return GPT_EIO; + if (put_sectors(disk, 0u, zero, 1u) != 0) return GPT_EIO; /* nothing written yet */ + if (disk->vt->flush(disk) != BLKIO_OK) return GPT_PART; /* the MBR may be gone */ } + /* from here the disk is changed: any failure says so (#168) */ if (put_sectors(disk, alt - GPT_ENT_SECTORS, g_gpt_new_entries, GPT_ENT_SECTORS) != 0 || put_sectors(disk, alt, bak, 1u) != 0 || put_sectors(disk, 2u, g_gpt_new_entries, GPT_ENT_SECTORS) != 0 || put_sectors(disk, 1u, hdr, 1u) != 0 || disk->vt->flush(disk) != BLKIO_OK || put_sectors(disk, 0u, mbr, 1u) != 0 || disk->vt->flush(disk) != BLKIO_OK) - return GPT_EIO; + return GPT_PART; return GPT_OK; } diff --git a/kernel/src/v4/sk_v4_mint.c b/kernel/src/v4/sk_v4_mint.c index a710f8ff..ad706ec2 100644 --- a/kernel/src/v4/sk_v4_mint.c +++ b/kernel/src/v4/sk_v4_mint.c @@ -11,7 +11,9 @@ * fresh Zuse minted into it, read back -- and only then the real key written * into the genesis marker on the system stick's Artemis. Any failure before * the marker leaves the marker as it was: the old Zuse stick, if there is - * one, still works. + * one, still works. A failure says what happened to the stick (#168): "nothing + * changed" only while nothing has been written to it; once its partition table + * is written, "the stick was erased". */ #include "starkernel/v4/sk_v4_mint.h" #include "starkernel/v4/sk_v4_identity.h" @@ -65,13 +67,20 @@ static int read_answer(char *buf, int max) } } -static int failed(void) +static int failed(void) /* nothing has been written to the stick */ { console_println("MINT-ZUSE: failed -- nothing changed"); sk_v4_identity_release_offered(); return -1; } +static int erased(void) /* its old partition table is gone (#168) */ +{ + console_println("MINT-ZUSE: failed -- the stick was erased; Zuse not minted"); + sk_v4_identity_release_offered(); + return -1; +} + int sk_v4_mint_zuse(void) { static blkio_dev_t part; @@ -115,27 +124,29 @@ int sk_v4_mint_zuse(void) sk_v4_identity_release_offered(); return -1; } - if (rc != GPT_OK || gpt_read(raw, parts, 4u, &n) != GPT_OK) return failed(); + if (rc == GPT_EIO) return failed(); /* its first write failed: nothing written */ + if (rc != GPT_OK) return erased(); + if (gpt_read(raw, parts, 4u, &n) != GPT_OK) return erased(); pi = gpt_find(parts, n, gpt_type_starship_identity); - if (pi < 0 || gpt_part_open(&part, &part_state, raw, &parts[pi]) != BLKIO_OK) return failed(); + if (pi < 0 || gpt_part_open(&part, &part_state, raw, &parts[pi]) != BLKIO_OK) return erased(); /* nothing of the stick's old bytes may read as an identity */ for (i = 0; i < MINT_ZERO_FBLOCKS; i++) - if (blkio_write(&part, i, zero) != BLKIO_OK) return failed(); + if (blkio_write(&part, i, zero) != BLKIO_OK) return erased(); /* Zuse, minted, then read back from the stick */ - if (capsule_zuse_boot_genesis_mint(&part, seed, pub) != 0) return failed(); + if (capsule_zuse_boot_genesis_mint(&part, seed, pub) != 0) return erased(); sig_rc = homeblocks_sig_check(&part, HOMEBLOCKS_SIG_START_FBLOCK, &sig); if (capsule_zuse_boot_check_key(&part, sig_rc, &sig, pub, rseed, rpub) != ZUSE_DRIVE_HERS || memcmp(rseed, seed, 32) != 0) - return failed(); + return erased(); /* only now the marker: the proxy kept, the real Zuse named */ m = *sk_v4_identity_marker(); - if (!m.present) return failed(); + if (!m.present) return erased(); memcpy(m.real, pub, 32); m.has_real = 1; - if (sk_v4_marker_write(&m) != 0) return failed(); + if (sk_v4_marker_write(&m) != 0) return erased(); sk_v4_identity_set_marker(&m); memset(seed, 0, sizeof seed); memset(rseed, 0, sizeof rseed); diff --git a/logs/20261112-122002/amd64/qemu-amd64-20261112-122002.log b/logs/20261112-122002/amd64/qemu-amd64-20261112-122002.log new file mode 100644 index 00000000..7a4af015 --- /dev/null +++ b/logs/20261112-122002/amd64/qemu-amd64-20261112-122002.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:611ffed31af9960ff4864bce58e55def3d307beb6ccc786ec42f6a2d318759c8 +size 7138 diff --git a/logs/20261112-122132/amd64/qemu-amd64-20261112-122132.log b/logs/20261112-122132/amd64/qemu-amd64-20261112-122132.log new file mode 100644 index 00000000..7b05206a --- /dev/null +++ b/logs/20261112-122132/amd64/qemu-amd64-20261112-122132.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:03985c57032ed07fcdfc0d377ce600d22c6361f998de260c27f8618cc149ce6e +size 7066 diff --git a/logs/20261112-122925/amd64/qemu-amd64-20261112-122925.log b/logs/20261112-122925/amd64/qemu-amd64-20261112-122925.log new file mode 100644 index 00000000..f4d7b5c3 --- /dev/null +++ b/logs/20261112-122925/amd64/qemu-amd64-20261112-122925.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:422516e7218625015b194fa13e8ed62f200d96d98b299dd2c7468998a863993b +size 7137 diff --git a/logs/20261112-123101/amd64/qemu-amd64-20261112-123101.log b/logs/20261112-123101/amd64/qemu-amd64-20261112-123101.log new file mode 100644 index 00000000..4265dc83 --- /dev/null +++ b/logs/20261112-123101/amd64/qemu-amd64-20261112-123101.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b60256c80ee8672fa2f690c3ada03a3e6b0b5f8fb3567d2a447c5cbb1ead390 +size 7085 diff --git a/logs/20261112-123325/amd64/qemu-amd64-20261112-123325.log b/logs/20261112-123325/amd64/qemu-amd64-20261112-123325.log new file mode 100644 index 00000000..f83c7066 --- /dev/null +++ b/logs/20261112-123325/amd64/qemu-amd64-20261112-123325.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:67bfa8c095ba6f2080a2ff5650ecf91263699d35bb24111c2f7451adf4c30cc5 +size 7137 diff --git a/logs/20261112-123459/amd64/qemu-amd64-20261112-123459.log b/logs/20261112-123459/amd64/qemu-amd64-20261112-123459.log new file mode 100644 index 00000000..86383e38 --- /dev/null +++ b/logs/20261112-123459/amd64/qemu-amd64-20261112-123459.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:909acdd61678ff84e3963fa2854c9a883a8f19b2d65a4bd9301b39858be71ffc +size 7066 diff --git a/logs/20261112-123722/amd64/qemu-amd64-20261112-123722.log b/logs/20261112-123722/amd64/qemu-amd64-20261112-123722.log new file mode 100644 index 00000000..065b8e93 --- /dev/null +++ b/logs/20261112-123722/amd64/qemu-amd64-20261112-123722.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99b945003da8cb5f534dc221e06462efe1ba56e6dca3f0e928495f4f5258eca5 +size 7137 diff --git a/logs/20261112-123900/amd64/qemu-amd64-20261112-123900.log b/logs/20261112-123900/amd64/qemu-amd64-20261112-123900.log new file mode 100644 index 00000000..3d4513d3 --- /dev/null +++ b/logs/20261112-123900/amd64/qemu-amd64-20261112-123900.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10b1858fa0df202c4f0cbca9278f0fd096b2e1326e55dd319a3af4cb7aa8743d +size 7578 diff --git a/scripts/qemu_session.py b/scripts/qemu_session.py index 84804d3d..0d6d33f6 100755 --- a/scripts/qemu_session.py +++ b/scripts/qemu_session.py @@ -87,6 +87,9 @@ the stick's, plan 2026-10-26-stick-1-screen-and-keyboard.md): no ok> for 30 s; the new one added: the prompt mint_fail the fixed screen; R, a stick whose writes all fail (blkdebug): "failed -- nothing changed", the screen again; the old stick added: the prompt (Review Focus 4) + mint_part_fail as mint_fail, but only writes to the identity partition's first sector (LBA 2048) + fail: the GPT is written, then the mint fails -- "failed -- the stick was erased; Zuse + not minted", never "nothing changed" (#168); no replacement; the old stick: the prompt system_pull_real logged in on her stick; the data stick pulled: hers let go first (no detach refusal), session ended; the data stick replugged, hers still in: she is logged in bam_flush --target qemu-stick --stick-gib 16: an Artemis block changed and SAVE-BUFFERS'd three @@ -136,7 +139,7 @@ NO_ZUSE_MODES = {"gpt_zuse", "gpt_hotplug", "gpt_foreign", "gpt_bad_crc", "gpt_b "screen_dark_until_login", "screen_console_after_login", "screen_dark_after_logout", "kbd_typeahead", "stick_boot", "stick_v3", "stick_zuse", "screen_boot", "genesis", "stick_genesis", "stick_grow", "stick_small", "bam_flush", - "zuse_stick_first", "fallback", "replace", "mint_fail", "system_pull_real"} + "zuse_stick_first", "fallback", "replace", "mint_fail", "mint_part_fail", "system_pull_real"} label = a.label or f"{a.mode}-{a.arch}" work = os.path.join(ROOT, "build", "qemu-session", label) shutil.rmtree(work, ignore_errors=True) @@ -188,7 +191,7 @@ if a.stick_gib: make_args.append(f"STICK_GIB={a.stick_gib}") if a.mode in ("stick_grow", "stick_small", "bam_flush"): make_args.append("KERNEL_ARGS=--log-level=info") # the blk: disk lines the sizes are read from -if a.mode in ("zuse_stick_first", "fallback", "replace", "mint_fail", "system_pull_real"): +if a.mode in ("zuse_stick_first", "fallback", "replace", "mint_fail", "mint_part_fail", "system_pull_real"): # a data stick made once into the session's directory: minted on, then booted again dstick = os.path.join(work, "data.img") subprocess.run(["make", "-f", "kernel/Makefile", f"ARCH={a.arch}", "datastick", f"DATASTICK_IMG={dstick}"], @@ -417,12 +420,16 @@ def add_zuse(n, img=None, fail_sector0=False): drive=f"zuse{n}", id=f"zusedev{n}") -def add_stick(n, img, port, fail_writes=False): +def add_stick(n, img, port, fail_writes=False, fail_sector=None): """A USB stick added over QMP on the xHCI controller's `port` (plan 2026-10-29-stick-5, Task 4); - with fail_writes, every write to it fails (blkdebug, EIO -- Task 5's mint_fail).""" + with fail_writes, every write to it fails (blkdebug, EIO -- Task 5's mint_fail); with fail_sector, + only writes touching that sector fail (#168's mint_part_fail).""" f = {"driver": "file", "filename": img} if fail_writes: f = {"driver": "blkdebug", "image": f, "inject-error": [{"event": "none", "iotype": "write", "errno": 5}]} + elif fail_sector is not None: + f = {"driver": "blkdebug", "image": f, + "inject-error": [{"event": "none", "iotype": "write", "sector": fail_sector, "errno": 5}]} qcmd("blockdev-add", driver="raw", **{"node-name": f"stick{n}"}, file=f) qcmd("device_add", driver="usb-storage", bus="xhci0.0", port=str(port), drive=f"stick{n}", id=f"stickdev{n}") @@ -728,7 +735,7 @@ elif m == "fallback": check("no ok> on the screen", "ok> " not in text()[text().rfind("Zuse: session ended"):]) check("no fault", count(r"EXCEPTION|PANIC|panic") == 0) -elif m in ("replace", "mint_fail", "system_pull_real"): +elif m in ("replace", "mint_fail", "mint_part_fail", "system_pull_real"): SCREEN0 = r"Zuse's stick is not here\." old_img = os.path.join(work, "zuse-real.img") subprocess.run(["truncate", "-s", "600M", old_img], check=True) @@ -743,7 +750,7 @@ elif m in ("replace", "mint_fail", "system_pull_real"): print("-- the second boot: the data stick alone", flush=True) boot([x for x in make_args if x != "clean"]) check("POST: PASSED (second boot)", wait_for(r"POST: PASSED", 900)) - if m in ("replace", "mint_fail"): + if m in ("replace", "mint_fail", "mint_part_fail"): check("the fixed screen", wait_for(SCREEN0, 120), near=r"Artemis: |Zuse") time.sleep(3) p0 = count(r"MINT-ZUSE: plug in the stick for the real Zuse") @@ -776,6 +783,25 @@ elif m in ("replace", "mint_fail", "system_pull_real"): add_stick(4, new_img, 3) check("the new one again: identity confirmed", wait_for(r"Zuse: identity confirmed from attached thumbdrive", 120, c)) check("the prompt", wait_for(PROMPT, 60)) + elif m == "mint_part_fail": + # #168: the GPT is written (sectors 0-33 and the disk's end), the identity partition's + # first sector is not -- the stick has been changed when the mint fails + shots = count(SCREEN0) + add_stick(2, new_img, 3, fail_sector=2048) + wait_for(r"MINT-ZUSE: type YES to write it", 120) + type_line("YES") + check("MINT-ZUSE: failed -- the stick was erased; Zuse not minted", + wait_for(r"MINT-ZUSE: failed -- the stick was erased; Zuse not minted", 300), near=r"MINT-ZUSE") + check("never \"nothing changed\" for a stick that was written", count(r"failed -- nothing changed") == 0) + check("no replacement minted (none in this boot's log)", count(r"real Zuse minted") == 0) + check("the fixed screen again", wait_for(SCREEN0, 60, shots)) + qcmd("device_del", id="stickdev2") + time.sleep(3) + qcmd("blockdev-del", **{"node-name": "stick2"}) + c = count(r"Zuse: identity confirmed from attached thumbdrive") + add_stick(3, old_img, 3) + check("the old stick still logs in", wait_for(r"Zuse: identity confirmed from attached thumbdrive", 120, c)) + check("the prompt", wait_for(PROMPT, 60)) else: shots = count(SCREEN0) add_stick(2, new_img, 3, fail_writes=True)