feat(v4.0.0): every node asks the kernel for its blocks; a born node is not POSTed
A block is a kernel request, as ENGINE.md 3.3 has it: the node puts the
block's number and the address of 256 cells on its stack and writes the
request to port 0, and the kernel leaves the status there. The requests
are -1, read, and -2, write, the same for every node. v4/system/blocks.c
serves them from the kernel's block subsystem, which is v3's. The four
storage registers are gone from the engine.
The device that spoke block messages (4a505a15) is withdrawn with its
test and its message types: Captain Bob ruled on 2026-10-07 that it, a
node's own drive, and nodes with no storage had left the OS as designed
(docs/v4.0.0/MESH.md 8.5).
Hera no longer sends POST to the nodes she births: POST is the kernel's,
once. Every node has its kernel on port 0; it serves a node's blocks and,
for Hera alone, her requests for nodes and capsules.
Bare metal: the node boots and is POSTed against POST's own block RAM,
and the kernel's chain -- fast RAM, the ramdrive, the virtio disk -- is
set up after POST and before the prompt, as on the v3 path. The disk is
read and not written: nothing in v4 yet gives the owner's word that it
may be formatted. A hosted program has the chain's fast RAM, as hosted
v3 has with no disk. Error 17 is Storage refused.
make -C v4 test and sanitize pass at both widths; hosted-check passes on
three ISAs; amd64, aarch64 and riscv64 boot, POST 538 of 538, with the
typed session: logs/20261007-081603, -081839, -082226. The hashes are
the same on all six.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
26a0748455
commit
d5b7235464
@@ -52,6 +52,8 @@ static int failures = 0, checks = 0;
|
||||
|
||||
#define CANARY ((v4_cell)0x0C0FFEE5)
|
||||
|
||||
#include "v4/blocks.h"
|
||||
#include "block_subsystem.h"
|
||||
#include "host_map.h"
|
||||
|
||||
static v4_node n;
|
||||
@@ -62,9 +64,44 @@ static v4_cell w_key, w_key_end, w_fault, capsule_latest;
|
||||
static v4_cell w_idle; /* (IDLE): where the node waits for a message */
|
||||
static char out[V4_CONSOLE_CAP + 1];
|
||||
|
||||
/* The storage device's blocks: zeros at every switch-on. */
|
||||
/* STORAGE (MESH.md section 8). The node asks its kernel for its blocks, and
|
||||
* these tests are its kernel (kernel_serve): the blocks are the kernel's
|
||||
* block subsystem (v3/src/block_subsystem.c). The chain is fast RAM,
|
||||
* blocks 0 to 2047; a device of 64 blocks, 2048 to 2111; and after that a
|
||||
* disk the subsystem has not been told it may format, which it reads and
|
||||
* will not write. The RAM and the device are zeros at every switch-on.
|
||||
* `disk` is where the chain keeps block 0, so disk + n * 1024 is block n of
|
||||
* it for any n below 2048, as the chain itself holds it. */
|
||||
#define DISK_BLOCKS 64u
|
||||
static unsigned char disk[DISK_BLOCKS * V4_BLOCK_BYTES];
|
||||
#define RAM_BYTES ((size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE)
|
||||
#define REFUSING_BLOCKS 2048u /* room for the subsystem's own reserved device blocks and some of the node's */
|
||||
#define REFUSING_FIRST "2112" /* its first block */
|
||||
static unsigned char ram[RAM_BYTES], common_dev[DISK_BLOCKS * V4_BLOCK_BYTES], refusing_dev[REFUSING_BLOCKS * V4_BLOCK_BYTES];
|
||||
#define disk (ram + (size_t)BLK_FORTH_SYS_RESERVED * BLK_FORTH_SIZE)
|
||||
static unsigned long block_requests; /* how many the node has made */
|
||||
|
||||
extern void sf_time_init(void);
|
||||
extern void log_set_level(int level);
|
||||
extern size_t blkio_ram_state_size(void);
|
||||
extern int blkio_ram_init_state(void *state_mem, size_t state_len, uint8_t *base, uint32_t total_blocks, uint32_t fbs, uint8_t read_only, void **out_opaque);
|
||||
extern const blkio_vtable_t *blkio_ram_vtable(void);
|
||||
|
||||
/* The chain is made once: the subsystem keeps what it allocates. */
|
||||
static int storage_made(void)
|
||||
{
|
||||
static blkio_dev_t dev;
|
||||
static unsigned char state[256];
|
||||
blkio_params_t params;
|
||||
void *opaque = 0;
|
||||
|
||||
sf_time_init();
|
||||
log_set_level(-1);
|
||||
if (blkio_ram_state_size() > sizeof state) return 0;
|
||||
if (blk_subsys_init(ram, sizeof ram) != BLK_OK || blk_subsys_add_raw_device(common_dev, DISK_BLOCKS) != BLK_OK) return 0;
|
||||
if (blkio_ram_init_state(state, sizeof state, refusing_dev, REFUSING_BLOCKS, 0, 0, &opaque) != BLKIO_OK) return 0;
|
||||
params.forth_block_size = 0; params.total_blocks = REFUSING_BLOCKS; params.opaque = opaque;
|
||||
return blkio_open(&dev, blkio_ram_vtable(), ¶ms) == BLKIO_OK && blk_subsys_attach_device(&dev) == BLK_OK;
|
||||
}
|
||||
/* A block holding `text`, the rest of it blanks. */
|
||||
static void put_block(unsigned num, const char *text)
|
||||
{
|
||||
@@ -85,6 +122,7 @@ static v4_cell last_request;
|
||||
static void kernel_serve(void)
|
||||
{
|
||||
last_request = n.request;
|
||||
if (v4_blocks_serve(&n, n.request)) { block_requests++; return; } /* a block, as for any node (blocks.h) */
|
||||
if (n.request == 1) {
|
||||
v4_cell xt = v4_dstack_pop(&n.ds);
|
||||
if (known_n < 1024) known[known_n++] = xt;
|
||||
@@ -227,8 +265,7 @@ static void boot_with(unsigned depth)
|
||||
n.mem[VOC_LINK] = 0;
|
||||
n.mem[SRC] = TIB;
|
||||
for (v4_cell k = BVARS; k < BVARS + 14; k++) if (k != SRC) n.mem[k] = 0; /* empty buffers, SCR and BLK 0, no hook */
|
||||
memset(disk, 0, sizeof disk);
|
||||
v4_node_storage_attach(&n, STORAGE_REG, disk, DISK_BLOCKS);
|
||||
memset(ram, 0, sizeof ram); memset(common_dev, 0, sizeof common_dev);
|
||||
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST);
|
||||
v4_node_fault_attach(&n, w_fault);
|
||||
v4_node_error_attach(&n, NODE_ERROR);
|
||||
@@ -772,6 +809,7 @@ int main(void)
|
||||
unsigned i;
|
||||
|
||||
printf("v4 host prompt tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS);
|
||||
CHECK(storage_made(), "the chains behind the storage port are made");
|
||||
|
||||
{
|
||||
static const char *const files[] = { "core.v4", "input.v4", "dict.v4", "codegen.v4", "compile.v4", "quit.v4", "forth.v4", "numout.v4", "words.v4", "system.v4", "qmath.v4", "blocks.v4", "log.v4", "acl.v4" };
|
||||
@@ -1133,6 +1171,15 @@ int main(void)
|
||||
CHECK(is(say("FORGET G3 G1 G2\n"), "AB ok\nok> ") && is(say("G3\n"), "UNKNOWN WORD: 'G3'\n ERROR\nok> "), "one above it can");
|
||||
}
|
||||
|
||||
/* ---- storage that refuses a write (MESH.md 8.2) ---- */
|
||||
boot();
|
||||
CHECK(is(say(REFUSING_FIRST " BLOCK DROP UPDATE SAVE-BUFFERS 65 EMIT\n"), "Storage refused\n ERROR\nok> "), "a write storage will not take says so, and the prompt returns");
|
||||
{
|
||||
unsigned long before = block_requests;
|
||||
CHECK(is(say(REFUSING_FIRST " BLOCK DROP\n"), " ok\nok> ") && block_requests == before + 1, "the buffer was emptied: the next BLOCK asks the kernel again");
|
||||
CHECK(is(say(REFUSING_FIRST " BLOCK DROP\n"), " ok\nok> ") && block_requests == before + 1, "and the one after finds it in the buffer");
|
||||
}
|
||||
|
||||
/* ---- blocks: text loaded from storage ---- */
|
||||
boot_bare();
|
||||
put_block(5, "65 EMIT 66 EMIT");
|
||||
@@ -1164,11 +1211,11 @@ int main(void)
|
||||
/* errors */
|
||||
CHECK(is(say("ABORT\n"), " ok\nok> "), "(an empty stack)");
|
||||
CHECK(is(say("7 0 BLOCK 65 EMIT\n"), "Block out of range\n ERROR\nok> ") && is(say("-1 BLOCK\n"), "Block out of range\n ERROR\nok> ")
|
||||
&& is(say("64 BLOCK\n"), "Block out of range\n ERROR\nok> ") && is(say("63 BLOCK DROP .S\n"), "<1> 7 \n ok\nok> "), "a block number below 1 or past the device's last");
|
||||
&& is(say("9000 BLOCK\n"), "Block out of range\n ERROR\nok> ") && is(say("2111 BLOCK DROP .S\n"), "<1> 7 \n ok\nok> "), "a block number below 1 or past the chain's last");
|
||||
CHECK(is(say("0 LOAD\n"), "Block out of range\n ERROR\nok> ") && is(say("-5 LOAD\n"), "Block out of range\n ERROR\nok> ")
|
||||
&& is(say("0 BUFFER\n"), "Block out of range\n ERROR\nok> ") && is(say("99 LIST\n"), "Block out of range\n ERROR\nok> "), "LOAD, BUFFER and LIST the same");
|
||||
&& is(say("0 BUFFER\n"), "Block out of range\n ERROR\nok> ") && is(say("9000 LIST\n"), "Block out of range\n ERROR\nok> "), "LOAD, BUFFER and LIST the same");
|
||||
CHECK(is(say("-->\n"), "No block is being loaded\n ERROR\nok> ") && is(say("65 EMIT\n"), "A ok\nok> "), "--> at the terminal");
|
||||
put_block(18, "1 99 LOAD 2");
|
||||
put_block(18, "1 9000 LOAD 2");
|
||||
CHECK(is(say("ABORT\n"), " ok\nok> "), "(an empty stack)");
|
||||
CHECK(is(say("18 LOAD\n.S\n"), "Block out of range\n ERROR\nok> <1> 1 \n ok\nok> "), "a block that loads one that is not there");
|
||||
|
||||
@@ -1269,7 +1316,7 @@ int main(void)
|
||||
SCREEN(31, "line A", "line D", "line B", "line C", "line E", "line F", "line G", "line H", "line I", "line J", "line K", "line L", "line M", "line N", "line O", "line P");
|
||||
CHECK(is(say("L DONE\n"), want), "every line from 1 moved down");
|
||||
CHECK(is(say("21 EDIT\n"), out) && is(say("WIPE N\n"), blank_screen(22)) && is(say("B\n"), blank_screen(21)), "WIPE blanks it; N and B move to the next and back");
|
||||
CHECK(is(say("99 EDIT\n"), "Block out of range\n ERROR\nok> ") && n.mem[SCR] == 21, "EDIT of a block that is not there");
|
||||
CHECK(is(say("9000 EDIT\n"), "Block out of range\n ERROR\nok> ") && n.mem[SCR] == 21, "EDIT of a block that is not there");
|
||||
/* v3's three, in FORTH */
|
||||
CHECK(is(say("FORTH 9 SCR ! 0 L 3 L\n"), " second, with blanks before it \n \n ok\nok> "),
|
||||
"FORTH's L shows a line, as v3's");
|
||||
|
||||
Reference in New Issue
Block a user