/* 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 #include #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; }