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:
rajames
2026-10-07 09:37:30 -04:00
co-authored by Claude Opus 5.5
parent 7f4d946d2e
commit 9bfd5071d2
21 changed files with 991 additions and 330 deletions
+76 -60
View File
@@ -315,34 +315,57 @@ in 8.6, so that it is not proposed again.
### 8.2 What a node does *(step 6)*
`BLOCK`, `BUFFER`, `UPDATE` and `SAVE-BUFFERS` keep their two buffers and
their FORTH-79 behaviour. The one word beneath them in
`v4/capsule/blocks.v4` that read or wrote a block by storing to four
registers makes a kernel request instead: it puts the block's number and
the word address of the buffer's 256 cells on the data stack and writes the
request's number to port 0, where its kernel is. When the write has been
served the status is on the stack in their place: 0 it worked, 2 it was
refused, 3 there is no such block.
**Ruled 2026-10-07: v3's way in full.** The block words are the kernel's.
`BLOCK`, `BUFFER`, `UPDATE`, `SAVE-BUFFERS` and `EMPTY-BUFFERS` in
`v4/capsule/blocks.v4` each write one request to port 0, where the node's
kernel is, and do nothing else: `BLOCK` and `BUFFER` with the block's
number on the stack, the others with nothing. The node gives the kernel no
address and keeps no record of what is in its buffers.
There are two requests, read a block and write a block. Their numbers are
the same for every node and are below zero; the requests a host names for
its own words (`KERNEL-WORD`) count up from 1.
The node has a **window** in its memory, four slots of 256 cells, as a v3
VM has four slots of 1 KiB at the top of its memory. The kernel copies a
block into a slot and gives the node the slot's address.
The five requests' numbers are the same for every node and are below zero,
-1 to -5; the requests a host names for its own words (`KERNEL-WORD`)
count up from 1. The status a request leaves: 0 it worked, 2 it was
refused (error 17, "Storage refused"), 3 there is no such block (error 13).
The four storage registers and `v4_node_storage_attach` go from the engine.
### 8.3 What the kernel does *(step 6)*
Every node has its kernel on port 0, whatever else is there for it: Hera's
requests for nodes (section 9) are honoured only from Hera, and the two
block requests from every node.
requests for nodes (section 9) are honoured only from Hera, and the block
requests from every node.
`v4/system/blocks.c`, which the hosted and the bare-metal builds and the
tests share, serves them: it takes the number and the address from the
node's data stack, checks that the 256 cells are in the node's memory, and
reads or writes the block through v3's block subsystem
(`v3/src/block_subsystem.c`), four characters to a cell, the first lowest.
The engine (`v4/src`) knows nothing of it.
tests share, serves them, from v3's block subsystem
(`v3/src/block_subsystem.c`). For each node it keeps what v3 keeps for each
VM (`v3/src/word_source/block_words.c`): which block is in which slot of
the window, which have been UPDATEd, and the chain as it was when they
were filled. The engine (`v4/src`) knows nothing of it.
- It writes into the node's window and nowhere else in the node. That is
how a request cannot overwrite the node's code (ruled 2026-10-07: the
kernel refuses it): there is no address for a node to give.
- `BLOCK` gives the slot a block is in, and reads it from storage only if
it is in none. `BUFFER` gives a slot of zeros without reading.
- **When a block is written is FORTH-79's:** `UPDATE` marks it, and it is
written by `SAVE-BUFFERS` or when its slot is wanted. v3's `UPDATE`
copies the slot to the kernel at once; the standard is followed
(standing ruling). A slot whose block is not marked is taken before one
whose block is.
- A block storage will not take is let go of, and its slot is empty.
- When the chain of devices changes, everything in the slots is let go and
none of it is written, as v3 does.
- Four characters to a cell, the first lowest; a read leaves the rest of
each cell zero and a write takes the low 32 bits.
- **Each node has its own copy of a block it holds, as each v3 VM has.**
If node A has block *n* in a slot and node B writes it, A goes on reading
what it had, and A's next `UPDATE` and `SAVE-BUFFERS` writes all of the
block over B's. v3 is the same. Closing that would be a departure from
v3 and is not ruled.
- v3's code does what it does: header, allocation map, block cards,
relocation, three blocks and their cards to a 4 KiB device block.
- There is one chain, as in v3: fast RAM at blocks 0 to 2047, then the
@@ -376,37 +399,20 @@ block number not to exist, and on the real chain it does.
step 6a.
- **Cloud stores and real USB drives** wait for their drivers.
### 8.5 Open, for ruling
### 8.5 Ruled 2026-10-07, after the review of step 6
Found by review of step 6 on 2026-10-07; nothing is built for either.
The review found that a block request could be made by hand with an
address in the node's own code, and said that a node's own copy of a
block was a departure from v3. The second was wrong: a v3 VM has a window
of four slots in its own memory and `BLOCK` copies the kernel's block into
one (`v3/include/vm.h`, `BLK_VM_SLOTS`; `block_words.c`, `blk_vm_load`).
It was reported to Captain Bob as a departure before it was checked
against v3, and he ruled on it as one; it was then checked and taken back.
- **A node keeps its own copy of a block.** `BLOCK` gives the address of
one of the node's two buffers, and the kernel is asked only when a block
is not in one. If node A has block *n* in a buffer and node B writes it,
A goes on reading what it had; and if A then does `UPDATE` and
`SAVE-BUFFERS`, all of B's block is overwritten.
**Corrected 2026-10-07.** This entry first said that in v3 a VM's `BLOCK`
gives the kernel's buffer, so that every VM sees one copy. That was
wrong, and was not checked against v3 before it was written. In v3 each
VM has a window of four 1 KiB slots at the top of its own memory
(`v3/include/vm.h`, `BLK_VM_SLOTS`; `v3/src/word_source/block_words.c`,
`blk_vm_load`): `BLOCK` copies the kernel's block into a slot, unless
the block is already in one, and gives the slot's address. So a v3 VM
keeps its own copy too, and two VMs can each hold a stale one, exactly
as here. Where v4 differs from v3:
- two buffers, where v3 has four slots;
- the node keeps the record of what is in them and does the asking,
where in v3 the block words are the kernel's and the kernel keeps the
record and does the copying, only ever into the window;
- `UPDATE` marks the buffer and it is written when the buffer is wanted
or at `SAVE-BUFFERS`, as FORTH-79 has it, where v3's `UPDATE` copies
the slot to the kernel at once;
- v3 lets go of every slot when the device chain changes; v4 does that
in one place only, after POST on bare metal.
- **A block read over the node's own code.** The kernel checks that the
256 cells are in the node's memory and nothing more, so a request made
by hand with an address in the nucleus overwrites it and the node stops.
`!` can do the same; whether the kernel should refuse it is not ruled.
His rulings, on what v3 does: **the kernel refuses a request that would
overwrite the node's code**, and **v3's way in full** — the block words
are the kernel's, by number only, with the kernel keeping the record of
the window and doing the copying. Both are built: 8.2 and 8.3.
### 8.6 Withdrawn 2026-10-07
@@ -628,17 +634,23 @@ Each is tested, committed and pushed before the next.
6. **Storage (section 8): every node asks the kernel for its blocks.**
**Done 2026-10-07.** It was first built another way and brought back;
8.6 says what was withdrawn and why.
- *The requests* (`v4/include/v4/blocks.h`, `v4/system/blocks.c`). -1
reads a block and -2 writes one, `( n waddr -- status )`, for any
node, from the kernel's block subsystem. `v4/tests/test_blocks.c`: 22
checks at 64 bits, 21 at 32 — a block written and read, the fast RAM,
numbers that are no block, cells that are not in the node's memory,
and that what v3's own calls wrote is what the node reads.
- *The node* (`v4/capsule/blocks.v4`). `(DEVICE)` makes the request on
port 0. The four storage registers and `v4_node_storage_attach` are
gone from the engine. Error 17 is "Storage refused".
`v4/tests/test_host_quit.c`: its block cases unchanged, and a disk
that will not be written says so and empties the buffer.
- *The requests* (`v4/include/v4/blocks.h`, `v4/system/blocks.c`). Five,
-1 to -5: `BLOCK`, `BUFFER`, `UPDATE`, `SAVE-BUFFERS`,
`EMPTY-BUFFERS`, for any node, by block number only. The kernel keeps
the node's window of four slots. `v4/tests/test_host_blocks.c`: 41
checks at 64 bits, 39 at 32 — a block copied into a slot and found
there again, `UPDATE` and when a block is written, `EMPTY-BUFFERS`,
`BUFFER`, more blocks than slots, numbers that are no block, too
little on the stack, a window not in the node's memory, and the chain
changing under what the slots hold.
- *The node* (`v4/capsule/blocks.v4`). Each block word is one request
on port 0; the node's own buffers and its record of them are gone, and
so are the four storage registers and `v4_node_storage_attach`. Error
17 is "Storage refused". The window takes 512 cells more than the two
buffers did, and the dictionary space ends 512 cells lower, at 13824.
`v4/tests/test_host_quit.c`: its block cases, those that counted on
two buffers rewritten for four slots, and a disk that will not be
written says so and is let go of.
- *v3* (`v3/src/block_subsystem.c`). `blk_subsys_init` takes no `VM`.
Nothing else of it is changed. Accepted on the v3 configuration, three
ISAs, `PARITY:M7.1a` hash `0x08873e0f44b7cb2a` as on 2026-10-03:
@@ -658,7 +670,9 @@ Each is tested, committed and pushed before the next.
a block that is not there. `logs/20261007-081603` (amd64), `-081839`
(aarch64), `-082226` (riscv64); and again after the review's changes
below, with blocks 1 and 2047 read clean at the prompt:
`logs/20261007-085017`, `-085254`, `-085636`. The parity hashes are the same on the
`logs/20261007-085017`, `-085254`, `-085636`; and as it stands, with the
block words the kernel's: `logs/20261007-092835`, `-093112`,
`-093456`. The parity hashes are the same on the
three hosted and the three bare-metal systems.
- *Review, 2026-10-07.* One reading of the whole step by a fresh
reviewer; no critical defect. Changed after it: a block request with
@@ -669,7 +683,9 @@ Each is tested, committed and pushed before the next.
`blocks.c` is built under each test's own warnings and sanitizers;
the hosted link no longer takes whatever objects lie in its
directory (it had linked the withdrawn `store_v3.o`). Two findings
are for ruling, section 8.5.
went to Captain Bob, and his rulings on them are section 8.5; they
made the block words the kernel's, which replaced the request first
built here, `( n waddr -- status )`.
- **Not as intended yet.**
- Who may have which block is not checked (8.4).
- On bare metal the virtio disk is read and not written. v3's