Files
LithosAnanke/src/starkernel/artemis_sig.c
T
Robert Allan JamesandClaude Sonnet 5 61755fde78
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
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

162 lines
6.3 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) 2023–2025 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.c - Artemis disk signature check (FABRIC-3.md §XXVI follow-on).
* See starkernel/artemis_sig.h for the format and interface design.
*/
#include "starkernel/artemis_sig.h"
#include <stddef.h>
#include <string.h>
#include "blkio.h"
#include "block_subsystem.h" /* compute_crc64() -- same CRC-64/ISO
* homeblocks_sig.c's own check reuses,
* not duplicated here either */
#include "starkernel/rng.h" /* rng_get_bytes() -- same entropy source
* capsule_mint.c already uses for an
* identity thumbdrive's own drive_uuid */
uint64_t artemis_sig_compute_crc(const artemis_sig_t *sig) {
/* Covers every field up to but not including hdr_crc itself (and never
* _pad, which sits after it) -- offsetof is the exact boundary, same
* discipline as homeblocks_sig_compute_crc(). */
size_t crc_span = offsetof(artemis_sig_t, hdr_crc);
return compute_crc64((const uint8_t *)sig, crc_span);
}
/* Resolve ARTEMIS_SIG_DEVBLOCK_FROM_TOP (a distance from the last physical
* devblock) to an absolute starting forth-block on `dev`, using only
* blkio_info() -- no attach or format-detection required, matching
* block_subsystem.c's own devblock_from_top arithmetic
* (total_devblocks - 1 - devblock_from_top) exactly, but computed
* independently since blk_meta_zone_*() isn't reachable pre-attach. The
* floor division (total forth-blocks / 4) matches block_subsystem.c's own
* udiv_floor(total_blkio_blocks_1k, 4) -- plain unsigned integer division
* already floors, so no separate helper is needed here.
*
* @return 0 and *out_fblock set on success; -1 if the device is too small
* to hold the reserved offset at all (couldn't happen on a real
* Artemis disk, but a foreign/tiny device must never underflow
* into a wild block). */
static int artemis_sig_start_fblock(struct blkio_dev *dev, uint32_t *out_fblock) {
blkio_info_t info;
if (blkio_info((blkio_dev_t *)dev, &info) != BLKIO_OK) return -1;
uint32_t total_devblocks = info.total_blocks / 4u;
if (total_devblocks < ARTEMIS_SIG_DEVBLOCK_FROM_TOP + 1u) return -1;
uint32_t devblock_idx = total_devblocks - 1u - ARTEMIS_SIG_DEVBLOCK_FROM_TOP;
*out_fblock = devblock_idx * 4u;
return 0;
}
artemis_sig_result_t artemis_sig_check(struct blkio_dev *dev,
artemis_sig_t *out_sig) {
uint8_t buf[4096];
uint32_t i;
uint32_t sig_start_fblock;
artemis_sig_t local;
uint64_t expected_crc;
if (!dev) return ARTEMIS_SIG_READ_ERROR;
if (artemis_sig_start_fblock(dev, &sig_start_fblock) != 0) return ARTEMIS_SIG_READ_ERROR;
/* artemis_sig_t is exactly one 4KiB devblock; blkio's own unit is a
* 1KiB "forth block" (BLKIO_FORTH_BLOCK_SIZE), so the header spans 4
* consecutive reads starting at sig_start_fblock -- same pattern as
* homeblocks_sig_check(). */
for (i = 0; i < 4; i++) {
if (blkio_read((blkio_dev_t *)dev, sig_start_fblock + i,
buf + (size_t)i * BLKIO_FORTH_BLOCK_SIZE) != BLKIO_OK) {
return ARTEMIS_SIG_READ_ERROR;
}
}
/* Copy into a properly-aligned local rather than reinterpreting buf's
* address directly -- same strict-aliasing/alignment reasoning as
* homeblocks_sig_check(). */
memcpy(&local, buf, sizeof(local));
if (ARTEMIS_SIG_GET_MAGIC(local.magic) != (uint32_t)(ARTEMIS_SIG_MAGIC & 0xFFFFFFFFULL)) {
return ARTEMIS_SIG_BLANK;
}
if (ARTEMIS_SIG_GET_VERSION(local.magic) != ARTEMIS_SIG_VERSION_0) {
return ARTEMIS_SIG_BAD_VERSION;
}
expected_crc = artemis_sig_compute_crc(&local);
if (expected_crc != local.hdr_crc) {
return ARTEMIS_SIG_BAD_CRC;
}
if (out_sig) *out_sig = local;
return ARTEMIS_SIG_OK;
}
int artemis_sig_genesis_stamp(struct blkio_dev *dev) {
if (!dev) return -1;
uint32_t sig_start_fblock;
if (artemis_sig_start_fblock(dev, &sig_start_fblock) != 0) return -1;
artemis_sig_t sig;
memset(&sig, 0, sizeof(sig));
sig.magic = ARTEMIS_SIG_PACK(ARTEMIS_SIG_VERSION_0);
if (rng_get_bytes(sig.disk_uuid, sizeof(sig.disk_uuid)) != 0) return -1;
sig.genesis_time_ns = 0; /* no monotonic-ns source yet, same open
* item as homeblocks_sig_t's own
* minted_time_ns field */
sig.metadata_devblocks = 1; /* just this header devblock for now --
* grows to cover the log region once
* step 4 allocates it (a normal rewrite
* of this same one-header location) */
sig.log_region_offset = 0; /* not yet allocated */
sig.log_region_devblocks = 0;
sig.hdr_crc = artemis_sig_compute_crc(&sig);
uint8_t buf[4096];
memcpy(buf, &sig, sizeof(sig));
uint32_t i;
for (i = 0; i < 4; i++) {
if (blkio_write((blkio_dev_t *)dev, sig_start_fblock + i,
buf + (size_t)i * BLKIO_FORTH_BLOCK_SIZE) != BLKIO_OK) {
return -1;
}
}
(void)blkio_flush((blkio_dev_t *)dev);
/* Read-back verification, same discipline as capsule_mint.c's own
* verify_mint() -- a successful blkio_write() return only means the
* command chain completed, not that the bytes survive to be read
* back correctly. */
artemis_sig_t verify;
if (artemis_sig_check(dev, &verify) != ARTEMIS_SIG_OK) return -1;
if (memcmp(&verify, &sig, sizeof(sig)) != 0) return -1;
return 0;
}