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
This commit is contained in:
Robert Allan James
2026-08-26 19:18:21 -04:00
co-authored by Claude Sonnet 5
parent e5cbc71f46
commit 2035ebeac0
15 changed files with 63373 additions and 4 deletions
+48
View File
@@ -526,6 +526,54 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
commented-out default. Verified clean compile and clean boot on all three architectures in
this reverted state.
**Substrate corrected (Captain Bob, 2026-08-26): no files, ever — this OS's entire reason
for being is anti-POSIX, anti-file.** The "dedicated system-identity disk" recommendation
above was framed in file/filesystem language by mistake; corrected before any code was
written. The only real persistence primitives here are content-addressed capsules and raw
LBN-numbered blocks (`block_subsystem.c`) — never a filesystem, never file paths. Saved as
`feedback_no_files_anti_posix.md` so this isn't re-learned next session.
**Design, agreed on request: a growable metadata fence at the TOP of a device's block
space, mirroring the bottom BAM reservation from the opposite end.** `block_subsystem.c`'s
BAM already reserves the bottom `BLK_DISK_SYS_RESERVED` (32) blocks of every attached
device, invisible to FORTH's `BLOCK`/`BUFFER`. Zuse's cert (and future system metadata)
gets a second reservation at the *top* of the same device, starting at
`BLK_META_FENCE_INIT` (128) blocks and growing downward as needed — the two reservations
grow toward each other from opposite ends, never colliding, same shape as a stack/heap.
Explicitly never RAM-backed (the fast-RAM/ramdrive LBN ranges are documented as volatile in
this same file's own header comment — losing Zuse's identity to a RAM eviction is exactly
the failure this is designed against). Reuses Artemis's own already-attached, already-proven
virtio-blk device — no new device attachment. Rejected reusing BAM's own bottom-reserved
zone directly: those 32 blocks are fully claimed by BAM/volume-metadata bookkeeping, not
free space.
**Step 1 (field round-trip) implemented and verified 2026-08-26, allocator not yet
touched.** New `meta_fence_blocks` field in `blk_volume_meta_t`, appended after
`reloc_devblocks` and carved from `_pad[]` — identical graceful-default technique the
`reloc_start`/`reloc_devblocks` fields already established (a pre-existing formatted volume
reads the field back as 0 via its zeroed former padding, not a format-breaking change).
Added a compile-time `_Static_assert(sizeof(blk_volume_meta_t) == 4096, ...)`, same
discipline `homeblocks_sig.h` already uses — caught a real bug immediately: the hand-summed
`_pad[]` size formula was off by 4 bytes (a compiler-inserted alignment gap before
`tracked_blocks` that the manual byte-count missed), found via `offsetof()` rather than by
re-deriving the arithmetic by hand again, consistent with this project's standing rule to
never trust a hand-derived numeric claim in this class of code.
Worked against disposable clones throughout, never the real `disk/artemis.img`
(`ARTDISK=...` is `?=`-overridable) — `disk/artemis-metafence-fresh.img` (blank, exercises
the fresh-format path) and `disk/artemis-metafence-test.img` (a copy of the pre-existing
`artemis.img`, exercises the graceful-default-on-reload path), both kept as regression
fixtures per `disk/README.md`'s existing convention (mirrors `artemis-reloc-test.img`
exactly). **Verified independently via direct byte reads of the disk image, not the
kernel's own self-report** (`log_message(LOG_INFO, ...)` turned out not to reach serial
output at all in this build — an unrelated, pre-existing log-level gap, not a regression):
fresh format writes `meta_fence_blocks=128` at header byte offset 184; a second boot without
reformatting reads it back unchanged; the pre-existing old-format image correctly reads back
0. Full 3-arch acceptance boot against the real, untouched `disk/artemis.img` also clean —
conservation intact, no panics. The allocator (`user_blocks` math) still doesn't know about
this reservation yet, and there are no read/write accessors for the zone's actual data —
both still open, next steps.
### 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
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-08-26T19:54:10Z -->
<!-- Generated by mkcapsule --manifest 2026-08-26T23:14:17Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. -->
+19
View File
@@ -83,6 +83,25 @@ carrying timestamp noise in git history.
`BLK-CONFIRM-FORMAT`-committed in the repo copy — commit that step live if
reusing this fixture for further reloc-table testing.
- `artemis-metafence-fresh.img` — 30MB raw image, blank at creation,
added 2026-08-26 for the top-of-device system-metadata fence
(`meta_fence_blocks` in `blk_volume_meta_t`, FABRIC-3.md Phase 8 §C).
Same reasoning as `artemis-reloc-test.img` above: the fence is only
initialized to `BLK_META_FENCE_INIT` (128) by
`blk_compute_fresh_geometry()` on a fresh format, so a genuinely blank
volume was needed to exercise that path. Verified round-trip: fresh
format writes 128 (confirmed via direct byte read at header offset 184,
independent of kernel self-report), a second boot without reformatting
reads it back unchanged. Keep in its formatted (`meta_fence_blocks=128`)
state — a future change that resets or corrupts this on reload is a
real regression.
- `artemis-metafence-test.img` — 30MB raw image, a direct copy of the
pre-existing (pre-fence) `artemis.img`, added 2026-08-26 alongside the
fixture above to verify the *other* direction: an old volume that
predates `meta_fence_blocks` correctly reads it back as 0 (graceful
default via zeroed former padding, not corruption) rather than crashing
or misreading adjacent fields.
- `zuse.img` — 64MB raw image simulating the physical Zuse superuser
thumbdrive for QEMU testing (FABRIC-3.md, Phase 8: `zuse.img` "bleach"
mechanism, added 2026-08-26). **64MB is only this fixture's size, not a
Binary file not shown.
Binary file not shown.
+32 -1
View File
@@ -89,6 +89,10 @@ extern "C" {
#ifndef BLK_DISK_SYS_RESERVED
#define BLK_DISK_SYS_RESERVED 32u /* First 32 blocks of each disk reserved (byte-aligned BAM offset) */
#endif
#ifndef BLK_META_FENCE_INIT
#define BLK_META_FENCE_INIT 128u /* Starting size (Forth 1 KiB blocks) of the top-of-device
* system-metadata fence, grows downward from here. */
#endif
/* =========================
* On-disk volume header v2
@@ -144,10 +148,28 @@ typedef struct {
uint32_t reloc_start; /* usually bam_start + bam_devblocks */
uint32_t reloc_devblocks; /* number of 4 KiB pages used by the reloc table (0 = none) */
/* System-metadata fence (Phase 8, 2026-08-26): a reserved zone at the
* TOP of the device's Forth block space, opposite end from
* reserved_disk_lo's bottom BAM reservation, growing DOWNWARD as
* system metadata (starting with Zuse's cert) needs more room.
* Never RAM-backed -- this field only exists on real disk-backed
* slots. Appended after reloc_devblocks, carved out of what was
* previously _pad[] -- same graceful-default technique as
* reloc_devblocks itself: a pre-existing formatted volume's zeroed
* padding reads back here as meta_fence_blocks=0 ("no fence yet"),
* not a format-breaking change. See FABRIC-3.md's Phase 8 §C
* writeup for the full design. */
uint32_t meta_fence_blocks; /* current fence size, in Forth 1 KiB blocks (0 = none yet) */
/* Padding to keep header ≤ 4096 bytes */
uint8_t _pad[4096 - (
4 + 4 + /* magic, version */
4 + 4 + 64 + /* total_volumes, flags, label */
4 + /* compiler alignment gap before total_devblocks'
* uint64_t sibling tracked_blocks -- verified via
* offsetof(), not hand-derived (2026-08-26: this
* exact formula was off by 4 bytes from trusting
* arithmetic alone before this fix) */
8 + /* total_devblocks */
4 + 4 + 4 + /* bam_start, bam_devblocks, devblock_base */
8 + 8 + 8 + /* tracked_blocks, total_blocks, free_blocks */
@@ -155,10 +177,19 @@ typedef struct {
4 + 4 + /* reserved ranges */
8 + 8 + /* timestamps */
8 + /* hdr_crc */
4 + 4 /* reloc_start, reloc_devblocks */
4 + 4 + /* reloc_start, reloc_devblocks */
4 /* meta_fence_blocks */
)];
} blk_volume_meta_t;
/* Same discipline homeblocks_sig.h's own header-size check uses: the pad
* math above is meant to keep this struct at exactly one 4 KiB devblock,
* verified at compile time rather than trusted by inspection -- adding
* meta_fence_blocks is exactly the kind of edit that could silently
* throw this off by a few bytes. */
_Static_assert(sizeof(blk_volume_meta_t) == 4096,
"blk_volume_meta_t must be exactly one 4 KiB devblock");
/* Per-1 KiB block metadata (packed into top 1 KiB region of each 4 KiB sector). */
typedef struct {
/* Core integrity (16 bytes) */
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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -526,6 +526,7 @@ static void blk_compute_fresh_geometry(blk_dev_slot_t *slot) {
* variable-size region that might need to grow later. */
slot->vol_meta.reloc_devblocks = 1;
slot->vol_meta.devblock_base = slot->vol_meta.reloc_start + slot->vol_meta.reloc_devblocks;
slot->vol_meta.meta_fence_blocks = BLK_META_FENCE_INIT;
compute_totals_from_B(&slot->vol_meta);
if (sf_has_rtc()) slot->vol_meta.created_time = sf_realtime_ns();
@@ -701,14 +702,15 @@ int blk_subsys_attach_device(struct blkio_dev *dev) {
log_message(LOG_INFO,
"blk: disk '%s' v2 LBN %u..%u (%u user blocks); "
"devblocks=%llu bam=%u base=%u total=%llu free=%llu",
"devblocks=%llu bam=%u base=%u total=%llu free=%llu fence=%u",
slot->vol_meta.label,
slot->start_lbn, slot->start_lbn + slot->user_blocks - 1,
slot->user_blocks,
(unsigned long long) slot->vol_meta.total_devblocks,
slot->vol_meta.bam_devblocks, slot->vol_meta.devblock_base,
(unsigned long long) slot->vol_meta.total_blocks,
(unsigned long long) slot->vol_meta.free_blocks);
(unsigned long long) slot->vol_meta.free_blocks,
slot->vol_meta.meta_fence_blocks);
return BLK_OK;
}