89d557c6785e6e4b25e1e428ab28ec02d3ce0c75
412
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
89d557c678 |
Capture full Tripod/Zuse identity vision (§D), supersede EXPIRE-as-TTL plan
No code changes -- pure design capture, per direct request ("capture
EVERYTHING first then we'll build a plan").
Core correction: a session IS a VM (Zuse is a player in the Stadium
fabric like any other, not a special boolean flag on Hera). This
supersedes an in-progress plan this session to implement EXPIRE as a
Stadium-TTL sweep on the zuse_session boolean -- caught mid-research,
before any code was written. Session end = VM detach, much closer to
the existing COOL/capsule_vm_kill() path than a new TTL mechanism.
Captured the full attach/mint flow: idle-loop thumbdrive watch,
cert-only auth (no password/username, no central user directory),
runtime capsule construction from drive content, ACL-based console/VM
binding (resolves Milestone 5's "reuse ACL-PIN vs. new primitive"
question in favor of reuse), ongoing MINT, and deferred detach/flush-
back. Standing completion criterion: nothing here is done until
hardwired calls are replaced by real Hermes messages.
Sorted the resulting gaps into answered (chain-of-trust mechanism,
ACL-reuse decision, no-central-directory) vs. genuinely deferred
(runtime capsule construction, exact ACL comparison semantics,
message-bus migration scope, polymorphic block-boundary behavior,
SSD-store scope, session state round-tripping, and the underlying
WRITE(10) hard blocker). Also flagged a small unrelated bug: the
prompt should read (Zuse)ok>, not zuse)ok>.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd
|
||
|
|
030a3e6dc0 |
Milestone 6: hard-refuse flip, scoped to INVALID only -- closes Milestone 6
Before flipping, found CAPSULE_SIG_MISSING (no signature at all) is the normal state everywhere except this machine -- CI and any other checkout have no access to the offline key, by design. Refusing on MISSING the same as INVALID would brick boot everywhere but here. Decided (on request): enforce ONLY on CAPSULE_SIG_INVALID (a signature that IS present but doesn't verify -- unambiguous tampering/corruption evidence). MISSING/NO_ROOT_KEY stay WARN-only permanently. All three capsule_birth.c call sites now return CAPSULE_RUN_ERR_INVALID on CAPSULE_SIG_INVALID, after logging the same WARN as before. Verified on all three architectures, both directions, per the original rollout commitment: positive case (real signed capsules) reboots clean with zero warnings on amd64/aarch64/riscv64. Negative case (same one-byte signature corruption used for the WARN-only proof, on Mama's own init.4th) now genuinely refuses identically on all three: "capsule sig: init.4th: INVALID" then "Init: Mama birth FAILED". The feared "no ok> at all" blast radius didn't materialize -- kernel_main.c already had graceful error handling for a failed Mama birth (log and continue, pre-existing code); the kernel reaches a degraded ok> rather than crashing, on all three architectures. Final acceptance pass (real signed capsules, tampering reverted) clean on all three. Milestone 6 is now fully closed except magic-number content-type detection (shared with Milestone 4, separate scope, not started). Documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd |
||
|
|
c640f99211 |
Milestone 6: BLOCK_MAP.md signature-status column
--manifest mode's file scan is a completely separate code path from build mode (only ever walks .4th files, never the embedded PKI cert or font capsule) -- extended it to accept the same optional --sign-key <path> prefix build mode already has, factoring the key-loading code into a shared load_sign_key(), so the manifest can report real per-capsule signing status without touching or requiring a rebuild of capsule_generated.c. New "Signed" column on the capsule summary table: yes/no when --sign-key was given, n/a (with an explanatory footnote) when it wasn't -- never a bare blank that could be misread as "unsigned". Makefile.starkernel's manifest-generation call site now passes the same SIGN_KEY_ARGS the real build uses, so capsules/BLOCK_MAP.md reflects this machine's actual signed state by default. Verified: clean compile, BLOCK_MAP.md correctly shows "yes" for all 31 tracked capsules on a real signed build; a quick amd64 boot (no kernel code touched, host tooling only) confirmed no regression. This closes every open Milestone 6 item except magic-number content-type detection (shared with Milestone 4, not started) and the hard-refuse flip (deliberately deferred). Documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd |
||
|
|
2fc55f47e1 |
Milestone 6: mkcapsule signing + capsule_birth.c wiring, WARN-only
First attempt shelled out to `openssl pkeyutl -sign` (fork/execlp, not system() -- avoided shell string interpolation of the key path). Corrected on request: no new external host binary dependency when the repo's own code can do the job -- same standing preference as the earlier anti-file correction. Rewritten to link ed25519_sign() (already verified against OpenSSL in Phase B) directly into mkcapsule. New tools/pkcs8_ed25519.c: a narrow DER walker (same shape as x509_ed25519.c, deliberately not shared -- small enough that duplicating a few TLV-walking lines beat threading a header between the kernel crypto tree and host tooling) extracting the raw seed from the intermediate's PKCS#8 private key, plus a minimal self-written base64 decoder (PEM is openssl genpkey's default output; no decoder existed anywhere in the repo). Verified end-to-end before wiring anything in: the extracted seed's derived pubkey matches the cert's exactly, and a full self-contained sign+verify round-trip (zero openssl) passes. CapsuleDesc had no spare bytes, so signatures live in a new parallel CapsuleSigEntry array, emitted by a new `mkcapsule --sign-key <path>` flag (omitted/missing key -> has_sig=0 everywhere, graceful, not a build failure -- CI has no access to the offline key). New capsule_sig.c/.h: capsule_verify_signature(), a separate function, not folded into the already-tested capsule_validate(). Finds and caches the embedded intermediate cert's pubkey once per boot, then verifies against it. Wired into all three capsule_validate() call sites in capsule_birth.c via log_message(LOG_WARN, ...) -- never refuses yet, per the earlier staged-rollout decision. Verified independently, both directions, live in the real kernel: a full clean build (38 signed capsules) boots clean on all three architectures with zero warnings. Separately, hand-corrupted one byte of Mama's own init.4th capsule's stored signature (not its payload/hash, which capsule_validate() already catches and would have masked the test) and rebuilt just the changed object: produced exactly "capsule sig: init.4th: INVALID -- signature does not verify" on boot, and the kernel still reached ok> -- proving warn-only doesn't refuse anything yet. Reverted before the final, untampered 3-arch acceptance pass. Still open: flipping WARN to hard-refuse (separate, deliberate step) and the BLOCK_MAP.md signature-status column. Documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd |
||
|
|
431bcb1f34 |
Milestone 6: root CA generated, snakeoil intermediate embedded, DER parser
Root CA + snakeoil intermediate generated entirely offline (/home/rajames/CLionProjects/lithosananke-ca/, outside this repo, private keys chmod 600) per this milestone's own requirement: Ed25519, root self-signed 20-year validity, intermediate real-CA-signed (CA:TRUE, pathlen:0), chain verified via openssl. Snakeoil intermediate embedded as a capsule (capsules/pki/ snakeoil-intermediate.der) -- confirmed the font-capsule precedent needed zero new infrastructure, any non-.4th file under capsules/ embeds verbatim already. New x509_ed25519.c: a from-scratch, narrow DER walker (not general ASN.1/X.509, per this milestone's design decision) extracting the raw Ed25519 pubkey from a cert's SubjectPublicKeyInfo -- handles the optional v3 version field, verifies the AlgorithmIdentifier OID is Ed25519 rather than assuming, handles both DER length forms. Verified against ground truth: the extracted key from the real embedded cert matches openssl's own reported pubkey byte-for-byte; refusal path checked against truncated/garbage/empty/wrong-algorithm (real RSA cert) input. Compiles clean on all three architectures. Still open: mkcapsule signing step, wiring ed25519_verify() into capsule_birth.c's three validate call sites (landing warn-only first, per decision -- a bug here could stop every capsule from birthing, including Mama's own, on all three arches), and the BLOCK_MAP.md signature-status column. Documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd |
||
|
|
a8692681a8 |
Phase 8 C (5/n): Zuse's cert wired to the fence -- first-boot mint works
Replaces the crashed NVRAM approach entirely. New include/starkernel/zuse_cert_devblock.h: a standalone on-disk record (magic + version + 32-byte seed + 32-byte pubkey + a real CRC-64/ISO from day one, same discipline homeblocks_sig_t established) occupying devblock_from_top=0 of the fence. Its own header, not inlined at the boot call site, since the still-open MINT word will be a second consumer of this exact format. kernel_main.c's mint-or-load logic now reads the fence, installs an existing valid cert, or mints fresh via virtio_rng+ed25519_keygen and writes it. Runs right after virtio_rng_init(), before capsule_birth_mama() -- unlike the crashed NVRAM attempt, raw block I/O against Artemis's already-proven device has no boot-timing risk, so the earlier "re-invoke ACL-ZUSE-BOOT after Mama birth" workaround is gone; ACL.4th's self-activating ACL-ZUSE-BOOT sees a correct cert on its one ordinary pass. Verified independently across every real scenario, never trusting the kernel's own report: fresh mint decodes correctly on disk with a CRC confirmed by a from-scratch Python re-implementation of the algorithm; a reboot without reformatting loads back byte-for-byte identical seed/pubkey (genuinely "mint once, ever"); a pre-fence volume refuses cleanly (no crash, no silent data loss, honest "not persistent" reporting); the real, untouched disk/artemis.img exercises the same graceful-refusal path identically on all three architectures. Phase 8's core arc is now functionally complete: real entropy -> real signing -> real anti-file block-native persistence -> a first-boot mint that survives reboots. Still open: the ongoing MINT word for minting additional regular users. Documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd |
||
|
|
6e9c1d3bc2 |
Phase 8 C (4/n): metadata fence allocator integration + zone I/O
Corrected meta_fence_blocks units from "Forth 1 KiB blocks" to 4 KiB devblocks (matching bam_devblocks/reloc_devblocks) before anything depended on the original meaning -- a clean fix, not a migration. This let the fence fold directly into compute_totals_from_B()'s existing payload4k formula (total_devblocks - 1 - B - R - F) instead of a separate user_blocks subtraction: total_blocks/user_blocks/free_blocks all shrink correctly for free, in both the fresh-format and reload paths, from one formula change. New blk_meta_zone_read()/blk_meta_zone_write() -- raw, unpacked 4 KiB devblock I/O, same shape as the header/BAM/reloc-table regions, addressed by devblock_from_top counting down from the device's last physical devblock. Refuses rather than clamps if the index exceeds the on-disk meta_fence_blocks. C-only, no FORTH word wraps either -- same discipline as vm_zuse_cert_install(), which will be this zone's first real tenant. Verified independently at every step, never trusting the kernel's own report: capacity math cross-checked against a from-scratch Python recomputation of the same formula (exact match); accessor correctness via a temporary probe (written/run/captured/reverted) that wrote a known pattern and read it back, then independently confirmed via a raw read of the disk image at the exact expected physical byte offset. Full 3-arch acceptance boot against the real, untouched disk/artemis.img, probe code fully reverted -- clean, conservation intact. Still open: wiring vm_zuse_cert_install() to actually persist through these accessors, and the MINT word itself. Documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd |
||
|
|
2035ebeac0 |
Phase 8 C (3/n): top-of-device metadata fence, step 1 (field round-trip)
Corrected substrate: this OS is anti-POSIX, anti-file by design -- the prior "dedicated system-identity disk" framing was wrong vocabulary, caught before any code was written (saved as feedback_no_files_anti_posix.md). The real primitives are content-addressed capsules and raw LBN blocks, never a filesystem. Design (agreed on request): a growable metadata fence at the TOP of a device's block space, mirroring block_subsystem.c's existing bottom BAM reservation from the opposite end -- the two grow toward each other, never colliding, same shape as a stack/heap. Starts at BLK_META_FENCE_INIT (128 blocks), explicitly never RAM-backed. Reuses Artemis's already-attached, already-proven virtio-blk device -- no new device. Rejected reusing BAM's own reserved zone directly: those blocks are fully claimed by BAM bookkeeping, not free space. Step 1 only: new meta_fence_blocks field in blk_volume_meta_t, appended after reloc_devblocks and carved from _pad[] -- identical graceful- default technique reloc_devblocks already established (a pre-existing volume reads it back as 0, not a format break). Added a compile-time _Static_assert on the struct's total size, same discipline homeblocks_sig.h uses -- caught a real bug immediately: the hand-summed _pad[] formula was off by 4 bytes (a compiler alignment gap the manual count missed), found via offsetof() rather than re-deriving by hand. Worked against disposable clones throughout, never the real disk/artemis.img (ARTDISK is ?=-overridable) -- artemis-metafence-fresh.img (blank, fresh-format path) and artemis-metafence-test.img (copy of the pre-existing artemis.img, graceful-default-on-reload path), kept as regression fixtures matching disk/README.md's existing convention. Verified independently via direct byte reads of the disk image, not the kernel's own log output (log_message(LOG_INFO,...) doesn't reach serial in this build -- unrelated pre-existing gap): fresh format writes 128 at header offset 184, a reboot without reformatting preserves it, the old pre-fence image reads back 0. Full 3-arch acceptance boot against the real, untouched disk/artemis.img also clean. Allocator (user_blocks math) and zone read/write accessors both still open -- next steps, documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd |
||
|
|
e5cbc71f46 |
Phase 8 C (2/n): expand cert storage; NVRAM persistence crashed, reverted
Cert storage expanded from the old 16-byte placeholder to a real 32-byte seed + 32-byte pubkey. vm_zuse_cert_install() now has a kernel-side duplicate in src/starkernel/vm/vm_core.c -- the kernel build's VM_EXCLUDE list drops src/vm.c entirely (same reason vm_set_base() already has two independent copies), so the hosted-only version added earlier this session was never actually linked into the kernel. FORTH-side ZUSE-CERT-LO@/HI@ replaced with ZUSE-PUBKEY@ (i -- u) over the public half only; ACL-ZUSE-BOOT now checks ZUSE-CERT-INSTALLED? before authenticating instead of unconditionally. Attempted NVRAM-based persistence (GetVariable/SetVariable) for the first-boot mint flow: page-faulted inside OVMF's variable service (CR2 in the flash MMIO window). Moving the call site to match the one proven-safe existing SetVariable call site in this codebase produced the identical crash -- not a timing issue. Localized with debug markers (one boot): GetVariable works; SetVariable with real data never returns. The existing "working" precedent call is actually a delete-of-nonexistent-variable (size=0, data=NULL), a cheaper path that never touches flash, so it proved nothing about real writes. Root cause: this kernel's VMM never maps the region OVMF's variable service needs for real flash writes -- a genuine gap in UEFI runtime- services support, not Zuse-specific, and not obviously fixable in a 3-arch-uniform way (flash window location is firmware/arch-specific). Independently, storing the raw seed in RUNTIME_ACCESS NVRAM would have been a real security defect regardless of the crash -- readable by any later-loaded UEFI app or the booted OS. Reverted to a known-safe state: all NVRAM/mint code removed from kernel_main.c, init.4th's ACL.4th line back to its documented commented-out default. Verified clean compile and clean boot on all three architectures. Cert storage expansion (the part that works) stays. A dedicated system-identity disk (virtio-blk, already proven for writes via Artemis) is the recommended next substrate -- not yet decided or built. Full investigation documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd |
||
|
|
f223a31cec |
Phase 8 C (1/n): QEMU harness now preserves NVRAM across runs
Scoping Phase C (the MINT word) surfaced a real blocker: "mint one Zuse, ever" needs the cert to survive reboots, but the qemu target copied a fresh, pristine OVMF_VARS.fd on every invocation -- a UEFI-NVRAM-based cert would never persist under this project's own normal test workflow. Digging further, aarch64 had no persistent NVRAM store at all (single -bios arg, no split VARS pflash like amd64/riscv64). Fixed rather than switching storage substrates: amd64/riscv64 now only copy the VARS template if the destination doesn't already exist, so `clean` (which deletes the whole build tree) is the bleach step and a bare `make qemu` preserves NVRAM -- matching the existing "always clean before qemu" acceptance convention exactly. aarch64 restructured to split CODE(ro)/VARS(rw) pflash drives matching the other two, with a graceful fallback to the old -bios mode on hosts without split firmware. Also resolves two design questions before any cert code: Zuse doesn't need Milestone 6's CA (that's the capsule-signing chain, a separate trust domain -- Zuse is a self-sovereign instance-local root of trust), and flags that this session's own earlier vm_zuse_cert_install() storage (16 bytes) is too small for a real Ed25519 keypair. Verified: all three architectures boot clean to ok> with the new pflash arrangement, Stadium conservation intact, no panics or guest errors. Infrastructure-only -- no cert code yet. Documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd |
||
|
|
53e6c5709f |
Phase 8 B: real Ed25519 keygen/signing, verified against OpenSSL
Extends the previously verify-only ed25519.c with ed25519_keygen() and ed25519_sign() per RFC 8032 5.1.5/5.1.6, reusing every point-arithmetic primitive verify already had -- only seed expansion/clamping and per-message nonce derivation are new. Signing is deterministic; only keygen ever touches entropy, via a caller-supplied seed (virtio_rng, Phase A) -- keygen still generates nothing itself. New scalar_muladd() (scalar25519.c) for signing's S = (k*a + r) mod L, the one scalar op verify never needed. Schoolbook multiply into a u128 wide accumulator with one final carry pass -- deliberately the same shape as fe25519.c's existing multiply, which has a documented history of a real bug from carrying mid-accumulation instead of in one pass. Verified against an independent implementation, not self-consistency: a throwaway host harness against Python's cryptography library (OpenSSL- backed) across 6 trials (5 random seed/message pairs + the empty-message case) produced byte-for-byte identical pubkeys and signatures every time. Clean compile on all three architectures and a full 3-arch QEMU acceptance boot, conservation intact, no panics or guest errors. Nothing calls the new functions from the live kernel path yet -- that's Phase C (the MINT word itself), still open, documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd |
||
|
|
309e792f07 |
Phase 8 A: virtio-rng entropy source for real Ed25519 signing
The kernel's ed25519_verify() is deliberately verify-only -- no signing, no keygen, no entropy source. That conflicts with the on-device MINT word vision (Zuse signing new user certs live at runtime), so this reopens that constraint on request rather than reshaping MINT around verify-only. vm_uuid.h already found the real gap: amd64 has RDRAND, riscv64 has Zkr, but QEMU's aarch64 CPU models have neither -- confirmed against QEMU 10.2.1. A deterministic PRNG (fine for VM UUIDs) is not safe for key generation, so this adds a virtio-rng device instead of a per-arch split: real host entropy, identical guest-side protocol on all three arches. New src/starkernel/virtio/virtio_rng.c + include/starkernel/virtio_rng.h, transport plumbing mirroring the existing virtio_blk.c driver exactly. Wired into kernel_main.c boot, -device virtio-rng-pci added to all three QEMU targets. Verified live (temp probe, written/run/captured/reverted): 16 real bytes pulled through the full request/notify/poll round trip on all three arches, three different values confirming real entropy. Final boot against the reverted, permanent code: clean compile, clean boot to ok> on amd64/aarch64/riscv64, Stadium conservation intact, no panics or guest errors. Ed25519 keygen/signing itself (Phase B) and the MINT word design (Phase C) remain open, documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd |
||
|
|
6f5605d479 |
Phase 8: zuse cert storage moved out of the dictionary (fuse-blow install)
Found that a pinned CONSTANT is not actually tamper-proof: ACL-PIN only blocks redefinition, not a >BODY-then-store on the word's existing data field. Moves the Zuse cert value into C-only VM struct fields (zuse_cert_lo/hi + zuse_cert_installed fuse bit) with a one-time vm_zuse_cert_install() and read-only ZUSE-CERT-LO@/HI@/INSTALLED? FORTH accessors, closing the tamper path structurally instead of by convention. Deletes the now-insecure ZUSE-CERT-LO/HI CONSTANT words from zuse.4th. vm_zuse_cert_install() has no caller yet -- the real mint flow (Milestone 6 CA, the MINT word) is still open; this is storage + accessors only, not a stand-in mint. Documented in FABRIC-3.md. Verified: hosted build clean, mkcapsule --lint clean (31/31), clean boot to ok> on amd64/aarch64/riscv64 with Stadium conservation intact and no panics. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd |
||
|
|
4dd1321ea4 |
Regenerate capsule manifest and artemis test image
Routine build/test artifacts left over from Phase 8 work session. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BrMTq3cJkrr8REFRkMRhLZ |
||
|
|
8b2fb448c7 |
Clarify: home-blocks thumbdrives are not bound to any particular size
Captain Bob's correction. 64MB (disk/zuse.img, usb-thumbdrive-test.img) was always just an arbitrary QEMU-test convenience size, never a real-world constraint -- but the docs and script didn't say so explicitly, and an earlier memory's "16GB reference size" phrasing (already hedged as tentative) risked reading as a decided target. Audited the actual format for hardcoded size assumptions: none found. homeblocks_sig_t already carries its own metadata_devblocks field, recording whatever size a real drive's partition actually is. Changes: scripts/bleach_zuse_img.sh gains a --size-mb override (tested both the override and the unchanged default); disk/README.md's zuse.img entry and FABRIC-3.md both now state the point explicitly; the memory file and its MEMORY.md index line corrected to match (the GPT layout's design point is the *proportions* -- small metadata partition, everything else block storage -- not any absolute size). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
35f84d4a45 |
Add disk/zuse.img + bleach_zuse_img.sh: first-boot mint testing (Phase 8)
Resolves the acl_pinned punch-list item's open question: credential data (ZUSE-CERT-LO/HI, ACL-CA-KEY-LO/HI) are CONSTANT words, i.e. real DictEntrys, and acl_pinned's enforcement (vm_create_word()'s unconditional shadow-refusal for any pinned name) already covers this generally -- no new flag needed, zuse.4th's ACL-ZUSE-BOOT already pins both cert constants today. That investigation surfaced a real gap: ACL-ZUSE-BOOT pins the cert constants unconditionally on every boot, before any legitimate mint could ever run, permanently locking in the 0 placeholder on the very first boot. This directly shaped Captain Bob's next design pass: a dedicated zuse.img test thumbdrive, "bleachable" back to pristine state for repeated first-boot testing; a one-time mint-then-pin flow (fixing the gap above); a separate ongoing S" name" MINT word for minting additional regular users; and an explicitly-deferred Zuse recovery path question. This commit is the first piece: disk/zuse.img (64MB blank, matching the existing USB-fixture convention) + scripts/bleach_zuse_img.sh (idempotent reset). Verified live via QMP hotplug -- reads back as HOMEBLOCKS_SIG_BLANK, correctly simulating a genuine first boot. Deliberately flat/raw, not GPT-partitioned, matching homeblocks_sig_check()'s current sig_start_fblock=0 assumption; both move to a real GPT-relative offset together once a parser exists. No kernel code touched -- host-side test tooling only, no 3-arch acceptance boot needed. Still open: the mint-then-pin boot fix, the MINT word, Zuse recovery. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
28c1b12c7f |
Wire homeblocks_sig_check() into USB attach: the warn half (Phase 8)
Wired into sk_repl_idle()'s USB hotplug attach handler, right between blkio_usb_open_msc() succeeding and blk_subsys_attach_device() -- logs a distinct message per outcome (recognized / blank-or-foreign / bad-version / bad-crc / read-error). The "refuse" half is deliberately not implemented -- there is nothing real to gate yet. blkio_usb.c has no SCSI WRITE(10) support at all, so there is no write path today to refuse; attach currently only enables read-only access, which is also the general-purpose USB block I/O path this repo already relies on for unrelated testing, not exclusively a home-blocks identity workflow. Refusing attach on blank media would break that legitimate use without protecting anything real -- same "don't build ahead of a real caller" reasoning EXPIRE's deferral used. Refuse belongs on the write path once WRITE(10) exists. sig_start_fblock is hardcoded to 0 at the call site -- correct for today's unpartitioned raw test/real media (no GPT parser exists yet), flagged in the comment as the one place that changes once a real GPT-partition-relative lookup exists, isolated from homeblocks_sig.c's own location-agnostic check logic. Verified live: hot-attached disk/usb-thumbdrive-test.img (blank media) through a running amd64 instance's QMP socket (blockdev-add + device_add usb-storage) -- captured exactly 4 real TUR+READ10 BOT cycles (matching the header's 4 forth-block span) followed by the correct "not recognized" warning, then normal attach completing successfully afterward (no regression). Conservation intact, no panic. Clean zero-warning compile and clean boot on all three architectures. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
2c45744995 |
Implement homeblocks_sig_check(): the drive signature check (Phase 8)
Real, complete verification logic -- not yet wired to any write path. homeblocks_sig_check(dev, sig_start_fblock, out_sig) reads the 4 consecutive 1KB blkio forth-blocks the 4KB header spans, verifies magic -> version -> CRC-64 in order, returns HOMEBLOCKS_SIG_OK/_BLANK/ _BAD_VERSION/_BAD_CRC/_READ_ERROR. Reuses block_subsystem.c's existing CRC-64/ISO (compute_crc64, previously static/file-local, now exposed via block_subsystem.h) rather than a second CRC implementation -- same algorithm already proven via per-block checksums. Takes the header's starting block as a plain parameter rather than resolving it internally: verifies a signature given a location, finding that location (GPT-partition-relative) stays the caller's job. Verified against the actual shipped code, not a reimplementation: a standalone host test links the real homeblocks_sig.c against a fake in-memory blkio_dev and exercises all four outcomes -- blank media, a correctly-minted header (round-trips drive_uuid/minted_time_ns), a flipped CRC, an unrecognized version. All four pass. A full QEMU-hotplug live test isn't proportionate yet since nothing calls this function from the live kernel path -- wiring it into the attach path is the next punch-list item. Clean zero-warning compile and clean boot on all three architectures confirms no build/link regression from exposing compute_crc64 and adding the new source file to every kernel build. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
10b96870c5 |
Add homeblocks_sig.h: drive signature header struct (Phase 8 kickoff)
Implements the design from the previous commit as-is: homeblocks_sig_t (4096 bytes, magic+version+drive_uuid+timestamp+cert/blockmap offset reservations+real hdr_crc), HOMEBLOCKS_SIG_PACK/_GET_MAGIC/_GET_VERSION macros mirroring CAPSULE_MAGIC_PACK's bit layout, and a C99 compile-time size assertion matching stadium.h's own discipline. Verified standalone (sizeof == 4096, clean under -std=c99 -Wall -Wextra -Werror) -- nothing consumes this header yet, so no functional kernel change and no 3-arch acceptance boot needed for this step. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
7cbf92ac8f |
FABRIC-3.md: design the home-blocks drive signature format (Phase 8 kickoff)
Mirrors CAPSULE_MAGIC_PACK's bit-packed magic and blk_volume_meta_t's magic+version+fields+pad-to-4096 structural convention exactly, per the punch-list item's own direction. Lives at the first 4KiB devblock of the ~1GB GPT metadata partition -- format doesn't depend on the still-missing GPT parser. Deliberately narrow: identifies/authenticates the drive only, does not invent the block-map or credential/cert formats -- reserves offset/ size pointers to where they'll live instead of embedding them, since neither format is designed yet (block-map is Milestone 3, cert format needs Milestone 6's still-ungenerated real CA). hdr_crc is real from day one, unlike blk_volume_meta_t's unused placeholder, since this header's whole job is gating the warn-and-refuse security check. Design only, not yet implemented as a header file -- presented for confirmation first. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
d95258ec65 |
.claude/CLAUDE.md: correct the stale ACL-RWT overhead figure
The June 2026 campaign's +0.0054%-0.0088% number was cited as current, but it measured the wrong thing under the wrong name -- the Rolling-Window-of-Truth mechanism it was named after was dead code, never reachable, and the measurement itself predates real compiler optimization. Kept the original entry for historical record, added a correction pointing to the actual current figure: +0.0603% ACL-TTL enforcement overhead (FABRIC-2.md SS T), the same closure just applied to FABRIC-3.md's stale re-measurement punch-list item. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
aa6efe4632 |
FABRIC-3.md: close stale ACL-RWT re-measurement item -- already done in §T
Corrects bookkeeping, not measurement. FABRIC-2.md Sections M-T (2026-08-19/21) already re-ran this and found the original "ACL-RWT" name was itself wrong -- the Rolling-Window-of-Truth mechanism it named was dead code removed 2026-07-08, never reachable. What the campaign actually measured was the live ACL-TTL mechanism. Final accepted result: +0.0603% ACL-TTL enforcement overhead, architecture-independent, CV=0.000% (FABRIC-2.md SS T, project_acl_ttl_overhead_final.md). The checkbox was simply never marked done when that work concluded -- no new campaign run. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
cbe7b49a59 |
Documentation debt sweep: 5 of 6 items resolved, 1 confirmed accurate
- docs/lithosananke/ROADMAP.md + M7.1.md: fixed stale "Branch: lithosananke" (no such branch post-split), M7.1's "Design Complete" status (shipped and live, redirected to FABRIC*.md), the M8/success-criteria self-contradiction (OBSOLETE marking vs. unqualified live criterion), and the stale AHCI/SATA claim for M9 (real implementation is virtio_blk.c) -- also corrected BLOCK/BUFFER/UPDATE/FLUSH and block device abstraction to [x] since both are confirmed live in src/word_source/block_words.c and block_subsystem.c. - Top-level ROADMAP.md: marked OBSOLETE (Captain Bob's call -- more than "stale," the architecture/branch topology/terminology it describes no longer exist), pointing to docs/lithosananke/ROADMAP.md and FABRIC*.md for current status. - docs/03-architecture/word-acl/DESIGN.md: fixed the ACL Phase 7 contradiction -- Phase 7 (LithosAnanke kernel parity) is independently verified complete per .claude/CLAUDE.md, not "remaining"; removed the stale lithosananke-branch-parity framing. - VM-FLEET-ATTRACTOR-DESIGN-20260705.md's doe-campaign.4th "broken" claim: investigated, ran SMOKE-CAMPAIGN live (completes clean, fleet heat conserved) -- initially read as contradicting the claim, corrected directly by Captain Bob: a clean execution trace doesn't disprove the doc's actual argument (no real controlled-experimental-factor mechanism). Confirmed accurate, left untouched. - Isabelle/HOL pipeline-metrics model/C-struct mismatch: confirmed a real proof-modeling gap (pm_last_accuracy_num/den has no analogue in the real PipelineGlobalMetrics struct), not stale prose -- tracked here rather than fixed, matching the .thy file's own scope boundary and this project's standing caution that each Isabelle gap needs its own subsystem model. ACL-RWT DoE overhead re-measurement (the 6th item) intentionally not started -- a full multi-architecture DoE campaign, not a doc-text fix, holding for explicit confirmation given the scale. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
6341b3b3dd |
FABRIC-3.md: decide EXPIRE's design questions -- defer to Phase 8
Decided directly on request, no code written. "ACLs -> EXPIRE" isn't wrong in spirit but was aimed at the wrong unit: acl_ttl-hits-zero always renews (never revokes), so it can't carry EXPIRE's residency-ending meaning. The unit that actually has a real lifetime and should be revoked is a zuse superuser session (Phase 8, not yet built) -- authenticate, hold elevated privilege for a bounded time, then actually drop back to non-zuse. Resolves to: admit the session (not a per-word ACL entry, which would be redundant with item 4.1's existing word patrons) as the Stadium patron, once Phase 8 exists. Not in scope now -- there is no session to admit regardless of any other choice, and building Stadium's missing generic ttl-decrement/reap mechanism ahead of its only real consumer would be speculative infrastructure, the same shape of premature build the no-stubs standard exists to prevent, just inverted. EXPIRE stays explicitly deferred until Phase 8 lands, with a concrete trigger for revisiting it -- not abandoned, not left ambiguous. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
329677b077 |
Fix COOL's misleading (stub) label too
Same fix as DELIVER, on request. COOL is real and live for both words (item 4.1) and VMs (this session) -- stadium_evict()'s own universal reservoir credit is the whole of what "cooling off the floor" means for both, no extra payload action needed. stadium_dispatch()'s COOL case now prints the departing patron's identity (word_id for a word, 0 -- the patron-zero convention -- for a VM) instead of "(stub)". Verified live: both shapes fired correctly on the same boot -- "COOL identity=0" at Hermes's/Artemis's own explicit channel-eviction self-test and again at their VM-patron eviction at PARITY:KILL, "COOL identity=1" at a second channel eviction -- conservation intact throughout. Clean zero-warning compile and clean boot with conservation intact on all three architectures. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
89723793fe |
FABRIC-3.md: full EXPIRE scoping -- two findings, four open design questions
Investigated on request. acl_ttl (DictEntry, per-word ACL-RECHECK amortization) and StadiumPatronHeader.ttl (residency countdown) are different mechanisms wearing the same name -- ACL-RECHECK always renews (allow=1, fresh ttl), never revokes, so it doesn't resemble a reap event at all. Separately, StadiumPatronHeader.ttl is completely inert across the whole codebase: every candidate constructor sets it to 0, nothing ever reads or decrements it -- the generic TTL-expiry mechanism EXPIRE would fire from doesn't exist in Stadium's own engine yet, a gap one level deeper than "ACL isn't wired to Stadium." Recorded four open questions that need a real decision before any code: whether "ACL patron" is even the right model, what unit would be admitted (redundant per-word vs. a session-scoped patron once Phase 8 PKI/zuse work lands), whether building Stadium's missing generic ttl-reap mechanism is in scope here, and what the reap action should actually do given ACL policy never revokes today. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
c755c48763 |
ONTOLOGY.md: fix stale "words not yet migrated" note
Words have been fully migrated and live via stadium_word_dispatch() since item 4.1 -- this line just never got updated. Small, independent doc fix flagged in FABRIC-3.md's punch list, addressed on request. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
ad9b9d0c7b |
Fix DELIVER's misleading (stub) label -- it was never actually a gap
Re-scoped on request. capsules/hermes/init.4th's MSG-ALLOC already
admits every message onto Stadium with SB-DELIVER, and MSG-FREE-NODE
(called from both MSG-ACK-LAST and heat-driven MSG-REAP) already
evicts it -- "behaviour=DELIVER (stub)" has been printing on boot logs
since at least 2026-08-05. The earlier "zero consumer, needs
substantial Hermes lifecycle mapping work" framing was wrong,
carried over unverified from FABRIC.md's old "open, not resolved" note
about which Hermes event maps to DELIVER vs. EXPIRE -- item 4.2
already answered that in code without the prose catching up. Same
documentation-drift class as the stale ONTOLOGY.md words note and the
earlier glibc misattribution.
Checked whether the dispatch body needed a real payload action the way
MIGRATE did: MSG-DELIVER (the FORTH word) already runs the actual
delivery (VM-EXEC of the payload) before eviction, decoupled from
Stadium reap -- so by dispatch time delivery is already done, same
shape as COOL, which needs no extra action beyond stadium_evict()'s
own universal reservoir credit.
Fix: stadium_dispatch()'s DELIVER case now prints the departing
message's real identity (DELIVER msg_idx=N, same shape as MIGRATE's
lbn= print) instead of a misleading (stub) label. COOL is in the
identical situation (real for both words and VMs) but left as-is --
out of scope for this pass, noted in stadium.c's own comment.
Also confirmed EXPIRE (ACL) is genuinely unscoped, not stale docs like
DELIVER turned out to be -- zero Stadium involvement anywhere in
ACL.4th/acl_recheck()/the ACL design doc. Stays open pending real
design decisions.
Verified live via a forced MSG-SEND/MSG-DELIVER-ALL/MSG-ACK-LAST
sequence from Hermes's own REPL context ("USE" now works after the
previous fix): "Stadium: dispatch cell=73653 behaviour=DELIVER
msg_idx=1". Clean zero-warning compile and clean boot with
conservation intact on all three architectures.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn
|
||
|
|
3e0966095c |
Stadium: real VM-patron admission + real COOL dispatch at KILL (FABRIC-3.md §B)
capsule_vm_kill() had zero Stadium involvement (vm_cleanup()/sf_free() only), and child-VM birth only ever called stadium_grant_quota() -- a resource pool for the VM's own future word/block patrons, never stadium_admit() for the VM itself. stadium_birth_hera() looked like a precedent but admits Hera into her own quota as a permanently pinned cell 0, which can never reach stadium_evict() -- not a working example of COOL firing for a VM. Adds size_t stadium_patron_cell to VMRegistryEntry. At birth, right after the existing stadium_grant_quota() call, admits a candidate into the new VM's own quota mirroring stadium_birth_hera()'s shape (identity=0, mass=1, behaviour=COOL) but deliberately unpinned -- pinning would need a new "unpin" primitive (none exists) to ever evict it later, and unpinned costs nothing since nothing wires COOL's dispatch body to kill anything; the worst case of an unrelated natural eviction is stale bookkeeping, tolerated the same way stadium_word_forget() already tolerates staleness elsewhere. At capsule_vm_kill() and capsule_vm_kill_all_nonmama(): stadium_evict() the tracked cell if still resident, silently tolerating refusal (already gone). stadium_dispatch()'s COOL case needed no new payload body -- same as it already is for words, where COOL has no defined extra action beyond stadium_evict()'s own universal reservoir credit. On investigation this turned out not to be entangled with the still-iterating Tripod/Zuse/messaging vision after all -- birth and kill already funnel through two single choke points, so the earlier deferral (previous commit) was overcautious. Verified live: a second, new "Stadium: dispatch cell=... behaviour= COOL" now fires immediately before every PARITY:KILL line, for both Hermes and Artemis, distinct from the pre-existing COMMON-CH word-eviction self-test's own COOL print. Conservation (resident_sum + reservoir == Q48_ONE) intact throughout. Clean zero-warning compile and clean boot on all three architectures. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
af1eb0ca2d |
Fix USE/RUN dictionary shadowing: remove dead lib.4th aliases
capsules/lib.4th:13-14 defined ": USE ( addr u -- ) EXEC ;" and the same for RUN, shadowing the C-registered mama_word_use()/RUN primitives (CLAUDE.md names both, with BIRTH, as untouchable C primitives) with an unrelated "load/exec a capsule" meaning. This broke the interactive USE-based VM-redirect (S" Artemis" USE printed "EXEC: failed: Artemis" instead of redirecting), discovered while chasing FABRIC-3.md's live-MIGRATE verification. Traced every real caller before touching anything: RUN's alias was dead code, never called anywhere as bare RUN. USE's alias had exactly one real caller -- capsules/hermes/init.4th:397, intentionally exploiting the shadow to load common:msg.4th right after lib.4th itself loaded. Both aliases were pure EXEC wrappers with zero added behavior, so this deletes both definitions outright and switches the one real call site (plus its matching doc comment in capsules/common/msg.4th) to call EXEC directly. No new names invented, the C primitives untouched. Verified live: Hermes still births and her COMMON-CH-eviction self-test (depends on common:msg.4th having loaded) still passes; interactively, S" Artemis" USE now correctly redirects the REPL and prints "USE: now using Artemis". mkcapsule --lint clean (31/31). Clean compile and clean boot with Stadium conservation intact (resident_sum + reservoir == Q48_ONE) on all three architectures. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
0f683c15fd |
FABRIC-3.md: close the live-MIGRATE verification gap; report a USE/RUN shadowing bug
Confirmed the real MIGRATE dispatch fires live: a temporary boot probe (STADIUM-ADMIT + STADIUM-EVICT against the live Artemis VM, inserted into the existing 4.6 self-test and reverted immediately after capture) produced "Stadium: dispatch cell=63257 behaviour=MIGRATE lbn=100" with blk_flush(100) firing and zero error -- closes the honest gap left open in the previous commit. Interactive flooding alone couldn't reach this: Hera's reservoir sits at the Q48_ONE/3 floor from boot self-tests, so block-touch candidates pull 0 heat and can never out-density an existing resident, a pre-existing reservoir-floor/eviction interaction unrelated to this pass. Also reports (not fixes, per CLAUDE.md) a real dictionary-shadowing bug found while chasing this: capsules/lib.4th:13 redefines USE as EXEC, shadowing the C primitive mama_word_use() (REPL VM-redirect) with an unrelated capsule-loading meaning -- same for RUN at lib.4th:14. Same bug class as the K-PUSH dictionary-shadowing issue. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
c7c9332321 |
Stadium: real block-patron admission + MIGRATE dispatch (FABRIC-3.md §B)
stadium_admit()'s mass==1 refusal looked like a hard blocker for 1024-byte blocks, but stadium_word_dispatch()'s real candidate construction proves Stadium cells carry pure identity/heat/bookkeeping, never the resident's actual content -- a block patron follows the same shape (identity=LBN, payload unused), so this was real, scoped work, not a case for stubbing. New stadium_blocks.h/.c mirror stadium_words.c's admission/cooling shape, keyed by (quota_slot, lbn) in a fixed-capacity open-addressing hash table (tombstone deletion) instead of a dense array, since LBN space isn't densely bounded like word_id. Wired into block_word_block()/buffer()/ update() (block_words.c), __STARKERNEL__-guarded. stadium_dispatch()'s MIGRATE case now calls blk_flush(lbn) for real instead of printing "(stub)". Three new Kconfig constants (STADIUM_BLOCK_HEAT_QUANTUM/ STADIUM_BLOCK_COOL_RATE_Q48/STADIUM_BLOCK_TRACK_CAP_MULT) mirror the word-patron ones, same three-layer wiring. VM-COOL/DELIVER/EXPIRE stay explicit punch-list items -- VM-COOL deferred pending the still-iterating Tripod/Zuse/messaging vision, DELIVER/EXPIRE are their own future subsystem integrations per FABRIC.md's own "open, not resolved" notes. Verified clean compile (zero warnings) and clean boot to REPL with conservation intact (resident_sum + reservoir == Q48_ONE) on all three architectures (amd64/aarch64/riscv64); BLOCK/BUFFER touches exercised live from the REPL with no crash; a 22,000-distinct-block flood loop against an artificially shrunk Stadium ran clean under heavy admission load. A live MIGRATE console fire was not directly observed this session (root-caused to a pre-existing reservoir-floor/density-eviction interaction unrelated to this change, documented in FABRIC-3.md) -- flagged as an honest follow-up, not silently claimed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
ecdb547786 |
FABRIC-3.md: block-patron admission investigation + Tripod final-shape vision
Investigated whether stadium_admit()'s mass==1 refusal blocks block-patron Stadium admission -- it doesn't: stadium_word_dispatch()'s real candidate construction confirms Stadium cells carry identity/heat/bookkeeping only, never the resident's actual content, so a block patron (identity=LBN, payload unused) fits the same pattern words already use. The real gap is just that no LBN->cell_index residency map or touch-on-access hook exists yet -- real, scoped, buildable work, not a blocker. Deferred VM-COOL pending the Tripod final-shape vision Captain Bob laid out (thumbdrive minting, one-time Zuse fuse-blow, messaging-only inter-VM interaction, polymorphic block-boundary behavior) -- recorded in a new §D so near-term Stadium work doesn't ignore it, without treating a one-sentence vision as a ready-to-implement spec. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
912cc8ea4b |
Claude
Signed-off-by: Robert Allan James <robert.allan.james@gmail.com> |
||
|
|
d68223d43f |
Close FABRIC-2.md, open FABRIC-3.md; audit Stadium dispatch stubs; new no-stubs rule
FABRIC-2.md closed archival at ~4,400 lines / 51 open items, same reasoning FABRIC.md itself was closed for at 7,595 lines -- continuing to append made still-open work hard to find. All 51 open items carried forward into FABRIC-3.md's new Section A, verified complete via programmatic diff against the source (49 unique + 2 confirmed pure duplicates from FABRIC-2.md's own F.3 cross-reference section, not dropped content). .claude/CLAUDE.md's pointer note updated to name FABRIC-3.md as current. New Section B: full audit of stadium_dispatch()'s four behaviour stubs (MIGRATE/DELIVER/EXPIRE/COOL), triggered by investigating "words/VMs/blocks/ messages should all be on the same engine". Found the picture is more nuanced than "everything's a stub" -- words are already fully live via a separate bespoke mechanism (stadium_word_dispatch(), wired into vm_core.c's real word-execution path, item 4.1), contradicting ONTOLOGY.md's stale "not yet migrated" claim (flagged for a follow-up fix). MIGRATE (blocks) and VM-COOL are genuinely stub with zero consumers; DELIVER (Hermes) and EXPIRE (ACL) are substantial, undecided subsystem integrations FABRIC.md itself already flagged as open, not touched here. New standing rule, saved as memory feedback_no_stubs_or_todos.md: stub implementations and TODO placeholders are never acceptable in this workflow, in any language, ever -- triggered by finding stadium_dispatch()'s stub handlers during this investigation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
36d832ff47 |
Single-block relocation: RELOCATE-BLOCK, resolve_lbn(), persisted exception table
Implements the full design from the prior commit in one pass. resolve_lbn()
is the single choke point threaded through the ten public LBN-consuming
entry points (blk_get_buffer, blk_update, blk_flush, blk_is_allocated,
blk_mark_allocated, blk_mark_free, blk_is_valid, blk_get_meta, blk_set_meta,
plus blk_get_empty_buffer covered via delegation) -- an LBN->LBN redirect,
not a new storage allocator, since the LBN space is already unified across
every attached blkio_dev backend. VM window cache staleness across a
relocation reuses the existing blk_vm_check_epoch() mechanism from
Milestone 2h's hot-detach fix for free -- g.epoch bumps on relocation too.
Persistence lands in the same pass: two new uint32_t fields
(reloc_start/reloc_devblocks) appended after hdr_crc in blk_volume_meta_t,
carved from existing padding without moving any earlier field's byte
offset -- an old formatted volume's zeroed padding reads back as
reloc_devblocks=0 ("no reloc capacity"), gracefully, not a format-breaking
change. compute_totals_from_B() generalized to account for the new
reserved region. reloc_flush_to_disk()/reloc_load_from_disk() mirror the
BAM I/O functions' own absolute-devblock-addressing shape; the persisted
copy's owner is first_disk_slot() (already existed, already used for this
exact "which device is canonical" question by blk_get_volume_meta()).
blk_subsys_relocate_block() is a mechanical primitive only -- copies
content (staged through a local buffer, since obtaining the target's
blk_get_buffer() result can evict and invalidate the source's cache
pointer if they share a device), frees the source BAM entry, appends the
exception entry, bumps the epoch, flushes to disk. RELOCATE-BLOCK exposes
it to FORTH, no policy of its own (ACL's job, per this session's direction).
A first live-test attempt gave a false negative against disk/artemis.img
(predates reloc capacity, so relocation only ever existed in memory that
boot) -- traced to the test's own setup before being mistaken for a bug,
then re-verified correctly against a fresh volume (new fixture,
disk/artemis-reloc-test.img): relocated a RAMDRIVE block to the fresh
disk, confirmed live resolution through the redirect, then confirmed both
the redirect and the relocated content survived an abrupt QEMU kill and
full reboot. Also fixed three lingering "glibc" doc-comment
misattributions from Milestone 2h (the actual allocator is this kernel's
own kmalloc) that survived an earlier FABRIC-2.md-only correction. All
three architectures re-verified clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn
|
||
|
|
073dae4f56 |
FABRIC-2.md: full design for single-block relocation, persistence included
Complete design, not just the earlier flag: resolve_lbn() as the single choke point threaded through ten public entry points, LBN->LBN redirect mechanism (no new storage allocator needed, the LBN space is already unified across all backend-agnostic blkio_dev devices), VM window staleness solved for free by reusing the existing epoch mechanism from Milestone 2h's hot-detach fix, in-memory table shape, and on-disk persistence via two new fields carved from blk_volume_meta_t's existing padding (byte-compatible with old formatted volumes) using the same absolute-devblock I/O pattern the BAM already uses. Ownership of the persisted copy assigned to first_disk_slot() (already exists, already used for this exact "which device is canonical" question), with the multi-primary-device question for a future multi-SSD/cloud world explicitly punted rather than hand-waved. Noted the known, separate limitation: USB relocation targets aren't exercisable until WRITE(10) exists in the xHCI driver -- verification will use two already-writable devices instead. Policy (when to relocate, whether a target is validly owned) stays ACL's job, not this primitive's. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
aea7f85d34 |
FABRIC-2.md: flag block_subsystem.c routing revision needed for single-block relocation
Milestone 3 direction from this session: ACL decides when to relocate (capacity pressure or a compudynamics heat/cold signal), migration expected rare not routine. Flagging only, not designing yet: current lbn_to_slot() is pure contiguous-range routing, one device owns one unbroken range -- incompatible with relocating an individual block to a different device while its LBN stays fixed. Direction sketched (a sparse LBN-to-actual-device exception table, consulted before the range walk, zero cost for the common never-relocated case) but the actual structure is still open. Block-migration implementation now explicitly depends on this revision landing first. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
306ee70468 |
FABRIC-2.md: identity-range allocation is first-touch, not a hash formula; identity is a CA-signed cert
Dropped the hash-modulo-MAX_IDENTITIES approach: there is no way to bound how many identities might ever exist, so no population size can be pre-divided into slots regardless of hash function. Resolution: first time an identity is seen, claim a range at the SSD's existing free-space frontier (g.total_user_lbn, already used by every device attach) sized to whatever it needs, write the assignment into the drive's own map; every later visit just reads it back. No central directory, no quota, matches Section U's existing requirements exactly. Identity itself is a certificate signed by this project's own (not yet created) CA root, extending Milestone 6's PKI design to users instead of inventing a separate mechanism. Derivation-relevant value is the cert's embedded pubkey, not its raw bytes -- a renewal shouldn't relocate an already-allocated range. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
4891fd0067 |
FABRIC-2.md: drop GPT partitioning for home-blocks USB drive, whole-device layout instead
Checked the math with Captain Bob: a PBAM for ~15GB of user blocks needs only ~1.92MB (15M blocks / 32768 per 4KB page), fitting easily alongside identity/credential/PKI material in a 1GB reserved region. No partition table needed -- same whole-device pattern artemis.img already uses today, just with a generously fixed ~1GB reserved region instead of tight bam_start+bam_devblocks packing. Eliminates the GPT-parser work item entirely (no GPT code exists in src/starkernel/ today). Confirmed no conflict with Milestone 8's USB boot drive, which is a separate physical drive (flashed starkernel.iso) with its own independent GPT/ESP need. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
8eaefeb9ee |
sk_repl_idle() auto-flush: implement Section V's "anything dirty? no? done" check
Makes blk_vm_flush_all() (block_words.c) non-static and declares it in block_words.h -- it's already the entire implementation behind SAVE-BUFFERS (block_word_save_buffers() is a one-line wrapper), so sk_repl_idle() can call the exact same flush path outside word dispatch without duplicating any logic. Cheap every idle tick regardless of dirty state: every check inside is a small fixed-size scan, so no separate pre-check was needed on top of it. Caught a real bug via a live persistence test before trusting the feature: the first version gated the flush on sk_repl_get_active_vm() returning non-NULL, but NULL is that accessor's documented default (Tripod's own USE-redirect override, "restore default dispatch") -- without an active USE redirect, the flush silently no-op'd for the entire session. Confirmed live: wrote a byte via BUFFER (no UPDATE/SAVE-BUFFERS), waited past the idle cadence, killed QEMU abruptly, rebooted with the same disk image, read back 0 instead of the written 65. Fixed by threading the VM sk_repl_run()'s own loop already resolves each iteration (g_repl_active_vm ? g_repl_active_vm : vm) down as a parameter through sk_readline() into sk_repl_idle(), rather than trying to re-derive it from an accessor with the wrong default. Re-ran the same test after the fix: read back 65, matching the written byte -- the write survived an abrupt kill with no explicit flush call anywhere in the test, proving the idle-tick auto-flush genuinely ran. All three architectures re-verified clean. FABRIC-2.md Section V item 6 and the corresponding Milestone 3 punch-list item marked done. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
1b80609cb0 |
FABRIC-2.md: correct glibc misattribution in 2h hot-detach writeup
block_subsystem.c is shared/vendored source -- its calloc()/free() calls resolve to real glibc in the hosted build, but to this kernel's own freestanding shims (src/starkernel/vm/host/shim.c, backed by kmalloc()/ kfree() in src/starkernel/memory/kmalloc.c) in the kernel build, which is where the address-reuse bug was actually diagnosed live. The previous writeup said "glibc's allocator" -- wrong environment entirely. Verified kmalloc_aligned() is a plain first-fit walk from heap_head with no coalescing, confirming the same free-then-immediate-same-size-alloc reuse behavior originally observed, just attributed to the right allocator. Caught by the user asking directly whether this project has any glibc dependency, given .claude/CLAUDE.md's strict ANSI C99/no-GNU-extensions requirement (confirmed -std=c99 throughout both Makefiles). Documentation correction only -- no code changed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
af267a52a6 |
Artemis Milestone 2h: hot-detach -- 2h complete
blk_subsys_detach_device() (block_subsystem.c) walks the device chain, refuses removal of anything but the current tail (a mid-chain removal would corrupt every later slot's start_lbn -- this architecture's own doc already argues USB stays last specifically to avoid that), unlinks, shrinks total_user_lbn, closes and frees the slot. Discards rather than flushes dirty state -- the device is physically gone by the time this runs (PORTSC disconnect only). Trigger wiring mirrors the attach path: bot_msc_attached (set only once attach actually succeeds) gates a new bot_msc_detach_pending flag set at PORTSC disconnect (not Disable Slot completion, which is conditionally skipped and would miss concurrent connect/disconnect pairs), consumed in sk_repl_idle(). Advisor flagged the real hazard ahead of time: block_words.c's VM block window (blk_vm_lbn[]/blk_vm_cbuf[]) can go stale across a detach then a same-LBN re-attach, and suggested a pointer-identity re-check in blk_vm_load() as a minimal fix. That fix was implemented, then directly falsified by its own designed-for-this test: attach a blank device, read a block (populating the cache), detach, re-attach a device with distinct content at the identical LBN, read again -- served stale content from the first device. Root cause, confirmed live: glibc's allocator hands free(slot) straight back to the very next same-size calloc(), so the "fresh" and stale pointers were bitwise identical despite being two different devices. Fixed properly with a monotonic blk_subsys_epoch() counter (bumped on every attach/detach) checked by a new blk_vm_check_epoch() helper at the one choke point (blk_vm_find(), plus blk_vm_flush_all() which reads the same arrays directly) that covers every path touching the window cache -- unfooled by address reuse. Verified live with a new disk/usb-thumbdrive-test2.img fixture (distinct content from the existing blank test image): attach A, read (cache hit populated), detach, re-attach B at the same LBN, read again -- correctly ran a fresh device read and returned B's real content, not A's stale cached zeros. The failing pointer-comparison attempt's own capture log kept as evidence, not deleted. All three architectures re-verified clean. FABRIC-2.md Section X 2h marked complete -- enumeration through hot-detach all live and verified; only WRITE(10) (2g's own still-open item) remains unimplemented in the driver, not blocking anything here. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
3b085dd875 |
Artemis Milestone 2h: blkio_usb.c backend -- USB thumb drive is now a real block device
Wires a hot-plugged USB Mass Storage device into the block subsystem's unified LBN chain. blkio_usb.c/blkio_usb.h mirror virtio_blk.c/ virtio_blk.h's established shape exactly (singleton state, blkio_vtable_t, a blkio_usb_open_msc() "find" function playing virtio_blk_find_artemis()'s role): read() translates a Forth block into a SCSI LBA/count pair and calls xhci_bot_read_block() + xhci_bot_wait_for_idle(); write() returns BLKIO_ENOSUP (no SCSI WRITE(10) exists yet, and blk_format_or_load_disk() never writes at attach time, so read-only is sufficient -- confirmed by reading that function first, not assumed). Refuses (-2) if the reported SCSI block size doesn't evenly divide the 1024-byte Forth block size. Connect-time wiring reuses the bot_msc_attach_pending/consume-in- sk_repl_idle() shape the prior increment's temp probe already validated, now made permanent: SET_CONFIGURATION sets the flag, sk_repl_idle() (strictly after its own xhci_poll_events() call returns) calls blkio_usb_open_msc() then blk_subsys_attach_device(). Verified live via hot-attach: full chain from USB connect through 'blkio_usb: MSC device ready' to 'blk: disk 'StarForth Volume' v2 LBN 26074..75184 (49111 user blocks)' -- real attachment, disk image confirmed byte-for-byte untouched after. Chased a real debugging detour along the way: the attach initially appeared silent (no blk: log line) -- traced to LOG_INFO filtering at the default LOG_WARN boot level, not a functional bug (settled via a temporary log-level bump, reverted after capture; also found and reported, but did not fix, a pre-existing unrelated Makefile.starkernel bug where --log-level=info via KERNEL_ARGS breaks printf parsing). All three architectures re-verified clean. FABRIC-2.md Section X 2h updated -- only hot-detach remains for 2h. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
d686f28853 |
Artemis Milestone 2h (foundational): sync wait bridge + SCSI READ CAPACITY(10)
Closes the gap block_subsystem.c needs before any of 2h's real work (blkio_usb.c, attach wiring, hot-detach) can start: this driver is fully async/polled with no way for a synchronous caller (blkio_read()/ blkio_info() etc.) to get a result back. xhci_bot_wait_for_idle() is a bounded busy-wait over xhci_poll_events() -- MUST be called only from outside xhci_poll_events()'s own call frame, never from within it or a next_action dispatch (recursion into live Event Ring/ERDP processing, same class of hazard already documented for doorbell rings in this driver). xhci_get_dev() exposes the module-static device handle to outside callers that didn't observe the original hotplug event. SCSI READ CAPACITY(10) (opcode 0x25) is the other half -- nothing could learn a device's block size/capacity before this. First attempt sent it bare and hit the classic first-command UNIT ATTENTION (CSW FAILED); fixed with the same TUR-guard pattern READ(10) already used, generalized via a new bot_tur_chain_target field so TEST UNIT READY's PASS handling can chain into either command. bot_data_buf grown 512->1024 bytes (one Forth block = two 512-byte SCSI blocks, per block_subsystem.c's own 1KiB-unit convention). Verified live via a temporary probe (hot-attached disk/usb-thumbdrive- test.img via QMP, reverted after capture): TUR-guarded READ CAPACITY10 correctly reported last LBA=0x1ffff, block size=0x200 -- exactly 64MiB, matching the test image byte for byte -- followed by a TUR-guarded 1024-byte/2-block READ10, both PASS. All three architectures re-verified clean, probe-free boot to ok> on the reverted tree. FABRIC-2.md Section X 2h updated with the writeup; the blkio_usb.c backend itself is next. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
65effbd1ba |
Artemis Milestone 2g: TEST UNIT READY unit-init sequence -- READ(10) now PASSes
Roots out the CSW status FAILED left unexplained in the prior increment: a freshly attached SCSI target's standing UNIT ATTENTION condition, which a bare READ(10) with no retry can never clear. xhci_bot_send_test_unit_ready() sends SCSI TEST UNIT READY (SPC-4 6.33) ahead of the real command; the CSW handler now tags command kind (bot_cmd_kind) to distinguish a TUR completion from a READ10 completion, chains TUR PASS into the real READ(10), and bounded-retries TUR on FAILED/PHASE ERROR (bot_tur_retries, capped at XHCI_BOT_TUR_MAX_RETRIES). xhci_bot_read_block() is the new intended entry point tying lba/num_blocks/block_size + the TUR-first sequencing together. Verified live via a temporary probe (hot-attached disk/usb-thumbdrive-test.img through the running instance's QMP socket), captured on amd64: full chain CBW(TUR) -> FAILED -> retry -> PASS -> CBW(READ10) -> Data-In -> CSW PASS. Probe reverted after capture; all three architectures re-verified clean, probe-free boot to ok>. FABRIC-2.md Section X 2g updated with the writeup. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn |
||
|
|
c54ea24aaf |
Artemis Milestone 2g: Data-In stage read and CSW receive/validation
Completes the CBW -> Data-In -> CSW chain for READ(10) started last commit. xhci_bot_read_data_in() and xhci_bot_receive_csw(), each a single Normal TRB on the bulk IN Transfer Ring via a new xhci_bulk_in_enqueue_and_ring() helper (mirrors the OUT-side helper from CBW send). All three stages now chain automatically via the existing deferred next_action pattern: CBW completion defers into Data-In, Data-In completion defers into CSW receive, CSW completion is where signature/tag/status validation happens. Data-In reads into a new fixed 512-byte bot_data_buf -- single-block scope for this increment, matches QEMU's usb-storage reported block size; xhci_bot_send_read10() now refuses rather than overflow/truncate if a request exceeds it. CSW validation (BOT spec section 5.2) checks dCSWSignature and dCSWTag (a new bot_last_tag field, latched from the CBW) before trusting bCSWStatus at all, so a garbled/misaligned CSW read can't be misread as a clean pass. usb_bot_csw_t follows the same struct-with-explicit-length-not-sizeof discipline as usb_bot_cbw_t. Verified live via a temporary probe (written, run once, log captured, reverted per this project's own probe convention), all three architectures, byte-identical: the full CBW -> Data-In -> CSW exchange completes cleanly, well-formed CSW with correct signature and echoed tag, no wedge, clean disconnect immediately after. The SCSI command itself reports CSW status FAILED against the current test fixture -- expected at this stage (no TEST UNIT READY / UNIT ATTENTION handling implemented yet, consistent with a fresh-attach unit-attention condition, not a transport-layer defect) and not root-caused further here; the BOT mechanism itself is confirmed correct end to end. Probe-free re-verification afterward on all three architectures. FABRIC-2.md Section X Milestone 2g's CSW checklist item marked done; "get one real READ(10) working end to end" stays explicitly open, distinguishing "the mechanism works" from "the SCSI command succeeds." Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R4VMX6VSKCten8nGgaMkq4 |
||
|
|
a88c004ecb |
Artemis Milestone 2g: CBW construction and send for SCSI READ(10)
First real use of the bulk Transfer Rings Configure Endpoint wired up. xhci_bot_send_read10() builds a 31-byte Command Block Wrapper (USB Mass Storage Class Bulk-Only Transport spec section 5.1) and submits it as a single Normal TRB on the bulk OUT ring via a new xhci_bulk_out_enqueue_and_ring() helper -- a CBW is always exactly one TRB, so unlike the EP0 helper this one rings its own doorbell rather than leaving that to a caller assembling a group. usb_bot_cbw_t is a real struct (every field up to the CDB array is naturally aligned, and this driver's targets are all little-endian already assumed everywhere else), but its DMA length is the explicit USB_BOT_CBW_LENGTH (31) constant, never sizeof(*cbw), since the compiler may pad the struct to 32 bytes. The SCSI READ(10) CDB itself is written byte-by-byte since its LBA/Transfer Length fields are big-endian on the wire, unlike everything else in this driver -- the one place two byte orders are both live in the same function. Completion is correlated via the existing pending_transfer_slot_id/ transfer_purpose gate (new XHCI_XFER_CBW_SENT purpose) -- no ring-specific dispatch needed, since this driver's single-outstanding- transfer scope already implies which ring produced an event. This covers construction and send only (one third of a full READ(10): CBW -> Data-In stage -> CSW) -- reading the Data-In stage and CSW receive/validation are separate, explicitly not-yet-implemented items. Verified live via a temporary probe (written, run once, log captured, reverted per this project's own probe convention) -- all three architectures, byte-identical: CBW submitted -> CBW send completed, then a clean disconnect even with the Data-In stage never drained (confirms no wedge on a dangling BOT transaction). Probe-free re-verification afterward on all three architectures. FABRIC-2.md Section X Milestone 2g's CBW checklist item marked done. Also records a monitoring gotcha hit three times this session: `ls -t` over the logs/ tree can return a stale leftover log from an earlier run in the same session -- fixed going forward by reading the log path off the actual running QEMU process's own command line instead, and a memory note added so it doesn't recur next session. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R4VMX6VSKCten8nGgaMkq4 |
||
|
|
92ce1f85dd |
Artemis Milestone 2g: Configure Endpoint command
Adds the xHCI Configure Endpoint command for the two bulk endpoints identified by the previous increment, and fixes control-transfer sequencing to match the spec: xHCI 1.2 section 4.3.5 requires Configure Endpoint before SET_CONFIGURATION is sent to the device, the reverse of the order this driver used through 2f (which happened to work against QEMU's lenient qemu-xhci emulation but wasn't spec-correct). New XHCI_TRB_TYPE_CONFIGURE_ENDPOINT_CMD, EP Context type constants for Bulk IN/OUT, and an XHCI_EP_ADDR_TO_DCI() macro (DCI = 2*EndpointNumber + Direction) in xhci.h. xhci_cmd_configure_endpoint() builds the Input Context (Slot + one EP Context per DCI up to the highest bulk endpoint in use) and submits the command via the existing next_action deferral mechanism, correlated on completion via a new XHCI_CONN_AWAIT_CONFIGURE_ENDPOINT connect_state, then chains into the existing SET_CONFIGURATION path. Two allocations had to grow beyond what Address Device sized them for: the Input Context (previously room for one EP Context only) and, less obviously, the Device Context that DCBAA[slot_id] itself points at -- the controller only touches DCIs named in a command's own Add/Drop flags, so growing that buffer required copying its existing Slot+EP0 content forward rather than zeroing it, to avoid handing the controller a blank EP0 out from under an endpoint this command isn't touching. Bulk Transfer Rings (bulk_in_ring/bulk_out_ring) are allocated and wired into the new EP Contexts but not yet exercised by an actual transfer -- CBW/CSW submission is next. Verified live via QMP hotplug, all three architectures, byte-identical: bulk endpoint identification -> configure endpoint command submitted -> configure endpoint succeeded -> the existing set configuration -> device configured chain, then a clean disconnect/disable-slot teardown afterward with the larger Device Context installed. FABRIC-2.md Section X Milestone 2g's endpoint identify+configure checklist item marked fully done. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R4VMX6VSKCten8nGgaMkq4 |
||
|
|
96d55fcd87 |
Artemis Milestone 2g (partial): bulk endpoint discovery + 2e disconnect teardown
Picked up from a crashed session: xhci_driver.h/xhci.h already had the bulk_in/out_ep_addr/max_packet fields and Endpoint-descriptor offset macros scaffolded, but the actual walk that populates them was never written. Added it: after 2f confirms a Mass Storage/SCSI/BOT interface, a nested walk continues through the Endpoint descriptors that follow it (bDescriptorType==5, stopping at the next Interface descriptor or end of stream), keeping only Bulk-type endpoints and splitting IN/OUT by bEndpointAddress bit 7. Also reset the four new fields in xhci_bringup(), which the scaffolding had missed. Also completed 2e's disconnect teardown, which was fully implemented this session (not scaffolded): a Disable Slot command is now submitted on a real disconnect, with the port's tracked slot ID captured and cleared from port_slot_id[] immediately (before the command completes) so a fresh connect on the same port isn't confused for one already in progress, and DCBAA[slot_id] cleared only on a successful completion. Verified live via QMP hotplug (deliberate device_add/device_del against freshly launched, individually-tracked instances -- not whatever happened to be attached at boot), all three architectures, byte-identical: bulk IN endpoint=0x81, bulk OUT endpoint=0x02, then a clean disconnect -> disable slot succeeded, no wedge. Caught and fixed a documentation near-miss in the same pass: an initial draft cited the probe-free three-arch acceptance boots as this feature's verification evidence, but a stale leftover log directory from a pre-crash orphaned QEMU process had been picked up by an `ls -dt | head -1` glob during monitoring and mistaken for this session's own result -- the real acceptance logs never had a device attached at all. Re-verified against real PIDs and real log paths before writing FABRIC-2.md's final writeup. FABRIC-2.md Section X Milestone 2 updated: 2e's disconnect-teardown checklist item marked done, 2g's endpoint-identification item marked partially done (identification only -- Configure Endpoint / EP Context wiring is still open). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R4VMX6VSKCten8nGgaMkq4 |