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
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-10-06T18:45:22Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-10-07T12:21:48Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
@@ -30,7 +30,7 @@
|
||||
| `turtle.4th` | 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108 | `0x4d470418ca543365` | n/a |
|
||||
| `user-font-demo.4th` | 4200, 4201, 4202 | `0xce1fd7d1b581a56d` | n/a |
|
||||
| `v4:forth79.4th` | 6000, 6001, 6002 | `0x4055641ee17d176b` | n/a |
|
||||
| `v4:hera.4th` | 8000, 8001, 8002, 8003, 8004 | `0x93a3160c447d7641` | n/a |
|
||||
| `v4:hera.4th` | 8000, 8001, 8002, 8003, 8004 | `0x526252bfef9b722b` | n/a |
|
||||
| `v4:post79.4th` | 7000, 7001, 7002, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, 7011, 7012, 7013, 7014, 7015, 7016, 7017, 7018, 7019, 7020, 7021, 7022, 7023, 7024, 7025, 7026, 7027, 7028, 7029, 7030, 7031, 7032, 7033, 7034, 7035, 7036, 7037, 7038, 7039, 7040, 7041, 7042, 7043, 7044, 7045, 7046, 7047, 7048, 7049, 7050, 7051, 7052, 7053, 7054, 7055, 7056, 7057, 7058, 7059, 7060, 7061, 7062, 7063 | `0xb26d1b974884179d` | n/a |
|
||||
| `workload-0.4th` | 2200, 2201 | `0x93f86f60aeba8feb` | n/a |
|
||||
| `workload-1-lite.4th` | 5058, 5059 | `0x44a7a7e3176dcc8d` | n/a |
|
||||
@@ -436,11 +436,11 @@
|
||||
| 7061 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7062 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7063 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 8000 | `v4:hera.4th` | `0x93a3160c447d7641` | ok |
|
||||
| 8001 | `v4:hera.4th` | `0x93a3160c447d7641` | ok |
|
||||
| 8002 | `v4:hera.4th` | `0x93a3160c447d7641` | ok |
|
||||
| 8003 | `v4:hera.4th` | `0x93a3160c447d7641` | ok |
|
||||
| 8004 | `v4:hera.4th` | `0x93a3160c447d7641` | ok |
|
||||
| 8000 | `v4:hera.4th` | `0x526252bfef9b722b` | ok |
|
||||
| 8001 | `v4:hera.4th` | `0x526252bfef9b722b` | ok |
|
||||
| 8002 | `v4:hera.4th` | `0x526252bfef9b722b` | ok |
|
||||
| 8003 | `v4:hera.4th` | `0x526252bfef9b722b` | ok |
|
||||
| 8004 | `v4:hera.4th` | `0x526252bfef9b722b` | ok |
|
||||
|
||||
## Conflicts
|
||||
|
||||
|
||||
@@ -40,14 +40,14 @@ Block 8002
|
||||
(ME) @ (TN) S" 2 NEIGHBOUR" (TS)
|
||||
(T) 0 (KIDP) @ SEND-ON (KID#) @ AWAIT (TOLD) ;
|
||||
( number port -- place A NODE IS BORN on that port of )
|
||||
( this one: empty, then the nucleus, then FORTH-79 and POST. )
|
||||
( this one: empty, then the nucleus, then FORTH-79. )
|
||||
: BIRTH ( number port -- place )
|
||||
(KIDP) ! (KID#) !
|
||||
NODE-BORN DUP 0< IF (FAIL) THEN (KID) !
|
||||
NODE-ME (KIDP) @ (KID) @ 2 NODE-WIRE 0= IF (FAIL) THEN
|
||||
(NUCLEUS)
|
||||
(KID#) @ (KIDP) @ ROUTE (KID#) @ (KIDP) @ NEIGHBOUR (WHO)
|
||||
S" v4:forth79.4th" (LINES) S" v4:post79.4th" (LINES)
|
||||
S" v4:forth79.4th" (LINES)
|
||||
Block 8003
|
||||
S" (SEAL)" (TELL)
|
||||
(KID) @ (KID#) @ NODE-PARITY
|
||||
|
||||
Binary file not shown.
+2
-2
@@ -620,8 +620,8 @@ LOADER_EXTRA_SRCS += $(KERNEL_SRC)/v4/sk_v4.c
|
||||
KERNEL_EXTRA_SRCS += $(KERNEL_SRC)/v4/sk_v4.c
|
||||
# v4/system/boot.c is the boot the hosted v4 system runs too: nucleus, then
|
||||
# the capsules from this kernel's own capsule directory, then the prompt.
|
||||
LOADER_V4_OBJS := $(patsubst v4/src/%.c,$(LOADER_OBJ_DIR)/v4engine/%.o,$(V4_ENGINE_SRCS)) $(LOADER_OBJ_DIR)/v4engine/v4_image.o $(LOADER_OBJ_DIR)/v4system/boot.o
|
||||
KERNEL_V4_OBJS := $(patsubst v4/src/%.c,$(KERNEL_OBJ_DIR)/v4engine/%.o,$(V4_ENGINE_SRCS)) $(KERNEL_OBJ_DIR)/v4engine/v4_image.o $(KERNEL_OBJ_DIR)/v4system/boot.o
|
||||
LOADER_V4_OBJS := $(patsubst v4/src/%.c,$(LOADER_OBJ_DIR)/v4engine/%.o,$(V4_ENGINE_SRCS)) $(LOADER_OBJ_DIR)/v4engine/v4_image.o $(LOADER_OBJ_DIR)/v4system/boot.o $(LOADER_OBJ_DIR)/v4system/blocks.o
|
||||
KERNEL_V4_OBJS := $(patsubst v4/src/%.c,$(KERNEL_OBJ_DIR)/v4engine/%.o,$(V4_ENGINE_SRCS)) $(KERNEL_OBJ_DIR)/v4engine/v4_image.o $(KERNEL_OBJ_DIR)/v4system/boot.o $(KERNEL_OBJ_DIR)/v4system/blocks.o
|
||||
endif
|
||||
|
||||
LOADER_SRCS := $(LOADER_SRCS_BASE) $(LOADER_EXTRA_SRCS)
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
/* Start one StarForth v4 host node from the capsule image linked into the
|
||||
* kernel, with its console on the kernel's console, and run it. Does not
|
||||
* return. */
|
||||
void sk_v4_run(void);
|
||||
/* `chain` sets the kernel's block chain up. It is called when the node has
|
||||
* booted and passed POST, before the prompt: as on the v3 path, POST runs
|
||||
* against block RAM of its own and the chain comes after it. */
|
||||
void sk_v4_run(void (*chain)(void));
|
||||
|
||||
#endif /* STARKERNEL_V4_SK_V4_H */
|
||||
|
||||
@@ -459,6 +459,45 @@ void kernel_main(BootInfo *boot_info) {
|
||||
kernel_main_deep(boot_info);
|
||||
}
|
||||
|
||||
#ifdef STARFORTH_V4
|
||||
/* THE BLOCK CHAIN, for the v4 node. It asks this kernel for its blocks
|
||||
* (v4/include/v4/blocks.h, docs/v4.0.0/MESH.md 8.3). The chain is set up
|
||||
* with the calls the v3 path makes and in its order -- fast RAM and the
|
||||
* ramdrive, then PCI, then the virtio disk -- and, as there, after POST.
|
||||
* What the v3 path does next with that disk is not done here, because it is
|
||||
* Artemis's and Zuse's and v4 has neither yet: the genesis signature,
|
||||
* Zuse's root key, and the owner's word that the disk may be formatted.
|
||||
* Until an owner gives that word the subsystem reads the disk and will not
|
||||
* write it. */
|
||||
static BootInfo *sk_v4_boot_info;
|
||||
static void sk_v4_block_chain(void)
|
||||
{
|
||||
const size_t ram_size = (size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE, krd_size = 1024u * 1024u;
|
||||
uint8_t *blk_ram = (uint8_t *)kmalloc(ram_size);
|
||||
uint8_t *krd = (uint8_t *)kmalloc(krd_size);
|
||||
static blkio_dev_t artemis_dev;
|
||||
size_t i;
|
||||
|
||||
if (!blk_ram || !krd) {
|
||||
console_println("StarForth v4: no memory for the block chain");
|
||||
for (;;) { }
|
||||
}
|
||||
for (i = 0; i < krd_size; i++) krd[i] = 0;
|
||||
if (capsule_blk_init(NULL, blk_ram, ram_size, krd) != 0) {
|
||||
console_println("StarForth v4: the block chain could not be set up");
|
||||
for (;;) { }
|
||||
}
|
||||
console_println("PCI: init...");
|
||||
pci_init(sk_v4_boot_info->acpi_table);
|
||||
if (virtio_blk_find_artemis(&artemis_dev) == 0) {
|
||||
console_println("Artemis: virtio-blk attached");
|
||||
blk_subsys_attach_device(&artemis_dev);
|
||||
} else {
|
||||
console_println("Artemis: no virtio-blk disk");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Deep kernel initialisation — M5 heartbeat, M7 VM bootstrap, and REPL.
|
||||
*
|
||||
@@ -519,8 +558,8 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
/* StarForth v4 at a single prompt (Kconfig STARFORTH_V4): one host node
|
||||
* of the F18-derived engine, in place of the v3 VM and everything below.
|
||||
* It does not return. */
|
||||
(void)boot_info;
|
||||
sk_v4_run();
|
||||
sk_v4_boot_info = boot_info;
|
||||
sk_v4_run(sk_v4_block_chain);
|
||||
#endif
|
||||
|
||||
#ifdef STARFORTH_ENABLE_VM
|
||||
|
||||
+10
-4
@@ -20,17 +20,21 @@
|
||||
#include "starkernel/console.h"
|
||||
#include "starkernel/arch.h"
|
||||
#include "v4/boot.h"
|
||||
#include "block_subsystem.h"
|
||||
|
||||
#define SK_V4_BLOCKS 64u
|
||||
#define SK_V4_LINE 255u /* what the line editor here takes */
|
||||
|
||||
static v4_node sk_v4_node;
|
||||
static v4_exec_state sk_v4_es;
|
||||
static v4_heat sk_v4_heat;
|
||||
static unsigned char sk_v4_disk[SK_V4_BLOCKS * V4_BLOCK_BYTES];
|
||||
|
||||
static char sk_v4_text[SK_V4_LINE + 1u];
|
||||
|
||||
/* POST's block RAM, as the v3 path has (sk_vm_bootstrap.c, sk_post_blk_ram):
|
||||
* the node boots and is POSTed with these blocks, 1 to 2047 and nothing
|
||||
* above them, and the kernel's real chain is set up afterwards. */
|
||||
static uint8_t sk_v4_post_blk_ram[BLK_RAM_BLOCKS * BLK_FORTH_SIZE];
|
||||
|
||||
static void sk_v4_out(const char *text, unsigned len)
|
||||
{
|
||||
unsigned i;
|
||||
@@ -78,7 +82,7 @@ static unsigned sk_v4_readline(void)
|
||||
}
|
||||
}
|
||||
|
||||
void sk_v4_run(void)
|
||||
void sk_v4_run(void (*chain)(void))
|
||||
{
|
||||
v4_boot boot;
|
||||
|
||||
@@ -86,10 +90,12 @@ void sk_v4_run(void)
|
||||
boot.n = &sk_v4_node; boot.es = &sk_v4_es; boot.h = &sk_v4_heat; boot.im = &v4_capsule_image;
|
||||
boot.out = sk_v4_out; boot.key = sk_v4_key;
|
||||
boot.words = sk_v4_words; boot.word_count = sizeof sk_v4_words / sizeof sk_v4_words[0]; boot.serve = sk_v4_serve;
|
||||
if (!v4_boot_run(&boot, sk_v4_disk, SK_V4_BLOCKS)) {
|
||||
blk_subsys_init(sk_v4_post_blk_ram, sizeof(sk_v4_post_blk_ram));
|
||||
if (!v4_boot_run(&boot)) {
|
||||
console_println("StarForth v4: not started");
|
||||
for (;;) { }
|
||||
}
|
||||
if (chain) chain();
|
||||
|
||||
/* The prompt is the host's: read a line, hand it to the node, say how it
|
||||
* ended. */
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
[2J[001;001H[=3h[2J[001;001H[2J[001;001H[8;042;160t[2J[001;001H[2J[001;001H[8;056;240t[2J[001;001HBdsDxe: loading Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
|
||||
BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
|
||||
[2J[001;001H[=3h[2J[001;001HStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
RAW SERIAL UP
|
||||
[CKPT 002] Serial (COM1) initialized
|
||||
Monolithic build - kernel linked directly
|
||||
Collecting boot information...
|
||||
CmdLine: parsed OK
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
GOP: linear framebuffer found
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
EBS...
|
||||
EBS OK
|
||||
Calling kernel_main (monolithic)...
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: amd64
|
||||
Build: Oct 7 2026 08:15:38
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 135
|
||||
Total memory: 1023 MB
|
||||
Usable memory: 966 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 248749
|
||||
Free pages : 247226
|
||||
Used pages : 1523
|
||||
Total MB : 971
|
||||
Free MB : 965
|
||||
Used MB : 5
|
||||
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM self-test: mapped OK at 0xffff800000000000
|
||||
VMM self-test complete.
|
||||
|
||||
IDT installed.
|
||||
|
||||
APIC: init...
|
||||
APIC: IA32_APIC_BASE MSR=0x00000000fee00900
|
||||
APIC: stale-ISR drain: 0 EOI(s) issued
|
||||
APIC: initialized (xAPIC MMIO, TPR=0, SIVR=0x1FF, EOI-clear)
|
||||
APIC: init done
|
||||
|
||||
I/O APIC: init...
|
||||
I/O APIC: base=0xfec00000, gsi_base=0, overrides=5
|
||||
override: source=00 gsi=2 flags=0x0000
|
||||
override: source=05 gsi=5 flags=0x000d
|
||||
override: source=09 gsi=9 flags=0x000d
|
||||
override: source=0a gsi=10 flags=0x000d
|
||||
override: source=0b gsi=11 flags=0x000d
|
||||
i8042: keyboard ACKed enable-scanning
|
||||
i8042: IRQ1 enabled in controller config byte
|
||||
I/O APIC: keyboard IRQ1 routed (masked)
|
||||
|
||||
Timer: init...
|
||||
Timer: init start
|
||||
Timer: PM_TMR_BLK discovered from FADT at port 1544
|
||||
Timer: VM mode detected (hypervisor present).
|
||||
Timer: HPET calibration disabled (VM-exit MMIO would poison timing).
|
||||
Timer: WARNING: invariant TSC not present under hypervisor.
|
||||
Timer: continuing in RELATIVE mode (no determinism guarantees).
|
||||
Timer: RDTSCP not present; using RDTSC (less serialized).
|
||||
Timer: CPUID frequency unavailable; trying PM Timer...
|
||||
Timer: trust=1 (0=NONE,1=REL,2=ABS), TSC=2098985198 Hz
|
||||
Timer: init done
|
||||
|
||||
Kernel heap initialized.
|
||||
Heap statistics:
|
||||
Total bytes: 536870872
|
||||
Free bytes: 536870872
|
||||
Used bytes: 0
|
||||
Peak bytes: 0
|
||||
Heap base addr: 24641536
|
||||
Heap end addr: 561512448
|
||||
|
||||
Heartbeat: init...
|
||||
APIC Timer: calibrating...
|
||||
APIC Timer: apic_hz=1009454547, tick_hz=100, initial_count=10094545
|
||||
APIC Timer: configured (masked, ready to start)
|
||||
Heartbeat: init done
|
||||
Kernel initialization complete.
|
||||
Boot successful!
|
||||
|
||||
StarForth v4: one host node, the F18-derived engine
|
||||
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x4eb584bf6c169ea1 capsule_hash=0x4eb584bf6c169ea1 words=310
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xe6453dd9449a3f64
|
||||
V4: capsule v4:post79.4th signature: missing (unsigned)
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0xab589e162cb5e638
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
|
||||
PCI: init...
|
||||
PCI: ECAM mapped (amd64)
|
||||
virtio-blk: found device
|
||||
Artemis: virtio-blk attached
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 71159025] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
ok> COLD
|
||||
FORTH-79 Cold Start
|
||||
System initialized.
|
||||
ok
|
||||
ok> 3 4 U* . .
|
||||
0 12 ok
|
||||
ok> FORGET U*
|
||||
Protected word
|
||||
ERROR
|
||||
ok> 9 KERNEL-WORD ASK9 ASK9
|
||||
Argument out of range
|
||||
ERROR
|
||||
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
|
||||
65 ok
|
||||
ok> 3072 BLOCK C@ .
|
||||
0 ok
|
||||
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
|
||||
Storage refused
|
||||
ERROR
|
||||
ok> 9999999 BLOCK
|
||||
Block out of range
|
||||
ERROR
|
||||
ok> 9999999 BLOCK
|
||||
Block out of range
|
||||
ERROR
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
@@ -0,0 +1,122 @@
|
||||
UEFI firmware (version 2025.11-3ubuntu7.3 built at 15:40:26 on Sep 23 2026)
|
||||
[2J[001;001H[=3h[2J[001;001H[2J[001;001H[8;031;100t[2J[001;001HBdsDxe: failed to load Boot0002 "UEFI Misc Device" from PciRoot(0x0)/Pci(0x2,0x0): Not Found
|
||||
BdsDxe: failed to load Boot0003 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:04.0-1" from PciRoot(0x0)/Pci(0x4,0x0)/USB(0x0,0x0): Not Found
|
||||
BdsDxe: loading Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
|
||||
BdsDxe: starting Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
|
||||
[2J[001;001H[=3h[2J[001;001HStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
Collecting boot information...
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: aarch64
|
||||
Build: Oct 7 2026 08:18:14
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 108
|
||||
Total memory: 4093 MB
|
||||
Usable memory: 4054 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 1039360
|
||||
Free pages : 1037949
|
||||
Used pages : 1411
|
||||
Total MB : 4060
|
||||
Free MB : 4054
|
||||
Used MB : 5
|
||||
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM self-test: mapped OK at 0xffff800000000000
|
||||
VMM self-test complete.
|
||||
|
||||
AArch64: running at EL1
|
||||
IDT installed.
|
||||
|
||||
APIC: init...
|
||||
PSCI: no DTB -- using HVC (QEMU virt-machine default)
|
||||
GICv2: no DTB GIC node -- using QEMU virt-machine defaults
|
||||
GICv2: distributor+CPU interface enabled, PPI 30
|
||||
APIC: init done
|
||||
|
||||
Timer: init...
|
||||
Timer: AArch64 generic timer initialised.
|
||||
Timer: init done
|
||||
|
||||
Kernel heap initialized.
|
||||
Heap statistics:
|
||||
Total bytes: 2147483608
|
||||
Free bytes: 2147483608
|
||||
Used bytes: 0
|
||||
Peak bytes: 0
|
||||
Heap base addr: 1207959552
|
||||
Heap end addr: 3355443200
|
||||
|
||||
Heartbeat: init...
|
||||
Heartbeat: init done
|
||||
Kernel initialization complete.
|
||||
Boot successful!
|
||||
|
||||
StarForth v4: one host node, the F18-derived engine
|
||||
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x4eb584bf6c169ea1 capsule_hash=0x4eb584bf6c169ea1 words=310
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xe6453dd9449a3f64
|
||||
V4: capsule v4:post79.4th signature: missing (unsigned)
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0xab589e162cb5e638
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
|
||||
PCI: init...
|
||||
virtio-blk: found device
|
||||
Artemis: virtio-blk attached
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 1268854] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
ok> COLD
|
||||
FORTH-79 Cold Start
|
||||
System initialized.
|
||||
ok
|
||||
ok> 3 4 U* . .
|
||||
0 12 ok
|
||||
ok> FORGET U*
|
||||
Protected word
|
||||
ERROR
|
||||
ok> 9 KERNEL-WORD ASK9 ASK9
|
||||
Argument out of range
|
||||
ERROR
|
||||
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
|
||||
65 ok
|
||||
ok> 3072 BLOCK C@ .
|
||||
0 ok
|
||||
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
|
||||
Storage refused
|
||||
ERROR
|
||||
ok> 9999999 BLOCK
|
||||
Block out of range
|
||||
ERROR
|
||||
ok> 9999999 BLOCK
|
||||
Block out of range
|
||||
ERROR
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
@@ -0,0 +1,192 @@
|
||||
|
||||
OpenSBI v1.8
|
||||
____ _____ ____ _____
|
||||
/ __ \ / ____| _ \_ _|
|
||||
| | | |_ __ ___ _ __ | (___ | |_) || |
|
||||
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
|
||||
| |__| | |_) | __/ | | |____) | |_) || |_
|
||||
\____/| .__/ \___|_| |_|_____/|____/_____|
|
||||
| |
|
||||
|_|
|
||||
|
||||
Platform Name : riscv-virtio,qemu
|
||||
Platform Features : medeleg
|
||||
Platform HART Count : 1
|
||||
Platform HART Protection : pmp
|
||||
Platform IPI Device : aclint-mswi
|
||||
Platform Timer Device : aclint-mtimer @ 10000000Hz
|
||||
Platform Console Device : uart8250
|
||||
Platform HSM Device : ---
|
||||
Platform PMU Device : ---
|
||||
Platform Reboot Device : syscon-reboot
|
||||
Platform Shutdown Device : syscon-poweroff
|
||||
Platform Suspend Device : ---
|
||||
Platform CPPC Device : ---
|
||||
Firmware Base : 0x80000000
|
||||
Firmware Size : 321 KB
|
||||
Firmware RW Offset : 0x40000
|
||||
Firmware RW Size : 65 KB
|
||||
Firmware Heap Offset : 0x47000
|
||||
Firmware Heap Size : 37 KB (total), 0 KB (reserved), 12 KB (used), 23 KB (free)
|
||||
Firmware Scratch Size : 4096 B (total), 1464 B (used), 2632 B (free)
|
||||
Runtime SBI Version : 3.0
|
||||
Standard SBI Extensions : ipi,pmu,srst,sse,hsm,rfnc,fwft,time,base,legacy,dbcn,dbtr
|
||||
Experimental SBI Extensions : none
|
||||
|
||||
Domain0 Name : root
|
||||
Domain0 Boot HART : 0
|
||||
Domain0 HARTs : 0*
|
||||
Domain0 Region00 : 0x0000000080040000-0x000000008005ffff M: (F,R,W) S/U: ()
|
||||
Domain0 Region01 : 0x0000000080000000-0x000000008003ffff M: (F,R,X) S/U: ()
|
||||
Domain0 Region02 : 0x0000000000100000-0x0000000000100fff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region03 : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region04 : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: ()
|
||||
Domain0 Region05 : 0x000000000c400000-0x000000000c5fffff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region06 : 0x000000000c000000-0x000000000c3fffff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region07 : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
|
||||
Domain0 Next Address : 0x0000000020000000
|
||||
Domain0 Next Arg1 : 0x00000000bfe00000
|
||||
Domain0 Next Mode : S-mode
|
||||
Domain0 SysReset : yes
|
||||
Domain0 SysSuspend : yes
|
||||
|
||||
Boot HART ID : 0
|
||||
Boot HART Domain : root
|
||||
Boot HART Priv Version : v1.12
|
||||
Boot HART Base ISA : rv64imafdch
|
||||
Boot HART ISA Extensions : sstc,zicntr,zihpm,zicboz,zicbom,sdtrig,svadu
|
||||
Boot HART PMP Count : 16
|
||||
Boot HART PMP Granularity : 2 bits
|
||||
Boot HART PMP Address Bits : 54
|
||||
Boot HART MHPM Info : 16 (0x0007fff8)
|
||||
Boot HART Debug Triggers : 2 triggers
|
||||
Boot HART MIDELEG : 0x0000000000001666
|
||||
Boot HART MEDELEG : 0x0000000000f4b509
|
||||
[2J[004D[=3h[2J[009D[2J[004D[8;031;100t[2J[016DRISC-V EDK2 firmware version 2025.11-3ubuntu7.3
|
||||
Press ESCAPE within 5 seconds for boot options ERROR: C40000002:V03051002 I0 6D33944A-EC75-4855-A54D-809C75241F6C 83FFF850
|
||||
BdsDxe: failed to load Boot0001 "UEFI Misc Device" fr
|
||||
om PciRoot(0x0)/Pci(0x1,0x0): Not Found
|
||||
[2J[001;001H[=3h[2J[009DStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
Monolithic build - kernel linked directly
|
||||
Collecting boot information...
|
||||
CmdLine: parsed OK
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
GOP: linear framebuffer found
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
Calling kernel_main (monolithic)...
|
||||
riscv64 item 4.3.5a: satp state at kernel entry (before switch)
|
||||
satp.MODE = 0x000000000000000a
|
||||
satp.PPN = 0x00000000000bf868
|
||||
__kernel_start = 0x00000000bdcab9c0
|
||||
riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a)
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: riscv64
|
||||
Build: Oct 7 2026 08:21:48
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 100
|
||||
Total memory: 1020 MB
|
||||
Usable memory: 975 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 250276
|
||||
Free pages : 249624
|
||||
Used pages : 652
|
||||
Total MB : 977
|
||||
Free MB : 975
|
||||
Used MB : 2
|
||||
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM self-test: mapped OK at 0xffff800000000000
|
||||
VMM self-test complete.
|
||||
|
||||
IDT installed.
|
||||
|
||||
APIC: init...
|
||||
PLIC: no DTB PLIC node -- using QEMU virt-machine default (base=0x0c000000)
|
||||
PLIC: init (S-mode context 1, threshold=0)
|
||||
APIC: init done
|
||||
|
||||
Timer: init...
|
||||
Timer: RISC-V time CSR @ 10000000 Hz (FALLBACK, no devicetree)
|
||||
Timer: init done
|
||||
|
||||
Kernel heap initialized.
|
||||
Heap statistics:
|
||||
Total bytes: 536870872
|
||||
Free bytes: 536870872
|
||||
Used bytes: 0
|
||||
Peak bytes: 0
|
||||
Heap base addr: 2214588416
|
||||
Heap end addr: 2751459328
|
||||
|
||||
Heartbeat: init...
|
||||
Heartbeat: init done
|
||||
Kernel initialization complete.
|
||||
Boot successful!
|
||||
|
||||
StarForth v4: one host node, the F18-derived engine
|
||||
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x4eb584bf6c169ea1 capsule_hash=0x4eb584bf6c169ea1 words=310
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xe6453dd9449a3f64
|
||||
V4: capsule v4:post79.4th signature: missing (unsigned)
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0xab589e162cb5e638
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
|
||||
PCI: init...
|
||||
virtio-blk: found device
|
||||
Artemis: virtio-blk attached
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 19521579] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
ok> COLD
|
||||
FORTH-79 Cold Start
|
||||
System initialized.
|
||||
ok
|
||||
ok> 3 4 U* . .
|
||||
0 12 ok
|
||||
ok> FORGET U*
|
||||
Protected word
|
||||
ERROR
|
||||
ok> 9 KERNEL-WORD ASK9 ASK9
|
||||
Argument out of range
|
||||
ERROR
|
||||
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
|
||||
65 ok
|
||||
ok> 3072 BLOCK C@ .
|
||||
0 ok
|
||||
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
|
||||
Storage refused
|
||||
ERROR
|
||||
ok> 9999999 BLOCK
|
||||
Block out of range
|
||||
ERROR
|
||||
ok> 9999999 BLOCK
|
||||
Block out of range
|
||||
ERROR
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
+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