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
@@ -0,0 +1,43 @@
|
||||
/* blocks.h -- a node asks its kernel for a block.
|
||||
* docs/v4.0.0/MESH.md section 8, docs/v4.0.0/ENGINE.md 3.3,
|
||||
* docs/v4.0.0/V3-PARITY.md 1d.
|
||||
*
|
||||
* A node only ever asks for a block by number, and it asks the kernel: a
|
||||
* request written to the port where its kernel is, as any kernel word is.
|
||||
* There are two, the same for every node,
|
||||
*
|
||||
* V4_REQ_BLOCK_READ ( n waddr -- status ) block n into the 256 cells at waddr
|
||||
* V4_REQ_BLOCK_WRITE ( n waddr -- status ) block n from them
|
||||
*
|
||||
* and their numbers are below zero: the requests a host names for its own
|
||||
* words (KERNEL-WORD) count up from 1. The status is 0, or
|
||||
* V4_BLOCK_REFUSED -- the cells are not all in the node's memory, or
|
||||
* storage will not have it -- or V4_BLOCK_RANGE, there is no such block.
|
||||
*
|
||||
* A block is 1024 bytes and 256 cells, four bytes to a cell, the first
|
||||
* lowest, whatever the cell width: a read leaves the rest of each cell
|
||||
* zero, and a write takes the low 32 bits.
|
||||
*
|
||||
* The blocks are the kernel's block subsystem's (v3/src/block_subsystem.c):
|
||||
* the device chain, the metadata and the map are its, as they were. Who
|
||||
* may have which block is not checked here yet (MESH.md 8.4).
|
||||
*/
|
||||
#ifndef V4_BLOCKS_H
|
||||
#define V4_BLOCKS_H
|
||||
|
||||
#include "v4/node.h"
|
||||
|
||||
#define V4_REQ_BLOCK_READ (-1)
|
||||
#define V4_REQ_BLOCK_WRITE (-2)
|
||||
|
||||
#define V4_BLOCK_OK 0
|
||||
#define V4_BLOCK_REFUSED 2
|
||||
#define V4_BLOCK_RANGE 3
|
||||
|
||||
/* If `request` is one of the two, serve it for the node, whose data stack
|
||||
* holds its arguments and is left holding its status, and return 1.
|
||||
* Otherwise touch nothing and return 0. The node is stopped, blocked at
|
||||
* its port, when this is called. */
|
||||
int v4_blocks_serve(v4_node *n, v4_cell request);
|
||||
|
||||
#endif /* V4_BLOCKS_H */
|
||||
@@ -49,22 +49,26 @@ typedef struct {
|
||||
* list, counting from 1. When the node writes a request to its port it
|
||||
* is blocked, and `serve` is called with the number: it takes the
|
||||
* word's arguments from the node's data stack and leaves its results
|
||||
* there, and when it returns the node goes on. A request that is not
|
||||
* in the list is an error on the node: 12, Argument out of range. */
|
||||
* there, and when it returns the node goes on. The kernel's two block
|
||||
* requests (v4/blocks.h) are served for every node and are not in the
|
||||
* list. Any other request is an error on the node: 12, Argument out of
|
||||
* range. */
|
||||
const char *const *words;
|
||||
unsigned word_count;
|
||||
void (*serve)(v4_node *n, unsigned request);
|
||||
|
||||
} v4_boot;
|
||||
|
||||
/* What `key` returns when it has no character. */
|
||||
#define V4_BOOT_KEY_NONE (-1) /* not yet: it is asked again */
|
||||
#define V4_BOOT_KEY_END (-2) /* and there never will be */
|
||||
|
||||
/* A node is born empty, with `disk` as its block storage (v4_image_born);
|
||||
* it is sent the nucleus through its port; and the capsules are loaded. Returns 1 with the node idle,
|
||||
/* A node is born empty (v4_image_born); it is sent the nucleus through its
|
||||
* port; and the capsules are loaded. Its blocks are the kernel's block
|
||||
* subsystem's, which whoever calls this has set up (v4/blocks.h). Returns 1 with the node idle,
|
||||
* waiting to be handed a line; or 0, the failure printed. It prints no
|
||||
* prompt: that is the host's. */
|
||||
int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks);
|
||||
int v4_boot_run(const v4_boot *b);
|
||||
|
||||
/* Hand the node a line and run it to its end, sending what it prints to
|
||||
* `out`. The line is sent to the node as a message and what it prints
|
||||
|
||||
@@ -42,7 +42,6 @@ typedef struct {
|
||||
v4_cell console_tx, console_rx, console_status;
|
||||
v4_cell dstack_reg, rstack_reg;
|
||||
v4_cell node_error;
|
||||
v4_cell storage_reg;
|
||||
|
||||
/* the dictionary's two variables: DP holds HERE, a byte address, and
|
||||
* LATEST the newest word of FORTH. What lies below HERE, and LATEST,
|
||||
@@ -72,13 +71,11 @@ void v4_image_seal(v4_node *n, const v4_image *im);
|
||||
uint64_t v4_image_dict_hash(const v4_node *n, const v4_image *im);
|
||||
|
||||
/* A node newly born (node.h), with the registers the nucleus expects of the
|
||||
* node it runs on -- and `disk`, `blocks` blocks of 1024 bytes, as its block
|
||||
* storage, or none if `disk` is 0. Its memory is empty and it is blocked
|
||||
* node it runs on. Its memory is empty and it is blocked
|
||||
* reading its ports: the nucleus is yet to be sent to it. Returns 1, or 0
|
||||
* if the description is not for this build of the engine (nothing is then
|
||||
* done). */
|
||||
int v4_image_born(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im,
|
||||
unsigned char *disk, unsigned blocks);
|
||||
int v4_image_born(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im);
|
||||
|
||||
/* 1 if the node is waiting for a character: it is inside KEY and none is
|
||||
* pending. */
|
||||
|
||||
@@ -34,18 +34,6 @@
|
||||
#define V4_MSG_OUTPUT 2 /* what a node printed */
|
||||
#define V4_MSG_DONE 3 /* how the text ended: one word, V4_TEXT_* */
|
||||
|
||||
/* Storage (docs/v4.0.0/MESH.md 8.3). A block's number is one word of
|
||||
* payload, written as it is; a block is 1024 characters. */
|
||||
#define V4_MSG_BLOCK_READ 4 /* the block's number; answered by BLOCK_DATA, or BLOCK_DONE with an error */
|
||||
#define V4_MSG_BLOCK_WRITE 5 /* the block's number; a BLOCK_DATA follows; answered by BLOCK_DONE */
|
||||
#define V4_MSG_BLOCK_DATA 6 /* 1024 characters */
|
||||
#define V4_MSG_BLOCK_DONE 7 /* one word, V4_STORE_* */
|
||||
|
||||
#define V4_STORE_OK 0
|
||||
#define V4_STORE_NONE 1 /* there is no storage for that number */
|
||||
#define V4_STORE_REFUSED 2
|
||||
#define V4_STORE_RANGE 3 /* there is no such block */
|
||||
|
||||
/* how text ended */
|
||||
#define V4_TEXT_QUIT 0 /* QUIT: nothing is to be said */
|
||||
#define V4_TEXT_COMPLETED 1 /* a console says " ok" */
|
||||
|
||||
+6
-27
@@ -116,10 +116,6 @@ typedef struct {
|
||||
v4_cell dstack_reg; /* its word address, or -1 */
|
||||
v4_cell rstack_reg; /* its word address, or -1 */
|
||||
|
||||
/* The block storage device (D-19). See v4_node_storage_attach. */
|
||||
v4_cell storage_reg; /* word address of its four registers, or -1 */
|
||||
unsigned char *storage; /* the blocks, 1024 bytes each */
|
||||
unsigned storage_blocks; /* how many */
|
||||
|
||||
/* NODE-ERROR as a trap (D-18). See v4_node_error_attach. */
|
||||
v4_cell error_reg; /* its word address, or -1 */
|
||||
@@ -335,31 +331,14 @@ void v4_node_fault(v4_node *n, unsigned kind, v4_cell addr);
|
||||
* detaches both (-1); the addresses are the caller's choice (D-4). */
|
||||
void v4_node_stack_regs_attach(v4_node *n, v4_cell d, v4_cell r);
|
||||
|
||||
/* THE BLOCK STORAGE DEVICE (DECOMPOSITION.md D-19, 5.11).
|
||||
*
|
||||
* Mass storage is a device service; until the mesh exists the model stands
|
||||
* in for it on the host node with four memory-mapped registers at `reg`:
|
||||
*
|
||||
* reg + 0 BLOCK-NUMBER which block, 0 .. blocks - 1
|
||||
* reg + 1 BLOCK-ADDRESS word address of 256 cells of node memory
|
||||
* reg + 2 BLOCK-COMMAND a store of 1 reads the block into those cells,
|
||||
* a store of 2 writes it from them
|
||||
* reg + 3 BLOCK-STATUS 0 after a command that worked, -1 after one
|
||||
* that did not: no such block, cells that are
|
||||
* not all in memory, or an unknown command
|
||||
*
|
||||
* The first, second and fourth are memory words the device reads and writes;
|
||||
* only a store to the third does anything. A block is 1024 bytes, and a
|
||||
* byte address is four times a word address plus 0 .. 3 (D-1), so a block
|
||||
* is 256 cells, four bytes to a cell, the first byte lowest, whatever the
|
||||
* cell width: a read leaves the rest of each cell zero, and a write takes
|
||||
* the low 32 bits.
|
||||
*
|
||||
* `bytes` is the caller's: `blocks` * 1024 bytes, which the device reads and
|
||||
* writes and never frees. v4_node_reset detaches it (reg -1). */
|
||||
/* A BLOCK (DECOMPOSITION.md D-19, 5.11). Mass storage is a device on a
|
||||
* port that speaks messages (docs/v4.0.0/MESH.md section 8; storage.h); the
|
||||
* node has nothing of it but these. A block is 1024 bytes, and a byte
|
||||
* address is four times a word address plus 0 .. 3 (D-1), so a block is 256
|
||||
* cells, four bytes to a cell, the first byte lowest, whatever the cell
|
||||
* width. */
|
||||
#define V4_BLOCK_BYTES 1024u
|
||||
#define V4_BLOCK_CELLS 256u
|
||||
void v4_node_storage_attach(v4_node *n, v4_cell reg, unsigned char *bytes, unsigned blocks);
|
||||
|
||||
/* NODE-ERROR as a trap (DECOMPOSITION.md D-18, ruled 2026-10-04: every error
|
||||
* is guarded, shown, and returns the node to its prompt).
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/* storage.h -- what is on a storage port. docs/v4.0.0/MESH.md section 8.
|
||||
*
|
||||
* A device (fabric.h) that speaks the block messages of message.h and
|
||||
* answers them from one or two chains of blocks: a view. The common chain
|
||||
* is the store its nodes share; the private chain is one node's own. What
|
||||
* a chain is, is its owner's affair: the device reaches it only through
|
||||
* v4_store_ops, and the kernel's block subsystem stands behind those
|
||||
* (system/store_v3.c).
|
||||
*
|
||||
* WHICH CHAIN A NUMBER MEANS. Block numbers are 1 to V4_STORE_TOP.
|
||||
* - Private block k is number V4_STORE_TOP - k, and is the private
|
||||
* chain's block V4_STORE_FIRST + k, its first on a device. A number
|
||||
* is the private chain's when k is less than the blocks that chain has
|
||||
* on devices.
|
||||
* - Any other number is the common chain's, as it is.
|
||||
* - With no common chain, a number below V4_STORE_FIRST is the private
|
||||
* chain's, as it is: its fast RAM.
|
||||
* - A number that belongs to no chain the view has: V4_STORE_NONE.
|
||||
* - 0, a number above V4_STORE_TOP, or one past the end of its chain:
|
||||
* V4_STORE_RANGE.
|
||||
*
|
||||
* It has a number, as a node has: messages for it are addressed to that
|
||||
* number and its answers come from it. A message for any other number is
|
||||
* let go.
|
||||
*
|
||||
* A write is two messages, BLOCK_WRITE with the number and BLOCK_DATA with
|
||||
* the block; they are paired by who sent them. BLOCK_DATA from a sender
|
||||
* with no BLOCK_WRITE before it is answered V4_STORE_REFUSED.
|
||||
*
|
||||
* The device always takes what it is sent, so that a node writing to it is
|
||||
* never left waiting on a device that is waiting to be read: answers queue,
|
||||
* V4_STORE_PENDING of them. Only when that many wait is the first word of
|
||||
* another message not taken yet.
|
||||
*
|
||||
* Nothing here uses the C library.
|
||||
*/
|
||||
#ifndef V4_STORAGE_H
|
||||
#define V4_STORAGE_H
|
||||
|
||||
#include "v4/message.h"
|
||||
|
||||
#define V4_STORE_TOP 0xffffffffUL
|
||||
#define V4_STORE_FIRST 2048UL
|
||||
#define V4_STORE_PENDING 64u
|
||||
#define V4_STORE_BYTES 1024u
|
||||
|
||||
/* A chain of blocks. read and write return V4_STORE_*; `blocks` is how
|
||||
* many block numbers the chain answers to, counting from 0, its fast RAM
|
||||
* included. */
|
||||
typedef struct {
|
||||
int (*read)(void *chain, uint32_t block, unsigned char *bytes);
|
||||
int (*write)(void *chain, uint32_t block, const unsigned char *bytes);
|
||||
uint32_t (*blocks)(void *chain);
|
||||
} v4_store_ops;
|
||||
|
||||
typedef struct {
|
||||
const v4_store_ops *ops;
|
||||
void *common, *private_chain; /* 0: the view has none */
|
||||
v4_cell number;
|
||||
|
||||
v4_message in; /* the message arriving */
|
||||
struct { int used; v4_cell from; uint64_t block; } writing[V4_STORE_PENDING]; /* BLOCK_WRITEs whose data has not come */
|
||||
struct { v4_cell to; int read; uint64_t block; int status; } answer[V4_STORE_PENDING];
|
||||
unsigned head, count; /* the answers waiting: the oldest, and how many */
|
||||
v4_message out; /* the answer being given */
|
||||
unsigned out_at;
|
||||
int out_ready;
|
||||
unsigned char bytes[V4_STORE_BYTES];
|
||||
|
||||
unsigned long taken; /* whole messages taken, for whoever counts */
|
||||
} v4_store;
|
||||
|
||||
void v4_store_init(v4_store *s, const v4_store_ops *ops, void *common, void *private_chain, v4_cell number);
|
||||
|
||||
/* The two functions of a v4_device; `self` is the v4_store. */
|
||||
int v4_store_take(void *self, v4_cell value);
|
||||
int v4_store_give(void *self, v4_cell *value);
|
||||
|
||||
/* The kernel's block subsystem as chains: a chain is a blk_chain_t *
|
||||
* (v3/include/block_subsystem.h). system/store_v3.c. */
|
||||
extern const v4_store_ops v4_store_v3;
|
||||
|
||||
#endif /* V4_STORAGE_H */
|
||||
Reference in New Issue
Block a user