feat(v4.0.0): every node asks the kernel for its blocks; a born node is not POSTed
A block is a kernel request, as ENGINE.md 3.3 has it: the node puts the
block's number and the address of 256 cells on its stack and writes the
request to port 0, and the kernel leaves the status there. The requests
are -1, read, and -2, write, the same for every node. v4/system/blocks.c
serves them from the kernel's block subsystem, which is v3's. The four
storage registers are gone from the engine.
The device that spoke block messages (4a505a15) is withdrawn with its
test and its message types: Captain Bob ruled on 2026-10-07 that it, a
node's own drive, and nodes with no storage had left the OS as designed
(docs/v4.0.0/MESH.md 8.5).
Hera no longer sends POST to the nodes she births: POST is the kernel's,
once. Every node has its kernel on port 0; it serves a node's blocks and,
for Hera alone, her requests for nodes and capsules.
Bare metal: the node boots and is POSTed against POST's own block RAM,
and the kernel's chain -- fast RAM, the ramdrive, the virtio disk -- is
set up after POST and before the prompt, as on the v3 path. The disk is
read and not written: nothing in v4 yet gives the owner's word that it
may be formatted. A hosted program has the chain's fast RAM, as hosted
v3 has with no disk. Error 17 is Storage refused.
make -C v4 test and sanitize pass at both widths; hosted-check passes on
three ISAs; amd64, aarch64 and riscv64 boot, POST 538 of 538, with the
typed session: logs/20261007-081603, -081839, -082226. The hashes are
the same on all six.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
26a0748455
commit
d5b7235464
+28
-26
@@ -49,21 +49,18 @@ HOST_DATA_RING := 30
|
||||
HOST_RET_RING := 31
|
||||
node_size = $(if $(findstring /test_host_,$(1)),-DV4_NODE_WORDS=$(HOST_WORDS) -DV4_DATA_RING=$(HOST_DATA_RING) -DV4_RET_RING=$(HOST_RET_RING))
|
||||
|
||||
# STORAGE (docs/v4.0.0/MESH.md section 8). What is on a storage port is
|
||||
# system/storage.c, held to this model's warnings; behind it is the kernel's
|
||||
# block subsystem, which is v3's, built as v3 builds it and reached through
|
||||
# system/store_v3.c. Tests named test_store*.c and 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.
|
||||
# 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
|
||||
# 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
|
||||
STORE_SRC := $(HERE)/system/storage.c
|
||||
STORE_V3_SRC := $(HERE)/system/store_v3.c
|
||||
BLOCKS_SRC := $(HERE)/system/blocks.c
|
||||
V3_INC := -isystem $(ROOT_DIR)/v3/include -isystem $(ROOT_DIR)/kernel/include
|
||||
uses_store = $(or $(findstring /test_store,$(1)),$(findstring /test_host_,$(1)))
|
||||
store_src = $(if $(call uses_store,$(1)),$(STORE_SRC))
|
||||
store_inc = $(if $(call uses_store,$(1)),$(V3_INC))
|
||||
uses_blocks = $(or $(findstring /test_blocks,$(1)),$(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
|
||||
@mkdir -p $(BINDIR)/v3o
|
||||
@@ -73,14 +70,17 @@ $(BINDIR)/san-v3blocks.o: $(V3_BLOCK_SRCS) $(wildcard $(ROOT_DIR)/v3/include/*.h
|
||||
@mkdir -p $(BINDIR)/san-v3o
|
||||
cd $(BINDIR)/san-v3o && $(CC) $(V3_CFLAGS) -O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined -fno-sanitize-recover=all -c $(V3_BLOCK_SRCS)
|
||||
$(LD) -r $(BINDIR)/san-v3o/*.o -o $@
|
||||
define STORE_V3_RULE
|
||||
$(BINDIR)/store_v3-$(1).o: $(STORE_V3_SRC) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/Makefile
|
||||
# blocks.c holds a node, so it is built for each node a test may have: by
|
||||
# width, and for the host node's size or the mesh node's.
|
||||
define BLOCKS_RULE
|
||||
$(BINDIR)/blocks-$(1)-$(2).o: $(BLOCKS_SRC) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/Makefile
|
||||
@mkdir -p $(BINDIR)
|
||||
$$(CC) $(V3_CFLAGS) -I$(HERE)/include -DV4_CELL_BITS=$(1) -c $(STORE_V3_SRC) -o $$@
|
||||
$$(CC) $(V3_CFLAGS) -I$(HERE)/include -DV4_CELL_BITS=$(1) $(if $(filter host,$(2)),-DV4_NODE_WORDS=$(HOST_WORDS) -DV4_DATA_RING=$(HOST_DATA_RING) -DV4_RET_RING=$(HOST_RET_RING)) -c $(BLOCKS_SRC) -o $$@
|
||||
endef
|
||||
$(foreach w,32 64,$(eval $(call STORE_V3_RULE,$(w))))
|
||||
store_objs = $(if $(call uses_store,$(2)),$(BINDIR)/store_v3-$(1).o $(BINDIR)/v3blocks.o)
|
||||
san_store_objs = $(if $(call uses_store,$(2)),$(BINDIR)/store_v3-$(1).o $(BINDIR)/san-v3blocks.o)
|
||||
$(foreach w,32 64,$(foreach k,host mesh,$(eval $(call BLOCKS_RULE,$(w),$(k)))))
|
||||
blocks_kind = $(if $(findstring /test_host_,$(1)),host,mesh)
|
||||
blocks_objs = $(if $(call uses_blocks,$(2)),$(BINDIR)/blocks-$(1)-$(call blocks_kind,$(2)).o $(BINDIR)/v3blocks.o)
|
||||
san_blocks_objs = $(if $(call uses_blocks,$(2)),$(BINDIR)/blocks-$(1)-$(call blocks_kind,$(2)).o $(BINDIR)/san-v3blocks.o)
|
||||
|
||||
# The capsule sources: definitions as text (include/v4/text.h), which tests
|
||||
# assemble onto a node. The directory is passed to the tests so that they
|
||||
@@ -158,9 +158,11 @@ $(CAPSULE_DIR_C): $(BINDIR)/mkcapsule $(CAPSULE_FILES) $(BINDIR)/v4_image_64.c
|
||||
$(BINDIR)/mkcapsule $(SIGN_KEY_ARGS) $(ROOT)/capsules $@
|
||||
|
||||
define HOSTED_RULE
|
||||
$(BINDIR)/starforth4-$(1): $(HERE)/tools/hosted.c $(BINDIR)/v4_image_64.c $(CAPSULE_DIR_C) $$(ENGINE_SRCS) $$(SYSTEM_SRCS) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/Makefile
|
||||
$$(CC_$(1)) $$(CFLAGS) -D_POSIX_C_SOURCE=200809L -I$(HERE)/include -DV4_CELL_BITS=64 $(HOST_DEFS) -c $(HERE)/tools/hosted.c -o $(BINDIR)/hosted-$(1).o
|
||||
$$(CC_$(1)) $$(SYSTEM_CFLAGS) -static $(BINDIR)/hosted-$(1).o $(BINDIR)/v4_image_64.c $(CAPSULE_DIR_C) $$(ENGINE_SRCS) $$(SYSTEM_SRCS) -o $$@
|
||||
$(BINDIR)/starforth4-$(1): $(HERE)/tools/hosted.c $(BINDIR)/v4_image_64.c $(CAPSULE_DIR_C) $$(ENGINE_SRCS) $$(SYSTEM_SRCS) $(BLOCKS_SRC) $(V3_BLOCK_SRCS) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/Makefile
|
||||
$$(CC_$(1)) $$(CFLAGS) -D_POSIX_C_SOURCE=200809L -I$(HERE)/include $(V3_INC) -DV4_CELL_BITS=64 $(HOST_DEFS) -c $(HERE)/tools/hosted.c -o $(BINDIR)/hosted-$(1).o
|
||||
@mkdir -p $(BINDIR)/v3o-$(1)
|
||||
cd $(BINDIR)/v3o-$(1) && $$(CC_$(1)) $(V3_CFLAGS) -I$(HERE)/include -DV4_CELL_BITS=64 $(HOST_DEFS) -c $(V3_BLOCK_SRCS) $(BLOCKS_SRC)
|
||||
$$(CC_$(1)) $$(SYSTEM_CFLAGS) -static $(BINDIR)/hosted-$(1).o $(BINDIR)/v4_image_64.c $(CAPSULE_DIR_C) $$(ENGINE_SRCS) $$(SYSTEM_SRCS) $(BINDIR)/v3o-$(1)/*.o -o $$@
|
||||
|
||||
$(BINDIR)/boot-$(1).txt: $(BINDIR)/starforth4-$(1)
|
||||
@echo " [hosted $(1)]"
|
||||
@@ -224,16 +226,16 @@ endif
|
||||
#
|
||||
# Both binaries depend on this Makefile, so a flag change rebuilds them.
|
||||
define TEST_RULE
|
||||
$(BINDIR)/$(1)-$(notdir $(2)): $(2) $$(SRCS) $(call store_src,$(2)) $(call store_objs,$(1),$(2)) $$(wildcard $(HERE)/include/v4/*.h) $$(wildcard $(HERE)/tests/*.h) $(HERE)/Makefile
|
||||
$(BINDIR)/$(1)-$(notdir $(2)): $(2) $$(SRCS) $(call blocks_objs,$(1),$(2)) $$(wildcard $(HERE)/include/v4/*.h) $$(wildcard $(HERE)/tests/*.h) $(HERE)/Makefile
|
||||
@mkdir -p $(BINDIR)
|
||||
$$(CC) $$(CFLAGS) -I$(HERE)/include $(call store_inc,$(2)) -DV4_CELL_BITS=$(1) $(call node_size,$(2)) $(capsule_dir) \
|
||||
$(2) $$(SRCS) $(call store_src,$(2)) $(call store_objs,$(1),$(2)) -o $$@
|
||||
$$(CC) $$(CFLAGS) -I$(HERE)/include $(call blocks_inc,$(2)) -DV4_CELL_BITS=$(1) $(call node_size,$(2)) $(capsule_dir) \
|
||||
$(2) $$(SRCS) $(call blocks_objs,$(1),$(2)) -o $$@
|
||||
|
||||
$(BINDIR)/san-$(1)-$(notdir $(2)): $(2) $$(SRCS) $(call store_src,$(2)) $(call san_store_objs,$(1),$(2)) $$(wildcard $(HERE)/include/v4/*.h) $$(wildcard $(HERE)/tests/*.h) $(HERE)/Makefile
|
||||
$(BINDIR)/san-$(1)-$(notdir $(2)): $(2) $$(SRCS) $(call san_blocks_objs,$(1),$(2)) $$(wildcard $(HERE)/include/v4/*.h) $$(wildcard $(HERE)/tests/*.h) $(HERE)/Makefile
|
||||
@mkdir -p $(BINDIR)
|
||||
$$(CC) $$(CSTD) $$(WARN) -O1 -g -fno-omit-frame-pointer \
|
||||
-fsanitize=address,undefined -fno-sanitize-recover=all -I$(HERE)/include $(call store_inc,$(2)) -DV4_CELL_BITS=$(1) $(call node_size,$(2)) $(capsule_dir) \
|
||||
$(2) $$(SRCS) $(call store_src,$(2)) $(call san_store_objs,$(1),$(2)) -o $$@
|
||||
-fsanitize=address,undefined -fno-sanitize-recover=all -I$(HERE)/include $(call blocks_inc,$(2)) -DV4_CELL_BITS=$(1) $(call node_size,$(2)) $(capsule_dir) \
|
||||
$(2) $$(SRCS) $(call san_blocks_objs,$(1),$(2)) -o $$@
|
||||
endef
|
||||
|
||||
# One run target per (width, test) and one prerequisite line per width, so
|
||||
|
||||
+21
-12
@@ -4,9 +4,12 @@
|
||||
\ EMPTY-BUFFERS LIST LOAD SCR BLK, and v3's FLUSH THRU and --> . Part of the
|
||||
\ compiler capsule; rests on everything before it.
|
||||
\
|
||||
\ THE DEVICE (D-19) is four registers: the block's number, the word address
|
||||
\ of 256 cells, a command -- 1 read, 2 write -- and a status that is 0 when
|
||||
\ the command worked.
|
||||
\ 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
|
||||
@@ -15,7 +18,7 @@
|
||||
\ and TIB are: what C@ CMOVE and TYPE take.
|
||||
\
|
||||
\ Constants the loader supplies:
|
||||
\ BLOCK-NUMBER BLOCK-ADDRESS BLOCK-COMMAND BLOCK-STATUS the device
|
||||
\ 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
|
||||
@@ -30,15 +33,21 @@ 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 or write buffer i's block. A block the device
|
||||
\ does not have is an error (code 13); the buffer is then empty.
|
||||
\ ( 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 (B) + a! @ BLOCK-NUMBER b! !b \ command i
|
||||
dup (BUF) BLOCK-ADDRESS b! !b
|
||||
push BLOCK-COMMAND b! !b pop \ i
|
||||
BLOCK-STATUS b! @b if FINE
|
||||
drop (B) + a! 0 !+ 0 ! \ nothing is in the buffer
|
||||
NODE-ERROR b! 13 !b ;
|
||||
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 ;
|
||||
E17: drop NODE-ERROR b! 17 !b ;
|
||||
FINE: drop drop ;
|
||||
|
||||
\ ( i -- ) if buffer i's block has been UPDATEd, write it
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
\ 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
|
||||
\ 17 Storage refused
|
||||
\ -1 the word has printed its own message
|
||||
|
||||
macro SWAP over push push drop pop pop endmacro
|
||||
|
||||
+2
-1
@@ -297,7 +297,7 @@ header ABORT
|
||||
POS: -1 + if M1 -1 + if M2 -1 + if M3 -1 + if M4
|
||||
-1 + if M5 -1 + if M6 -1 + if M7 -1 + if M8 -1 + if M9
|
||||
-1 + if M10 -1 + if M11 -1 + if M12 -1 + if M13 -1 + if M14
|
||||
-1 + if M15 -1 + if M16
|
||||
-1 + if M15 -1 + if M16 -1 + if M17
|
||||
drop 2 jump (DONE)
|
||||
M1: drop $6167654E (EMIT4) $65766974 (EMIT4) $756F6320 (EMIT4) $746E (EMIT4) CR 2 jump (DONE)
|
||||
M2: drop $20746F4E (EMIT4) $756E2061 (EMIT4) $7265626D (EMIT4) CR 2 jump (DONE)
|
||||
@@ -315,6 +315,7 @@ header ABORT
|
||||
M14: drop $62206F4E (EMIT4) $6B636F6C (EMIT4) $20736920 (EMIT4) $6E696562 (EMIT4) $6F6C2067 (EMIT4) $64656461 (EMIT4) CR 2 jump (DONE)
|
||||
M15: drop $65666544 (EMIT4) $64657272 (EMIT4) $726F7720 (EMIT4) $6F6E2064 (EMIT4) $65732074 (EMIT4) $74 (EMIT4) CR 2 jump (DONE)
|
||||
M16: drop $20746F4E (EMIT4) $6F772061 (EMIT4) $6472 (EMIT4) CR 2 jump (DONE)
|
||||
M17: drop $726F7453 (EMIT4) $20656761 (EMIT4) $75666572 (EMIT4) $646573 (EMIT4) CR 2 jump (DONE)
|
||||
|
||||
\ The table the loader gives the node: six words, one for each kind of
|
||||
\ fault in the node's order, each a jump. A jump fills its word, so they
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/* blocks.h -- a node asks its kernel for a block.
|
||||
* docs/v4.0.0/MESH.md section 8, docs/v4.0.0/ENGINE.md 3.3,
|
||||
* docs/v4.0.0/V3-PARITY.md 1d.
|
||||
*
|
||||
* A node only ever asks for a block by number, and it asks the kernel: a
|
||||
* request written to the port where its kernel is, as any kernel word is.
|
||||
* There are two, the same for every node,
|
||||
*
|
||||
* V4_REQ_BLOCK_READ ( n waddr -- status ) block n into the 256 cells at waddr
|
||||
* V4_REQ_BLOCK_WRITE ( n waddr -- status ) block n from them
|
||||
*
|
||||
* and their numbers are below zero: the requests a host names for its own
|
||||
* words (KERNEL-WORD) count up from 1. The status is 0, or
|
||||
* V4_BLOCK_REFUSED -- the cells are not all in the node's memory, or
|
||||
* storage will not have it -- or V4_BLOCK_RANGE, there is no such block.
|
||||
*
|
||||
* A block is 1024 bytes and 256 cells, four bytes to a cell, the first
|
||||
* lowest, whatever the cell width: a read leaves the rest of each cell
|
||||
* zero, and a write takes the low 32 bits.
|
||||
*
|
||||
* The blocks are the kernel's block subsystem's (v3/src/block_subsystem.c):
|
||||
* the device chain, the metadata and the map are its, as they were. Who
|
||||
* may have which block is not checked here yet (MESH.md 8.4).
|
||||
*/
|
||||
#ifndef V4_BLOCKS_H
|
||||
#define V4_BLOCKS_H
|
||||
|
||||
#include "v4/node.h"
|
||||
|
||||
#define V4_REQ_BLOCK_READ (-1)
|
||||
#define V4_REQ_BLOCK_WRITE (-2)
|
||||
|
||||
#define V4_BLOCK_OK 0
|
||||
#define V4_BLOCK_REFUSED 2
|
||||
#define V4_BLOCK_RANGE 3
|
||||
|
||||
/* If `request` is one of the two, serve it for the node, whose data stack
|
||||
* holds its arguments and is left holding its status, and return 1.
|
||||
* Otherwise touch nothing and return 0. The node is stopped, blocked at
|
||||
* its port, when this is called. */
|
||||
int v4_blocks_serve(v4_node *n, v4_cell request);
|
||||
|
||||
#endif /* V4_BLOCKS_H */
|
||||
@@ -49,22 +49,26 @@ typedef struct {
|
||||
* list, counting from 1. When the node writes a request to its port it
|
||||
* is blocked, and `serve` is called with the number: it takes the
|
||||
* word's arguments from the node's data stack and leaves its results
|
||||
* there, and when it returns the node goes on. A request that is not
|
||||
* in the list is an error on the node: 12, Argument out of range. */
|
||||
* there, and when it returns the node goes on. The kernel's two block
|
||||
* requests (v4/blocks.h) are served for every node and are not in the
|
||||
* list. Any other request is an error on the node: 12, Argument out of
|
||||
* range. */
|
||||
const char *const *words;
|
||||
unsigned word_count;
|
||||
void (*serve)(v4_node *n, unsigned request);
|
||||
|
||||
} v4_boot;
|
||||
|
||||
/* What `key` returns when it has no character. */
|
||||
#define V4_BOOT_KEY_NONE (-1) /* not yet: it is asked again */
|
||||
#define V4_BOOT_KEY_END (-2) /* and there never will be */
|
||||
|
||||
/* A node is born empty, with `disk` as its block storage (v4_image_born);
|
||||
* it is sent the nucleus through its port; and the capsules are loaded. Returns 1 with the node idle,
|
||||
/* A node is born empty (v4_image_born); it is sent the nucleus through its
|
||||
* port; and the capsules are loaded. Its blocks are the kernel's block
|
||||
* subsystem's, which whoever calls this has set up (v4/blocks.h). Returns 1 with the node idle,
|
||||
* waiting to be handed a line; or 0, the failure printed. It prints no
|
||||
* prompt: that is the host's. */
|
||||
int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks);
|
||||
int v4_boot_run(const v4_boot *b);
|
||||
|
||||
/* Hand the node a line and run it to its end, sending what it prints to
|
||||
* `out`. The line is sent to the node as a message and what it prints
|
||||
|
||||
@@ -42,7 +42,6 @@ typedef struct {
|
||||
v4_cell console_tx, console_rx, console_status;
|
||||
v4_cell dstack_reg, rstack_reg;
|
||||
v4_cell node_error;
|
||||
v4_cell storage_reg;
|
||||
|
||||
/* the dictionary's two variables: DP holds HERE, a byte address, and
|
||||
* LATEST the newest word of FORTH. What lies below HERE, and LATEST,
|
||||
@@ -72,13 +71,11 @@ void v4_image_seal(v4_node *n, const v4_image *im);
|
||||
uint64_t v4_image_dict_hash(const v4_node *n, const v4_image *im);
|
||||
|
||||
/* A node newly born (node.h), with the registers the nucleus expects of the
|
||||
* node it runs on -- and `disk`, `blocks` blocks of 1024 bytes, as its block
|
||||
* storage, or none if `disk` is 0. Its memory is empty and it is blocked
|
||||
* node it runs on. Its memory is empty and it is blocked
|
||||
* reading its ports: the nucleus is yet to be sent to it. Returns 1, or 0
|
||||
* if the description is not for this build of the engine (nothing is then
|
||||
* done). */
|
||||
int v4_image_born(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im,
|
||||
unsigned char *disk, unsigned blocks);
|
||||
int v4_image_born(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im);
|
||||
|
||||
/* 1 if the node is waiting for a character: it is inside KEY and none is
|
||||
* pending. */
|
||||
|
||||
@@ -34,18 +34,6 @@
|
||||
#define V4_MSG_OUTPUT 2 /* what a node printed */
|
||||
#define V4_MSG_DONE 3 /* how the text ended: one word, V4_TEXT_* */
|
||||
|
||||
/* Storage (docs/v4.0.0/MESH.md 8.3). A block's number is one word of
|
||||
* payload, written as it is; a block is 1024 characters. */
|
||||
#define V4_MSG_BLOCK_READ 4 /* the block's number; answered by BLOCK_DATA, or BLOCK_DONE with an error */
|
||||
#define V4_MSG_BLOCK_WRITE 5 /* the block's number; a BLOCK_DATA follows; answered by BLOCK_DONE */
|
||||
#define V4_MSG_BLOCK_DATA 6 /* 1024 characters */
|
||||
#define V4_MSG_BLOCK_DONE 7 /* one word, V4_STORE_* */
|
||||
|
||||
#define V4_STORE_OK 0
|
||||
#define V4_STORE_NONE 1 /* there is no storage for that number */
|
||||
#define V4_STORE_REFUSED 2
|
||||
#define V4_STORE_RANGE 3 /* there is no such block */
|
||||
|
||||
/* how text ended */
|
||||
#define V4_TEXT_QUIT 0 /* QUIT: nothing is to be said */
|
||||
#define V4_TEXT_COMPLETED 1 /* a console says " ok" */
|
||||
|
||||
+6
-27
@@ -116,10 +116,6 @@ typedef struct {
|
||||
v4_cell dstack_reg; /* its word address, or -1 */
|
||||
v4_cell rstack_reg; /* its word address, or -1 */
|
||||
|
||||
/* The block storage device (D-19). See v4_node_storage_attach. */
|
||||
v4_cell storage_reg; /* word address of its four registers, or -1 */
|
||||
unsigned char *storage; /* the blocks, 1024 bytes each */
|
||||
unsigned storage_blocks; /* how many */
|
||||
|
||||
/* NODE-ERROR as a trap (D-18). See v4_node_error_attach. */
|
||||
v4_cell error_reg; /* its word address, or -1 */
|
||||
@@ -335,31 +331,14 @@ void v4_node_fault(v4_node *n, unsigned kind, v4_cell addr);
|
||||
* detaches both (-1); the addresses are the caller's choice (D-4). */
|
||||
void v4_node_stack_regs_attach(v4_node *n, v4_cell d, v4_cell r);
|
||||
|
||||
/* THE BLOCK STORAGE DEVICE (DECOMPOSITION.md D-19, 5.11).
|
||||
*
|
||||
* Mass storage is a device service; until the mesh exists the model stands
|
||||
* in for it on the host node with four memory-mapped registers at `reg`:
|
||||
*
|
||||
* reg + 0 BLOCK-NUMBER which block, 0 .. blocks - 1
|
||||
* reg + 1 BLOCK-ADDRESS word address of 256 cells of node memory
|
||||
* reg + 2 BLOCK-COMMAND a store of 1 reads the block into those cells,
|
||||
* a store of 2 writes it from them
|
||||
* reg + 3 BLOCK-STATUS 0 after a command that worked, -1 after one
|
||||
* that did not: no such block, cells that are
|
||||
* not all in memory, or an unknown command
|
||||
*
|
||||
* The first, second and fourth are memory words the device reads and writes;
|
||||
* only a store to the third does anything. A block is 1024 bytes, and a
|
||||
* byte address is four times a word address plus 0 .. 3 (D-1), so a block
|
||||
* is 256 cells, four bytes to a cell, the first byte lowest, whatever the
|
||||
* cell width: a read leaves the rest of each cell zero, and a write takes
|
||||
* the low 32 bits.
|
||||
*
|
||||
* `bytes` is the caller's: `blocks` * 1024 bytes, which the device reads and
|
||||
* writes and never frees. v4_node_reset detaches it (reg -1). */
|
||||
/* A BLOCK (DECOMPOSITION.md D-19, 5.11). Mass storage is a device on a
|
||||
* port that speaks messages (docs/v4.0.0/MESH.md section 8; storage.h); the
|
||||
* node has nothing of it but these. A block is 1024 bytes, and a byte
|
||||
* address is four times a word address plus 0 .. 3 (D-1), so a block is 256
|
||||
* cells, four bytes to a cell, the first byte lowest, whatever the cell
|
||||
* width. */
|
||||
#define V4_BLOCK_BYTES 1024u
|
||||
#define V4_BLOCK_CELLS 256u
|
||||
void v4_node_storage_attach(v4_node *n, v4_cell reg, unsigned char *bytes, unsigned blocks);
|
||||
|
||||
/* NODE-ERROR as a trap (DECOMPOSITION.md D-18, ruled 2026-10-04: every error
|
||||
* is guarded, shown, and returns the node to its prompt).
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/* storage.h -- what is on a storage port. docs/v4.0.0/MESH.md section 8.
|
||||
*
|
||||
* A device (fabric.h) that speaks the block messages of message.h and
|
||||
* answers them from one or two chains of blocks: a view. The common chain
|
||||
* is the store its nodes share; the private chain is one node's own. What
|
||||
* a chain is, is its owner's affair: the device reaches it only through
|
||||
* v4_store_ops, and the kernel's block subsystem stands behind those
|
||||
* (system/store_v3.c).
|
||||
*
|
||||
* WHICH CHAIN A NUMBER MEANS. Block numbers are 1 to V4_STORE_TOP.
|
||||
* - Private block k is number V4_STORE_TOP - k, and is the private
|
||||
* chain's block V4_STORE_FIRST + k, its first on a device. A number
|
||||
* is the private chain's when k is less than the blocks that chain has
|
||||
* on devices.
|
||||
* - Any other number is the common chain's, as it is.
|
||||
* - With no common chain, a number below V4_STORE_FIRST is the private
|
||||
* chain's, as it is: its fast RAM.
|
||||
* - A number that belongs to no chain the view has: V4_STORE_NONE.
|
||||
* - 0, a number above V4_STORE_TOP, or one past the end of its chain:
|
||||
* V4_STORE_RANGE.
|
||||
*
|
||||
* It has a number, as a node has: messages for it are addressed to that
|
||||
* number and its answers come from it. A message for any other number is
|
||||
* let go.
|
||||
*
|
||||
* A write is two messages, BLOCK_WRITE with the number and BLOCK_DATA with
|
||||
* the block; they are paired by who sent them. BLOCK_DATA from a sender
|
||||
* with no BLOCK_WRITE before it is answered V4_STORE_REFUSED.
|
||||
*
|
||||
* The device always takes what it is sent, so that a node writing to it is
|
||||
* never left waiting on a device that is waiting to be read: answers queue,
|
||||
* V4_STORE_PENDING of them. Only when that many wait is the first word of
|
||||
* another message not taken yet.
|
||||
*
|
||||
* Nothing here uses the C library.
|
||||
*/
|
||||
#ifndef V4_STORAGE_H
|
||||
#define V4_STORAGE_H
|
||||
|
||||
#include "v4/message.h"
|
||||
|
||||
#define V4_STORE_TOP 0xffffffffUL
|
||||
#define V4_STORE_FIRST 2048UL
|
||||
#define V4_STORE_PENDING 64u
|
||||
#define V4_STORE_BYTES 1024u
|
||||
|
||||
/* A chain of blocks. read and write return V4_STORE_*; `blocks` is how
|
||||
* many block numbers the chain answers to, counting from 0, its fast RAM
|
||||
* included. */
|
||||
typedef struct {
|
||||
int (*read)(void *chain, uint32_t block, unsigned char *bytes);
|
||||
int (*write)(void *chain, uint32_t block, const unsigned char *bytes);
|
||||
uint32_t (*blocks)(void *chain);
|
||||
} v4_store_ops;
|
||||
|
||||
typedef struct {
|
||||
const v4_store_ops *ops;
|
||||
void *common, *private_chain; /* 0: the view has none */
|
||||
v4_cell number;
|
||||
|
||||
v4_message in; /* the message arriving */
|
||||
struct { int used; v4_cell from; uint64_t block; } writing[V4_STORE_PENDING]; /* BLOCK_WRITEs whose data has not come */
|
||||
struct { v4_cell to; int read; uint64_t block; int status; } answer[V4_STORE_PENDING];
|
||||
unsigned head, count; /* the answers waiting: the oldest, and how many */
|
||||
v4_message out; /* the answer being given */
|
||||
unsigned out_at;
|
||||
int out_ready;
|
||||
unsigned char bytes[V4_STORE_BYTES];
|
||||
|
||||
unsigned long taken; /* whole messages taken, for whoever counts */
|
||||
} v4_store;
|
||||
|
||||
void v4_store_init(v4_store *s, const v4_store_ops *ops, void *common, void *private_chain, v4_cell number);
|
||||
|
||||
/* The two functions of a v4_device; `self` is the v4_store. */
|
||||
int v4_store_take(void *self, v4_cell value);
|
||||
int v4_store_give(void *self, v4_cell *value);
|
||||
|
||||
/* The kernel's block subsystem as chains: a chain is a blk_chain_t *
|
||||
* (v3/include/block_subsystem.h). system/store_v3.c. */
|
||||
extern const v4_store_ops v4_store_v3;
|
||||
|
||||
#endif /* V4_STORAGE_H */
|
||||
+1
-3
@@ -4,8 +4,7 @@
|
||||
*/
|
||||
#include "v4/image.h"
|
||||
|
||||
int v4_image_born(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im,
|
||||
unsigned char *disk, unsigned blocks)
|
||||
int v4_image_born(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im)
|
||||
{
|
||||
if (im->cell_bits != (unsigned)V4_CELL_BITS || im->node_words != (unsigned)V4_NODE_WORDS
|
||||
|| im->data_ring != (unsigned)V4_DATA_RING || im->ret_ring != (unsigned)V4_RET_RING)
|
||||
@@ -20,7 +19,6 @@ int v4_image_born(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im,
|
||||
v4_node_stack_regs_attach(n, im->dstack_reg, im->rstack_reg);
|
||||
v4_node_error_attach(n, im->node_error);
|
||||
v4_node_fault_attach(n, im->fault_table);
|
||||
if (disk && blocks) v4_node_storage_attach(n, im->storage_reg, disk, blocks);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,43 +18,6 @@ void v4_node_reset(v4_node *n)
|
||||
v4_node_stack_regs_attach(n, -1, -1);
|
||||
v4_node_error_attach(n, -1);
|
||||
v4_node_port_attach(n, -1);
|
||||
v4_node_storage_attach(n, -1, 0, 0);
|
||||
}
|
||||
|
||||
void v4_node_storage_attach(v4_node *n, v4_cell reg, unsigned char *bytes, unsigned blocks)
|
||||
{
|
||||
n->storage_reg = reg;
|
||||
n->storage = bytes;
|
||||
n->storage_blocks = blocks;
|
||||
}
|
||||
|
||||
/* BLOCK-COMMAND: do it, and leave the status in BLOCK-STATUS. */
|
||||
static void storage_command(v4_node *n, v4_cell command)
|
||||
{
|
||||
v4_cell num = n->mem[n->storage_reg], addr = n->mem[n->storage_reg + 1];
|
||||
unsigned char *b;
|
||||
unsigned i;
|
||||
|
||||
n->mem[n->storage_reg + 3] = (v4_cell)-1;
|
||||
if (!n->storage || num < 0 || (v4_ucell)num >= (v4_ucell)n->storage_blocks) return;
|
||||
if (!v4_node_addr_ok(addr) || !v4_node_addr_ok(addr + (v4_cell)(V4_BLOCK_CELLS - 1u))) return;
|
||||
b = n->storage + (size_t)num * V4_BLOCK_BYTES;
|
||||
if (command == 1) {
|
||||
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 if (command == 2) {
|
||||
for (i = 0; i < V4_BLOCK_CELLS; i++) {
|
||||
v4_ucell w = (v4_ucell)n->mem[addr + (v4_cell)i];
|
||||
b[4u * i] = (unsigned char)(w & 0xFFu);
|
||||
b[4u * i + 1u] = (unsigned char)((w >> 8) & 0xFFu);
|
||||
b[4u * i + 2u] = (unsigned char)((w >> 16) & 0xFFu);
|
||||
b[4u * i + 3u] = (unsigned char)((w >> 24) & 0xFFu);
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
n->mem[n->storage_reg + 3] = 0;
|
||||
}
|
||||
|
||||
void v4_node_error_attach(v4_node *n, v4_cell addr)
|
||||
@@ -137,8 +100,6 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value)
|
||||
if (addr == n->rstack_reg) { v4_rstack_clear(&n->rs); return; }
|
||||
if (!v4_node_addr_ok(addr)) return;
|
||||
n->mem[(unsigned)addr] = value;
|
||||
/* BLOCK-COMMAND. storage_reg is -1 when no device is attached. */
|
||||
if (n->storage_reg >= 0 && addr == n->storage_reg + 2) { storage_command(n, value); return; }
|
||||
/* NODE-ERROR: a non-zero code raises an error. -1 when not attached. */
|
||||
if (addr == n->error_reg && value != 0) v4_node_fault(n, V4_FAULT_RAISED, value);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/* blocks.c -- the kernel serves a node's block requests. See blocks.h.
|
||||
*
|
||||
* This is the one file of v4's that includes v3's headers: every block goes
|
||||
* through v3's own calls.
|
||||
*/
|
||||
#include "v4/blocks.h"
|
||||
#include "block_subsystem.h"
|
||||
|
||||
int v4_blocks_serve(v4_node *n, v4_cell request)
|
||||
{
|
||||
v4_cell addr, num;
|
||||
uint32_t block;
|
||||
uint8_t *b;
|
||||
unsigned i;
|
||||
int status;
|
||||
|
||||
if (request != V4_REQ_BLOCK_READ && request != V4_REQ_BLOCK_WRITE) return 0;
|
||||
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 (blk_update(block) == BLK_OK && blk_flush(block) == BLK_OK) status = V4_BLOCK_OK;
|
||||
}
|
||||
}
|
||||
v4_dstack_push(&n->ds, (v4_cell)status);
|
||||
return 1;
|
||||
}
|
||||
+9
-6
@@ -4,6 +4,7 @@
|
||||
* It is not part of the engine (v4/src): it needs the capsule directory,
|
||||
* which only a whole system has.
|
||||
*/
|
||||
#include "v4/blocks.h"
|
||||
#include "v4/boot.h"
|
||||
#include "v4/message.h"
|
||||
#include "starkernel/capsule.h"
|
||||
@@ -109,9 +110,9 @@ static void post_watch(char c)
|
||||
|
||||
/* The boot is the node's neighbour on two of its ports. On port 0 it is
|
||||
* the kernel: the node writes the number of a request there (ENGINE.md
|
||||
* 3.3). On port 1 it is the console: it writes the node a message of text
|
||||
* and reads back messages of what the node printed and of how the text
|
||||
* ended (message.h). */
|
||||
* 3.3) -- for a block (blocks.h), or for one of the host's words. On port
|
||||
* 1 it is the console: it writes the node a message of text and reads back
|
||||
* messages of what the node printed and of how the text ended (message.h). */
|
||||
#define KERNEL_PORT 0u
|
||||
#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 */
|
||||
@@ -130,7 +131,9 @@ 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 (b->serve && n->request >= 1 && n->request <= (v4_cell)b->word_count) {
|
||||
if (v4_blocks_serve(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);
|
||||
v4_node_port_served(n);
|
||||
} else {
|
||||
@@ -335,13 +338,13 @@ static int load_nucleus(const v4_boot *b)
|
||||
|
||||
/* ---- the whole boot ------------------------------------------------------ */
|
||||
|
||||
int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks)
|
||||
int v4_boot_run(const v4_boot *b)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
post_len = 0; post_seen = 0; post_clean = 0;
|
||||
/* a node is born with nothing in it, and is sent the nucleus */
|
||||
if (!v4_image_born(b->n, b->es, b->h, b->im, disk, blocks)) {
|
||||
if (!v4_image_born(b->n, b->es, b->h, b->im)) {
|
||||
say(b, "V4: the nucleus is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
/* storage.c -- what is on a storage port. See storage.h.
|
||||
* docs/v4.0.0/MESH.md section 8.
|
||||
*
|
||||
* Nothing here uses the C library: the bare-metal kernel links this file.
|
||||
*/
|
||||
#include "v4/storage.h"
|
||||
|
||||
void v4_store_init(v4_store *s, const v4_store_ops *ops, void *common, void *private_chain, v4_cell number)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
s->ops = ops;
|
||||
s->common = common;
|
||||
s->private_chain = private_chain;
|
||||
s->number = number;
|
||||
s->in.count = 0;
|
||||
for (i = 0; i < V4_STORE_PENDING; i++) s->writing[i].used = 0;
|
||||
s->head = 0;
|
||||
s->count = 0;
|
||||
s->out.count = 0;
|
||||
s->out_at = 0;
|
||||
s->out_ready = 0;
|
||||
s->taken = 0;
|
||||
}
|
||||
|
||||
/* The chain a number means and the block in it; or 0, with why in *status. */
|
||||
static void *chain_for(const v4_store *s, uint64_t n, uint32_t *block, int *status)
|
||||
{
|
||||
*status = V4_STORE_RANGE;
|
||||
if (n == 0 || n > V4_STORE_TOP) return 0;
|
||||
if (s->private_chain) {
|
||||
uint64_t k = V4_STORE_TOP - n, have = s->ops->blocks(s->private_chain);
|
||||
if (have > V4_STORE_FIRST && k < have - V4_STORE_FIRST) { *block = (uint32_t)(V4_STORE_FIRST + k); return s->private_chain; }
|
||||
}
|
||||
*block = (uint32_t)n;
|
||||
if (s->common) return s->common;
|
||||
if (s->private_chain && n < V4_STORE_FIRST) return s->private_chain;
|
||||
*status = V4_STORE_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void queue(v4_store *s, v4_cell to, int read, uint64_t block, int status)
|
||||
{
|
||||
unsigned at = (s->head + s->count) % V4_STORE_PENDING;
|
||||
|
||||
if (s->count == V4_STORE_PENDING) return; /* take does not let it come to this */
|
||||
s->answer[at].to = to;
|
||||
s->answer[at].read = read;
|
||||
s->answer[at].block = block;
|
||||
s->answer[at].status = status;
|
||||
s->count++;
|
||||
}
|
||||
|
||||
/* A block of data has come from `from`: it is written where that sender's
|
||||
* BLOCK_WRITE said, if there was one. */
|
||||
static int write_block(v4_store *s, v4_cell from)
|
||||
{
|
||||
unsigned i, k;
|
||||
uint32_t block;
|
||||
int status;
|
||||
void *chain;
|
||||
|
||||
for (i = 0; i < V4_STORE_PENDING && !(s->writing[i].used && s->writing[i].from == from); i++) { }
|
||||
if (i == V4_STORE_PENDING) return V4_STORE_REFUSED;
|
||||
s->writing[i].used = 0;
|
||||
if (v4_message_length(&s->in) != V4_STORE_BYTES) return V4_STORE_REFUSED;
|
||||
chain = chain_for(s, s->writing[i].block, &block, &status);
|
||||
if (!chain) return status;
|
||||
for (k = 0; k < V4_STORE_BYTES; k++) s->bytes[k] = (unsigned char)v4_message_char(&s->in, k);
|
||||
return s->ops->write(chain, block, s->bytes);
|
||||
}
|
||||
|
||||
/* The message in s->in is whole. */
|
||||
static void served(v4_store *s)
|
||||
{
|
||||
v4_cell from = v4_message_from(&s->in), type = v4_message_type(&s->in);
|
||||
uint64_t block = (uint64_t)(v4_ucell)s->in.word[V4_MSG_HEADER];
|
||||
unsigned i, free_at = V4_STORE_PENDING;
|
||||
|
||||
s->taken++;
|
||||
if (v4_message_to(&s->in) != s->number) return;
|
||||
if (type == V4_MSG_BLOCK_DATA) { queue(s, from, 0, 0, write_block(s, from)); return; }
|
||||
if (type != V4_MSG_BLOCK_READ && type != V4_MSG_BLOCK_WRITE) return;
|
||||
if (v4_message_length(&s->in) != 4) { queue(s, from, 0, 0, V4_STORE_REFUSED); return; }
|
||||
if (type == V4_MSG_BLOCK_READ) { queue(s, from, 1, block, 0); return; }
|
||||
|
||||
/* a write: its number is kept until its block comes. A sender's second
|
||||
* write before then takes the place of its first. */
|
||||
for (i = 0; i < V4_STORE_PENDING; i++) {
|
||||
if (s->writing[i].used && s->writing[i].from == from) { free_at = i; break; }
|
||||
if (!s->writing[i].used && free_at == V4_STORE_PENDING) free_at = i;
|
||||
}
|
||||
if (free_at == V4_STORE_PENDING) { queue(s, from, 0, 0, V4_STORE_REFUSED); return; }
|
||||
s->writing[free_at].used = 1;
|
||||
s->writing[free_at].from = from;
|
||||
s->writing[free_at].block = block;
|
||||
}
|
||||
|
||||
int v4_store_take(void *self, v4_cell value)
|
||||
{
|
||||
v4_store *s = (v4_store *)self;
|
||||
|
||||
if (s->in.count == 0 && s->count == V4_STORE_PENDING) return 0;
|
||||
if (v4_message_word(&s->in, value)) {
|
||||
served(s);
|
||||
s->in.count = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The oldest answer waiting becomes the message in s->out. */
|
||||
static void answer(v4_store *s)
|
||||
{
|
||||
v4_cell to = s->answer[s->head].to;
|
||||
int status = s->answer[s->head].status;
|
||||
unsigned i;
|
||||
|
||||
if (s->answer[s->head].read) {
|
||||
uint32_t block;
|
||||
void *chain = chain_for(s, s->answer[s->head].block, &block, &status);
|
||||
if (chain) status = s->ops->read(chain, block, s->bytes);
|
||||
} else if (status == V4_STORE_OK) {
|
||||
status = -1; /* a write that worked: said below */
|
||||
}
|
||||
s->head = (s->head + 1) % V4_STORE_PENDING;
|
||||
s->count--;
|
||||
|
||||
s->out.word[0] = to;
|
||||
s->out.word[1] = s->number;
|
||||
s->out.word[3] = 0;
|
||||
s->out.word[4] = 0;
|
||||
s->out.word[5] = 0;
|
||||
if (status == V4_STORE_OK) { /* a read that worked: the block */
|
||||
s->out.word[2] = V4_MSG_BLOCK_DATA;
|
||||
s->out.word[6] = (v4_cell)V4_STORE_BYTES;
|
||||
for (i = 0; i < V4_STORE_BYTES / 4u; i++)
|
||||
s->out.word[V4_MSG_HEADER + i] = (v4_cell)((v4_ucell)s->bytes[4u * i] | (v4_ucell)s->bytes[4u * i + 1u] << 8
|
||||
| (v4_ucell)s->bytes[4u * i + 2u] << 16 | (v4_ucell)s->bytes[4u * i + 3u] << 24);
|
||||
s->out.count = V4_MSG_HEADER + V4_STORE_BYTES / 4u;
|
||||
} else {
|
||||
s->out.word[2] = V4_MSG_BLOCK_DONE;
|
||||
s->out.word[6] = 4;
|
||||
s->out.word[V4_MSG_HEADER] = status < 0 ? V4_STORE_OK : status;
|
||||
s->out.count = V4_MSG_HEADER + 1u;
|
||||
}
|
||||
s->out_at = 0;
|
||||
s->out_ready = 1;
|
||||
}
|
||||
|
||||
int v4_store_give(void *self, v4_cell *value)
|
||||
{
|
||||
v4_store *s = (v4_store *)self;
|
||||
|
||||
if (!s->out_ready) {
|
||||
if (s->count == 0) return 0;
|
||||
answer(s);
|
||||
}
|
||||
*value = s->out.word[s->out_at++];
|
||||
if (s->out_at == s->out.count) s->out_ready = 0;
|
||||
return 1;
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/* store_v3.c -- the kernel's block subsystem as the chains behind a storage
|
||||
* port. docs/v4.0.0/MESH.md 8.4, docs/v4.0.0/V3-PARITY.md 1d.
|
||||
*
|
||||
* A chain is a blk_chain_t (v3/include/block_subsystem.h). Every block goes
|
||||
* through v3's own calls, so the header, the allocation map, the block
|
||||
* cards and relocation are v3's, as they were. Who may have a block is not
|
||||
* checked here yet (MESH.md 8.5).
|
||||
*
|
||||
* This is the one file of v4's that includes v3's headers.
|
||||
*/
|
||||
#include "v4/storage.h"
|
||||
#include "block_subsystem.h"
|
||||
|
||||
static int v3_read(void *chain, uint32_t block, unsigned char *bytes)
|
||||
{
|
||||
blk_chain_t *was = blk_chain_select((blk_chain_t *)chain);
|
||||
int status = V4_STORE_RANGE;
|
||||
unsigned i;
|
||||
|
||||
if (blk_is_valid(block)) {
|
||||
const uint8_t *b = blk_get_buffer(block, 0);
|
||||
status = b ? V4_STORE_OK : V4_STORE_REFUSED;
|
||||
if (b) for (i = 0; i < V4_STORE_BYTES; i++) bytes[i] = b[i];
|
||||
}
|
||||
blk_chain_select(was);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int v3_write(void *chain, uint32_t block, const unsigned char *bytes)
|
||||
{
|
||||
blk_chain_t *was = blk_chain_select((blk_chain_t *)chain);
|
||||
int status = V4_STORE_RANGE;
|
||||
unsigned i;
|
||||
|
||||
if (blk_is_valid(block)) {
|
||||
uint8_t *b = blk_get_buffer(block, 1);
|
||||
status = V4_STORE_REFUSED;
|
||||
if (b) {
|
||||
for (i = 0; i < V4_STORE_BYTES; i++) b[i] = bytes[i];
|
||||
if (blk_update(block) == BLK_OK && blk_flush(block) == BLK_OK) status = V4_STORE_OK;
|
||||
}
|
||||
}
|
||||
blk_chain_select(was);
|
||||
return status;
|
||||
}
|
||||
|
||||
static uint32_t v3_blocks(void *chain)
|
||||
{
|
||||
blk_chain_t *was = blk_chain_select((blk_chain_t *)chain);
|
||||
uint32_t n = blk_get_total_blocks();
|
||||
blk_chain_select(was);
|
||||
return n;
|
||||
}
|
||||
|
||||
const v4_store_ops v4_store_v3 = { v3_read, v3_write, v3_blocks };
|
||||
+4
-6
@@ -12,6 +12,7 @@
|
||||
#ifndef V4_TESTS_HOST_MAP_H
|
||||
#define V4_TESTS_HOST_MAP_H
|
||||
|
||||
#include "v4/blocks.h"
|
||||
#include "v4/text.h"
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -58,12 +59,11 @@
|
||||
#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) and the device's four */
|
||||
#define BVARS (QBASE - 16) /* blocks.v4: (B) 6 cells, then SCR BLK (SRC) (SRC-HOOK) */
|
||||
#define SCR (BVARS + 6)
|
||||
#define BLK (BVARS + 7)
|
||||
#define SRC (BVARS + 8)
|
||||
#define SRC_HOOK (BVARS + 9)
|
||||
#define STORAGE_REG (BVARS + 10)
|
||||
#define ACL_HOOK (BUF0_W - 6) /* the xt of the access control recheck word, or 0 */
|
||||
#define EMIT_HOOK (BUF0_W - 7) /* the xt of a word EMIT gives its character to, or 0: the console */
|
||||
#define CATCH (BUF0_W - 8) /* non-zero: a line's error is recorded here, -1, and the line ends " ok" */
|
||||
@@ -209,10 +209,8 @@ 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-NUMBER", STORAGE_REG);
|
||||
v4_text_constant(tx, "BLOCK-ADDRESS", STORAGE_REG + 1);
|
||||
v4_text_constant(tx, "BLOCK-COMMAND", STORAGE_REG + 2);
|
||||
v4_text_constant(tx, "BLOCK-STATUS", STORAGE_REG + 3);
|
||||
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, "(Q/)", QBASE); /* 5 cells */
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/* 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");
|
||||
|
||||
/* ---- 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;
|
||||
}
|
||||
@@ -52,6 +52,8 @@ static int failures = 0, checks = 0;
|
||||
|
||||
#define CANARY ((v4_cell)0x0C0FFEE5)
|
||||
|
||||
#include "v4/blocks.h"
|
||||
#include "block_subsystem.h"
|
||||
#include "host_map.h"
|
||||
|
||||
static v4_node n;
|
||||
@@ -62,9 +64,44 @@ static v4_cell w_key, w_key_end, w_fault, capsule_latest;
|
||||
static v4_cell w_idle; /* (IDLE): where the node waits for a message */
|
||||
static char out[V4_CONSOLE_CAP + 1];
|
||||
|
||||
/* The storage device's blocks: zeros at every switch-on. */
|
||||
/* STORAGE (MESH.md section 8). The node asks its kernel for its blocks, and
|
||||
* these tests are its kernel (kernel_serve): the blocks are the kernel's
|
||||
* block subsystem (v3/src/block_subsystem.c). The chain is fast RAM,
|
||||
* blocks 0 to 2047; a device of 64 blocks, 2048 to 2111; and after that a
|
||||
* disk the subsystem has not been told it may format, which it reads and
|
||||
* will not write. The RAM and the device are zeros at every switch-on.
|
||||
* `disk` is where the chain keeps block 0, so disk + n * 1024 is block n of
|
||||
* it for any n below 2048, as the chain itself holds it. */
|
||||
#define DISK_BLOCKS 64u
|
||||
static unsigned char disk[DISK_BLOCKS * V4_BLOCK_BYTES];
|
||||
#define RAM_BYTES ((size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE)
|
||||
#define REFUSING_BLOCKS 2048u /* room for the subsystem's own reserved device blocks and some of the node's */
|
||||
#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 */
|
||||
|
||||
extern void sf_time_init(void);
|
||||
extern void log_set_level(int level);
|
||||
extern size_t blkio_ram_state_size(void);
|
||||
extern int blkio_ram_init_state(void *state_mem, size_t state_len, uint8_t *base, uint32_t total_blocks, uint32_t fbs, uint8_t read_only, void **out_opaque);
|
||||
extern const blkio_vtable_t *blkio_ram_vtable(void);
|
||||
|
||||
/* The chain is made once: the subsystem keeps what it allocates. */
|
||||
static int storage_made(void)
|
||||
{
|
||||
static blkio_dev_t dev;
|
||||
static unsigned char state[256];
|
||||
blkio_params_t params;
|
||||
void *opaque = 0;
|
||||
|
||||
sf_time_init();
|
||||
log_set_level(-1);
|
||||
if (blkio_ram_state_size() > sizeof state) return 0;
|
||||
if (blk_subsys_init(ram, sizeof ram) != BLK_OK || blk_subsys_add_raw_device(common_dev, DISK_BLOCKS) != BLK_OK) return 0;
|
||||
if (blkio_ram_init_state(state, sizeof state, refusing_dev, REFUSING_BLOCKS, 0, 0, &opaque) != BLKIO_OK) return 0;
|
||||
params.forth_block_size = 0; params.total_blocks = REFUSING_BLOCKS; params.opaque = opaque;
|
||||
return blkio_open(&dev, blkio_ram_vtable(), ¶ms) == BLKIO_OK && blk_subsys_attach_device(&dev) == BLK_OK;
|
||||
}
|
||||
/* A block holding `text`, the rest of it blanks. */
|
||||
static void put_block(unsigned num, const char *text)
|
||||
{
|
||||
@@ -85,6 +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 (n.request == 1) {
|
||||
v4_cell xt = v4_dstack_pop(&n.ds);
|
||||
if (known_n < 1024) known[known_n++] = xt;
|
||||
@@ -227,8 +265,7 @@ static void boot_with(unsigned depth)
|
||||
n.mem[VOC_LINK] = 0;
|
||||
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(disk, 0, sizeof disk);
|
||||
v4_node_storage_attach(&n, STORAGE_REG, disk, DISK_BLOCKS);
|
||||
memset(ram, 0, sizeof ram); memset(common_dev, 0, sizeof common_dev);
|
||||
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST);
|
||||
v4_node_fault_attach(&n, w_fault);
|
||||
v4_node_error_attach(&n, NODE_ERROR);
|
||||
@@ -772,6 +809,7 @@ int main(void)
|
||||
unsigned i;
|
||||
|
||||
printf("v4 host prompt tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS);
|
||||
CHECK(storage_made(), "the chains behind the storage port are made");
|
||||
|
||||
{
|
||||
static const char *const files[] = { "core.v4", "input.v4", "dict.v4", "codegen.v4", "compile.v4", "quit.v4", "forth.v4", "numout.v4", "words.v4", "system.v4", "qmath.v4", "blocks.v4", "log.v4", "acl.v4" };
|
||||
@@ -1133,6 +1171,15 @@ int main(void)
|
||||
CHECK(is(say("FORGET G3 G1 G2\n"), "AB ok\nok> ") && is(say("G3\n"), "UNKNOWN WORD: 'G3'\n ERROR\nok> "), "one above it can");
|
||||
}
|
||||
|
||||
/* ---- storage that refuses a write (MESH.md 8.2) ---- */
|
||||
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");
|
||||
}
|
||||
|
||||
/* ---- blocks: text loaded from storage ---- */
|
||||
boot_bare();
|
||||
put_block(5, "65 EMIT 66 EMIT");
|
||||
@@ -1164,11 +1211,11 @@ int main(void)
|
||||
/* errors */
|
||||
CHECK(is(say("ABORT\n"), " ok\nok> "), "(an empty stack)");
|
||||
CHECK(is(say("7 0 BLOCK 65 EMIT\n"), "Block out of range\n ERROR\nok> ") && is(say("-1 BLOCK\n"), "Block out of range\n ERROR\nok> ")
|
||||
&& is(say("64 BLOCK\n"), "Block out of range\n ERROR\nok> ") && is(say("63 BLOCK DROP .S\n"), "<1> 7 \n ok\nok> "), "a block number below 1 or past the device's last");
|
||||
&& is(say("9000 BLOCK\n"), "Block out of range\n ERROR\nok> ") && is(say("2111 BLOCK DROP .S\n"), "<1> 7 \n ok\nok> "), "a block number below 1 or past the chain's last");
|
||||
CHECK(is(say("0 LOAD\n"), "Block out of range\n ERROR\nok> ") && is(say("-5 LOAD\n"), "Block out of range\n ERROR\nok> ")
|
||||
&& is(say("0 BUFFER\n"), "Block out of range\n ERROR\nok> ") && is(say("99 LIST\n"), "Block out of range\n ERROR\nok> "), "LOAD, BUFFER and LIST the same");
|
||||
&& is(say("0 BUFFER\n"), "Block out of range\n ERROR\nok> ") && is(say("9000 LIST\n"), "Block out of range\n ERROR\nok> "), "LOAD, BUFFER and LIST the same");
|
||||
CHECK(is(say("-->\n"), "No block is being loaded\n ERROR\nok> ") && is(say("65 EMIT\n"), "A ok\nok> "), "--> at the terminal");
|
||||
put_block(18, "1 99 LOAD 2");
|
||||
put_block(18, "1 9000 LOAD 2");
|
||||
CHECK(is(say("ABORT\n"), " ok\nok> "), "(an empty stack)");
|
||||
CHECK(is(say("18 LOAD\n.S\n"), "Block out of range\n ERROR\nok> <1> 1 \n ok\nok> "), "a block that loads one that is not there");
|
||||
|
||||
@@ -1269,7 +1316,7 @@ int main(void)
|
||||
SCREEN(31, "line A", "line D", "line B", "line C", "line E", "line F", "line G", "line H", "line I", "line J", "line K", "line L", "line M", "line N", "line O", "line P");
|
||||
CHECK(is(say("L DONE\n"), want), "every line from 1 moved down");
|
||||
CHECK(is(say("21 EDIT\n"), out) && is(say("WIPE N\n"), blank_screen(22)) && is(say("B\n"), blank_screen(21)), "WIPE blanks it; N and B move to the next and back");
|
||||
CHECK(is(say("99 EDIT\n"), "Block out of range\n ERROR\nok> ") && n.mem[SCR] == 21, "EDIT of a block that is not there");
|
||||
CHECK(is(say("9000 EDIT\n"), "Block out of range\n ERROR\nok> ") && n.mem[SCR] == 21, "EDIT of a block that is not there");
|
||||
/* v3's three, in FORTH */
|
||||
CHECK(is(say("FORTH 9 SCR ! 0 L 3 L\n"), " second, with blanks before it \n \n ok\nok> "),
|
||||
"FORTH's L shows a line, as v3's");
|
||||
|
||||
+79
-12
@@ -17,15 +17,22 @@
|
||||
*
|
||||
* What stands in here for the host: the capsules are read from the files
|
||||
* the build bakes in, and the nucleus is assembled as mkimage assembles it.
|
||||
* Each node has a disk of its own, attached when it is born, as the boot's
|
||||
* one node has; storage through the ports is step 6.
|
||||
*
|
||||
* STORAGE (MESH.md section 8, acceptance 3). Every node asks its kernel
|
||||
* for its blocks, on its port 0, and the kernel's block subsystem
|
||||
* (v3/src/block_subsystem.c) has them: one chain, fast RAM and a device of
|
||||
* 64 blocks. For Hera the kernel on that port also answers her requests
|
||||
* for nodes and capsules; for every other node it serves blocks and
|
||||
* nothing else.
|
||||
*/
|
||||
#include "v4/capsule.h"
|
||||
#include "v4/fabric.h"
|
||||
#include "v4/image.h"
|
||||
#include "v4/manage.h"
|
||||
#include "v4/blocks.h"
|
||||
#include "v4/message.h"
|
||||
#include "v4/text.h"
|
||||
#include "block_subsystem.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -36,13 +43,23 @@ 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 PLACES 10u
|
||||
#define DISK_BLOCKS 64u
|
||||
static v4_text tx;
|
||||
static v4_node nucleus; /* the nucleus, assembled: what the capsule of F18 code is written from */
|
||||
static v4_cell w_idle, w_fault;
|
||||
static v4_fabric_node pool[PLACES];
|
||||
static int pool_used[PLACES];
|
||||
static unsigned char disks[PLACES][DISK_BLOCKS * V4_BLOCK_BYTES];
|
||||
|
||||
/* the kernel's blocks: fast RAM and a device of 64 blocks */
|
||||
#define DISK_BLOCKS 64u
|
||||
static unsigned char common_ram[(size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE], common_dev[DISK_BLOCKS * V4_BLOCK_BYTES];
|
||||
extern void sf_time_init(void);
|
||||
extern void log_set_level(int level);
|
||||
static int storage_made(void)
|
||||
{
|
||||
sf_time_init();
|
||||
log_set_level(-1);
|
||||
return blk_subsys_init(common_ram, sizeof common_ram) == BLK_OK && blk_subsys_add_raw_device(common_dev, DISK_BLOCKS) == BLK_OK;
|
||||
}
|
||||
static v4_place places[PLACES];
|
||||
static v4_fabric f;
|
||||
static v4_image im; /* only what the dictionary hash needs of it */
|
||||
@@ -93,6 +110,16 @@ static unsigned born_count, parity_count;
|
||||
static v4_cell parity_number[8];
|
||||
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 int node_kernel_take(void *self, v4_cell value)
|
||||
{
|
||||
if (!v4_blocks_serve((v4_node *)self, value)) refused++;
|
||||
return 1;
|
||||
}
|
||||
static v4_device node_kernel[PLACES];
|
||||
|
||||
static int host_born(void *self)
|
||||
{
|
||||
unsigned k;
|
||||
@@ -108,8 +135,10 @@ 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);
|
||||
memset(disks[k], 0, sizeof disks[k]);
|
||||
v4_node_storage_attach(&pool[k].n, STORAGE_REG, disks[k], DISK_BLOCKS);
|
||||
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;
|
||||
if (!v4_fabric_wire_device(&f, (unsigned)place, 0, &node_kernel[k])) return -1;
|
||||
}
|
||||
return place;
|
||||
}
|
||||
static void host_gone(void *self, unsigned place, v4_fabric_node *was)
|
||||
@@ -147,7 +176,7 @@ static const v4_manage manage = { &f, host_born, host_gone, host_open, host_cell
|
||||
|
||||
/* ---- the kernel: a device on Hera's port 0 ---- */
|
||||
#define FIRST_REQUEST 1 /* the number of NODE-ME on her */
|
||||
static unsigned hera_place, given_cells, refused;
|
||||
static unsigned hera_place, given_cells;
|
||||
static int kernel_give(void *self, v4_cell *value) /* the nucleus, to a node that has nothing in it */
|
||||
{
|
||||
(void)self;
|
||||
@@ -158,6 +187,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 (value < FIRST_REQUEST || !v4_manage_serve(&manage, hera_place, (unsigned)(value - FIRST_REQUEST))) refused++;
|
||||
return 1;
|
||||
}
|
||||
@@ -219,6 +249,17 @@ static int any_running(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* What every node is doing, for a test that does not end. */
|
||||
static unsigned long step_limit = 4000000000ul;
|
||||
static void stuck(void)
|
||||
{
|
||||
unsigned k;
|
||||
for (k = 0; k < PLACES; k++) {
|
||||
const v4_fabric_node *x = v4_fabric_node_at(&f, k);
|
||||
if (x) printf(" stuck: node %ld p=%ld asking=%d port=%u reading=%d rport=%u given=%d stopped=%d mq=%ld lost=%ld\n", (long)x->n.mem[ME], (long)x->n.p,
|
||||
x->n.asking, x->n.ask_port, x->n.reading, x->n.read_port, x->n.given, x->n.stopped, (long)x->n.mem[MQ_COUNT], (long)x->n.mem[LOST]);
|
||||
}
|
||||
}
|
||||
/* Send text to a node and let everything that follows from it happen. */
|
||||
static const char *tell(v4_cell node, const char *text)
|
||||
{
|
||||
@@ -227,8 +268,8 @@ static const char *tell(v4_cell node, const char *text)
|
||||
ended = 0; ended_from = -1; ended_how = -1;
|
||||
if (!v4_message_text(&going, node, CONSOLE_ID, V4_MSG_TEXT, text, (unsigned)strlen(text))) return "(too long)";
|
||||
going_at = 0;
|
||||
while (steps < 4000000000ul && (v4_fabric_step(&f) != 0 || any_running())) steps++;
|
||||
if (steps >= 4000000000ul) return "(still running)";
|
||||
while (steps < step_limit && (v4_fabric_step(&f) != 0 || any_running())) steps++;
|
||||
if (steps >= step_limit) { stuck(); return "(still running)"; }
|
||||
return printed;
|
||||
}
|
||||
static int told(v4_cell node, const char *text) { return tell(node, text)[0] == 0 && ended == 1 && ended_how == V4_TEXT_COMPLETED; }
|
||||
@@ -282,6 +323,7 @@ int main(void)
|
||||
printf(" not run at this width: the capsules are the 64-bit products'\n");
|
||||
return 0;
|
||||
}
|
||||
CHECK(storage_made(), "the kernel's blocks are there before any node is");
|
||||
{
|
||||
static const char *const files[] = { "core.v4", "input.v4", "dict.v4", "codegen.v4", "compile.v4", "quit.v4", "forth.v4",
|
||||
"numout.v4", "words.v4", "system.v4", "qmath.v4", "blocks.v4", "log.v4", "acl.v4" };
|
||||
@@ -342,9 +384,9 @@ int main(void)
|
||||
CHECK(ended == 1 && ended_how == V4_TEXT_COMPLETED && ended_from == 10, "Hera births four nodes and joins them: \"%s\"", r);
|
||||
}
|
||||
CHECK(born_count == 4 && refused == 0, "four nodes were asked for: %u", born_count);
|
||||
CHECK(post_passed[11] == 1 && post_passed[12] == 1 && post_passed[13] == 1 && post_passed[14] == 1 && post_other == 0,
|
||||
"each took in the nucleus and FORTH-79 through its port from Hera, and passed POST: %u %u %u %u, %u failed",
|
||||
post_passed[11], post_passed[12], post_passed[13], post_passed[14], post_other);
|
||||
CHECK(post_passed[10] == 1 && post_passed[11] == 0 && post_passed[12] == 0 && post_passed[13] == 0 && post_passed[14] == 0 && post_other == 0,
|
||||
"a node she births is not POSTed: POST is the kernel's, once, on Hera: %u; %u %u %u %u, %u failed",
|
||||
post_passed[10], post_passed[11], post_passed[12], post_passed[13], post_passed[14], post_other);
|
||||
CHECK(parity_count == 4 && parity_number[0] == 11 && parity_number[1] == 12 && parity_number[2] == 13 && parity_number[3] == 14, "the parity of each was recorded: %u", parity_count);
|
||||
CHECK(parity_hash[0] == parity_hash[1] && parity_hash[1] == parity_hash[2] && parity_hash[2] == parity_hash[3],
|
||||
"and their dictionary hashes are the same: %016llx %016llx %016llx %016llx",
|
||||
@@ -381,6 +423,31 @@ int main(void)
|
||||
}
|
||||
CHECK(strcmp(tell(10, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(11, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(14, "(LOST) @ ."), "0 ") == 0, "nothing was let go");
|
||||
|
||||
/* ---- they share the store (acceptance 3) ---- */
|
||||
step_limit = 30000000ul;
|
||||
CHECK(told(12, "2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS") && common_dev[52 * V4_BLOCK_BYTES] == 'A' && common_dev[52 * V4_BLOCK_BYTES + 1] == ' ',
|
||||
"node 12 writes a block, by asking the kernel: \"%s\"", printed);
|
||||
for (i = 10; i <= 14; i++) {
|
||||
if (i == 12) continue;
|
||||
CHECK(strcmp(tell((v4_cell)i, "2100 BLOCK C@ ."), "65 ") == 0, "node %u reads what node 12 wrote: \"%s\"", i, printed);
|
||||
}
|
||||
CHECK(strstr(tell(14, "9000 BLOCK"), "Block out of range") != NULL && strcmp(tell(14, "14 100 * ."), "1400 ") == 0, "a block there is not says so, and the node goes on: \"%s\"", printed);
|
||||
{
|
||||
char text[64];
|
||||
unsigned before = refused, born = born_count;
|
||||
snprintf(text, sizeof text, "%u KERNEL-WORD ASK-BORN ASK-BORN", FIRST_REQUEST + 1);
|
||||
(void)tell(12, text);
|
||||
CHECK(refused == before + 1 && born_count == born, "an outer node's request for a node is not honoured: only its blocks are");
|
||||
}
|
||||
|
||||
/* two nodes use the store at once */
|
||||
CHECK(told(10, ": W 10 0 DO 97 I 2060 + BLOCK C! UPDATE SAVE-BUFFERS LOOP ;") && told(12, ": W 10 0 DO 98 I 2070 + BLOCK C! UPDATE SAVE-BUFFERS LOOP ;"), "each of two nodes has ten blocks to write");
|
||||
CHECK(told(10, ": GO S\" W\" 12 SEND W ; GO"), "Hera sets node 12 writing and writes her own meanwhile: \"%s\"", printed);
|
||||
for (i = 0; i < 10 && common_dev[(12 + i) * V4_BLOCK_BYTES] == 'a' && common_dev[(22 + i) * V4_BLOCK_BYTES] == 'b'; i++) { }
|
||||
CHECK(i == 10, "all twenty blocks reached the store: the first that did not is %u", i);
|
||||
CHECK(strcmp(tell(10, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(12, "(LOST) @ ."), "0 ") == 0, "and no message was let go");
|
||||
CHECK(waiting(10) && waiting(11) && waiting(12) && waiting(13) && waiting(14), "all five are waiting at their ports again");
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures != 0;
|
||||
}
|
||||
|
||||
@@ -195,69 +195,6 @@ static void test_out_of_range_word_address_touches_nothing(void)
|
||||
free(n);
|
||||
}
|
||||
|
||||
/* D-19: the block storage device. Four registers; a store to the third
|
||||
* reads a block into 256 cells of memory or writes it from them. */
|
||||
static void test_storage_device(void)
|
||||
{
|
||||
static unsigned char blocks[4 * V4_BLOCK_BYTES];
|
||||
const v4_cell REG = 40, BUF = 100;
|
||||
v4_node *n = malloc(sizeof *n);
|
||||
unsigned i;
|
||||
int ok;
|
||||
if (!n) { CHECK(0, "malloc"); return; }
|
||||
v4_node_reset(n);
|
||||
CHECK(n->storage_reg == -1, "a fresh node has no storage");
|
||||
for (i = 0; i < sizeof blocks; i++) blocks[i] = (unsigned char)(i * 7u + (i >> 10));
|
||||
v4_node_storage_attach(n, REG, blocks, 4);
|
||||
|
||||
/* read block 2 */
|
||||
n->mem[BUF - 1] = 111; n->mem[BUF + 256] = 222;
|
||||
v4_node_store(n, REG, 2); v4_node_store(n, REG + 1, BUF);
|
||||
n->mem[REG + 3] = 55;
|
||||
v4_node_store(n, REG + 2, 1);
|
||||
CHECK(n->mem[REG + 3] == 0, "a read that works leaves status 0");
|
||||
for (ok = 1, i = 0; i < V4_BLOCK_CELLS; i++) {
|
||||
const unsigned char *b = blocks + 2 * V4_BLOCK_BYTES + 4u * i;
|
||||
v4_ucell want = (v4_ucell)b[0] | ((v4_ucell)b[1] << 8) | ((v4_ucell)b[2] << 16) | ((v4_ucell)b[3] << 24);
|
||||
if ((v4_ucell)n->mem[BUF + (v4_cell)i] != want) ok = 0;
|
||||
}
|
||||
CHECK(ok, "the block is in memory, four bytes to a cell, the first lowest, the rest of the cell zero");
|
||||
CHECK(n->mem[BUF - 1] == 111 && n->mem[BUF + 256] == 222, "and only 256 cells were written");
|
||||
CHECK(n->mem[REG] == 2 && n->mem[REG + 1] == BUF && n->mem[REG + 2] == 1, "the registers are memory words");
|
||||
|
||||
/* write it to block 0, changed */
|
||||
n->mem[BUF] = (v4_cell)0x44332211; n->mem[BUF + 255] = (v4_cell)(v4_ucell)0xAABBCCDDu;
|
||||
v4_node_store(n, REG, 0);
|
||||
v4_node_store(n, REG + 2, 2);
|
||||
CHECK(n->mem[REG + 3] == 0 && blocks[0] == 0x11 && blocks[1] == 0x22 && blocks[2] == 0x33 && blocks[3] == 0x44
|
||||
&& blocks[1020] == 0xDD && blocks[1023] == 0xAA, "a write puts the low four bytes of each cell on the device");
|
||||
CHECK(memcmp(blocks + 4, blocks + 2 * V4_BLOCK_BYTES + 4, 1016) == 0, "the rest of the block as it was read");
|
||||
CHECK(blocks[V4_BLOCK_BYTES] == (unsigned char)(1024u * 7u + 1u), "and the next block untouched");
|
||||
|
||||
/* what does not work: status -1, nothing moved */
|
||||
v4_node_store(n, REG, 4); v4_node_store(n, REG + 2, 1);
|
||||
CHECK(n->mem[REG + 3] == -1, "a block past the last");
|
||||
v4_node_store(n, REG, -1); v4_node_store(n, REG + 2, 1);
|
||||
CHECK(n->mem[REG + 3] == -1, "a negative block");
|
||||
v4_node_store(n, REG, 1); v4_node_store(n, REG + 1, (v4_cell)V4_NODE_WORDS - 255); v4_node_store(n, REG + 2, 1);
|
||||
CHECK(n->mem[REG + 3] == -1, "cells that run past the end of memory");
|
||||
v4_node_store(n, REG + 1, -4); v4_node_store(n, REG + 2, 2);
|
||||
CHECK(n->mem[REG + 3] == -1, "a negative address");
|
||||
v4_node_store(n, REG + 1, BUF); v4_node_store(n, REG + 2, 3);
|
||||
CHECK(n->mem[REG + 3] == -1, "an unknown command");
|
||||
v4_node_store(n, REG + 1, (v4_cell)V4_NODE_WORDS - 256); v4_node_store(n, REG + 2, 1);
|
||||
CHECK(n->mem[REG + 3] == 0 && v4_node_guards_intact(n), "the last 256 cells of memory are usable");
|
||||
|
||||
/* detached: plain memory */
|
||||
v4_node_storage_attach(n, -1, 0, 0);
|
||||
n->mem[REG + 3] = 9; v4_node_store(n, REG + 2, 1);
|
||||
CHECK(n->mem[REG + 3] == 9 && n->mem[REG + 2] == 1, "detached, the registers are memory");
|
||||
v4_node_storage_attach(n, REG, blocks, 4);
|
||||
v4_node_reset(n);
|
||||
CHECK(n->storage_reg == -1, "reset detaches the device");
|
||||
free(n);
|
||||
}
|
||||
|
||||
static void test_ends_are_distinguishable(void)
|
||||
{
|
||||
/* The two boundaries carry different patterns precisely so that a failure
|
||||
@@ -317,7 +254,6 @@ int main(void)
|
||||
test_linear_overrun_low_is_caught();
|
||||
test_linear_overrun_high_is_caught();
|
||||
test_out_of_range_word_address_touches_nothing();
|
||||
test_storage_device();
|
||||
test_ends_are_distinguishable();
|
||||
test_workload_never_false_alarms();
|
||||
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
/* test_store.c -- what is on a storage port, spoken to in messages.
|
||||
* docs/v4.0.0/MESH.md sections 8.3 and 8.4.
|
||||
*
|
||||
* No node: the test is the neighbour. The chains are v3's block subsystem
|
||||
* (v3/src/block_subsystem.c), two of them, each with its fast RAM and a
|
||||
* device of 64 blocks.
|
||||
*/
|
||||
#include "v4/storage.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 STORE 90
|
||||
#define DEVICE_BLOCKS 64u
|
||||
#define RAM_BYTES ((size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE)
|
||||
|
||||
static blk_chain_t *make_chain(blk_chain_t *c)
|
||||
{
|
||||
blk_chain_t *was = blk_chain_select(c);
|
||||
int ok = blk_subsys_init((uint8_t *)calloc(1, RAM_BYTES), RAM_BYTES) == BLK_OK
|
||||
&& blk_subsys_add_raw_device((uint8_t *)calloc(DEVICE_BLOCKS, BLK_FORTH_SIZE), DEVICE_BLOCKS) == BLK_OK;
|
||||
blk_chain_select(was);
|
||||
return ok ? c : 0;
|
||||
}
|
||||
|
||||
/* give the device a whole message; 1 if every word was taken */
|
||||
static int send(v4_store *s, const v4_message *m)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < m->count; i++) if (!v4_store_take(s, m->word[i])) return 0;
|
||||
return 1;
|
||||
}
|
||||
static int ask(v4_store *s, v4_cell from, v4_cell type, v4_cell block)
|
||||
{
|
||||
v4_message m;
|
||||
v4_message_text(&m, STORE, from, type, "\0\0\0\0", 4);
|
||||
m.word[V4_MSG_HEADER] = block;
|
||||
return send(s, &m);
|
||||
}
|
||||
static int data(v4_store *s, v4_cell from, char fill)
|
||||
{
|
||||
static char block[V4_STORE_BYTES];
|
||||
v4_message m;
|
||||
memset(block, fill, sizeof block);
|
||||
v4_message_text(&m, STORE, from, V4_MSG_BLOCK_DATA, block, sizeof block);
|
||||
return send(s, &m);
|
||||
}
|
||||
/* the device's next answer, whole; 0 if it has none */
|
||||
static int answer(v4_store *s, v4_message *m)
|
||||
{
|
||||
v4_cell w;
|
||||
m->count = 0;
|
||||
if (!v4_store_give(s, &w)) return 0;
|
||||
while (!v4_message_word(m, w)) if (!v4_store_give(s, &w)) return 0;
|
||||
return 1;
|
||||
}
|
||||
static int is_done(const v4_message *m, v4_cell to, v4_cell status)
|
||||
{
|
||||
return v4_message_to(m) == to && v4_message_from(m) == STORE && v4_message_type(m) == V4_MSG_BLOCK_DONE
|
||||
&& v4_message_length(m) == 4 && m->word[V4_MSG_HEADER] == status;
|
||||
}
|
||||
static int is_data(const v4_message *m, v4_cell to, char fill)
|
||||
{
|
||||
unsigned i;
|
||||
if (v4_message_to(m) != to || v4_message_from(m) != STORE || v4_message_type(m) != V4_MSG_BLOCK_DATA
|
||||
|| v4_message_length(m) != V4_STORE_BYTES) return 0;
|
||||
for (i = 0; i < V4_STORE_BYTES; i++) if (v4_message_char(m, i) != fill) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
static v4_store both, common_only, private_only;
|
||||
static v4_message m;
|
||||
blk_chain_t *common, *own;
|
||||
v4_cell top = (v4_cell)(v4_ucell)V4_STORE_TOP;
|
||||
|
||||
printf("v4 storage tests: V4_CELL_BITS=%d\n", V4_CELL_BITS);
|
||||
sf_time_init();
|
||||
log_set_level(-1);
|
||||
common = make_chain(blk_chain_default());
|
||||
own = make_chain(blk_chain_new());
|
||||
CHECK(common && own && common != own, "two chains are made");
|
||||
if (!common || !own) return 1;
|
||||
v4_store_init(&both, &v4_store_v3, common, own, STORE);
|
||||
v4_store_init(&common_only, &v4_store_v3, common, 0, STORE);
|
||||
v4_store_init(&private_only, &v4_store_v3, 0, own, STORE);
|
||||
|
||||
/* ---- a block is written and read ---- */
|
||||
CHECK(!answer(&both, &m), "with nothing asked there is nothing to read");
|
||||
CHECK(ask(&both, 11, V4_MSG_BLOCK_WRITE, 2050) && !answer(&both, &m), "a write is not answered until its block has come");
|
||||
CHECK(data(&both, 11, 'A') && answer(&both, &m) && is_done(&m, 11, V4_STORE_OK), "then it is answered done, to who sent it, from storage");
|
||||
CHECK(ask(&both, 12, V4_MSG_BLOCK_READ, 2050) && answer(&both, &m) && is_data(&m, 12, 'A'), "another reads what was written");
|
||||
CHECK(ask(&common_only, 12, V4_MSG_BLOCK_READ, 2050) && answer(&common_only, &m) && is_data(&m, 12, 'A'), "and so does one on another port of the same chain");
|
||||
CHECK(ask(&both, 12, V4_MSG_BLOCK_WRITE, 20) && data(&both, 12, 'R') && answer(&both, &m) && is_done(&m, 12, V4_STORE_OK)
|
||||
&& ask(&common_only, 13, V4_MSG_BLOCK_READ, 20) && answer(&common_only, &m) && is_data(&m, 13, 'R'), "a block below 2048 is the common chain's fast RAM");
|
||||
|
||||
/* ---- private blocks, from the top down ---- */
|
||||
CHECK(ask(&both, 13, V4_MSG_BLOCK_WRITE, top) && data(&both, 13, 'P') && answer(&both, &m) && is_done(&m, 13, V4_STORE_OK), "the top number is written");
|
||||
CHECK(ask(&both, 13, V4_MSG_BLOCK_READ, top) && answer(&both, &m) && is_data(&m, 13, 'P'), "and read back");
|
||||
CHECK(ask(&private_only, 13, V4_MSG_BLOCK_READ, top) && answer(&private_only, &m) && is_data(&m, 13, 'P'), "it is the private chain's");
|
||||
{
|
||||
blk_chain_t *was = blk_chain_select(own);
|
||||
uint8_t *b = blk_get_buffer((uint32_t)V4_STORE_FIRST, 0);
|
||||
CHECK(b && b[0] == 'P' && b[1023] == 'P', "private block 0 is that chain's block 2048");
|
||||
blk_chain_select(common);
|
||||
b = blk_get_buffer((uint32_t)V4_STORE_FIRST, 0);
|
||||
CHECK(b && b[0] != 'P', "and is not in the common chain");
|
||||
blk_chain_select(was);
|
||||
}
|
||||
CHECK(ask(&both, 13, V4_MSG_BLOCK_WRITE, top - (v4_cell)(DEVICE_BLOCKS - 1)) && data(&both, 13, 'L') && answer(&both, &m) && is_done(&m, 13, V4_STORE_OK), "the last private block is written");
|
||||
CHECK(ask(&both, 13, V4_MSG_BLOCK_READ, top - (v4_cell)DEVICE_BLOCKS) && answer(&both, &m) && is_done(&m, 13, V4_STORE_RANGE), "one below the private blocks is no block of the common chain");
|
||||
CHECK(ask(&common_only, 12, V4_MSG_BLOCK_READ, top) && answer(&common_only, &m) && is_done(&m, 12, V4_STORE_RANGE), "with no private chain the top number is no such block");
|
||||
CHECK(ask(&private_only, 13, V4_MSG_BLOCK_READ, 2050) && answer(&private_only, &m) && is_done(&m, 13, V4_STORE_NONE), "with no common chain a common number has no storage");
|
||||
CHECK(ask(&private_only, 13, V4_MSG_BLOCK_WRITE, 20) && data(&private_only, 13, 'r') && answer(&private_only, &m) && is_done(&m, 13, V4_STORE_OK)
|
||||
&& ask(&private_only, 13, V4_MSG_BLOCK_READ, 20) && answer(&private_only, &m) && is_data(&m, 13, 'r'), "and a number below 2048 is the private chain's fast RAM");
|
||||
CHECK(ask(&common_only, 12, V4_MSG_BLOCK_READ, 20) && answer(&common_only, &m) && is_data(&m, 12, 'R'), "which is not the common chain's");
|
||||
|
||||
/* ---- numbers that are no block ---- */
|
||||
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, 0) && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "0 is no block");
|
||||
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, (v4_cell)(V4_STORE_FIRST + DEVICE_BLOCKS)) && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "one past the end of the common chain is no block");
|
||||
CHECK(ask(&both, 11, V4_MSG_BLOCK_WRITE, (v4_cell)(V4_STORE_FIRST + DEVICE_BLOCKS)) && data(&both, 11, 'X') && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "nor can it be written");
|
||||
#if V4_CELL_BITS > 32
|
||||
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, (v4_cell)0x100000000LL) && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "a number 32 bits do not hold is no block");
|
||||
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, -1) && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "nor is a negative one, on cells wider than 32 bits");
|
||||
#else
|
||||
CHECK(top == -1, "on 32-bit cells the top number is -1");
|
||||
#endif
|
||||
|
||||
/* ---- data nobody announced ---- */
|
||||
CHECK(data(&both, 14, 'Z') && answer(&both, &m) && is_done(&m, 14, V4_STORE_REFUSED), "a block of data with no write before it is refused");
|
||||
CHECK(ask(&both, 12, V4_MSG_BLOCK_READ, 2050) && answer(&both, &m) && is_data(&m, 12, 'A'), "and nothing was written");
|
||||
CHECK(ask(&both, 11, V4_MSG_BLOCK_WRITE, 2051) && data(&both, 11, 'B') && answer(&both, &m) && is_done(&m, 11, V4_STORE_OK)
|
||||
&& data(&both, 11, 'C') && answer(&both, &m) && is_done(&m, 11, V4_STORE_REFUSED)
|
||||
&& ask(&both, 11, V4_MSG_BLOCK_READ, 2051) && answer(&both, &m) && is_data(&m, 11, 'B'), "a write is for one block of data and no more");
|
||||
|
||||
/* ---- two senders at once ---- */
|
||||
CHECK(ask(&both, 11, V4_MSG_BLOCK_WRITE, 2060) && ask(&both, 12, V4_MSG_BLOCK_WRITE, 2061)
|
||||
&& data(&both, 12, 'b') && data(&both, 11, 'a'), "two writes are begun before either's block comes, and the blocks come the other way round");
|
||||
CHECK(answer(&both, &m) && is_done(&m, 12, V4_STORE_OK) && answer(&both, &m) && is_done(&m, 11, V4_STORE_OK), "each is answered, in the order the blocks came");
|
||||
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, 2060) && ask(&both, 12, V4_MSG_BLOCK_READ, 2061), "a second request is taken while the answer to the first has not been read");
|
||||
CHECK(answer(&both, &m) && is_data(&m, 11, 'a') && answer(&both, &m) && is_data(&m, 12, 'b'), "and each block is its own sender's");
|
||||
|
||||
/* ---- what is not for storage ---- */
|
||||
{
|
||||
v4_message other;
|
||||
unsigned long before = both.taken;
|
||||
v4_message_text(&other, 55, 11, V4_MSG_BLOCK_READ, "\0\0\0\0", 4);
|
||||
other.word[V4_MSG_HEADER] = 2050;
|
||||
CHECK(send(&both, &other) && both.taken == before + 1 && !answer(&both, &m), "a message for another number is taken and let go");
|
||||
v4_message_text(&other, STORE, 11, V4_MSG_TEXT, "1 2 +", 5);
|
||||
CHECK(send(&both, &other) && !answer(&both, &m), "and so is text");
|
||||
}
|
||||
|
||||
/* ---- what v3 wrote reads through the port ---- */
|
||||
{
|
||||
blk_chain_t *was = blk_chain_select(common);
|
||||
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"); }
|
||||
blk_chain_select(was);
|
||||
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, 2070) && answer(&both, &m) && is_data(&m, 11, 'V'), "what v3's own calls wrote is read through the port");
|
||||
CHECK(blk_chain_select(0) == was, "the chain selected is the one that was, after the device has been used");
|
||||
}
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
+12
-4
@@ -7,15 +7,21 @@
|
||||
* when it stops.
|
||||
*/
|
||||
#include "v4/boot.h"
|
||||
#include "block_subsystem.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define BLOCKS 64u
|
||||
|
||||
static v4_node n;
|
||||
static v4_exec_state es;
|
||||
static v4_heat h;
|
||||
static unsigned char disk[BLOCKS * V4_BLOCK_BYTES];
|
||||
|
||||
/* STORAGE. The node asks its kernel for its blocks (v4/blocks.h), and the
|
||||
* kernel's block subsystem has them (docs/v4.0.0/MESH.md section 8). A
|
||||
* Linux program is given what hosted v3 has when it is started with no
|
||||
* disk: the chain's fast RAM, blocks 1 to 2047, which is gone when the
|
||||
* program ends. */
|
||||
static unsigned char block_ram[(size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE];
|
||||
extern void sf_time_init(void);
|
||||
|
||||
static void console_out(const char *text, unsigned len)
|
||||
{
|
||||
@@ -55,7 +61,9 @@ int main(void)
|
||||
boot.n = &n; boot.es = &es; boot.h = &h; boot.im = &v4_capsule_image;
|
||||
boot.out = console_out; boot.key = console_key;
|
||||
boot.words = kernel_words; boot.word_count = sizeof kernel_words / sizeof kernel_words[0]; boot.serve = serve;
|
||||
if (!v4_boot_run(&boot, disk, BLOCKS)) { fflush(stdout); return 1; }
|
||||
sf_time_init();
|
||||
if (blk_subsys_init(block_ram, sizeof block_ram) != BLK_OK) { fputs("V4: no block storage\n", stderr); return 1; }
|
||||
if (!v4_boot_run(&boot)) { fflush(stdout); return 1; }
|
||||
|
||||
for (;;) {
|
||||
size_t len;
|
||||
|
||||
+2
-3
@@ -88,7 +88,6 @@ 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 = STORAGE_REG; k < STORAGE_REG + 4; k++) n.mem[k] = 0;
|
||||
for (k = BUF0_W; k < BUF0_W + 2 * 256; 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;
|
||||
@@ -114,8 +113,8 @@ 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,\n %ld, %ld,\n %ld, %ld\n};\n", (long)CONSOLE_TX, (long)CONSOLE_RX, (long)CONSOLE_ST,
|
||||
(long)DSTACK_REG, (long)RSTACK_REG, (long)NODE_ERROR, (long)STORAGE_REG, (long)DP, (long)LATEST,
|
||||
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,
|
||||
(long)DSTACK_REG, (long)RSTACK_REG, (long)NODE_ERROR, (long)DP, (long)LATEST,
|
||||
(long)BOOT_CELLS, (long)FENCE);
|
||||
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]);
|
||||
|
||||
Reference in New Issue
Block a user