feat(v4.0.0): the block words are the kernel's -- v3's way in full
Ruled 2026-10-07. BLOCK, BUFFER, UPDATE, SAVE-BUFFERS and EMPTY-BUFFERS are each one kernel request on port 0, by block number only. The node has a window of four slots in its memory, as a v3 VM has; the kernel copies blocks into it, keeps the record of which block is in which slot, and decides when a block is written (v4/system/blocks.c). The node keeps no record and gives the kernel no address, so a request cannot overwrite the node's code. When a block is written stays FORTH-79's: UPDATE marks it. When the chain of devices changes the slots are let go, as in v3. The window is 512 cells more than the two buffers were; the dictionary space ends that much lower, at 13824. MESH.md 8.5 had said, from the review, that a v3 VM's BLOCK is the kernel's buffer. It is not, and the section now says what was reported, what v3 does, and what was ruled. make -C v4 test, sanitize and hosted-check pass; amd64, aarch64 and riscv64 boot, POST 538 of 538, the same hashes on all six: logs/20261007-092835, -093112, -093456. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7f4d946d2e
commit
9bfd5071d2
+126
-33
@@ -6,48 +6,141 @@
|
||||
#include "v4/blocks.h"
|
||||
#include "block_subsystem.h"
|
||||
|
||||
int v4_blocks_serve(v4_node *n, v4_cell request)
|
||||
static void let_go(v4_blocks *w)
|
||||
{
|
||||
v4_cell addr, num;
|
||||
uint32_t block;
|
||||
uint8_t *b;
|
||||
unsigned i;
|
||||
for (i = 0; i < V4_BLOCK_SLOTS; i++) { w->block[i] = 0; w->changed[i] = 0; }
|
||||
w->next = 0;
|
||||
w->last = -1;
|
||||
}
|
||||
|
||||
void v4_blocks_init(v4_blocks *w, v4_cell window)
|
||||
{
|
||||
w->window = window;
|
||||
w->epoch = blk_subsys_epoch();
|
||||
let_go(w);
|
||||
}
|
||||
|
||||
static v4_cell slot_addr(const v4_blocks *w, unsigned s)
|
||||
{
|
||||
return w->window + (v4_cell)(s * V4_BLOCK_CELLS);
|
||||
}
|
||||
|
||||
/* The block in slot s is written. Whether or not storage takes it, it is
|
||||
* no longer marked. */
|
||||
static int write_slot(v4_blocks *w, v4_node *n, unsigned s)
|
||||
{
|
||||
uint8_t *b = blk_get_buffer(w->block[s], 1);
|
||||
v4_cell at = slot_addr(w, s);
|
||||
unsigned i;
|
||||
|
||||
w->changed[s] = 0;
|
||||
if (!b) return V4_BLOCK_REFUSED;
|
||||
for (i = 0; i < V4_BLOCK_CELLS; i++) {
|
||||
v4_ucell c = (v4_ucell)n->mem[at + (v4_cell)i];
|
||||
b[4u * i] = (uint8_t)(c & 0xFFu);
|
||||
b[4u * i + 1u] = (uint8_t)((c >> 8) & 0xFFu);
|
||||
b[4u * i + 2u] = (uint8_t)((c >> 16) & 0xFFu);
|
||||
b[4u * i + 3u] = (uint8_t)((c >> 24) & 0xFFu);
|
||||
}
|
||||
return (blk_update(w->block[s]) == BLK_OK && blk_flush(w->block[s]) == BLK_OK) ? V4_BLOCK_OK : V4_BLOCK_REFUSED;
|
||||
}
|
||||
|
||||
/* A slot to put a block in: an empty one, or one whose block is not marked,
|
||||
* or, with every one marked, the next in turn, its block written first.
|
||||
* *status is not V4_BLOCK_OK if that write was refused; the slot is empty
|
||||
* all the same. */
|
||||
static unsigned take_slot(v4_blocks *w, v4_node *n, int *status)
|
||||
{
|
||||
unsigned i, s;
|
||||
|
||||
*status = V4_BLOCK_OK;
|
||||
for (i = 0; i < V4_BLOCK_SLOTS; i++) if (w->block[i] == 0) return i;
|
||||
for (i = 0; i < V4_BLOCK_SLOTS; i++) {
|
||||
s = (w->next + i) % V4_BLOCK_SLOTS;
|
||||
if (!w->changed[s]) break;
|
||||
}
|
||||
if (i == V4_BLOCK_SLOTS) {
|
||||
s = w->next;
|
||||
*status = write_slot(w, n, s);
|
||||
}
|
||||
w->next = (s + 1u) % V4_BLOCK_SLOTS;
|
||||
w->block[s] = 0;
|
||||
w->changed[s] = 0;
|
||||
if (w->last == (int)s) w->last = -1;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* BLOCK and BUFFER: ( n -- waddr status ) */
|
||||
static void slot_for(v4_blocks *w, v4_node *n, int read)
|
||||
{
|
||||
v4_cell num, at;
|
||||
uint32_t block;
|
||||
unsigned i, s;
|
||||
int status;
|
||||
|
||||
if (request != V4_REQ_BLOCK_READ && request != V4_REQ_BLOCK_WRITE) return 0;
|
||||
if (n->ds.depth < 2u) { /* not both arguments: nothing is done with what is there */
|
||||
v4_dstack_reset(&n->ds);
|
||||
if (n->ds.depth < 1u) { v4_dstack_reset(&n->ds); v4_dstack_push(&n->ds, 0); v4_dstack_push(&n->ds, (v4_cell)V4_BLOCK_REFUSED); return; }
|
||||
num = v4_dstack_pop(&n->ds);
|
||||
block = (uint32_t)num;
|
||||
if (num < 1 || (uint64_t)num > 0xFFFFFFFFull || !blk_is_valid(block)) {
|
||||
v4_dstack_push(&n->ds, 0); v4_dstack_push(&n->ds, (v4_cell)V4_BLOCK_RANGE);
|
||||
return;
|
||||
}
|
||||
for (s = 0; s < V4_BLOCK_SLOTS && w->block[s] != block; s++) { }
|
||||
if (s == V4_BLOCK_SLOTS) {
|
||||
const uint8_t *b = 0;
|
||||
s = take_slot(w, n, &status);
|
||||
if (status == V4_BLOCK_OK && read) {
|
||||
b = blk_get_buffer(block, 0);
|
||||
if (!b) status = V4_BLOCK_REFUSED;
|
||||
}
|
||||
if (status != V4_BLOCK_OK) { v4_dstack_push(&n->ds, 0); v4_dstack_push(&n->ds, (v4_cell)status); return; }
|
||||
at = slot_addr(w, s);
|
||||
for (i = 0; i < V4_BLOCK_CELLS; i++)
|
||||
n->mem[at + (v4_cell)i] = b ? (v4_cell)((v4_ucell)b[4u * i] | ((v4_ucell)b[4u * i + 1u] << 8)
|
||||
| ((v4_ucell)b[4u * i + 2u] << 16) | ((v4_ucell)b[4u * i + 3u] << 24))
|
||||
: 0;
|
||||
w->block[s] = block;
|
||||
w->changed[s] = 0;
|
||||
}
|
||||
w->last = (int)s;
|
||||
v4_dstack_push(&n->ds, slot_addr(w, s));
|
||||
v4_dstack_push(&n->ds, (v4_cell)V4_BLOCK_OK);
|
||||
}
|
||||
|
||||
int v4_blocks_serve(v4_blocks *w, v4_node *n, v4_cell request)
|
||||
{
|
||||
unsigned s;
|
||||
int status = V4_BLOCK_OK;
|
||||
|
||||
if (request > V4_REQ_BLOCK || request < V4_REQ_EMPTY_BUFFERS) return 0;
|
||||
|
||||
/* the chain is not what it was: what the slots hold is of no chain now */
|
||||
if (blk_subsys_epoch() != w->epoch) { let_go(w); w->epoch = blk_subsys_epoch(); }
|
||||
|
||||
if (request == V4_REQ_EMPTY_BUFFERS) { let_go(w); return 1; }
|
||||
|
||||
/* the window is all in the node's memory, or nothing is done */
|
||||
if (!v4_node_addr_ok(w->window) || !v4_node_addr_ok(w->window + (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS - 1u))) {
|
||||
if (request == V4_REQ_BLOCK || request == V4_REQ_BUFFER) {
|
||||
if (n->ds.depth >= 1u) (void)v4_dstack_pop(&n->ds);
|
||||
v4_dstack_push(&n->ds, 0);
|
||||
}
|
||||
v4_dstack_push(&n->ds, (v4_cell)V4_BLOCK_REFUSED);
|
||||
return 1;
|
||||
}
|
||||
addr = v4_dstack_pop(&n->ds);
|
||||
num = v4_dstack_pop(&n->ds);
|
||||
block = (uint32_t)num;
|
||||
|
||||
if (!v4_node_addr_ok(addr) || !v4_node_addr_ok(addr + (v4_cell)(V4_BLOCK_CELLS - 1u))) {
|
||||
status = V4_BLOCK_REFUSED;
|
||||
} else if (num < 1 || (uint64_t)num > 0xFFFFFFFFull || !blk_is_valid(block)) {
|
||||
status = V4_BLOCK_RANGE;
|
||||
} else if (request == V4_REQ_BLOCK_READ) {
|
||||
b = blk_get_buffer(block, 0);
|
||||
status = b ? V4_BLOCK_OK : V4_BLOCK_REFUSED;
|
||||
if (b)
|
||||
for (i = 0; i < V4_BLOCK_CELLS; i++)
|
||||
n->mem[addr + (v4_cell)i] = (v4_cell)((v4_ucell)b[4u * i] | ((v4_ucell)b[4u * i + 1u] << 8)
|
||||
| ((v4_ucell)b[4u * i + 2u] << 16) | ((v4_ucell)b[4u * i + 3u] << 24));
|
||||
} else {
|
||||
b = blk_get_buffer(block, 1);
|
||||
status = V4_BLOCK_REFUSED;
|
||||
if (b) {
|
||||
for (i = 0; i < V4_BLOCK_CELLS; i++) {
|
||||
v4_ucell w = (v4_ucell)n->mem[addr + (v4_cell)i];
|
||||
b[4u * i] = (uint8_t)(w & 0xFFu);
|
||||
b[4u * i + 1u] = (uint8_t)((w >> 8) & 0xFFu);
|
||||
b[4u * i + 2u] = (uint8_t)((w >> 16) & 0xFFu);
|
||||
b[4u * i + 3u] = (uint8_t)((w >> 24) & 0xFFu);
|
||||
if (request == V4_REQ_BLOCK) { slot_for(w, n, 1); return 1; }
|
||||
if (request == V4_REQ_BUFFER) { slot_for(w, n, 0); return 1; }
|
||||
if (request == V4_REQ_UPDATE) {
|
||||
if (w->last >= 0 && w->block[w->last] != 0) w->changed[w->last] = 1;
|
||||
} else { /* SAVE-BUFFERS */
|
||||
for (s = 0; s < V4_BLOCK_SLOTS; s++)
|
||||
if (w->block[s] != 0 && w->changed[s] && write_slot(w, n, s) != V4_BLOCK_OK) {
|
||||
status = V4_BLOCK_REFUSED;
|
||||
w->block[s] = 0;
|
||||
if (w->last == (int)s) w->last = -1;
|
||||
}
|
||||
if (blk_update(block) == BLK_OK && blk_flush(block) == BLK_OK) status = V4_BLOCK_OK;
|
||||
}
|
||||
}
|
||||
v4_dstack_push(&n->ds, (v4_cell)status);
|
||||
return 1;
|
||||
|
||||
Reference in New Issue
Block a user