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>
102 lines
5.2 KiB
C
102 lines
5.2 KiB
C
/* test_blocks.c -- the kernel serves a node's block requests.
|
|
* docs/v4.0.0/MESH.md sections 8.2 and 8.3.
|
|
*
|
|
* A node, stopped, with a number and an address on its data stack; the
|
|
* blocks are v3's block subsystem (v3/src/block_subsystem.c): its fast RAM
|
|
* and a device of 64 blocks.
|
|
*/
|
|
#include "v4/blocks.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "block_subsystem.h"
|
|
|
|
extern void sf_time_init(void);
|
|
extern void log_set_level(int level);
|
|
|
|
static int failures = 0, checks = 0;
|
|
#define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0)
|
|
|
|
#define DEVICE_BLOCKS 64u
|
|
#define BUF 100
|
|
static unsigned char ram[(size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE], dev[DEVICE_BLOCKS * BLK_FORTH_SIZE];
|
|
static v4_node n;
|
|
|
|
static v4_cell ask(v4_cell request, v4_cell block, v4_cell addr)
|
|
{
|
|
v4_dstack_push(&n.ds, 7777);
|
|
v4_dstack_push(&n.ds, block);
|
|
v4_dstack_push(&n.ds, addr);
|
|
if (!v4_blocks_serve(&n, request)) return -100;
|
|
{
|
|
v4_cell status = v4_dstack_pop(&n.ds);
|
|
return v4_dstack_pop(&n.ds) == 7777 ? status : -200; /* what was under its arguments is still there */
|
|
}
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
unsigned i;
|
|
v4_cell top = (v4_cell)V4_NODE_WORDS;
|
|
|
|
printf("v4 block request tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS);
|
|
sf_time_init();
|
|
log_set_level(-1);
|
|
CHECK(blk_subsys_init(ram, sizeof ram) == BLK_OK && blk_subsys_add_raw_device(dev, DEVICE_BLOCKS) == BLK_OK, "the kernel's chain: fast RAM and a device of 64 blocks");
|
|
v4_node_reset(&n);
|
|
|
|
/* ---- a block is written and read ---- */
|
|
for (i = 0; i < V4_BLOCK_CELLS; i++) n.mem[BUF + i] = (v4_cell)((v4_ucell)0x41424344u + i);
|
|
#if V4_CELL_BITS > 32
|
|
n.mem[BUF + 5] = (v4_cell)0x7766554433221100LL; /* only its low 32 bits are the block's */
|
|
#endif
|
|
CHECK(ask(V4_REQ_BLOCK_WRITE, 2050, BUF) == V4_BLOCK_OK, "a block is written");
|
|
CHECK(dev[2 * 1024] == 0x44 && dev[2 * 1024 + 1] == 0x43 && dev[2 * 1024 + 2] == 0x42 && dev[2 * 1024 + 3] == 0x41 && dev[2 * 1024 + 4] == 0x45,
|
|
"block 2050 is the device's third, four bytes to a cell, the first lowest");
|
|
CHECK(dev[1 * 1024 + 1023] == 0 && dev[3 * 1024] == 0, "and no other block was touched");
|
|
for (i = 0; i < V4_BLOCK_CELLS; i++) n.mem[400 + i] = -1;
|
|
CHECK(ask(V4_REQ_BLOCK_READ, 2050, 400) == V4_BLOCK_OK, "it is read");
|
|
for (i = 0; i < V4_BLOCK_CELLS && (i == 5 || n.mem[400 + i] == (v4_cell)((v4_ucell)0x41424344u + i)); i++) { }
|
|
CHECK(i == V4_BLOCK_CELLS, "as it was written: cell %u differs", i);
|
|
#if V4_CELL_BITS > 32
|
|
CHECK(n.mem[400 + 5] == (v4_cell)0x33221100, "the rest of each cell is zero");
|
|
#else
|
|
CHECK(n.mem[400 + 5] == (v4_cell)((v4_ucell)0x41424344u + 5u), "cell 5 as the others");
|
|
#endif
|
|
CHECK(n.mem[399] == 0 && n.mem[400 + V4_BLOCK_CELLS] == 0, "256 cells and no more");
|
|
n.mem[BUF] = 0x5A;
|
|
CHECK(ask(V4_REQ_BLOCK_WRITE, 20, BUF) == V4_BLOCK_OK && ram[((size_t)20 + BLK_FORTH_SYS_RESERVED) * 1024] == 0x5A, "a block below 2048 is the chain's fast RAM");
|
|
|
|
/* ---- numbers that are no block ---- */
|
|
CHECK(ask(V4_REQ_BLOCK_READ, 0, 400) == V4_BLOCK_RANGE, "0 is no block");
|
|
CHECK(ask(V4_REQ_BLOCK_READ, -1, 400) == V4_BLOCK_RANGE && ask(V4_REQ_BLOCK_WRITE, -5, BUF) == V4_BLOCK_RANGE, "nor is a number below it");
|
|
CHECK(ask(V4_REQ_BLOCK_READ, 2111, 400) == V4_BLOCK_OK && ask(V4_REQ_BLOCK_READ, 2112, 400) == V4_BLOCK_RANGE, "the device's last block is, and the one after it is not");
|
|
CHECK(ask(V4_REQ_BLOCK_WRITE, 2112, BUF) == V4_BLOCK_RANGE, "nor can it be written");
|
|
#if V4_CELL_BITS > 32
|
|
CHECK(ask(V4_REQ_BLOCK_READ, (v4_cell)0x100000014LL, 400) == V4_BLOCK_RANGE, "a number 32 bits do not hold is no block, whatever its low bits are");
|
|
#endif
|
|
|
|
/* ---- cells that are not in the node's memory ---- */
|
|
n.mem[top - 1] = 123;
|
|
CHECK(ask(V4_REQ_BLOCK_READ, 2050, top - 255) == V4_BLOCK_REFUSED && n.mem[top - 1] == 123, "256 cells that run past the end of memory are refused, and nothing is read");
|
|
CHECK(ask(V4_REQ_BLOCK_READ, 2050, top - 256) == V4_BLOCK_OK, "the last 256 cells are not");
|
|
CHECK(ask(V4_REQ_BLOCK_READ, 2050, -1) == V4_BLOCK_REFUSED && ask(V4_REQ_BLOCK_WRITE, 2050, top) == V4_BLOCK_REFUSED, "an address below 0, or at the end, is refused");
|
|
CHECK(dev[2 * 1024] == 0x44, "and nothing was written");
|
|
|
|
/* ---- what v3 wrote ---- */
|
|
{
|
|
uint8_t *b = blk_get_buffer(2070, 1);
|
|
CHECK(b != 0, "v3 gives a buffer for block 2070");
|
|
if (b) { memset(b, 'V', BLK_FORTH_SIZE); CHECK(blk_update(2070) == BLK_OK && blk_flush(2070) == BLK_OK, "and v3 writes it"); }
|
|
CHECK(ask(V4_REQ_BLOCK_READ, 2070, 400) == V4_BLOCK_OK && n.mem[400] == (v4_cell)0x56565656 && n.mem[400 + 255] == (v4_cell)0x56565656, "what v3's own calls wrote is what the node reads");
|
|
}
|
|
|
|
/* ---- what is not a block request ---- */
|
|
v4_dstack_push(&n.ds, 11); v4_dstack_push(&n.ds, 22);
|
|
CHECK(!v4_blocks_serve(&n, 5) && !v4_blocks_serve(&n, 0) && !v4_blocks_serve(&n, -3) && v4_dstack_pop(&n.ds) == 22 && v4_dstack_pop(&n.ds) == 11,
|
|
"any other request is not served here, and the stack is as it was");
|
|
|
|
printf(" %d checks, %d failures\n", checks, failures);
|
|
return failures ? 1 : 0;
|
|
}
|