Files
LithosAnanake/v4/tests/test_blocks.c
T
rajamesandClaude Opus 5.5 bcfd6556a8 fix(v4.0.0): storage -- what the review of step 6 found
A block request with fewer than two values on the stack is refused; it
had acted on whatever the stack ring held and stopped the node.

Bare metal: when the kernel's chain takes the place of POST's block RAM
the node's two buffers are emptied, so it no longer holds POST's copy of
a block; and the chain's fast RAM is cleared, so a node cannot read what
was in the kernel's heap.

blocks.c is built with each test under that test's own warnings and
sanitizers; it had been left out of both.  The hosted link cleans its
object directory first: it had linked the withdrawn store_v3.o left there
from the day before.

node.h and DECOMPOSITION.md D-19 no longer describe the message device or
the four registers as current.  MESH.md 8.5 records two findings for
ruling: a node's own copy of a block, and a block read over a node's code.

From a clean build: make -C v4 test, sanitize and hosted-check pass;
amd64, aarch64 and riscv64 boot, POST 538 of 538, same hashes, blocks 1
and 2047 clean at the prompt: logs/20261007-085017, -085254, -085636.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 08:58:52 -04:00

113 lines
5.9 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");
/* ---- a request with too little on the stack ---- */
{
unsigned char before = dev[2 * 1024];
v4_cell low = n.mem[0];
v4_dstack_reset(&n.ds);
CHECK(v4_blocks_serve(&n, V4_REQ_BLOCK_READ) && n.ds.depth == 1 && v4_dstack_pop(&n.ds) == V4_BLOCK_REFUSED, "a request with nothing on the stack is refused, and the status is all that is left");
v4_dstack_push(&n.ds, 2050);
CHECK(v4_blocks_serve(&n, V4_REQ_BLOCK_WRITE) && n.ds.depth == 1 && v4_dstack_pop(&n.ds) == V4_BLOCK_REFUSED, "and so is one with a single value");
CHECK(dev[2 * 1024] == before && n.mem[0] == low, "nothing was read or 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;
}