§H.12 step 18: zuse_eligibility.c -- read/add/membership-check
is_member() (fail-closed) and add() (idempotent, chains new devblocks onto the tail as needed) over the item-17 eligibility-list devblock chain, mirroring capsule_zuse_boot.c's magic/version/CRC-64 validation convention. No callers yet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QgooKd5hJNtTYqB6CyK5f9
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
21ad5f7373
commit
f4615cf605
+13
-1
@@ -4111,7 +4111,19 @@ work, not new invention.
|
|||||||
confirms the header is syntactically clean (verified via a standalone `gcc -fsyntax-only`
|
confirms the header is syntactically clean (verified via a standalone `gcc -fsyntax-only`
|
||||||
compile, and via the struct-size static assert) and doesn't break the build. Verified
|
compile, and via the struct-size static assert) and doesn't break the build. Verified
|
||||||
3-arch boot to `ok>` (amd64/aarch64/riscv64).
|
3-arch boot to `ok>` (amd64/aarch64/riscv64).
|
||||||
- [ ] **18.** Implement read/add/membership-check functions in C.
|
- [x] **18. DONE 2026-09-03.** `include/starkernel/zuse_eligibility.h` +
|
||||||
|
`src/starkernel/capsule/zuse_eligibility.c`: `zuse_eligibility_is_member()`/
|
||||||
|
`zuse_eligibility_add()` over the item-17 devblock chain, mirroring
|
||||||
|
`capsule_zuse_boot.c`'s own magic/version/CRC-64 validation convention exactly (the fence
|
||||||
|
region is never zeroed at format time, so blank and corrupt devblocks are indistinguishable
|
||||||
|
and handled identically — merged into a single "absent" read failure). `is_member()` is
|
||||||
|
fail-closed (any read/chain error reads as "not eligible," never permissive). `add()` is
|
||||||
|
idempotent (re-adding an existing entry is a no-op success) and chains a fresh devblock onto
|
||||||
|
the tail when the current one fills, relying on `blk_meta_zone_write()`'s own existing
|
||||||
|
bounds check against `meta_fence_blocks` to surface an exhausted fence budget as an ordinary
|
||||||
|
write failure rather than duplicating that check here. No callers yet (that's item 19).
|
||||||
|
Verified 3-arch boot to `ok>` (amd64/aarch64/riscv64), each run in the foreground per
|
||||||
|
CLAUDE.md's QEMU rule.
|
||||||
- [ ] **19.** Add a Zuse-only FORTH word to add an entry, gated by `zuse_session`.
|
- [ ] **19.** Add a Zuse-only FORTH word to add an entry, gated by `zuse_session`.
|
||||||
|
|
||||||
**Phase 7 — Message card gate + `ELEVATE-REQUEST` (H.8)**
|
**Phase 7 — Message card gate + `ELEVATE-REQUEST` (H.8)**
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Capsule Block Manifest — Auto-generated
|
# Capsule Block Manifest — Auto-generated
|
||||||
<!-- Generated by mkcapsule --manifest 2026-09-03T15:10:29Z -->
|
<!-- Generated by mkcapsule --manifest 2026-09-03T15:22:32Z -->
|
||||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||||
<!-- Hand-written justifications and immutability notes live -->
|
<!-- Hand-written justifications and immutability notes live -->
|
||||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
StarForth — Steady-State Virtual Machine Runtime
|
||||||
|
|
||||||
|
Copyright (c) 2023–2025 Robert A. James
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the StarForth License, Version 1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zuse_eligibility.h - Read/add/membership-check over the on-disk
|
||||||
|
* elevation eligibility list (FABRIC-3.md §H.5/§H.12 Phase 6,
|
||||||
|
* zuse_eligibility_list.h's zuse_eligibility_devblock_t chain). Zuse
|
||||||
|
* checks zuse_eligibility_is_member() before honoring any
|
||||||
|
* ELEVATE-REQUEST (§H.7/§H.8) -- a gating layer on top of the
|
||||||
|
* message-based elevation trigger, not a replacement for it. Adding an
|
||||||
|
* entry (zuse_eligibility_add()) is meant to be reachable only from a
|
||||||
|
* Zuse-only FORTH word (§H.12 item 19, gated by zuse_session), not
|
||||||
|
* called directly from arbitrary session code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STARKERNEL_ZUSE_ELIGIBILITY_H
|
||||||
|
#define STARKERNEL_ZUSE_ELIGIBILITY_H
|
||||||
|
|
||||||
|
#ifdef __STARKERNEL__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zuse_eligibility_is_member - Check whether pubkey appears anywhere in
|
||||||
|
* the eligibility list chain. Fail-closed: an empty/nonexistent list, a
|
||||||
|
* corrupt/foreign devblock encountered mid-chain, or any I/O error all
|
||||||
|
* read as "not eligible" (0), never "eligible" -- this gates elevation,
|
||||||
|
* so an unreadable list must never be treated as permissive.
|
||||||
|
*
|
||||||
|
* @param pubkey 32-byte Ed25519 public key to look up.
|
||||||
|
* @return 1 if found, 0 otherwise (including on any error).
|
||||||
|
*/
|
||||||
|
int zuse_eligibility_is_member(const uint8_t pubkey[32]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zuse_eligibility_add - Add pubkey to the eligibility list, creating
|
||||||
|
* the list's head devblock if it doesn't exist yet and chaining a fresh
|
||||||
|
* devblock onto the tail if the current tail is full. Idempotent: adding
|
||||||
|
* an already-present pubkey is a no-op success, not a duplicate entry.
|
||||||
|
*
|
||||||
|
* @param pubkey 32-byte Ed25519 public key to add.
|
||||||
|
* @return 0 on success (added, or already present), -1 on
|
||||||
|
* failure (fence write failed, fence budget exhausted, or
|
||||||
|
* a corrupt devblock was encountered mid-chain).
|
||||||
|
*/
|
||||||
|
int zuse_eligibility_add(const uint8_t pubkey[32]);
|
||||||
|
|
||||||
|
#endif /* __STARKERNEL__ */
|
||||||
|
|
||||||
|
#endif /* STARKERNEL_ZUSE_ELIGIBILITY_H */
|
||||||
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
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
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 <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user