fix(v4.0.0): storage -- what the review of step 6 found
A block request with fewer than two values on the stack is refused; it had acted on whatever the stack ring held and stopped the node. Bare metal: when the kernel's chain takes the place of POST's block RAM the node's two buffers are emptied, so it no longer holds POST's copy of a block; and the chain's fast RAM is cleared, so a node cannot read what was in the kernel's heap. blocks.c is built with each test under that test's own warnings and sanitizers; it had been left out of both. The hosted link cleans its object directory first: it had linked the withdrawn store_v3.o left there from the day before. node.h and DECOMPOSITION.md D-19 no longer describe the message device or the four registers as current. MESH.md 8.5 records two findings for ruling: a node's own copy of a block, and a block read over a node's code. From a clean build: make -C v4 test, sanitize and hosted-check pass; amd64, aarch64 and riscv64 boot, POST 538 of 538, same hashes, blocks 1 and 2047 clean at the prompt: logs/20261007-085017, -085254, -085636. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6d90375da7
commit
bcfd6556a8
+12
-18
@@ -63,24 +63,18 @@ 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
|
||||
@rm -rf $(BINDIR)/v3o && mkdir -p $(BINDIR)/v3o
|
||||
cd $(BINDIR)/v3o && $(CC) $(V3_CFLAGS) -c $(V3_BLOCK_SRCS)
|
||||
$(LD) -r $(BINDIR)/v3o/*.o -o $@
|
||||
$(BINDIR)/san-v3blocks.o: $(V3_BLOCK_SRCS) $(wildcard $(ROOT_DIR)/v3/include/*.h) $(HERE)/Makefile
|
||||
@mkdir -p $(BINDIR)/san-v3o
|
||||
@rm -rf $(BINDIR)/san-v3o && 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 $@
|
||||
# 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) $(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,$(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)
|
||||
# blocks.c is compiled with each test that uses it, under that test's own
|
||||
# flags: its warnings, its sanitizers, its cell width and node size.
|
||||
blocks_src = $(if $(call uses_blocks,$(1)),$(BLOCKS_SRC))
|
||||
blocks_objs = $(if $(call uses_blocks,$(1)),$(BINDIR)/v3blocks.o)
|
||||
san_blocks_objs = $(if $(call uses_blocks,$(1)),$(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
|
||||
@@ -160,7 +154,7 @@ $(CAPSULE_DIR_C): $(BINDIR)/mkcapsule $(CAPSULE_FILES) $(BINDIR)/v4_image_64.c
|
||||
define HOSTED_RULE
|
||||
$(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)
|
||||
@rm -rf $(BINDIR)/v3o-$(1) && 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 $$@
|
||||
|
||||
@@ -226,16 +220,16 @@ endif
|
||||
#
|
||||
# Both binaries depend on this Makefile, so a flag change rebuilds them.
|
||||
define TEST_RULE
|
||||
$(BINDIR)/$(1)-$(notdir $(2)): $(2) $$(SRCS) $(call blocks_objs,$(1),$(2)) $$(wildcard $(HERE)/include/v4/*.h) $$(wildcard $(HERE)/tests/*.h) $(HERE)/Makefile
|
||||
$(BINDIR)/$(1)-$(notdir $(2)): $(2) $$(SRCS) $(call blocks_src,$(2)) $(call blocks_objs,$(2)) $$(wildcard $(HERE)/include/v4/*.h) $$(wildcard $(HERE)/tests/*.h) $(HERE)/Makefile
|
||||
@mkdir -p $(BINDIR)
|
||||
$$(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 $$@
|
||||
$(2) $$(SRCS) $(call blocks_src,$(2)) $(call blocks_objs,$(2)) -o $$@
|
||||
|
||||
$(BINDIR)/san-$(1)-$(notdir $(2)): $(2) $$(SRCS) $(call san_blocks_objs,$(1),$(2)) $$(wildcard $(HERE)/include/v4/*.h) $$(wildcard $(HERE)/tests/*.h) $(HERE)/Makefile
|
||||
$(BINDIR)/san-$(1)-$(notdir $(2)): $(2) $$(SRCS) $(call blocks_src,$(2)) $(call san_blocks_objs,$(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 blocks_inc,$(2)) -DV4_CELL_BITS=$(1) $(call node_size,$(2)) $(capsule_dir) \
|
||||
$(2) $$(SRCS) $(call san_blocks_objs,$(1),$(2)) -o $$@
|
||||
$(2) $$(SRCS) $(call blocks_src,$(2)) $(call san_blocks_objs,$(2)) -o $$@
|
||||
endef
|
||||
|
||||
# One run target per (width, test) and one prerequisite line per width, so
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -331,9 +331,9 @@ 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);
|
||||
|
||||
/* 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
|
||||
/* A BLOCK (DECOMPOSITION.md D-19, 5.11). Mass storage is the kernel's: a
|
||||
* node asks it for a block by a request on port 0 (docs/v4.0.0/MESH.md
|
||||
* section 8; blocks.h), and 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. */
|
||||
|
||||
@@ -15,6 +15,11 @@ int v4_blocks_serve(v4_node *n, v4_cell request)
|
||||
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);
|
||||
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;
|
||||
|
||||
@@ -83,6 +83,17 @@ int main(void)
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user