feat(v4.0.0): the nucleus is a capsule, sent to a node born empty
MESH.md step 2. A capsule of F18 code is the words a neighbour writes to a node's port: for each stretch of memory, "@p a! @p push", the address and count, "@p !+ unext" and the words; then a jump to the start. A node born empty executes that from its port, so it needs nothing in it beforehand. - capsule.h/.c: v4_capsule_write, any node's memory as such a capsule - mkimage writes the nucleus so, to capsules/v4/nucleus-64.f18, and the addresses a host needs as a C file; the memory image is no longer linked into either product - mkcapsule is unchanged: the nucleus capsule is a built file kept under capsules/, as BLOCK_MAP.md is, and is baked, hashed and signed with the rest - boot: the node is born empty (v4_image_born); the nucleus capsule is found, its hash and signature checked, and given to the node a word at a time as it reads its port; PARITY:V4_NUCLEUS carries its name and hash Verified: test_fabric.c (59 checks, both widths, and under ASan and UBSan): a memory with a programme and scattered words arrives word for word in an empty node and runs. The nucleus capsule rebuilds byte for byte. hosted-check passes on three ISAs; clean qemu with STARFORTH_V4=1 on amd64, aarch64 and riscv64 takes the nucleus in, passes POST (550 of 550) and answers lines typed at each prompt (logs/20261006-102421, -102706, -103048). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
9af442f793
commit
42610e844f
+70
-16
@@ -80,17 +80,6 @@ uint64_t v4_boot_dict_hash(const v4_node *n, const v4_image *im)
|
||||
return hash_cell(h, n->mem[im->latest]);
|
||||
}
|
||||
|
||||
static uint64_t image_hash(const v4_image *im)
|
||||
{
|
||||
uint64_t h = FNV_OFFSET;
|
||||
unsigned i;
|
||||
for (i = 0; i < im->count; i++) {
|
||||
h = hash_cell(h, im->cells[i].addr);
|
||||
h = hash_cell(h, im->cells[i].value);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
/* how many words FORTH holds: the list from LATEST, each entry's link in the
|
||||
* cell before its code */
|
||||
static uint32_t word_count(const v4_node *n, const v4_image *im)
|
||||
@@ -258,6 +247,72 @@ static int load_capsule(const v4_boot *b, const char *name)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ---- the nucleus ----------------------------------------------------------- */
|
||||
|
||||
/* The nucleus is a capsule of F18 code (v4/include/v4/capsule.h): the words
|
||||
* themselves, to be written to the port of a node that has nothing in it.
|
||||
* The boot is that node's neighbour. It finds the capsule, checks its hash
|
||||
* and its signature as it does any capsule's, and gives the node a word
|
||||
* each time it reads its port, until the node has taken them all and is
|
||||
* where the nucleus waits to be handed a line. */
|
||||
#define NUCLEUS_NAME_(bits) "v4:nucleus-" #bits ".f18"
|
||||
#define NUCLEUS_NAME__(bits) NUCLEUS_NAME_(bits)
|
||||
#define NUCLEUS_NAME NUCLEUS_NAME__(V4_CELL_BITS)
|
||||
#define CELL_BYTES (V4_CELL_BITS / 8)
|
||||
|
||||
static int load_nucleus(const v4_boot *b)
|
||||
{
|
||||
const CapsuleDirHeader *dir = capsule_get_directory();
|
||||
const CapsuleDesc *descs = capsule_get_descriptors();
|
||||
const CapsuleNameEntry *names = capsule_get_names();
|
||||
const uint8_t *arena = capsule_get_arena();
|
||||
const CapsuleDesc *cap;
|
||||
const uint8_t *p;
|
||||
CapsuleValidateResult vr;
|
||||
CapsuleSigResult sr;
|
||||
v4_node *n = b->n;
|
||||
uint64_t words, at = 0, steps = 0;
|
||||
|
||||
cap = capsule_find_by_name(dir, descs, names, NUCLEUS_NAME);
|
||||
if (!cap) { say(b, "V4: capsule " NUCLEUS_NAME " is not in this binary\n"); return 0; }
|
||||
vr = capsule_validate(cap, arena, dir->arena_size, 1);
|
||||
if (vr != CAPSULE_VALID) {
|
||||
say(b, "V4: capsule " NUCLEUS_NAME ": "); say(b, capsule_validate_result_str(vr)); say(b, "\n");
|
||||
return 0;
|
||||
}
|
||||
sr = capsule_verify_signature(descs, names, capsule_get_signatures(), arena, dir->desc_count, (int)(cap - descs));
|
||||
if (sr != CAPSULE_SIG_OK) {
|
||||
say(b, "V4: capsule " NUCLEUS_NAME " signature: "); say(b, capsule_sig_result_str(sr)); say(b, "\n");
|
||||
if (sr == CAPSULE_SIG_INVALID) return 0;
|
||||
}
|
||||
p = capsule_get_payload(cap, arena);
|
||||
if (!p || cap->length % CELL_BYTES != 0) { say(b, "V4: capsule " NUCLEUS_NAME " is not whole words\n"); return 0; }
|
||||
words = cap->length / CELL_BYTES;
|
||||
|
||||
while (at < words || !v4_line_done(n, b->im)) {
|
||||
(void)v4_exec_step_word(n, b->es, b->h);
|
||||
if (n->stopped || n->asking || ++steps > STEP_LIMIT) {
|
||||
say(b, "V4: the node did not take the nucleus in\n");
|
||||
return 0;
|
||||
}
|
||||
if (n->reading && !n->given) {
|
||||
v4_ucell word = 0;
|
||||
unsigned i;
|
||||
if (at >= words) { say(b, "V4: the node wants more than the nucleus holds\n"); return 0; }
|
||||
for (i = 0; i < CELL_BYTES; i++) word |= (v4_ucell)p[at * CELL_BYTES + i] << (8 * i);
|
||||
v4_node_port_give(n, 0, (v4_cell)word);
|
||||
at++;
|
||||
}
|
||||
}
|
||||
|
||||
say(b, "PARITY:V4_NUCLEUS name=" NUCLEUS_NAME);
|
||||
say(b, " capsule_id="); say_hex(b, cap->capsule_id);
|
||||
say(b, " capsule_hash="); say_hex(b, cap->content_hash);
|
||||
say(b, " words="); say_dec(b, word_count(n, b->im));
|
||||
say(b, "\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ---- the whole boot ------------------------------------------------------ */
|
||||
|
||||
int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks)
|
||||
@@ -265,13 +320,12 @@ int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks)
|
||||
unsigned i;
|
||||
|
||||
post_len = 0; post_seen = 0; post_clean = 0;
|
||||
if (!v4_image_boot(b->n, b->es, b->h, b->im, disk, blocks)) {
|
||||
say(b, "V4: the nucleus image is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n");
|
||||
/* a node is born with nothing in it, and is sent the nucleus */
|
||||
if (!v4_image_born(b->n, b->es, b->h, b->im, disk, blocks)) {
|
||||
say(b, "V4: the nucleus is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
say(b, "PARITY:V4_NUCLEUS words="); say_dec(b, word_count(b->n, b->im));
|
||||
say(b, " image_hash="); say_hex(b, image_hash(b->im));
|
||||
say(b, "\n");
|
||||
if (!load_nucleus(b)) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; }
|
||||
|
||||
/* the kernel's words, before any capsule that might use one */
|
||||
for (i = 0; i < b->word_count; i++) {
|
||||
|
||||
Reference in New Issue
Block a user