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
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
53e6c5709f
commit
f223a31cec
+43
@@ -432,6 +432,49 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
|
||||
keypair needs to chain to the Milestone 6 offline root CA or is a self-sovereign
|
||||
instance-local root of trust.
|
||||
|
||||
**Phase C scoping, 2026-08-26.** Three findings before any code:
|
||||
|
||||
1. **Resolved, not a real conflict: Zuse doesn't need Milestone 6's CA.** That CA chain is
|
||||
specifically for *capsule/code signing* (root → snakeoil intermediate → per-capsule
|
||||
Ed25519 signatures verified at capsule-load time) — a different trust domain from *user
|
||||
identity*. The vision's own framing ("we mint one and only one Zuse user and blow a fuse
|
||||
... the only way around is a new system") already implies Zuse's authority comes from
|
||||
being the unique first-boot mint on *this instance*, not from an external chain.
|
||||
**Decided: Zuse is a self-sovereign, instance-local root of trust**, keypair generated
|
||||
on-device from real entropy (Phase A+B). Regular users, minted later via `MINT`, get
|
||||
certs signed by *Zuse's* key, not the Milestone 6 CA — two independent PKI domains.
|
||||
2. **A real gap in this session's own earlier work:** `vm_zuse_cert_install(vm, lo, hi)`
|
||||
(the very first change this session made, before Phase A existed) only holds two
|
||||
`uint64_t` (16 bytes) — sized against the old placeholder `ZUSE-CERT-LO`/`HI` FORTH-cell
|
||||
design, not against what a real Ed25519 keypair needs (32-byte pubkey alone, well over
|
||||
100 bytes for a full cert). Needs expanding before Phase C can store anything real.
|
||||
3. **A genuine blocker, found by asking where the cert would actually live:** "mint once,
|
||||
ever" requires surviving reboots, but `Makefile.starkernel`'s `qemu` target copied a
|
||||
fresh, pristine `OVMF_VARS.fd` on *every* invocation (not just after `clean`) — so a
|
||||
UEFI-NVRAM-based cert (the real-hardware-compatible option, and this codebase already has
|
||||
a live `SetVariable`/`GetVariable` precedent via `SF_VAR_REBOOT_TRIES`/`SF_VAR_BOOT_ARGS`)
|
||||
would never actually persist under this project's own normal test workflow. Digging
|
||||
further: aarch64's `qemu` recipe had no persistent NVRAM store *at all* — a single
|
||||
combined `-bios $AAVMF_CODE` argument, no separate writable VARS pflash drive like
|
||||
amd64/riscv64 have. **Decided (on request): fix the harness rather than switch
|
||||
substrates.** amd64/riscv64: the VARS-template copy is now conditional on the destination
|
||||
not already existing, so `clean` (which deletes the whole `build/$(ARCH)/kernel` tree,
|
||||
`OVMF_VARS.fd`/`RISCV_VARS.fd` included) is the bleach step, and a bare `make qemu` now
|
||||
preserves NVRAM across runs — exactly matching the existing "always pass `clean` before
|
||||
`qemu`" acceptance convention, no new bleach script needed. aarch64: restructured to
|
||||
split CODE(ro)/VARS(rw) pflash drives matching the other two (host has
|
||||
`/usr/share/AAVMF/AAVMF_VARS.fd` alongside the existing `AAVMF_CODE.fd`), with a
|
||||
graceful fallback to the old single-`-bios` mode (and a console note) on a host that only
|
||||
has non-split firmware packaged, so this doesn't regress environments without one.
|
||||
**Verified live:** all three architectures still boot clean to `ok>` with the new
|
||||
pflash arrangement, Stadium conservation intact, no panics or guest errors — this is
|
||||
infrastructure-only (no cert code yet), so a plain boot-regression check is the right
|
||||
verification tier.
|
||||
|
||||
**Still open:** the actual cert struct (expanding past the 16-byte placeholder), the
|
||||
first-boot mint-vs-already-minted boot sequence using `SetVariable`/`GetVariable`, and the
|
||||
`MINT` word itself.
|
||||
|
||||
### From FABRIC-2.md §X, Milestone 5 — Console/VM key-match binding
|
||||
|
||||
- [ ] Settle the still-open question: reuse `ACL-PIN`/`acl_allow` directly, or build a
|
||||
|
||||
+23
-3
@@ -737,7 +737,9 @@ ifeq ($(ARCH),amd64)
|
||||
-o $(QEMU_ISO) $(BUILD_DIR)/iso 2>&1
|
||||
@if [ ! -f $(OVMF_CODE) ]; then echo "Error: $(OVMF_CODE) not found"; exit 1; fi
|
||||
@if [ ! -f $(OVMF_VARS_RO) ]; then echo "Error: $(OVMF_VARS_RO) not found"; exit 1; fi
|
||||
@cp $(OVMF_VARS_RO) $(BUILD_DIR)/OVMF_VARS.fd
|
||||
@if [ ! -f $(BUILD_DIR)/OVMF_VARS.fd ]; then \
|
||||
cp $(OVMF_VARS_RO) $(BUILD_DIR)/OVMF_VARS.fd; \
|
||||
fi
|
||||
@mkdir -p $(QEMU_LOG_DIR) $(DOE_RUNS_DIR) $(DOE_LATEST_DIR)
|
||||
@TS=$$(date +%Y%m%d-%H%M%S); \
|
||||
SESSION=$(QEMU_LOG_DIR)/$$TS; \
|
||||
@@ -798,14 +800,26 @@ else ifeq ($(ARCH),aarch64)
|
||||
@mkdir -p $(QEMU_LOG_DIR) $(DOE_RUNS_DIR) $(DOE_LATEST_DIR)
|
||||
@if [ -f /usr/share/AAVMF/AAVMF_CODE.fd ]; then \
|
||||
AAVMF_CODE=/usr/share/AAVMF/AAVMF_CODE.fd; \
|
||||
AAVMF_VARS_RO=/usr/share/AAVMF/AAVMF_VARS.fd; \
|
||||
elif [ -f /usr/share/qemu-efi-aarch64/QEMU_EFI.fd ]; then \
|
||||
AAVMF_CODE=/usr/share/qemu-efi-aarch64/QEMU_EFI.fd; \
|
||||
AAVMF_VARS_RO=/usr/share/qemu-efi-aarch64/vars-template-pflash.raw; \
|
||||
elif [ -f /usr/share/qemu/edk2-aarch64-code.fd ]; then \
|
||||
AAVMF_CODE=/usr/share/qemu/edk2-aarch64-code.fd; \
|
||||
AAVMF_VARS_RO=/usr/share/qemu/edk2-aarch64-vars.fd; \
|
||||
else \
|
||||
echo "Error: aarch64 UEFI firmware not found. Install qemu-efi-aarch64."; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
if [ -f "$$AAVMF_VARS_RO" ]; then \
|
||||
if [ ! -f $(BUILD_DIR)/AAVMF_VARS.fd ]; then \
|
||||
cp $$AAVMF_VARS_RO $(BUILD_DIR)/AAVMF_VARS.fd; \
|
||||
fi; \
|
||||
AAVMF_VARS=$(BUILD_DIR)/AAVMF_VARS.fd; \
|
||||
else \
|
||||
echo " (no split VARS template found for this firmware -- NVRAM will not persist across runs)"; \
|
||||
AAVMF_VARS=""; \
|
||||
fi; \
|
||||
TS=$$(date +%Y%m%d-%H%M%S); \
|
||||
SESSION=$(QEMU_LOG_DIR)/$$TS; \
|
||||
mkdir -p $$SESSION/aarch64; \
|
||||
@@ -824,7 +838,11 @@ else ifeq ($(ARCH),aarch64)
|
||||
qemu-system-aarch64 \
|
||||
-machine virt \
|
||||
-cpu cortex-a57 \
|
||||
-bios $$AAVMF_CODE \
|
||||
$$(if [ -n "$$AAVMF_VARS" ]; then \
|
||||
echo "-drive if=pflash,format=raw,readonly=on,file=$$AAVMF_CODE -drive if=pflash,format=raw,file=$$AAVMF_VARS"; \
|
||||
else \
|
||||
echo "-bios $$AAVMF_CODE"; \
|
||||
fi) \
|
||||
-drive if=virtio,format=raw,file=$(QEMU_ISO),media=cdrom \
|
||||
-drive id=artdisk,file=$(ARTDISK),format=raw,if=none \
|
||||
-device virtio-blk-pci,drive=artdisk \
|
||||
@@ -865,7 +883,9 @@ else ifeq ($(ARCH),riscv64)
|
||||
fi; \
|
||||
EDK2_RISCV_VARS=""; \
|
||||
if [ -f /usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd ]; then \
|
||||
cp /usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd $(BUILD_DIR)/RISCV_VARS.fd; \
|
||||
if [ ! -f $(BUILD_DIR)/RISCV_VARS.fd ]; then \
|
||||
cp /usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd $(BUILD_DIR)/RISCV_VARS.fd; \
|
||||
fi; \
|
||||
EDK2_RISCV_VARS=$(BUILD_DIR)/RISCV_VARS.fd; \
|
||||
fi; \
|
||||
DISK=$(BUILD_DIR)/starkernel_rv64.img; \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-26T18:43:12Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-26T19:15:03Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user