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:
co-authored by
Claude Opus 5.5
parent
7f4d946d2e
commit
9bfd5071d2
@@ -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.
@@ -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
@@ -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
|
||||
|
||||
@@ -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 @@
|
||||
[2J[001;001H[=3h[2J[001;001H[2J[001;001H[8;042;160t[2J[001;001H[2J[001;001H[8;056;240t[2J[001;001HBdsDxe: 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)
|
||||
[2J[001;001H[=3h[2J[001;001HStarKernel 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
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
|
||||
PCI: init...
|
||||
PCI: ECAM mapped (amd64)
|
||||
virtio-blk: found device
|
||||
Artemis: virtio-blk attached
|
||||
[32m[HADES][INFO ] [0m[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)
|
||||
[2J[001;001H[=3h[2J[001;001H[2J[001;001H[8;031;100t[2J[001;001HBdsDxe: 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)
|
||||
[2J[001;001H[=3h[2J[001;001HStarKernel 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
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
|
||||
PCI: init...
|
||||
virtio-blk: found device
|
||||
Artemis: virtio-blk attached
|
||||
[32m[HADES][INFO ] [0m[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
|
||||
[2J[004D[=3h[2J[009D[2J[004D[8;031;100t[2J[016DRISC-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
|
||||
[2J[001;001H[=3h[2J[009DStarKernel 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
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
|
||||
PCI: init...
|
||||
virtio-blk: found device
|
||||
Artemis: virtio-blk attached
|
||||
[32m[HADES][INFO ] [0m[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
@@ -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
@@ -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
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user