Files
LithosAnanake/include/starkernel/artemis_sig.h
T
Robert Allan JamesandClaude Sonnet 5 61755fde78
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Artemis genesis stamp: fix a BAM-corrupting offset before it ever ran (FABRIC-3.md §XXVI follow-on, Step 3)
Step 3: one-time artemis_sig_t genesis stamp, written once
kernel_main.c's virtio-blk path confirms Artemis's own disk, so the disk
image is later recognizable generically (repl.c's idle-loop USB-MSC scan,
built in the prior commit) regardless of which bus found it.

Correction made before this ever touched the real disk: the signature's
first design (committed in 29b6789) placed it at a fixed bottom-of-device
forth-block (4, devblock 1) -- copying homeblocks_sig_t's own convention,
which is safe for a raw identity thumbdrive but not for Artemis's own
disk. Artemis's disk is block_subsystem.c's own STFR/v2-formatted volume:
devblock 0 holds that format's header and devblock 1 is the FIRST
DEVBLOCK OF THE LIVE BAM (blk_compute_fresh_geometry(): bam_start = 1).
The original design would have overwritten Artemis's live allocation map
on the very first real boot. Caught via direct cross-reference against
block_subsystem.c before the genesis-stamp call site was ever run against
the real image -- no corruption occurred.

Fixed by moving the header to a fixed offset from the END of the device
instead (ARTEMIS_SIG_DEVBLOCK_FROM_TOP=64), the same top-of-device region
block_subsystem.c's own meta_fence_blocks reservation (128 devblocks)
already carves out for system metadata, and where Zuse's genesis marker/
eligibility list already live -- but computed independently via
blkio_info() rather than through blk_meta_zone_*(), since that accessor
needs an already-attached, format-detected slot, which is exactly the
state pre-attach generic discovery doesn't have yet. Picked well clear of
Zuse's two tenants (devblock_from_top 0 and 1+, open-ended) so the two
subsystems' independent math can never collide.

Also reordered kernel_main.c: rng_init() now runs before the Artemis
virtio-blk block (was after) -- the genesis stamp needs rng_get_bytes()
for disk_uuid, and the original order would have failed the stamp on
every boot.

Verified live: booted amd64 against the real disk/artemis.img twice --
first boot logs "Artemis: genesis signature stamped" (confirmed blank at
the target offset beforehand via a host-side read), second boot on the
now-stamped image logs no re-stamp (idempotent, CRC/read-back verified)
-- both boots and aarch64/riscv64 (against the same now-stamped image)
all still report "Artemis: 22998 data blocks" / "PASS: persist-read"
unchanged, confirming the BAM and data pool were never touched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXieurDfDSsDFdnSyusuWo
2026-09-13 07:31:21 -04:00

263 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 20232025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
express or implied, including but not limited to the warranties of
merchantability, fitness for a particular purpose, and noninfringement.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* artemis_sig.h - Artemis disk signature format (FABRIC-3.md §XXVI follow-on,
* 2026-09-13)
*
* Identifies Artemis's own disk, distinct from an identity thumbdrive's
* homeblocks_sig_t -- needed once Artemis's disk stops being found by a
* hardcoded PCI virtio-blk vendor/device ID scan (QEMU-only; real hardware
* has no reason to expose a virtio-blk PCI device at all, since virtio is a
* paravirtualization standard, not something a physical storage controller
* speaks) and starts being discovered generically instead, the same way
* WIREBIND already discovers identity thumbdrives -- by content signature,
* not by which bus happened to present the device. Without a distinct
* signature, generic discovery on real hardware (where an identity
* thumbdrive and Artemis's own disk could both be attached as USB-MSC
* devices simultaneously) would have no way to tell them apart.
*
* Mirrors homeblocks_sig_t's own structural convention (magic + version +
* CRC, one 4KiB header) -- a sibling format, not a field bolted onto
* homeblocks_sig_t itself: homeblocks_sig_t's own header comment already
* states it's "deliberately narrow in scope" (identity-drive fields only,
* no spare room), and Artemis's disk is conceptually a different kind of
* thing (one dedicated fleet-owned device, not one of many candidate
* identity drives), not a variant of the same one.
*
* CORRECTION, same day: the first version of this format placed the header
* at a fixed bottom-of-device forth-block (4, i.e. devblock 1), copying
* homeblocks_sig_t's own devblock-1 convention. That convention is safe for
* an identity thumbdrive (raw, dedicated storage -- capsule_mint.c writes
* directly, no block-subsystem format involved), but Artemis's disk is
* block_subsystem.c's own STFR/v2-formatted volume: devblock 0 holds that
* format's header (read_header_4k()/write_header_4k(), block_subsystem.c)
* and devblock 1 is the FIRST DEVBLOCK OF THE LIVE BAM
* (blk_compute_fresh_geometry(): bam_start = 1). Writing artemis_sig_t
* there would have overwritten Artemis's own live allocation map on the
* very first real boot this ran against -- caught (via `git status`/`grep`
* cross-reference against block_subsystem.c, not against a live disk)
* before the genesis-stamp call site ever executed against the real image.
*
* The header now lives at a fixed offset from the END of the device
* instead (ARTEMIS_SIG_DEVBLOCK_FROM_TOP, see below) -- the same
* "top-of-device, outside the user-addressable LBN pool" region
* block_subsystem.c's own meta_fence_blocks reservation (128 devblocks by
* default, BLK_META_FENCE_INIT) already carves out for exactly this kind
* of system metadata, and where Zuse's own genesis marker/eligibility list
* already live (blk_meta_zone_read()/write(), devblock_from_top 0 and 1+
* respectively). This format deliberately does NOT go through
* blk_meta_zone_*, though: that accessor requires the device to already be
* blk_subsys_attach_device()'d and format-detected (first_disk_slot()) --
* exactly the state generic pre-attach discovery (repl.c's idle-loop
* USB-MSC scan) doesn't have yet, which is the entire reason this format
* exists. artemis_sig_check()/artemis_sig_genesis_stamp() instead compute
* the same top-of-device arithmetic independently via blkio_info(), so
* they work on a raw, not-yet-attached device exactly like
* homeblocks_sig_check() already does. ARTEMIS_SIG_DEVBLOCK_FROM_TOP is
* fixed well clear of Zuse's two tenants (0 and 1+, open-ended but
* realistically small -- 127 pubkeys per chained devblock) so the two
* subsystems' independent top-of-device math can never collide, without
* this format needing to know how far the eligibility chain has actually
* grown on any given boot.
*
* Reserves offset/size pointers to the growable per-VM log-persistence
* region (FABRIC-3.md §XXVI follow-on's own log-record work), the same way
* homeblocks_sig_t reserves pointers to where the cert and identity source
* attach -- this format doesn't need revisiting when that region's own
* internal layout is designed.
*/
#ifndef STARKERNEL_ARTEMIS_SIG_H
#define STARKERNEL_ARTEMIS_SIG_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*===========================================================================
* Magic Field Packing -- same bit layout convention as HOMEBLOCKS_SIG_PACK
*
* bits 0..31 : 'ARTM' (0x4D545241 little-endian) -- distinct from
* homeblocks_sig_t's 'LAHB', so a generic scan can tell an
* Artemis disk apart from an identity thumbdrive by content
* alone, regardless of which bus either was found on.
* bits 32..39 : version (0 for v0)
* bits 40..63 : reserved (zero)
*===========================================================================*/
#define ARTEMIS_SIG_MAGIC 0x4D545241ULL /* 'ARTM' */
#define ARTEMIS_SIG_VERSION_0 0
#define ARTEMIS_SIG_PACK(ver) \
(ARTEMIS_SIG_MAGIC | ((uint64_t)(ver) << 32))
#define ARTEMIS_SIG_GET_MAGIC(m) ((uint32_t)((m) & 0xFFFFFFFFULL))
#define ARTEMIS_SIG_GET_VERSION(m) ((uint8_t)(((m) >> 32) & 0xFF))
/* Fence-relative top-of-device index -- see this header's own CORRECTION
* comment above for why this replaced a fixed bottom-of-device forth-block.
* Same "distance from the very last physical devblock" convention
* block_subsystem.c's blk_meta_zone_read()/write() use internally (0 =
* last devblock, 1 = second-to-last, ...), computed independently here via
* blkio_info() rather than through that accessor (which needs an already-
* attached, format-detected slot this code runs before). Fixed well past
* Zuse's genesis marker (devblock_from_top 0) and eligibility list
* (devblock_from_top 1, chained upward as needed) -- see
* zuse_eligibility_list.h -- so the two subsystems' independent top-of-
* device math can never collide regardless of how large the eligibility
* chain grows in practice. Well inside BLK_META_FENCE_INIT (128 devblocks,
* block_subsystem.h) on any real Artemis disk. */
#define ARTEMIS_SIG_DEVBLOCK_FROM_TOP 64u
/*===========================================================================
* artemis_sig_t - Artemis disk signature header (exactly one 4KiB devblock)
*===========================================================================*/
typedef struct {
uint64_t magic; /* ARTEMIS_SIG_PACK(...) */
uint8_t disk_uuid[16]; /* Mirrors homeblocks_sig_t's drive_uuid --
* one Artemis disk exists today, but costs
* nothing to future-proof the same way. */
uint64_t genesis_time_ns; /* Monotonic timestamp when this signature
* was first stamped (the one-time genesis
* step, not every boot). */
uint64_t metadata_devblocks; /* Size of the metadata region at the start
* of this raw device (sig header + log
* region), in 4KiB devblocks -- everything
* past this is Artemis's own general
* block-storage pool, same "no partition
* boundary" convention homeblocks_sig_t
* uses for an identity's own pool. */
uint32_t log_region_offset; /* Devblock offset where the growable
* per-VM log-persistence region starts;
* 0 = not yet allocated. */
uint32_t log_region_devblocks; /* Current reserved size of the log
* region, in devblocks -- grows over
* time (same growable-reservation
* mechanism the metadata fence design
* already uses elsewhere), not a single
* fixed guess made once at genesis. */
uint64_t hdr_crc; /* Computed over every field above this
* one, same boundary/discipline as
* homeblocks_sig_compute_crc(). */
/* Padding to keep the header exactly one 4KiB devblock. */
uint8_t _pad[4096 - (
8 + /* magic */
16 + /* disk_uuid */
8 + /* genesis_time_ns */
8 + /* metadata_devblocks */
4 + 4 + /* log_region_offset, log_region_devblocks */
8 /* hdr_crc */
)];
} artemis_sig_t;
/* C99-portable compile-time size assertion (no _Static_assert -- that's
* C11), same discipline homeblocks_sig.h's own check uses. */
typedef char artemis_sig_size_check[(sizeof(artemis_sig_t) == 4096) ? 1 : -1];
/*===========================================================================
* Signature check (mirrors homeblocks_sig_result_t exactly)
*===========================================================================*/
typedef enum {
ARTEMIS_SIG_OK = 0, /* magic, version, and crc all check out */
ARTEMIS_SIG_BLANK, /* magic does not match -- blank, foreign, or
* an identity thumbdrive (different magic) */
ARTEMIS_SIG_BAD_VERSION, /* magic matches, version unrecognized */
ARTEMIS_SIG_BAD_CRC, /* magic+version match, crc fails -- corrupt
* or tampered */
ARTEMIS_SIG_READ_ERROR /* could not read from the device at all */
} artemis_sig_result_t;
/* Forward-declared, not included here -- same reasoning as
* homeblocks_sig.h's own forward declaration of struct blkio_dev. */
struct blkio_dev;
/*
* artemis_sig_check - Read and verify the Artemis disk signature header, at
* the fixed ARTEMIS_SIG_DEVBLOCK_FROM_TOP offset from whatever `dev`
* reports as its own total size (blkio_info()) -- no attach or format
* detection required, same "works on a raw, not-yet-attached device"
* contract homeblocks_sig_check() already has.
*
* @param dev Open block device to read from.
* @param out_sig On ARTEMIS_SIG_OK, populated with the verified
* header. Left unspecified on any other result.
* @return ARTEMIS_SIG_OK, or the specific reason for refusal.
*/
artemis_sig_result_t artemis_sig_check(struct blkio_dev *dev,
artemis_sig_t *out_sig);
/*
* artemis_sig_compute_crc - CRC-64 over every field of `sig` up to but not
* including hdr_crc itself and the trailing padding. Exposed publicly for
* the same reason homeblocks_sig_compute_crc() is: both the check and the
* future genesis-stamping step need the identical computation.
*
* @param sig Header to checksum. hdr_crc and _pad are not read.
* @return The CRC-64 value that hdr_crc should hold for `sig` to verify.
*/
uint64_t artemis_sig_compute_crc(const artemis_sig_t *sig);
/*
* artemis_sig_genesis_stamp - One-time write of a fresh artemis_sig_t onto
* a disk already confirmed to be Artemis's own (never called speculatively
* on an unidentified/blank device -- see the call site in kernel_main.c for
* why that's always safe there: virtio_blk_find_artemis() only ever
* succeeds against the one dedicated PCI device, so a BLANK read at this
* fblock unambiguously means "this disk has never been stamped," not
* "this might be some other blank drive"). log_region_offset/devblocks are
* written as 0 (not yet allocated) -- step 4's own log-persistence design
* allocates them later via a normal artemis_sig_t rewrite, same one-header
* location.
*
* disk_uuid is drawn from rng_get_bytes(), same entropy source
* capsule_mint.c already uses for an identity thumbdrive's drive_uuid.
* genesis_time_ns is written as 0 -- no monotonic-ns source exists
* anywhere in this codebase yet, same open item homeblocks_sig_t's own
* minted_time_ns field already carries.
*
* Idempotent by construction: a caller must check artemis_sig_check()
* returns ARTEMIS_SIG_BLANK first (this function does not re-check, to
* avoid a second redundant read the caller already just performed).
*
* @param dev Open block device to write to. Must already be confirmed as
* Artemis's own disk.
* @return 0 on success (including read-back verification), -1 on any
* entropy, write, or verify failure -- the disk is left however
* the failed write left it, same as capsule_mint.c's own
* write-then-verify discipline.
*/
int artemis_sig_genesis_stamp(struct blkio_dev *dev);
#ifdef __cplusplus
}
#endif
#endif /* STARKERNEL_ARTEMIS_SIG_H */