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
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-10-07T12:55:58Z -->
<!-- Generated by mkcapsule --manifest 2026-10-07T13:34:17Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. -->
Binary file not shown.
+1 -1
View File
@@ -180,7 +180,7 @@ definition below depends on one, it says so.
| **D-16** | Stack overflow and underflow; `DEPTH`, `PICK`, `ROLL` (ruled 2026-10-04; revises D-2). | **Guarded: a stack fault.** Each stack counts what it holds. Before every opcode the node checks that the stacks hold what the opcode takes — including a `T` or `S` it only reads — and have room for what it leaves. If not, the opcode does nothing and the node faults exactly as for a bad address (D-14), to that kind's handler: `Stack overflow`, `Stack underflow`, `Return stack overflow`, `Return stack underflow`, then ` ERROR` and the prompt. **Every fault, D-14's included, empties both stacks**: the handler does not return, and what a word stopped part-way has left on the data stack is of no use to its caller. (v3 keeps what the failing word had not taken, and names the word: `DROP: Stack underflow`.) The fault handler is a table of five words, one per kind, each a jump (`(FAULTS)` in `v4/capsule/quit.v4`). Two registers (§7) are all a programme sees of the stacks: `DSTACK-DEPTH` and `RSTACK-DEPTH` read as the depth, and a store to one empties that stack. There is still no stack pointer and no address for a stack cell, so `SP@` and `SP!` stay retired; `.S` waits for number output to reach the capsule. The sizes were unchanged by this ruling, 10 and 9 (D-17 then deepens the host node's): one more value, or one more level of call, is now an error message where it used to be silent corruption. Executed on the golden model (2026-10-04): `tests/test_exec.c` for every opcode at every depth of both stacks, `tests/test_host_quit.c` from the prompt. |
| **D-17** | Stack sizes on the host node (2026-10-04, following D-16). | **32 values and 32 return entries on the host node; a mesh node keeps the F18's 10 and 9.** Once the stacks are counted (D-16) their size is a parameter of the node, like its memory, and the host node is the one that runs the interpreter and the compiler underneath the user's programme: at 10 and 9 the prompt left a programme about six values, and `/` could be used only four words deep. The mechanism is the same at both sizes — top registers over a ring — and so is every word's definition. v3's stacks are deeper still. In the golden model the sizes are `V4_DATA_RING` and `V4_RET_RING` (`stack.h`), set for the host-node tests in `v4/Makefile`. |
| **D-18** | Every other error (ruled 2026-10-04: "guard all errors"). | **A word that finds an error raises it, and the line ends there.** The word takes its own arguments off the stack and stores an error code in `NODE-ERROR` (§7). On a node with a prompt that store is a trap, a sixth kind of fault beside D-14's and D-16's: nothing after it executes, the return stack is emptied, and the prompt prints the code's message, ends any definition that was open, prints ` ERROR` and waits for the next line. Unlike the other faults it leaves the data stack as the word left it. The codes: 1 `Negative count` (`CMOVE`, `TYPE`), 2 `Not a number` (`NUMBER`), 3 `Number too long` (`HOLD` into a full buffer), 4 `Not a character` (`HOLD`), 5 `Dictionary full` (`,` `C,` `ALLOT`, any defining word), 6 `Name missing` (a defining word with nothing after it), 7 `Control structure mismatch`, 8 `Control structures too deep`, 9 `Shift count out of range`, 10 `Protected word`, 11 `Division by zero` (`Q./`), 12 `Argument out of range`, 13 `Block out of range`, 14 `No block is being loaded`, 15 `Deferred word not set`, 16 `Not a word`; −1 means the word printed its own message (`UNKNOWN WORD: 'xxx'`, `xxx: compile-only`). Before this ruling these set the flag and the line ran on to its end. v3 stops at once too; its messages name the word (`LOOP: missing DO`) where v4's name the fault. With the trap not attached `NODE-ERROR` is plain memory and the word returns, which is how the words are tested below the prompt. D-11, D-12 and D-13 said "set `NODE-ERROR`"; on a node with a prompt that now means this. Executed on the golden model (2026-10-04): `tests/test_exec.c`, `tests/test_host_quit.c`. |
| **D-19** | Block storage on the golden model (2026-10-05). **Replaced 2026-10-07:** the four registers are gone; a node asks its kernel for a block by a request on port 0 (`MESH.md` section 8, `v4/include/v4/blocks.h`). What follows is the decision as it was. | **Four memory-mapped registers on the host node, until the mesh carries the storage service.** `BLOCK-NUMBER`, `BLOCK-ADDRESS` (the word address of 256 cells), `BLOCK-COMMAND` (a store of 1 reads the block into those cells, 2 writes it from them) and `BLOCK-STATUS` (0 when the command worked, −1 for a block the device has not got or cells not all in memory). A block is 1024 bytes: 256 cells, four bytes to a cell, the first byte lowest, at either cell width. This is the console's arrangement (§7) applied to storage; like it, it is the model's stand-in and not the mesh protocol, which §5.11 still leaves to the device node. |
| **D-19** | Block storage on the golden model (2026-10-05). **Replaced 2026-10-07:** the four registers are gone; the block words are kernel requests on port 0, by block number, and the kernel keeps the node's window of four slots (`MESH.md` section 8, `v4/include/v4/blocks.h`). What follows is the decision as it was. | **Four memory-mapped registers on the host node, until the mesh carries the storage service.** `BLOCK-NUMBER`, `BLOCK-ADDRESS` (the word address of 256 cells), `BLOCK-COMMAND` (a store of 1 reads the block into those cells, 2 writes it from them) and `BLOCK-STATUS` (0 when the command worked, −1 for a block the device has not got or cells not all in memory). A block is 1024 bytes: 256 cells, four bytes to a cell, the first byte lowest, at either cell width. This is the console's arrangement (§7) applied to storage; like it, it is the model's stand-in and not the mesh protocol, which §5.11 still leaves to the device node. |
**Consequences of D-2 that every definition must respect.** The data stack holds 10 items and the
return stack 9, and every `call`, `FOR`, `DO` loop frame and `push` uses return-stack slots. Nesting
+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
+4 -3
View File
@@ -188,9 +188,10 @@ array behind its four block registers (D-19), which skips the address
space, the metadata, the owner, the ACL and the Stadium.
**As of 2026-10-07** (`MESH.md` step 6): the registers and the RAM array
are gone. A node's block words make a kernel request and
`v4/system/blocks.c` serves it through v3's block subsystem, so the address
space and the metadata are v3's. Still skipped: the owner and first-touch
are gone. A node's block words are kernel requests by block number, and
`v4/system/blocks.c` serves them through v3's block subsystem into the
node's window of four slots, as v3's block words do for a VM; so the
address space and the metadata are v3's. Still skipped: the owner and first-touch
claim, the ACL, and the Stadium touch, which need the node's identity. A
first build of that step had block requests passed from node to node and
nodes with no storage; Captain Bob withdrew it as a divergence from this
@@ -0,0 +1,148 @@
[=3hBdsDxe: loading Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
[=3hStarKernel UEFI Loader
Loading kernel from ESP...
[CKPT 001] Entered efi_main - ConOut live
RAW SERIAL UP
[CKPT 002] Serial (COM1) initialized
Monolithic build - kernel linked directly
Collecting boot information...
CmdLine: parsed OK
[CKPT 004] Command line parsed
[CKPT 005] Kernel stack allocation decided
[CKPT 006] Boot info collected (ACPI table located)
GOP: linear framebuffer found
[CKPT 007] GOP: linear framebuffer found
[CKPT 008] About to enter ExitBootServices retry loop
EBS...
EBS OK
Calling kernel_main (monolithic)...
_____ _ _ __ _
/ ____| | | |/ / | |
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
____) | || (_| | | | . \ __/ | | | | | __/ |
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
LithosAnanke v2.1.0
Architecture: amd64
Build: Oct 7 2026 09:28:08
UEFI BootServices: EXITED
=== StarKernel Boot Information ===
Memory map entries: 131
Total memory: 1023 MB
Usable memory: 966 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 248746
Free pages : 247223
Used pages : 1523
Total MB : 971
Free MB : 965
Used MB : 5
VMM initialized (mapped RAM, CR3 switched)
VMM initialized (mapped RAM, CR3 switched)
VMM self-test: mapped OK at 0xffff800000000000
VMM self-test complete.
IDT installed.
APIC: init...
APIC: IA32_APIC_BASE MSR=0x00000000fee00900
APIC: stale-ISR drain: 0 EOI(s) issued
APIC: initialized (xAPIC MMIO, TPR=0, SIVR=0x1FF, EOI-clear)
APIC: init done
I/O APIC: init...
I/O APIC: base=0xfec00000, gsi_base=0, overrides=5
override: source=00 gsi=2 flags=0x0000
override: source=05 gsi=5 flags=0x000d
override: source=09 gsi=9 flags=0x000d
override: source=0a gsi=10 flags=0x000d
override: source=0b gsi=11 flags=0x000d
i8042: keyboard ACKed enable-scanning
i8042: IRQ1 enabled in controller config byte
I/O APIC: keyboard IRQ1 routed (masked)
Timer: init...
Timer: init start
Timer: PM_TMR_BLK discovered from FADT at port 1544
Timer: VM mode detected (hypervisor present).
Timer: HPET calibration disabled (VM-exit MMIO would poison timing).
Timer: WARNING: invariant TSC not present under hypervisor.
Timer: continuing in RELATIVE mode (no determinism guarantees).
Timer: RDTSCP not present; using RDTSC (less serialized).
Timer: CPUID frequency unavailable; trying PM Timer...
Timer: trust=1 (0=NONE,1=REL,2=ABS), TSC=2097105936 Hz
Timer: init done
Kernel heap initialized.
Heap statistics:
Total bytes: 536870872
Free bytes: 536870872
Used bytes: 0
Peak bytes: 0
Heap base addr: 24641536
Heap end addr: 561512448
Heartbeat: init...
APIC Timer: calibrating...
APIC Timer: apic_hz=1008484717, tick_hz=100, initial_count=10084847
APIC Timer: configured (masked, ready to start)
Heartbeat: init done
Kernel initialization complete.
Boot successful!
StarForth v4: one host node, the F18-derived engine
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x1d4ff6398f190c13 capsule_hash=0x1d4ff6398f190c13 words=310
V4: capsule v4:forth79.4th signature: missing (unsigned)
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x1c771623a746cc7f
V4: capsule v4:post79.4th signature: missing (unsigned)
PARITY:V4_POST tests=538 pass=538 fail=0
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0xd8221bd6371d0ef3
PARITY:OK
POST: PASSED
[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
PCI: init...
PCI: ECAM mapped (amd64)
virtio-blk: found device
Artemis: virtio-blk attached
[HADES][INFO ] [KRELTSC: 83774187] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
ok> : SQ DUP * ;
ok
ok> 7 SQ . 3 4 U* . .
49 0 12 ok
ok> COLD
FORTH-79 Cold Start
System initialized.
ok
ok> 3 4 U* . .
0 12 ok
ok> FORGET U*
Protected word
ERROR
ok> 9 KERNEL-WORD ASK9 ASK9
Argument out of range
ERROR
ok> 1 BLOCK C@ . 2047 BLOCK C@ .
0 0 ok
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
65 ok
ok> 3072 BLOCK C@ .
0 ok
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
Storage refused
ERROR
ok> 9999999 BLOCK
Block out of range
ERROR
ok> BYE
BYE: cold restart
@@ -0,0 +1,121 @@
UEFI firmware (version 2025.11-3ubuntu7.3 built at 15:40:26 on Sep 23 2026)
[=3hBdsDxe: failed to load Boot0002 "UEFI Misc Device" from PciRoot(0x0)/Pci(0x2,0x0): Not Found
BdsDxe: failed to load Boot0003 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:04.0-1" from PciRoot(0x0)/Pci(0x4,0x0)/USB(0x0,0x0): Not Found
BdsDxe: loading Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
BdsDxe: starting Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
[=3hStarKernel UEFI Loader
Loading kernel from ESP...
[CKPT 001] Entered efi_main - ConOut live
Collecting boot information...
[CKPT 004] Command line parsed
[CKPT 005] Kernel stack allocation decided
[CKPT 006] Boot info collected (ACPI table located)
[CKPT 007] GOP: linear framebuffer found
[CKPT 008] About to enter ExitBootServices retry loop
_____ _ _ __ _
/ ____| | | |/ / | |
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
____) | || (_| | | | . \ __/ | | | | | __/ |
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
LithosAnanke v2.1.0
Architecture: aarch64
Build: Oct 7 2026 09:30:46
UEFI BootServices: EXITED
=== StarKernel Boot Information ===
Memory map entries: 108
Total memory: 4093 MB
Usable memory: 4054 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 1039359
Free pages : 1037948
Used pages : 1411
Total MB : 4059
Free MB : 4054
Used MB : 5
VMM initialized (mapped RAM, CR3 switched)
VMM initialized (mapped RAM, CR3 switched)
VMM self-test: mapped OK at 0xffff800000000000
VMM self-test complete.
AArch64: running at EL1
IDT installed.
APIC: init...
PSCI: no DTB -- using HVC (QEMU virt-machine default)
GICv2: no DTB GIC node -- using QEMU virt-machine defaults
GICv2: distributor+CPU interface enabled, PPI 30
APIC: init done
Timer: init...
Timer: AArch64 generic timer initialised.
Timer: init done
Kernel heap initialized.
Heap statistics:
Total bytes: 2147483608
Free bytes: 2147483608
Used bytes: 0
Peak bytes: 0
Heap base addr: 1207959552
Heap end addr: 3355443200
Heartbeat: init...
Heartbeat: init done
Kernel initialization complete.
Boot successful!
StarForth v4: one host node, the F18-derived engine
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x1d4ff6398f190c13 capsule_hash=0x1d4ff6398f190c13 words=310
V4: capsule v4:forth79.4th signature: missing (unsigned)
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x1c771623a746cc7f
V4: capsule v4:post79.4th signature: missing (unsigned)
PARITY:V4_POST tests=538 pass=538 fail=0
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0xd8221bd6371d0ef3
PARITY:OK
POST: PASSED
[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
PCI: init...
virtio-blk: found device
Artemis: virtio-blk attached
[HADES][INFO ] [KRELTSC: 1376451] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
ok> : SQ DUP * ;
ok
ok> 7 SQ . 3 4 U* . .
49 0 12 ok
ok> COLD
FORTH-79 Cold Start
System initialized.
ok
ok> 3 4 U* . .
0 12 ok
ok> FORGET U*
Protected word
ERROR
ok> 9 KERNEL-WORD ASK9 ASK9
Argument out of range
ERROR
ok> 1 BLOCK C@ . 2047 BLOCK C@ .
0 0 ok
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
65 ok
ok> 3072 BLOCK C@ .
0 ok
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
Storage refused
ERROR
ok> 9999999 BLOCK
Block out of range
ERROR
ok> BYE
BYE: cold restart
@@ -0,0 +1,191 @@
OpenSBI v1.8
____ _____ ____ _____
/ __ \ / ____| _ \_ _|
| | | |_ __ ___ _ __ | (___ | |_) || |
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
| |__| | |_) | __/ | | |____) | |_) || |_
\____/| .__/ \___|_| |_|_____/|____/_____|
| |
|_|
Platform Name : riscv-virtio,qemu
Platform Features : medeleg
Platform HART Count : 1
Platform HART Protection : pmp
Platform IPI Device : aclint-mswi
Platform Timer Device : aclint-mtimer @ 10000000Hz
Platform Console Device : uart8250
Platform HSM Device : ---
Platform PMU Device : ---
Platform Reboot Device : syscon-reboot
Platform Shutdown Device : syscon-poweroff
Platform Suspend Device : ---
Platform CPPC Device : ---
Firmware Base : 0x80000000
Firmware Size : 321 KB
Firmware RW Offset : 0x40000
Firmware RW Size : 65 KB
Firmware Heap Offset : 0x47000
Firmware Heap Size : 37 KB (total), 0 KB (reserved), 12 KB (used), 23 KB (free)
Firmware Scratch Size : 4096 B (total), 1464 B (used), 2632 B (free)
Runtime SBI Version : 3.0
Standard SBI Extensions : ipi,pmu,srst,sse,hsm,rfnc,fwft,time,base,legacy,dbcn,dbtr
Experimental SBI Extensions : none
Domain0 Name : root
Domain0 Boot HART : 0
Domain0 HARTs : 0*
Domain0 Region00 : 0x0000000080040000-0x000000008005ffff M: (F,R,W) S/U: ()
Domain0 Region01 : 0x0000000080000000-0x000000008003ffff M: (F,R,X) S/U: ()
Domain0 Region02 : 0x0000000000100000-0x0000000000100fff M: (I,R,W) S/U: (R,W)
Domain0 Region03 : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W)
Domain0 Region04 : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: ()
Domain0 Region05 : 0x000000000c400000-0x000000000c5fffff M: (I,R,W) S/U: (R,W)
Domain0 Region06 : 0x000000000c000000-0x000000000c3fffff M: (I,R,W) S/U: (R,W)
Domain0 Region07 : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
Domain0 Next Address : 0x0000000020000000
Domain0 Next Arg1 : 0x00000000bfe00000
Domain0 Next Mode : S-mode
Domain0 SysReset : yes
Domain0 SysSuspend : yes
Boot HART ID : 0
Boot HART Domain : root
Boot HART Priv Version : v1.12
Boot HART Base ISA : rv64imafdch
Boot HART ISA Extensions : sstc,zicntr,zihpm,zicboz,zicbom,sdtrig,svadu
Boot HART PMP Count : 16
Boot HART PMP Granularity : 2 bits
Boot HART PMP Address Bits : 54
Boot HART MHPM Info : 16 (0x0007fff8)
Boot HART Debug Triggers : 2 triggers
Boot HART MIDELEG : 0x0000000000001666
Boot HART MEDELEG : 0x0000000000f4b509
[=3hRISC-V EDK2 firmware version 2025.11-3ubuntu7.3
Press ESCAPE within 5 seconds for boot options ERROR: C40000002:V03051002 I0 6D33944A-EC75-4855-A54D-809C75241F6C 83FFF850
BdsDxe: failed to load Boot0001 "UEFI Misc Device" fr
om PciRoot(0x0)/Pci(0x1,0x0): Not Found
[=3hStarKernel UEFI Loader
Loading kernel from ESP...
[CKPT 001] Entered efi_main - ConOut live
Monolithic build - kernel linked directly
Collecting boot information...
CmdLine: parsed OK
[CKPT 004] Command line parsed
[CKPT 005] Kernel stack allocation decided
[CKPT 006] Boot info collected (ACPI table located)
GOP: linear framebuffer found
[CKPT 007] GOP: linear framebuffer found
[CKPT 008] About to enter ExitBootServices retry loop
Calling kernel_main (monolithic)...
riscv64 item 4.3.5a: satp state at kernel entry (before switch)
satp.MODE = 0x000000000000000a
satp.PPN = 0x00000000000bf868
__kernel_start = 0x00000000bdcab9f8
riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a)
_____ _ _ __ _
/ ____| | | |/ / | |
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
____) | || (_| | | | . \ __/ | | | | | __/ |
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
LithosAnanke v2.1.0
Architecture: riscv64
Build: Oct 7 2026 09:34:18
UEFI BootServices: EXITED
=== StarKernel Boot Information ===
Memory map entries: 100
Total memory: 1020 MB
Usable memory: 975 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 250276
Free pages : 249624
Used pages : 652
Total MB : 977
Free MB : 975
Used MB : 2
VMM initialized (mapped RAM, CR3 switched)
VMM initialized (mapped RAM, CR3 switched)
VMM self-test: mapped OK at 0xffff800000000000
VMM self-test complete.
IDT installed.
APIC: init...
PLIC: no DTB PLIC node -- using QEMU virt-machine default (base=0x0c000000)
PLIC: init (S-mode context 1, threshold=0)
APIC: init done
Timer: init...
Timer: RISC-V time CSR @ 10000000 Hz (FALLBACK, no devicetree)
Timer: init done
Kernel heap initialized.
Heap statistics:
Total bytes: 536870872
Free bytes: 536870872
Used bytes: 0
Peak bytes: 0
Heap base addr: 2214588416
Heap end addr: 2751459328
Heartbeat: init...
Heartbeat: init done
Kernel initialization complete.
Boot successful!
StarForth v4: one host node, the F18-derived engine
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x1d4ff6398f190c13 capsule_hash=0x1d4ff6398f190c13 words=310
V4: capsule v4:forth79.4th signature: missing (unsigned)
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x1c771623a746cc7f
V4: capsule v4:post79.4th signature: missing (unsigned)
PARITY:V4_POST tests=538 pass=538 fail=0
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0xd8221bd6371d0ef3
PARITY:OK
POST: PASSED
[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
PCI: init...
virtio-blk: found device
Artemis: virtio-blk attached
[HADES][INFO ] [KRELTSC: 23947707] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
ok> : SQ DUP * ;
ok
ok> 7 SQ . 3 4 U* . .
49 0 12 ok
ok> COLD
FORTH-79 Cold Start
System initialized.
ok
ok> 3 4 U* . .
0 12 ok
ok> FORGET U*
Protected word
ERROR
ok> 9 KERNEL-WORD ASK9 ASK9
Argument out of range
ERROR
ok> 1 BLOCK C@ . 2047 BLOCK C@ .
0 0 ok
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
65 ok
ok> 3072 BLOCK C@ .
0 ok
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
Storage refused
ERROR
ok> 9999999 BLOCK
Block out of range
ERROR
ok> BYE
BYE: cold restart
+3 -3
View File
@@ -51,15 +51,15 @@ node_size = $(if $(findstring /test_host_,$(1)),-DV4_NODE_WORDS=$(HOST_WORDS) -D
# BLOCKS (docs/v4.0.0/MESH.md section 8). A node asks its kernel for a
# block, and system/blocks.c serves it from the kernel's block subsystem,
# which is v3's and is built as v3 builds it. Tests named test_blocks*.c
# and test_host_*.c link them. V3_BLOCK_SRCS is all of v3 that the block
# which is v3's and is built as v3 builds it. Tests named
# test_host_*.c link them. V3_BLOCK_SRCS is all of v3 that the block
# subsystem needs: its two device back ends, the log and the clock.
ROOT_DIR := $(abspath $(HERE)/..)
V3_BLOCK_SRCS := $(addprefix $(ROOT_DIR)/v3/src/,block_subsystem.c blkio_ram.c blkio_file.c log.c platform/platform_init.c platform/linux/time.c)
V3_CFLAGS := -std=gnu99 -Wall $(OPT) -I$(ROOT_DIR)/v3/include -I$(ROOT_DIR)/kernel/include
BLOCKS_SRC := $(HERE)/system/blocks.c
V3_INC := -isystem $(ROOT_DIR)/v3/include -isystem $(ROOT_DIR)/kernel/include
uses_blocks = $(or $(findstring /test_blocks,$(1)),$(findstring /test_host_,$(1)))
uses_blocks = $(findstring /test_host_,$(1))
blocks_inc = $(if $(call uses_blocks,$(1)),$(V3_INC))
$(BINDIR)/v3blocks.o: $(V3_BLOCK_SRCS) $(wildcard $(ROOT_DIR)/v3/include/*.h) $(HERE)/Makefile
+8 -5
View File
@@ -133,11 +133,14 @@ The two products are not yet this: they become the five nodes at steps 8
and 9.
A node she births is not POSTed: POST is the kernel's, once, on Hera.
**Blocks** (`MESH.md` step 6). A node asks its kernel for a block, as a v3
VM does: `BLOCK` and its family keep two buffers, and the word beneath them
writes a request to port 0 with the block's number and the buffer's address
on the stack (`v4/include/v4/blocks.h`). `v4/system/blocks.c` serves it from
the kernel's block subsystem, which is v3's (`v3/src/block_subsystem.c`):
**Blocks** (`MESH.md` step 6). The block words are the kernel's, as they
are for a v3 VM: `BLOCK`, `BUFFER`, `UPDATE`, `SAVE-BUFFERS` and
`EMPTY-BUFFERS` each write one request to port 0, by block number only
(`v4/include/v4/blocks.h`). The node has a window of four slots in its
memory; the kernel copies blocks into it, keeps the record of what is in
it, and decides when a block is written (`v4/system/blocks.c`). The blocks
are the kernel's block subsystem's, which is v3's
(`v3/src/block_subsystem.c`):
one chain, the same blocks at the same numbers for every node. A hosted
program has the chain's fast RAM, blocks 1 to 2047. On bare metal the chain
is fast RAM, the ramdrive and the virtio disk; the disk is read and not yet
+29 -67
View File
@@ -4,103 +4,65 @@
\ EMPTY-BUFFERS LIST LOAD SCR BLK, and v3's FLUSH THRU and --> . Part of the
\ compiler capsule; rests on everything before it.
\
\ STORAGE IS THE KERNEL'S (docs/v4.0.0/MESH.md section 8, ENGINE.md 3.3). A
\ node only ever asks for a block by number, and it asks its kernel: with
\ the number and the word address of 256 cells on the data stack it writes
\ the request to port 0, where its kernel is, and 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.
\
\ THE BUFFERS. There are two, of 256 cells each, so two blocks can be in
\ memory at once -- one can be copied to another. A buffer is given to the
\ block that asks for one, turn about; if it held a block that was UPDATEd,
\ that block is written first. BLOCK and BUFFER give a byte address, as PAD
\ and TIB are: what C@ CMOVE and TYPE take.
\ STORAGE IS THE KERNEL'S (docs/v4.0.0/MESH.md section 8, ENGINE.md 3.3), as
\ it is for a v3 VM. A node only ever asks for a block by number, and it
\ asks its kernel: it writes a request to port 0, where its kernel is, and
\ when the write has been served the answer is on the data stack. The
\ node has a window in its memory, four slots of 256 cells; the kernel
\ keeps the record of which block is in which slot, does the copying, and
\ decides when a block is written. The requests (v4/include/v4/blocks.h):
\ BLOCK# ( n -- waddr status ) the slot holding block n
\ BUFFER# ( n -- waddr status ) a slot for block n, not read
\ UPDATE# ( -- status ) the block last asked for is changed
\ SAVE-BUFFERS# ( -- status ) every changed block is written
\ EMPTY-BUFFERS# ( -- ) every slot is let go
\ The status is 0, or 2 storage refused (error 17), or 3 there is no such
\ block (error 13). BLOCK and BUFFER give a byte address, as PAD and TIB
\ are: what C@ CMOVE and TYPE take.
\
\ Constants the loader supplies:
\ BLOCK-READ# BLOCK-WRITE# the numbers of the kernel's two requests
\ BUF0 BUF1 word addresses of the two buffers
\ (B) word address of six cells: (B)+0 the block in buffer 0, or 0
\ (B)+1 whether it has been UPDATEd (B)+2 (B)+3 the same for
\ buffer 1 (B)+4 which buffer was asked for last, 0 or 2
\ (B)+5 the block number while a buffer is found for it
\ BLOCK# BUFFER# UPDATE# SAVE-BUFFERS# EMPTY-BUFFERS# the requests' numbers
\ SCR word address of the variable: the block LIST showed last
\ BLK (SRC) (SRC-HOOK) see input.v4
header SCR inline : SCR' SCR ;
header BLK inline : BLK' BLK ;
\ ( i -- addr ) the word address of buffer i, where i is 0 or 2
: (BUF) if Z drop BUF1 ; Z: drop BUF0 ;
\ ( command i -- ) read buffer i's block from storage, command 1, or
\ write it, command 2: a request of this node's kernel ( n waddr -- status ).
\ If it did not work nothing is in the buffer, and it is an error: 17 if
\ storage refused, 13 if there is no such block.
: (DEVICE)
dup push SWAP push \ i R: i command
dup (B) + a! @ SWAP (BUF) \ n waddr
pop -1 + if RD drop BLOCK-WRITE# jump ASK
RD: drop BLOCK-READ#
ASK: (PORT) b! !b \ status
pop SWAP \ i status
if FINE
push (B) + a! 0 !+ 0 ! pop
-2 + if E17 drop NODE-ERROR b! 13 !b ;
\ ( status -- ) a request that did not work is an error
: (B-ERR)
if FINE -2 + if E17 drop NODE-ERROR b! 13 !b ;
E17: drop NODE-ERROR b! 17 !b ;
FINE: drop drop ;
FINE: drop ;
\ ( i -- ) if buffer i's block has been UPDATEd, write it
: (SAVE)
dup (B) + 1 + a! @ if CLEAN
drop 0 ! 2 SWAP jump (DEVICE)
CLEAN: drop drop ;
\ ( n -- i flag ) the buffer for block n: the one that has it, flag -1; or
\ the other of the one asked for last, its old block written if need be and
\ n's number put on it, flag 0 -- what it holds is not block n yet. A block
\ number below 1 is an error (code 13).
: (FIND-BUF)
-if POS jump BAD
POS: if BAD
(B)+5 a! !
(B) a! @ (B)+5 a! @ xor if HIT0 drop
(B)+2 a! @ (B)+5 a! @ xor if HIT2 drop
(B)+4 a! @ 2 xor dup ! \ the other one
dup (SAVE)
dup (B) + a! (B)+5 b! @b !+ 0 ! \ its block now, not updated
0 ;
HIT0: drop 0 (B)+4 a! ! 0 -1 ;
HIT2: drop 2 (B)+4 a! ! 2 -1 ;
BAD: drop NODE-ERROR b! 13 !b 0 -1 ;
\ ( waddr status -- baddr ) the slot's byte address, or the error
: (B-GOT)
if OK SWAP drop jump (B-ERR)
OK: drop 2* 2* ;
\ ( n -- baddr ) FORTH-79: the address of a buffer assigned to block n.
\ What the buffer holds is not read from storage.
header BUFFER
: BUFFER (FIND-BUF) drop (BUF) 2* 2* ;
: BUFFER BUFFER# (PORT) b! !b jump (B-GOT)
\ ( n -- baddr ) FORTH-79: the address of a buffer that holds block n,
\ read from storage if it is not already in one.
header BLOCK
: BLOCK
(FIND-BUF) if READ drop (BUF) 2* 2* ;
READ: drop drop \ which buffer is in (B)+4: nothing waits on the stack
1 (B)+4 a! @ (DEVICE) (B)+4 a! @ (BUF) 2* 2* ;
: BLOCK BLOCK# (PORT) b! !b jump (B-GOT)
\ FORTH-79: mark the block most recently asked for as changed, so that it is
\ written before its buffer is used for another.
header UPDATE
: UPDATE (B)+4 a! @ (B) + a! @+ if NONE drop -1 ! ; NONE: drop ;
: UPDATE UPDATE# (PORT) b! !b jump (B-ERR)
\ FORTH-79: write every block that has been UPDATEd.
header SAVE-BUFFERS
: SAVE-BUFFERS 0 (SAVE) 2 jump (SAVE)
: SAVE-BUFFERS SAVE-BUFFERS# (PORT) b! !b jump (B-ERR)
\ As v3: the same.
header FLUSH
: FLUSH jump SAVE-BUFFERS
\ FORTH-79: forget what is in the buffers; nothing is written.
header EMPTY-BUFFERS
: EMPTY-BUFFERS (B) a! 0 !+ 0 !+ 0 !+ 0 ! ;
: EMPTY-BUFFERS EMPTY-BUFFERS# (PORT) b! !b ;
\ ---- LIST ----------------------------------------------------------------------
\ ( n -- ) as v3: a new line, "Block n", then sixteen lines of 64
+58 -19
View File
@@ -4,42 +4,81 @@
*
* 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,
* This is v3's way (v3/src/word_source/block_words.c). The node has a
* WINDOW in its memory, V4_BLOCK_SLOTS slots of 256 cells, as a v3 VM has
* four slots of 1 KiB at the top of its memory. The kernel keeps the
* record of which block is in which slot and does all the copying, and it
* writes into the window and nowhere else in the node. The requests, 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
* V4_REQ_BLOCK ( n -- waddr status ) the slot that holds block n, read
* from storage if it is not in one
* V4_REQ_BUFFER ( n -- waddr status ) a slot for block n, of zeros if it
* is not in one: nothing is read
* V4_REQ_UPDATE ( -- status ) the block most recently asked for
* is marked changed
* V4_REQ_SAVE_BUFFERS ( -- status ) every block marked is written
* V4_REQ_EMPTY_BUFFERS ( -- ) every slot is let go; nothing is
* written
*
* 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 the stack did not hold both arguments, in
* which case the status is all that is left on it -- or V4_BLOCK_RANGE,
* there is no such block.
* Their numbers are below zero: the requests a host names for its own
* words (KERNEL-WORD) count up from 1.
*
* waddr is the word address of the slot, or 0 when the status is not 0.
* The status is 0; or V4_BLOCK_RANGE, there is no such block; or
* V4_BLOCK_REFUSED: storage would not have a block that had to be written,
* the stack did not hold the request's argument, or the window is not all
* in the node's memory.
*
* 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 for another block. (v3's
* UPDATE copies the slot to the kernel at once; the standard is followed.)
* A slot whose block is not marked is taken before one whose block is. A
* block that storage would not take is let go of: its slot is empty.
*
* When the kernel's chain of devices changes, everything in the slots is
* let go and nothing of it is written, as v3 does (blk_vm_check_epoch).
*
* 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.
* lowest, whatever the cell width: a block 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).
* The blocks are the kernel's block subsystem's (v3/src/block_subsystem.c).
* 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_REQ_BLOCK (-1)
#define V4_REQ_BUFFER (-2)
#define V4_REQ_UPDATE (-3)
#define V4_REQ_SAVE_BUFFERS (-4)
#define V4_REQ_EMPTY_BUFFERS (-5)
#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.
#define V4_BLOCK_SLOTS 4u
/* What the kernel keeps for one node. */
typedef struct {
v4_cell window; /* word address of the first slot */
uint32_t block[V4_BLOCK_SLOTS]; /* the block in each slot; 0: none */
unsigned char changed[V4_BLOCK_SLOTS]; /* UPDATEd and not yet written */
unsigned next; /* where the search for a slot to take begins */
int last; /* the slot most recently asked for; -1: none */
uint64_t epoch; /* the chain as it was when the slots were filled */
} v4_blocks;
/* A node's window is at `window`, and nothing is in it. */
void v4_blocks_init(v4_blocks *w, v4_cell window);
/* If `request` is one of the five, serve it for the node 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);
int v4_blocks_serve(v4_blocks *w, v4_node *n, v4_cell request);
#endif /* V4_BLOCKS_H */
+4
View File
@@ -54,6 +54,10 @@ typedef struct {
* that loads capsules when it boots sets them again when it has
* (v4_image_seal). */
v4_cell boot_cells, fence;
/* the block window: the word address of the slots the kernel copies
* blocks into (v4/blocks.h) */
v4_cell block_window;
} v4_image;
/* The description of the nucleus built from v4/capsule (the generated file
+126 -33
View File
@@ -6,48 +6,141 @@
#include "v4/blocks.h"
#include "block_subsystem.h"
int v4_blocks_serve(v4_node *n, v4_cell request)
static void let_go(v4_blocks *w)
{
v4_cell addr, num;
uint32_t block;
uint8_t *b;
unsigned i;
for (i = 0; i < V4_BLOCK_SLOTS; i++) { w->block[i] = 0; w->changed[i] = 0; }
w->next = 0;
w->last = -1;
}
void v4_blocks_init(v4_blocks *w, v4_cell window)
{
w->window = window;
w->epoch = blk_subsys_epoch();
let_go(w);
}
static v4_cell slot_addr(const v4_blocks *w, unsigned s)
{
return w->window + (v4_cell)(s * V4_BLOCK_CELLS);
}
/* The block in slot s is written. Whether or not storage takes it, it is
* no longer marked. */
static int write_slot(v4_blocks *w, v4_node *n, unsigned s)
{
uint8_t *b = blk_get_buffer(w->block[s], 1);
v4_cell at = slot_addr(w, s);
unsigned i;
w->changed[s] = 0;
if (!b) return V4_BLOCK_REFUSED;
for (i = 0; i < V4_BLOCK_CELLS; i++) {
v4_ucell c = (v4_ucell)n->mem[at + (v4_cell)i];
b[4u * i] = (uint8_t)(c & 0xFFu);
b[4u * i + 1u] = (uint8_t)((c >> 8) & 0xFFu);
b[4u * i + 2u] = (uint8_t)((c >> 16) & 0xFFu);
b[4u * i + 3u] = (uint8_t)((c >> 24) & 0xFFu);
}
return (blk_update(w->block[s]) == BLK_OK && blk_flush(w->block[s]) == BLK_OK) ? V4_BLOCK_OK : V4_BLOCK_REFUSED;
}
/* A slot to put a block in: an empty one, or one whose block is not marked,
* or, with every one marked, the next in turn, its block written first.
* *status is not V4_BLOCK_OK if that write was refused; the slot is empty
* all the same. */
static unsigned take_slot(v4_blocks *w, v4_node *n, int *status)
{
unsigned i, s;
*status = V4_BLOCK_OK;
for (i = 0; i < V4_BLOCK_SLOTS; i++) if (w->block[i] == 0) return i;
for (i = 0; i < V4_BLOCK_SLOTS; i++) {
s = (w->next + i) % V4_BLOCK_SLOTS;
if (!w->changed[s]) break;
}
if (i == V4_BLOCK_SLOTS) {
s = w->next;
*status = write_slot(w, n, s);
}
w->next = (s + 1u) % V4_BLOCK_SLOTS;
w->block[s] = 0;
w->changed[s] = 0;
if (w->last == (int)s) w->last = -1;
return s;
}
/* BLOCK and BUFFER: ( n -- waddr status ) */
static void slot_for(v4_blocks *w, v4_node *n, int read)
{
v4_cell num, at;
uint32_t block;
unsigned i, s;
int status;
if (request != V4_REQ_BLOCK_READ && request != V4_REQ_BLOCK_WRITE) return 0;
if (n->ds.depth < 2u) { /* not both arguments: nothing is done with what is there */
v4_dstack_reset(&n->ds);
if (n->ds.depth < 1u) { v4_dstack_reset(&n->ds); v4_dstack_push(&n->ds, 0); v4_dstack_push(&n->ds, (v4_cell)V4_BLOCK_REFUSED); return; }
num = v4_dstack_pop(&n->ds);
block = (uint32_t)num;
if (num < 1 || (uint64_t)num > 0xFFFFFFFFull || !blk_is_valid(block)) {
v4_dstack_push(&n->ds, 0); v4_dstack_push(&n->ds, (v4_cell)V4_BLOCK_RANGE);
return;
}
for (s = 0; s < V4_BLOCK_SLOTS && w->block[s] != block; s++) { }
if (s == V4_BLOCK_SLOTS) {
const uint8_t *b = 0;
s = take_slot(w, n, &status);
if (status == V4_BLOCK_OK && read) {
b = blk_get_buffer(block, 0);
if (!b) status = V4_BLOCK_REFUSED;
}
if (status != V4_BLOCK_OK) { v4_dstack_push(&n->ds, 0); v4_dstack_push(&n->ds, (v4_cell)status); return; }
at = slot_addr(w, s);
for (i = 0; i < V4_BLOCK_CELLS; i++)
n->mem[at + (v4_cell)i] = b ? (v4_cell)((v4_ucell)b[4u * i] | ((v4_ucell)b[4u * i + 1u] << 8)
| ((v4_ucell)b[4u * i + 2u] << 16) | ((v4_ucell)b[4u * i + 3u] << 24))
: 0;
w->block[s] = block;
w->changed[s] = 0;
}
w->last = (int)s;
v4_dstack_push(&n->ds, slot_addr(w, s));
v4_dstack_push(&n->ds, (v4_cell)V4_BLOCK_OK);
}
int v4_blocks_serve(v4_blocks *w, v4_node *n, v4_cell request)
{
unsigned s;
int status = V4_BLOCK_OK;
if (request > V4_REQ_BLOCK || request < V4_REQ_EMPTY_BUFFERS) return 0;
/* the chain is not what it was: what the slots hold is of no chain now */
if (blk_subsys_epoch() != w->epoch) { let_go(w); w->epoch = blk_subsys_epoch(); }
if (request == V4_REQ_EMPTY_BUFFERS) { let_go(w); return 1; }
/* the window is all in the node's memory, or nothing is done */
if (!v4_node_addr_ok(w->window) || !v4_node_addr_ok(w->window + (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS - 1u))) {
if (request == V4_REQ_BLOCK || request == V4_REQ_BUFFER) {
if (n->ds.depth >= 1u) (void)v4_dstack_pop(&n->ds);
v4_dstack_push(&n->ds, 0);
}
v4_dstack_push(&n->ds, (v4_cell)V4_BLOCK_REFUSED);
return 1;
}
addr = v4_dstack_pop(&n->ds);
num = v4_dstack_pop(&n->ds);
block = (uint32_t)num;
if (!v4_node_addr_ok(addr) || !v4_node_addr_ok(addr + (v4_cell)(V4_BLOCK_CELLS - 1u))) {
status = V4_BLOCK_REFUSED;
} else if (num < 1 || (uint64_t)num > 0xFFFFFFFFull || !blk_is_valid(block)) {
status = V4_BLOCK_RANGE;
} else if (request == V4_REQ_BLOCK_READ) {
b = blk_get_buffer(block, 0);
status = b ? V4_BLOCK_OK : V4_BLOCK_REFUSED;
if (b)
for (i = 0; i < V4_BLOCK_CELLS; i++)
n->mem[addr + (v4_cell)i] = (v4_cell)((v4_ucell)b[4u * i] | ((v4_ucell)b[4u * i + 1u] << 8)
| ((v4_ucell)b[4u * i + 2u] << 16) | ((v4_ucell)b[4u * i + 3u] << 24));
} else {
b = blk_get_buffer(block, 1);
status = V4_BLOCK_REFUSED;
if (b) {
for (i = 0; i < V4_BLOCK_CELLS; i++) {
v4_ucell w = (v4_ucell)n->mem[addr + (v4_cell)i];
b[4u * i] = (uint8_t)(w & 0xFFu);
b[4u * i + 1u] = (uint8_t)((w >> 8) & 0xFFu);
b[4u * i + 2u] = (uint8_t)((w >> 16) & 0xFFu);
b[4u * i + 3u] = (uint8_t)((w >> 24) & 0xFFu);
if (request == V4_REQ_BLOCK) { slot_for(w, n, 1); return 1; }
if (request == V4_REQ_BUFFER) { slot_for(w, n, 0); return 1; }
if (request == V4_REQ_UPDATE) {
if (w->last >= 0 && w->block[w->last] != 0) w->changed[w->last] = 1;
} else { /* SAVE-BUFFERS */
for (s = 0; s < V4_BLOCK_SLOTS; s++)
if (w->block[s] != 0 && w->changed[s] && write_slot(w, n, s) != V4_BLOCK_OK) {
status = V4_BLOCK_REFUSED;
w->block[s] = 0;
if (w->last == (int)s) w->last = -1;
}
if (blk_update(block) == BLK_OK && blk_flush(block) == BLK_OK) status = V4_BLOCK_OK;
}
}
v4_dstack_push(&n->ds, (v4_cell)status);
return 1;
+5 -1
View File
@@ -117,6 +117,9 @@ static void post_watch(char c)
#define CONSOLE_PORT 1u
#define CONSOLE_ID 1 /* the console's number as a sender; the node's is 0 until it is given one */
/* What the kernel keeps of the node's block window (blocks.h). */
static v4_blocks boot_blocks;
int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
{
static v4_message out, in; /* one line at a time: the boot is not re-entered */
@@ -131,7 +134,7 @@ int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
if (n->stopped) return V4_BOOT_LINE_STOPPED;
if (n->asking && n->ask_port == KERNEL_PORT) { /* a request: the kernel's turn */
if (v4_blocks_serve(n, n->request)) { /* a block, as for any node */
if (v4_blocks_serve(&boot_blocks, n, n->request)) { /* a block, as for any node */
v4_node_port_served(n);
} else if (b->serve && n->request >= 1 && n->request <= (v4_cell)b->word_count) {
b->serve(n, (unsigned)n->request);
@@ -352,6 +355,7 @@ int v4_boot_run(const v4_boot *b)
* when it is written: the node need never wait before writing to them
* (node.h, v4_node_port_status) */
v4_node_port_status(b->n, 0, 1u << KERNEL_PORT | 1u << CONSOLE_PORT);
v4_blocks_init(&boot_blocks, b->im->block_window);
if (!load_nucleus(b)) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; }
/* the kernel's words, before any capsule that might use one */
+8 -9
View File
@@ -59,7 +59,7 @@
#define HEND ((HBUF_W + 16) * 4)
#define XVARS (HBUF_W - 4) /* (X): words.v4, 4 cells */
#define QBASE (XVARS - 40) /* the Q words' and DUMP's scratch cells, 39 in all */
#define BVARS (QBASE - 16) /* blocks.v4: (B) 6 cells, then SCR BLK (SRC) (SRC-HOOK) */
#define BVARS (QBASE - 16) /* blocks.v4: 6 cells not used now, then SCR BLK (SRC) (SRC-HOOK) */
#define SCR (BVARS + 6)
#define BLK (BVARS + 7)
#define SRC (BVARS + 8)
@@ -99,8 +99,7 @@
#define WORD_FORGOTTEN (BUF0_W - 12) /* the xt of the word that is told when entries go, or 0 */
#define LOG_LEVEL (BUF0_W - 5) /* the level at or below which a message is printed */
#define BOOT_CELLS (BVARS + 14) /* (BOOT): DP and LATEST as the loader left them, 2 cells */
#define BUF0_W (BVARS - 2 * 256) /* the two block buffers, 256 cells each */
#define BUF1_W (BUF0_W + 256)
#define BUF0_W (BVARS - (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS)) /* the block window: the kernel's slots, 256 cells each (v4/blocks.h) */
/* the ports' ten addresses and the two that follow them end below BUF0_W - 20 */
typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1];
/* Nothing printed and no message waiting: a node's variables for them, as at switch-on. */
@@ -116,7 +115,7 @@ typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1];
#define DICT_W ((v4_cell)8192)
#endif
#ifndef DICT_END_W
#define DICT_END_W ((v4_cell)14336)
#define DICT_END_W ((v4_cell)13824) /* 512 cells lower than it was: the block window is four slots now */
#endif
/* the messages waiting are above the dictionary */
typedef char host_map_queue_fits[(MQ_W >= DICT_END_W) ? 1 : -1];
@@ -169,7 +168,6 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
v4_text_constant(tx, "(S)", SVARS);
v4_text_constant(tx, "(W)", WVARS);
v4_text_constant(tx, "(X)", XVARS);
v4_text_constant(tx, "(B)", BVARS);
v4_text_constant(tx, "(BOOT)", BOOT_CELLS);
v4_text_constant(tx, "(LOG-LEVEL)", LOG_LEVEL);
v4_text_constant(tx, "(ACL-HOOK)", ACL_HOOK);
@@ -209,10 +207,11 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
v4_text_constant(tx, "(OUT)", OUT_W);
v4_text_constant(tx, "(WORD-DEFINED)", WORD_DEFINED);
v4_text_constant(tx, "(WORD-FORGOTTEN)", WORD_FORGOTTEN);
v4_text_constant(tx, "BLOCK-READ#", V4_REQ_BLOCK_READ); /* the kernel's two block requests (v4/blocks.h) */
v4_text_constant(tx, "BLOCK-WRITE#", V4_REQ_BLOCK_WRITE);
v4_text_constant(tx, "BUF0", BUF0_W);
v4_text_constant(tx, "BUF1", BUF1_W);
v4_text_constant(tx, "BLOCK#", V4_REQ_BLOCK); /* the kernel's block requests (v4/blocks.h) */
v4_text_constant(tx, "BUFFER#", V4_REQ_BUFFER);
v4_text_constant(tx, "UPDATE#", V4_REQ_UPDATE);
v4_text_constant(tx, "SAVE-BUFFERS#", V4_REQ_SAVE_BUFFERS);
v4_text_constant(tx, "EMPTY-BUFFERS#", V4_REQ_EMPTY_BUFFERS);
v4_text_constant(tx, "(Q/)", QBASE); /* 5 cells */
v4_text_constant(tx, "(QE)", QBASE + 5); /* 8 */
v4_text_constant(tx, "(QR)", QBASE + 13); /* 5 */
-112
View File
@@ -1,112 +0,0 @@
/* 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;
}
+176
View File
@@ -0,0 +1,176 @@
/* test_blocks.c -- the kernel serves a node's block requests.
* docs/v4.0.0/MESH.md sections 8.2 and 8.3.
*
* A host node, stopped, with what a request takes 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. The window is four slots of 256 cells in the node's
* memory, and the kernel keeps the record of what is in them.
*/
#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 WINDOW 200
static unsigned char ram[(size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE], dev[DEVICE_BLOCKS * BLK_FORTH_SIZE];
static v4_node n;
static v4_blocks w;
static v4_cell got_addr;
/* a request that takes a block's number: its status, and the address in got_addr */
static v4_cell ask(v4_cell request, v4_cell block)
{
v4_cell status;
v4_dstack_reset(&n.ds);
v4_dstack_push(&n.ds, 7777);
v4_dstack_push(&n.ds, block);
if (!v4_blocks_serve(&w, &n, request)) return -100;
status = v4_dstack_pop(&n.ds);
got_addr = v4_dstack_pop(&n.ds);
return (v4_dstack_pop(&n.ds) == 7777 && n.ds.depth == 0) ? status : -200; /* what was under its argument is still there */
}
/* a request that takes nothing: its status */
static v4_cell tell(v4_cell request)
{
v4_dstack_reset(&n.ds);
v4_dstack_push(&n.ds, 7777);
if (!v4_blocks_serve(&w, &n, request)) return -100;
if (request == V4_REQ_EMPTY_BUFFERS) return (v4_dstack_pop(&n.ds) == 7777 && n.ds.depth == 0) ? 0 : -200;
{
v4_cell status = v4_dstack_pop(&n.ds);
return (v4_dstack_pop(&n.ds) == 7777 && n.ds.depth == 0) ? status : -200;
}
}
static int in_window(v4_cell a) { return a >= WINDOW && a < WINDOW + (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS) && (a - WINDOW) % (v4_cell)V4_BLOCK_CELLS == 0; }
static unsigned char *chain_block(unsigned num) { return num < 2048u ? ram + ((size_t)num + BLK_FORTH_SYS_RESERVED) * 1024u : dev + (size_t)(num - 2048u) * 1024u; }
int main(void)
{
unsigned i;
v4_cell a, b;
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);
v4_blocks_init(&w, WINDOW);
/* ---- BLOCK: the block is copied into a slot of the window ---- */
memset(chain_block(2050), 'A', 1024);
chain_block(2050)[1] = 'B';
n.mem[WINDOW - 1] = 111; n.mem[WINDOW + V4_BLOCK_SLOTS * V4_BLOCK_CELLS] = 222;
CHECK(ask(V4_REQ_BLOCK, 2050) == V4_BLOCK_OK && in_window(got_addr), "BLOCK gives the address of a slot of the window");
a = got_addr;
CHECK(n.mem[a] == (v4_cell)0x41414241 && n.mem[a + 255] == (v4_cell)0x41414141, "holding the block, four bytes to a cell, the first lowest, the rest of each cell zero");
chain_block(2050)[0] = 'Z';
CHECK(ask(V4_REQ_BLOCK, 2050) == V4_BLOCK_OK && got_addr == a && n.mem[a] == (v4_cell)0x41414241, "a block already in a slot is given as it is there, and not read again");
CHECK(ask(V4_REQ_BLOCK, 20) == V4_BLOCK_OK && in_window(got_addr) && got_addr != a, "another block is put in another slot");
CHECK(n.mem[WINDOW - 1] == 111 && n.mem[WINDOW + V4_BLOCK_SLOTS * V4_BLOCK_CELLS] == 222, "nothing outside the window is written");
/* ---- UPDATE marks; nothing is written until SAVE-BUFFERS ---- */
CHECK(ask(V4_REQ_BLOCK, 2051) == V4_BLOCK_OK, "block 2051 is asked for");
b = got_addr;
n.mem[b] = (v4_cell)0x64636261;
#if V4_CELL_BITS > 32
n.mem[b + 1] = (v4_cell)0x7766554433221100LL; /* only its low 32 bits are the block's */
#endif
CHECK(tell(V4_REQ_UPDATE) == V4_BLOCK_OK && chain_block(2051)[0] == 0, "UPDATE marks it, and it is not written yet");
CHECK(tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK && memcmp(chain_block(2051), "abcd", 4) == 0, "SAVE-BUFFERS writes it");
#if V4_CELL_BITS > 32
CHECK(chain_block(2051)[4] == 0x00 && chain_block(2051)[7] == 0x33 && chain_block(2051)[8] == 0, "the low 32 bits of each cell, and no more");
#endif
CHECK(chain_block(2050)[0] == 'Z' && chain_block(2050)[1] == 'B', "a block that was not UPDATEd is not written");
n.mem[b] = (v4_cell)0x31313131;
CHECK(tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK && chain_block(2051)[0] == 'a', "nor is one changed again and not UPDATEd again");
CHECK(tell(V4_REQ_UPDATE) == V4_BLOCK_OK, "UPDATE is of the block most recently asked for");
CHECK(ask(V4_REQ_BLOCK, 2050) == V4_BLOCK_OK && tell(V4_REQ_UPDATE) == V4_BLOCK_OK && tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK
&& chain_block(2051)[0] == '1' && chain_block(2050)[0] == 'A', "two UPDATEd blocks are both written, each from its own slot");
/* ---- EMPTY-BUFFERS forgets, and writes nothing ---- */
n.mem[a] = (v4_cell)0x58585858;
CHECK(ask(V4_REQ_BLOCK, 2050) == V4_BLOCK_OK && tell(V4_REQ_UPDATE) == V4_BLOCK_OK && tell(V4_REQ_EMPTY_BUFFERS) == 0 && tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK
&& chain_block(2050)[0] == 'A', "EMPTY-BUFFERS forgets an UPDATEd block: it is not written");
CHECK(tell(V4_REQ_UPDATE) == V4_BLOCK_OK && tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK, "UPDATE with no block asked for does nothing");
chain_block(2050)[0] = 'Q';
CHECK(ask(V4_REQ_BLOCK, 2050) == V4_BLOCK_OK && (n.mem[got_addr] & 0xFF) == 'Q', "and the block is read again when it is next asked for");
/* ---- BUFFER: a slot for the block, not read ---- */
(void)tell(V4_REQ_EMPTY_BUFFERS);
CHECK(ask(V4_REQ_BUFFER, 2060) == V4_BLOCK_OK && in_window(got_addr) && n.mem[got_addr] == 0 && n.mem[got_addr + 255] == 0, "BUFFER gives a slot of zeros, as v3's does");
n.mem[got_addr] = (v4_cell)0x62626262;
CHECK(tell(V4_REQ_UPDATE) == V4_BLOCK_OK && tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK && chain_block(2060)[0] == 'b' && chain_block(2060)[4] == 0, "and what is put in it is written");
CHECK(ask(V4_REQ_BLOCK, 2060) == V4_BLOCK_OK && n.mem[got_addr] == (v4_cell)0x62626262, "BLOCK then finds it in its slot");
/* ---- more blocks than slots ---- */
(void)tell(V4_REQ_EMPTY_BUFFERS);
for (i = 0; i < V4_BLOCK_SLOTS; i++) {
CHECK(ask(V4_REQ_BLOCK, 2100 + (v4_cell)i) == V4_BLOCK_OK, "block %u is asked for", 2100 + i);
n.mem[got_addr] = (v4_cell)(0x30303030 + i);
}
CHECK(tell(V4_REQ_UPDATE) == V4_BLOCK_OK, "the last of them is UPDATEd");
CHECK(ask(V4_REQ_BLOCK, 2110) == V4_BLOCK_OK && chain_block(2100 + V4_BLOCK_SLOTS - 1)[0] == 0, "one more takes a slot whose block was not UPDATEd, and nothing is written");
for (i = 0; i < V4_BLOCK_SLOTS; i++) { (void)ask(V4_REQ_BLOCK, 2100 + (v4_cell)i); n.mem[got_addr] = (v4_cell)(0x30303030 + i); (void)tell(V4_REQ_UPDATE); }
for (i = 0; i < V4_BLOCK_SLOTS && chain_block(2100 + i)[0] == 0; i++) { }
CHECK(i == V4_BLOCK_SLOTS, "with every slot UPDATEd nothing is written until a slot is wanted");
{
unsigned written = 0;
CHECK(ask(V4_REQ_BLOCK, 2111) == V4_BLOCK_OK, "one more block is asked for");
for (i = 0; i < V4_BLOCK_SLOTS; i++) if (chain_block(2100 + i)[0] != 0) written++;
CHECK(written >= 1, "and the block whose slot it took was written first: %u written", written);
}
(void)tell(V4_REQ_EMPTY_BUFFERS);
/* ---- numbers that are no block ---- */
CHECK(ask(V4_REQ_BLOCK, 0) == V4_BLOCK_RANGE && got_addr == 0, "0 is no block");
CHECK(ask(V4_REQ_BLOCK, -1) == V4_BLOCK_RANGE && ask(V4_REQ_BUFFER, -5) == V4_BLOCK_RANGE, "nor is a number below it");
CHECK(ask(V4_REQ_BLOCK, 2111) == V4_BLOCK_OK && ask(V4_REQ_BLOCK, 2112) == V4_BLOCK_RANGE && ask(V4_REQ_BUFFER, 2112) == V4_BLOCK_RANGE, "the device's last block is, and the one after it is not");
#if V4_CELL_BITS > 32
CHECK(ask(V4_REQ_BLOCK, (v4_cell)0x100000014LL) == V4_BLOCK_RANGE, "a number 32 bits do not hold is no block, whatever its low bits are");
#endif
/* ---- a request with too little on the stack ---- */
v4_dstack_reset(&n.ds);
CHECK(v4_blocks_serve(&w, &n, V4_REQ_BLOCK) && n.ds.depth == 2 && v4_dstack_pop(&n.ds) == V4_BLOCK_REFUSED && v4_dstack_pop(&n.ds) == 0,
"BLOCK with nothing on the stack is refused: no address, and the status");
/* ---- a window that is not in the node's memory ---- */
{
v4_blocks bad;
v4_cell top = (v4_cell)V4_NODE_WORDS, status;
v4_blocks_init(&bad, top - (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS) + 1);
n.mem[top - 1] = 123;
v4_dstack_reset(&n.ds); v4_dstack_push(&n.ds, 2050);
CHECK(v4_blocks_serve(&bad, &n, V4_REQ_BLOCK), "a request with a window that runs past the end of memory is served");
status = v4_dstack_pop(&n.ds);
CHECK(status == V4_BLOCK_REFUSED && v4_dstack_pop(&n.ds) == 0 && n.mem[top - 1] == 123, "by being refused, with nothing written");
}
/* ---- the chain changes: what is in the slots is let go, as v3 does ---- */
(void)tell(V4_REQ_EMPTY_BUFFERS);
CHECK(ask(V4_REQ_BLOCK, 2070) == V4_BLOCK_OK && (n.mem[got_addr] = (v4_cell)0x59595959) != 0 && tell(V4_REQ_UPDATE) == V4_BLOCK_OK, "a block is in a slot, UPDATEd");
{
static unsigned char more[4 * 1024];
CHECK(blk_subsys_add_raw_device(more, 4) == BLK_OK, "a device joins the chain");
memset(chain_block(2070), 'N', 1024);
CHECK(tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK && chain_block(2070)[0] == 'N', "what was in the slots is not written to the chain as it now is");
CHECK(ask(V4_REQ_BLOCK, 2070) == V4_BLOCK_OK && (n.mem[got_addr] & 0xFF) == 'N', "and the block is read again");
}
/* ---- what is not a block request ---- */
v4_dstack_reset(&n.ds);
v4_dstack_push(&n.ds, 11); v4_dstack_push(&n.ds, 22);
CHECK(!v4_blocks_serve(&w, &n, 5) && !v4_blocks_serve(&w, &n, 0) && !v4_blocks_serve(&w, &n, -6) && 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;
}
+23 -10
View File
@@ -78,7 +78,7 @@ static char out[V4_CONSOLE_CAP + 1];
#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 */
static v4_blocks kblocks; /* what the kernel keeps of the node's block window */
extern void sf_time_init(void);
extern void log_set_level(int level);
@@ -122,7 +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 (v4_blocks_serve(&kblocks, &n, n.request)) 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;
@@ -266,6 +266,7 @@ static void boot_with(unsigned depth)
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(ram, 0, sizeof ram); memset(common_dev, 0, sizeof common_dev);
v4_blocks_init(&kblocks, BUF0_W);
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST);
v4_node_fault_attach(&n, w_fault);
v4_node_error_attach(&n, NODE_ERROR);
@@ -1175,9 +1176,10 @@ int main(void)
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");
unsigned k, held = 0;
for (k = 0; k < V4_BLOCK_SLOTS; k++) if (kblocks.block[k] == 2112u) held++;
CHECK(held == 0, "the block storage would not take is let go: no slot holds it");
CHECK(is(say(REFUSING_FIRST " BLOCK C@ .\n"), "0 ok\nok> "), "and the next BLOCK reads it from storage again");
}
/* ---- blocks: text loaded from storage ---- */
@@ -1225,11 +1227,22 @@ int main(void)
"an UPDATEd block is not written yet");
CHECK(is(say("FLUSH\n"), " ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'H' && disk[3 * V4_BLOCK_BYTES + 1] == ' ' && disk[3 * V4_BLOCK_BYTES + 1023] == 'I'
&& disk[2 * V4_BLOCK_BYTES + 1023] == 0 && disk[4 * V4_BLOCK_BYTES] == 0, "FLUSH writes it, all 1024 bytes and no others");
CHECK(is(say("74 3 BLOCK C! UPDATE 4 BLOCK DROP\n"), " ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'H', "with two buffers a second block does not disturb it");
CHECK(is(say("5 BLOCK DROP\n"), " ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'J', "a third takes its buffer, and it is written first");
CHECK(is(say("EMPTY-BUFFERS 74 3 BLOCK C! UPDATE 4 BLOCK DROP 5 BLOCK DROP 6 BLOCK DROP\n"), " ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'H',
"with four slots three more blocks do not disturb it");
CHECK(is(say("7 BLOCK DROP\n"), " ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'H', "a fifth takes the slot of a block that was not UPDATEd, and nothing is written");
CHECK(is(say("77 4 BLOCK C! UPDATE 77 5 BLOCK C! UPDATE 77 6 BLOCK C! UPDATE\n"), " ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'H'
&& disk[4 * V4_BLOCK_BYTES] != 'M' && disk[5 * V4_BLOCK_BYTES] != 'M' && disk[6 * V4_BLOCK_BYTES] != 'M', "four UPDATEd blocks fill the slots, and none is written yet");
{
unsigned written;
CHECK(is(say("8 BLOCK DROP\n"), " ok\nok> "), "one more block is asked for");
written = (disk[3 * V4_BLOCK_BYTES] == 'J') + (disk[4 * V4_BLOCK_BYTES] == 'M') + (disk[5 * V4_BLOCK_BYTES] == 'M') + (disk[6 * V4_BLOCK_BYTES] == 'M');
CHECK(written == 1, "it takes the slot of one of them, and that one is written first: %u written", written);
}
CHECK(is(say("FLUSH\n"), " ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'J' && disk[4 * V4_BLOCK_BYTES] == 'M' && disk[5 * V4_BLOCK_BYTES] == 'M' && disk[6 * V4_BLOCK_BYTES] == 'M',
"FLUSH writes the rest");
CHECK(is(say("3 BLOCK C@ . 75 3 BLOCK C! UPDATE EMPTY-BUFFERS FLUSH 3 BLOCK C@ .\n"), "74 74 ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'J',
"EMPTY-BUFFERS forgets a change without writing it");
CHECK(is(say("76 3 BLOCK C! 5 BLOCK DROP 6 BLOCK DROP 3 BLOCK C@ .\n"), "74 ok\nok> "), "a change without UPDATE is lost when the buffer is taken");
CHECK(is(say("76 3 BLOCK C! 5 BLOCK DROP 6 BLOCK DROP 7 BLOCK DROP 8 BLOCK DROP 3 BLOCK C@ .\n"), "74 ok\nok> "), "a change without UPDATE is lost when the slot is taken");
CHECK(is(say("3 BLOCK 4 BLOCK 1024 CMOVE UPDATE SAVE-BUFFERS\n"), " ok\nok> ") && memcmp(disk + 3 * V4_BLOCK_BYTES, disk + 4 * V4_BLOCK_BYTES, V4_BLOCK_BYTES) == 0
&& disk[4 * V4_BLOCK_BYTES] == 'J', "two blocks in memory at once: one copied to the other");
put_block(40, "this is on the device");
@@ -1238,7 +1251,7 @@ int main(void)
{
v4_cell k, wide = 0;
(void)say("3 BLOCK DROP\n");
for (k = 0; k < 2 * 256; k++) if ((v4_ucell)n.mem[BUF0_W + k] > 0xFFFFFFFFu) wide = 1;
for (k = 0; k < (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS); k++) if ((v4_ucell)n.mem[BUF0_W + k] > 0xFFFFFFFFu) wide = 1;
CHECK(!wide, "a block's bytes are four to a cell at every cell width");
}
{
@@ -1396,7 +1409,7 @@ int main(void)
&& is(say("4 LOG-LEVEL!\n"), " ok\nok> "), "a vocabulary, a base, a block, a screen, a log level");
CHECK(is(say("1 2 COLD 65 EMIT\n"), "FORTH-79 Cold Start\nSystem initialized.\n ok\nok> "), "COLD says so");
CHECK(n.mem[DP] == dp && n.mem[LATEST] == capsule_latest && n.mem[CONTEXT] == LATEST && n.mem[CURRENT] == LATEST && n.mem[VOC_LINK] == 0
&& n.mem[BASE] == 10 && n.mem[SCR] == 0 && n.mem[BVARS] == 0 && n.mem[FENCE] == dp / 4 && n.mem[LOG_LEVEL] == 2, "and everything is as the loader left it");
&& n.mem[BASE] == 10 && n.mem[SCR] == 0 && kblocks.block[0] == 0 && kblocks.block[1] == 0 && kblocks.block[2] == 0 && kblocks.block[3] == 0 && n.mem[FENCE] == dp / 4 && n.mem[LOG_LEVEL] == 2, "and everything is as the loader left it");
CHECK(is(say("KEEPME\n"), "UNKNOWN WORD: 'KEEPME'\n ERROR\nok> ") && is(say("VV\n"), "UNKNOWN WORD: 'VV'\n ERROR\nok> ")
&& is(say(".S : NEW 16 . ; NEW\n"), "<0> \n16 ok\nok> "), "what was defined is gone, and the system works");
}
+6 -3
View File
@@ -113,9 +113,11 @@ static uint64_t parity_hash[8];
/* THE KERNEL, for a node that is not Hera: on its port 0 it serves the
* node's blocks, and no other request (MESH.md 8.3). */
static unsigned refused;
static v4_blocks node_blocks[PLACES]; /* what the kernel keeps of each node's block window, by where the node is in the pool */
static int node_kernel_take(void *self, v4_cell value)
{
if (!v4_blocks_serve((v4_node *)self, value)) refused++;
v4_fabric_node *x = (v4_fabric_node *)self;
if (!v4_blocks_serve(&node_blocks[x - pool], &x->n, value)) refused++;
return 1;
}
static v4_device node_kernel[PLACES];
@@ -135,8 +137,9 @@ static int host_born(void *self)
v4_node_stack_regs_attach(&pool[k].n, DSTACK_REG, RSTACK_REG);
v4_node_error_attach(&pool[k].n, NODE_ERROR);
v4_node_fault_attach(&pool[k].n, w_fault);
v4_blocks_init(&node_blocks[k], BUF0_W);
if (k != 0) { /* Hera's kernel is wired where she is made */
node_kernel[k].take = node_kernel_take; node_kernel[k].give = 0; node_kernel[k].self = &pool[k].n;
node_kernel[k].take = node_kernel_take; node_kernel[k].give = 0; node_kernel[k].self = &pool[k];
if (!v4_fabric_wire_device(&f, (unsigned)place, 0, &node_kernel[k])) return -1;
}
return place;
@@ -187,7 +190,7 @@ static int kernel_give(void *self, v4_cell *value) /* the nucleus, to a nod
static int kernel_take(void *self, v4_cell value) /* a request */
{
(void)self;
if (v4_blocks_serve(&v4_fabric_node_at(&f, hera_place)->n, value)) return 1; /* a block, as for any node */
if (v4_blocks_serve(&node_blocks[0], &pool[0].n, value)) return 1; /* a block, as for any node; Hera is the first in the pool */
if (value < FIRST_REQUEST || !v4_manage_serve(&manage, hera_place, (unsigned)(value - FIRST_REQUEST))) refused++;
return 1;
}
+3 -3
View File
@@ -88,7 +88,7 @@ int main(int argc, char **argv)
n.mem[NODE_ERROR] = 0;
n.mem[BLK] = 0; n.mem[SCR] = 0; n.mem[SRC] = TIB; n.mem[SRC_HOOK] = 0;
for (k = BVARS; k < BVARS + 6; k++) n.mem[k] = 0;
for (k = BUF0_W; k < BUF0_W + 2 * 256; k++) n.mem[k] = 0;
for (k = BUF0_W; k < BUF0_W + (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS); k++) n.mem[k] = 0;
for (k = TIB_W; k < TIB_W + 260; k++) n.mem[k] = 0;
for (k = WBUF_W; k < WBUF_W + WBUF_CELLS; k++) n.mem[k] = 0;
for (k = PAD_W; k < PAD_W + 21; k++) n.mem[k] = 0;
@@ -113,9 +113,9 @@ int main(int argc, char **argv)
fprintf(out, "const v4_image v4_capsule_image = {\n %uu, %uu, %uu, %uu,\n",
(unsigned)V4_CELL_BITS, (unsigned)V4_NODE_WORDS, (unsigned)V4_DATA_RING, (unsigned)V4_RET_RING);
fprintf(out, " %ld,\n %ld, %ld,\n %ld, %ld,\n", (long)idle, (long)faults, (long)PORT, (long)w_key, (long)w_key_end);
fprintf(out, " %ld, %ld, %ld,\n %ld, %ld,\n %ld,\n %ld, %ld,\n %ld, %ld\n};\n", (long)CONSOLE_TX, (long)CONSOLE_RX, (long)CONSOLE_ST,
fprintf(out, " %ld, %ld, %ld,\n %ld, %ld,\n %ld,\n %ld, %ld,\n %ld, %ld,\n %ld\n};\n", (long)CONSOLE_TX, (long)CONSOLE_RX, (long)CONSOLE_ST,
(long)DSTACK_REG, (long)RSTACK_REG, (long)NODE_ERROR, (long)DP, (long)LATEST,
(long)BOOT_CELLS, (long)FENCE);
(long)BOOT_CELLS, (long)FENCE, (long)BUF0_W);
if (fclose(out) != 0) die("cannot write", argv[1]);
fprintf(stderr, "mkimage: a capsule of %u words, dictionary to word %ld, %d-bit cells -> %s, %s\n", count, (long)((n.mem[DP] + 3) / 4), V4_CELL_BITS, argv[2], argv[1]);
return 0;