/* StarForth — Steady-State Virtual Machine Runtime Copyright (c) 2023–2025 Robert A. James All rights reserved. Licensed under the StarForth License, Version 1.0 */ #ifndef __STARKERNEL__ #error "zuse_eligibility.c is kernel-only" #endif #include "starkernel/zuse_eligibility.h" #include "starkernel/zuse_eligibility_list.h" #include "block_subsystem.h" /* compute_crc64(), blk_meta_zone_read/write */ #include #include /* Read and validate one devblock of the chain. Mirrors * capsule_zuse_boot.c's genesis_marker_read() convention exactly: magic * mismatch, version mismatch, and CRC mismatch are all merged into a * single -1 ("treat as absent/corrupt, not present") -- the fence region * is never zeroed at format time (block_subsystem.c's blk_commit_format() * only zeroes BAM/reloc), so blank foreign bytes and genuine corruption * are indistinguishable and must be handled identically here. */ static int list_devblock_read(uint32_t devblock_from_top, zuse_eligibility_devblock_t *out) { if (blk_meta_zone_read(devblock_from_top, (uint8_t *)out) != 0) return -1; if (out->magic != ZUSE_ELIGIBILITY_LIST_MAGIC) return -1; if (out->version != ZUSE_ELIGIBILITY_LIST_VERSION) return -1; if (out->count > ZUSE_ELIGIBILITY_ENTRIES_PER_DEVBLOCK) return -1; uint64_t want_crc = compute_crc64((const uint8_t *)out, offsetof(zuse_eligibility_devblock_t, crc)); if (want_crc != out->crc) return -1; return 0; } static int list_devblock_write(uint32_t devblock_from_top, zuse_eligibility_devblock_t *blk) { blk->crc = compute_crc64((const uint8_t *)blk, offsetof(zuse_eligibility_devblock_t, crc)); return blk_meta_zone_write(devblock_from_top, (const uint8_t *)blk) == 0 ? 0 : -1; } int zuse_eligibility_is_member(const uint8_t pubkey[32]) { uint32_t devblock = ZUSE_ELIGIBILITY_LIST_HEAD_DEVBLOCK; zuse_eligibility_devblock_t blk; for (;;) { if (list_devblock_read(devblock, &blk) != 0) return 0; /* fail-closed */ for (uint32_t i = 0; i < blk.count; i++) { if (memcmp(blk.entries[i], pubkey, 32) == 0) return 1; } if (blk.next_devblock_from_top == ZUSE_ELIGIBILITY_LIST_NO_NEXT) return 0; devblock = blk.next_devblock_from_top; } } int zuse_eligibility_add(const uint8_t pubkey[32]) { uint32_t devblock = ZUSE_ELIGIBILITY_LIST_HEAD_DEVBLOCK; zuse_eligibility_devblock_t blk; if (list_devblock_read(devblock, &blk) != 0) { /* List doesn't exist yet (blank fence, or this is genuinely the * first entry ever added) -- create the head devblock fresh. */ memset(&blk, 0, sizeof(blk)); blk.magic = ZUSE_ELIGIBILITY_LIST_MAGIC; blk.version = ZUSE_ELIGIBILITY_LIST_VERSION; blk.count = 1; blk.next_devblock_from_top = ZUSE_ELIGIBILITY_LIST_NO_NEXT; memcpy(blk.entries[0], pubkey, 32); return list_devblock_write(devblock, &blk); } /* Walk the existing chain: bail out early (idempotent success) if * already present, otherwise track the tail devblock to append to. */ for (;;) { for (uint32_t i = 0; i < blk.count; i++) { if (memcmp(blk.entries[i], pubkey, 32) == 0) return 0; /* already eligible */ } if (blk.next_devblock_from_top == ZUSE_ELIGIBILITY_LIST_NO_NEXT) break; devblock = blk.next_devblock_from_top; if (list_devblock_read(devblock, &blk) != 0) return -1; /* corrupt mid-chain */ } if (blk.count < ZUSE_ELIGIBILITY_ENTRIES_PER_DEVBLOCK) { memcpy(blk.entries[blk.count], pubkey, 32); blk.count++; return list_devblock_write(devblock, &blk); } /* Tail devblock is full -- chain a fresh one drawn from the * already-reserved fence budget, one devblock_from_top past the * current tail (blk_meta_zone_write() itself refuses any offset at * or beyond the on-disk meta_fence_blocks reservation, so an * exhausted budget surfaces here as an ordinary write failure). */ uint32_t new_devblock = devblock + 1; zuse_eligibility_devblock_t new_blk; memset(&new_blk, 0, sizeof(new_blk)); new_blk.magic = ZUSE_ELIGIBILITY_LIST_MAGIC; new_blk.version = ZUSE_ELIGIBILITY_LIST_VERSION; new_blk.count = 1; new_blk.next_devblock_from_top = ZUSE_ELIGIBILITY_LIST_NO_NEXT; memcpy(new_blk.entries[0], pubkey, 32); if (list_devblock_write(new_devblock, &new_blk) != 0) return -1; blk.next_devblock_from_top = new_devblock; return list_devblock_write(devblock, &blk); }