feat(v4.0.0): the nucleus is a capsule, sent to a node born empty
MESH.md step 2. A capsule of F18 code is the words a neighbour writes to a node's port: for each stretch of memory, "@p a! @p push", the address and count, "@p !+ unext" and the words; then a jump to the start. A node born empty executes that from its port, so it needs nothing in it beforehand. - capsule.h/.c: v4_capsule_write, any node's memory as such a capsule - mkimage writes the nucleus so, to capsules/v4/nucleus-64.f18, and the addresses a host needs as a C file; the memory image is no longer linked into either product - mkcapsule is unchanged: the nucleus capsule is a built file kept under capsules/, as BLOCK_MAP.md is, and is baked, hashed and signed with the rest - boot: the node is born empty (v4_image_born); the nucleus capsule is found, its hash and signature checked, and given to the node a word at a time as it reads its port; PARITY:V4_NUCLEUS carries its name and hash Verified: test_fabric.c (59 checks, both widths, and under ASan and UBSan): a memory with a programme and scattered words arrives word for word in an empty node and runs. The nucleus capsule rebuilds byte for byte. hosted-check passes on three ISAs; clean qemu with STARFORTH_V4=1 on amd64, aarch64 and riscv64 takes the nucleus in, passes POST (550 of 550) and answers lines typed at each prompt (logs/20261006-102421, -102706, -103048). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
9af442f793
commit
42610e844f
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-10-06T11:54:53Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-10-06T14:30:09Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
Binary file not shown.
+29
-8
@@ -149,14 +149,15 @@ to 8 and are made of capsule code and of the host that owns the devices.
|
||||
|
||||
## 5. A capsule of F18 code, and how an empty node takes it in
|
||||
|
||||
**Proposal.** A capsule of F18 code is words of memory and where they go:
|
||||
runs of `(address, count, the words)`, and the address to start at. The
|
||||
nucleus image `mkimage` writes today is this already, as a C file linked
|
||||
into the binary; it becomes a capsule in the capsule directory, with a
|
||||
name, a hash and a signature like any other. (`mkcapsule` takes what is
|
||||
under `capsules/`; the nucleus is built, not written. How a built file
|
||||
enters the directory is to be settled when this is reached, and reported
|
||||
before it is done, since `mkcapsule` is v3's tool too.)
|
||||
**As built.** A capsule of F18 code is not a format a node has to
|
||||
understand. It is the words a neighbour writes to the node's port, in the
|
||||
order they are written: for each stretch of memory that is not zero the
|
||||
two instruction words below with their address and count and then the
|
||||
words themselves, and at the end a jump to where to start. The file is
|
||||
those words, each in a cell's bytes, low byte first, and its hash is the
|
||||
hash of exactly what is sent. The nucleus is one, in the capsule directory
|
||||
with a name, a hash and a signature like any other. `mkcapsule` was not
|
||||
changed: the nucleus capsule is a built file kept under `capsules/v4/`.
|
||||
|
||||
A neighbour puts it into an empty node by writing to the port between
|
||||
them, and the node executes what arrives (4.1). For each run it sends
|
||||
@@ -264,6 +265,26 @@ Each is tested, committed and pushed before the next.
|
||||
(`logs/20261006-074907`, `-075150`, `-075532`).
|
||||
2. **The nucleus as a capsule, and an empty node made a StarForth node
|
||||
through its port (section 5).**
|
||||
**Done 2026-10-06.** `v4/src/capsule.c`, `v4_capsule_write`: any
|
||||
node's memory as the words to send an empty node. `mkimage` writes the
|
||||
nucleus so, to `capsules/v4/nucleus-64.f18`, a built file kept in the
|
||||
tree as `BLOCK_MAP.md` is; `mkcapsule` is unchanged and bakes, hashes
|
||||
and signs it with the rest. The boot's node is born empty
|
||||
(`v4_image_born`) and is given the nucleus a word at a time as it reads
|
||||
its port, after the capsule's hash and signature are checked
|
||||
(`v4/system/boot.c`). Nothing of the nucleus is linked into either
|
||||
product any more. `test_fabric.c`: a memory with a programme and words
|
||||
here and there arrives word for word and runs. Both products boot so:
|
||||
`hosted-check` on three ISAs; `logs/20261006-102421`, `-102706`,
|
||||
`-103048`.
|
||||
**What a newborn node still has from its host:** the registers the
|
||||
nucleus expects — the console's three, the two stack registers, the
|
||||
error register, the fault table's address, the storage registers — are
|
||||
attached by the host when the node is born, from the description
|
||||
`mkimage` writes. They are the node's hardware as the golden model has
|
||||
it, at addresses the memory map (D-4, still open) will fix. The console
|
||||
and storage ones go when those become devices on ports (steps 3 and
|
||||
6).
|
||||
3. **Messages (section 6):** a StarForth node that reads messages when
|
||||
idle; the console as a device; a line typed is a message.
|
||||
4. **Finding the way (section 7).**
|
||||
|
||||
+4
-1
@@ -613,7 +613,7 @@ V4_DEFS := -DSTARFORTH_V4=1 -Iv4/include \
|
||||
KERNEL_CFLAGS += $(V4_DEFS)
|
||||
LOADER_CFLAGS += $(V4_DEFS)
|
||||
|
||||
V4_ENGINE_SRCS := $(addprefix v4/src/,node.c exec.c stack.c iword.c heat.c guard.c image.c fabric.c)
|
||||
V4_ENGINE_SRCS := $(addprefix v4/src/,node.c exec.c stack.c iword.c heat.c guard.c image.c fabric.c capsule.c)
|
||||
V4_IMAGE_C := $(BUILD_DIR)/v4_image_64.c
|
||||
|
||||
LOADER_EXTRA_SRCS += $(KERNEL_SRC)/v4/sk_v4.c
|
||||
@@ -765,6 +765,9 @@ endif
|
||||
# StarForth v4: the capsule image, built on this machine by v4's Makefile, and
|
||||
# the engine, compiled like any other kernel source.
|
||||
ifeq ($(STARFORTH_V4),1)
|
||||
# The nucleus is a capsule (capsules/v4/nucleus-64.f18), written by the same
|
||||
# step that writes its description, so the capsule directory waits for it.
|
||||
$(CAPSULE_GENERATED): $(V4_IMAGE_C)
|
||||
$(V4_IMAGE_C): FORCE
|
||||
@mkdir -p $(dir $@)
|
||||
@echo " V4IMG v4/capsule -> $@"
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
[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 6 2026 10:23:55
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 131
|
||||
Total memory: 1023 MB
|
||||
Usable memory: 968 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 249253
|
||||
Free pages : 247730
|
||||
Used pages : 1523
|
||||
Total MB : 973
|
||||
Free MB : 967
|
||||
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=2097406618 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=1009199508, tick_hz=100, initial_count=10091995
|
||||
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=0xbcc90cc5a638a299 capsule_hash=0xbcc90cc5a638a299 words=298
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x88fb8dcbb051f7b9
|
||||
V4: capsule v4:post79.4th signature: missing (unsigned)
|
||||
PARITY:V4_POST tests=550 pass=550 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb987dbb4388990bd capsule_hash=0xb987dbb4388990bd dict_hash=0x843716689a31b60a
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
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> BYE
|
||||
BYE: cold restart
|
||||
@@ -0,0 +1,104 @@
|
||||
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 6 2026 10:26:41
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 108
|
||||
Total memory: 4093 MB
|
||||
Usable memory: 4056 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 1039869
|
||||
Free pages : 1038458
|
||||
Used pages : 1411
|
||||
Total MB : 4061
|
||||
Free MB : 4056
|
||||
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=0xbcc90cc5a638a299 capsule_hash=0xbcc90cc5a638a299 words=298
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x88fb8dcbb051f7b9
|
||||
V4: capsule v4:post79.4th signature: missing (unsigned)
|
||||
PARITY:V4_POST tests=550 pass=550 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb987dbb4388990bd capsule_hash=0xb987dbb4388990bd dict_hash=0x843716689a31b60a
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
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> BYE
|
||||
BYE: cold restart
|
||||
@@ -0,0 +1,174 @@
|
||||
|
||||
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 = 0x00000000bdea88f8
|
||||
riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a)
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: riscv64
|
||||
Build: Oct 6 2026 10:30:10
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 100
|
||||
Total memory: 1020 MB
|
||||
Usable memory: 977 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 250783
|
||||
Free pages : 250131
|
||||
Used pages : 652
|
||||
Total MB : 979
|
||||
Free MB : 977
|
||||
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=0xbcc90cc5a638a299 capsule_hash=0xbcc90cc5a638a299 words=298
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x88fb8dcbb051f7b9
|
||||
V4: capsule v4:post79.4th signature: missing (unsigned)
|
||||
PARITY:V4_POST tests=550 pass=550 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb987dbb4388990bd capsule_hash=0xb987dbb4388990bd dict_hash=0x843716689a31b60a
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
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> BYE
|
||||
BYE: cold restart
|
||||
+11
-4
@@ -60,8 +60,16 @@ capsule_dir := -DV4_CAPSULE_DIR='"$(HERE)/capsule"'
|
||||
# capsule/*.v4 -- and writes it as a C file, $(BINDIR)/v4_image_<width>.c.
|
||||
# The hosted system below and the bare-metal kernel (kernel/Makefile,
|
||||
# STARFORTH_V4=1) link the 64-bit one. docs/v4.0.0/NUCLEUS.md.
|
||||
# The nucleus as a capsule of F18 code (include/v4/capsule.h). The 64-bit one
|
||||
# is a product's: it goes into the capsule directory, ../capsules/v4, where
|
||||
# the repository's mkcapsule finds it, hashes it and signs it with every
|
||||
# other capsule. It is a built file kept in the tree, as
|
||||
# ../capsules/BLOCK_MAP.md is, and is to be committed when it changes.
|
||||
ROOT := $(abspath $(HERE)/..)
|
||||
NUCLEUS_64 := $(ROOT)/capsules/v4/nucleus-64.f18
|
||||
NUCLEUS_32 := $(BINDIR)/nucleus-32.f18
|
||||
HOST_DEFS := -DV4_NODE_WORDS=$(HOST_WORDS) -DV4_DATA_RING=$(HOST_DATA_RING) -DV4_RET_RING=$(HOST_RET_RING)
|
||||
ENGINE_SRCS := $(addprefix $(HERE)/src/,node.c exec.c stack.c iword.c heat.c guard.c image.c fabric.c)
|
||||
ENGINE_SRCS := $(addprefix $(HERE)/src/,node.c exec.c stack.c iword.c heat.c guard.c image.c fabric.c capsule.c)
|
||||
|
||||
define IMAGE_RULE
|
||||
$(BINDIR)/mkimage-$(1): $(HERE)/tools/mkimage.c $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/tests/host_map.h $(HERE)/Makefile
|
||||
@@ -69,7 +77,7 @@ $(BINDIR)/mkimage-$(1): $(HERE)/tools/mkimage.c $$(SRCS) $$(wildcard $(HERE)/inc
|
||||
$$(CC) $$(CFLAGS) -I$(HERE)/include -DV4_CELL_BITS=$(1) $(HOST_DEFS) $(capsule_dir) $(HERE)/tools/mkimage.c $$(SRCS) -o $$@
|
||||
|
||||
$(BINDIR)/v4_image_$(1).c: $(BINDIR)/mkimage-$(1) $$(CAPSULES)
|
||||
$(BINDIR)/mkimage-$(1) $$@
|
||||
$(BINDIR)/mkimage-$(1) $$@ $$(NUCLEUS_$(1))
|
||||
endef
|
||||
$(foreach w,$(WIDTHS),$(eval $(call IMAGE_RULE,$(w))))
|
||||
|
||||
@@ -85,7 +93,6 @@ image: $(foreach w,$(WIDTHS),$(BINDIR)/v4_image_$(w).c)
|
||||
# SIGN_KEY), and the code that reads it is the kernel's, compiled here as it
|
||||
# is there. The binaries are static, so the two foreign ones run under
|
||||
# user-mode QEMU with nothing else installed.
|
||||
ROOT := $(abspath $(HERE)/..)
|
||||
HOSTED_ISAS := amd64 aarch64 riscv64
|
||||
CC_amd64 ?= cc
|
||||
CC_aarch64 ?= aarch64-linux-gnu-gcc
|
||||
@@ -114,7 +121,7 @@ $(BINDIR)/mkcapsule: $(MKCAPSULE_SRCS)
|
||||
@mkdir -p $(BINDIR)
|
||||
cc -std=c99 -Wall -Wextra -O2 -I$(ROOT)/v3/include -I$(ROOT)/kernel/include -I$(ROOT)/tools -o $@ $(MKCAPSULE_SRCS)
|
||||
|
||||
$(CAPSULE_DIR_C): $(BINDIR)/mkcapsule $(CAPSULE_FILES)
|
||||
$(CAPSULE_DIR_C): $(BINDIR)/mkcapsule $(CAPSULE_FILES) $(BINDIR)/v4_image_64.c
|
||||
$(BINDIR)/mkcapsule $(SIGN_KEY_ARGS) $(ROOT)/capsules $@
|
||||
|
||||
define HOSTED_RULE
|
||||
|
||||
+6
-5
@@ -29,7 +29,7 @@ under ASan and UBSan. There is no POST and no K measurement yet.
|
||||
| Part | Where | What it is |
|
||||
|---|---|---|
|
||||
| Engine | `v4/src` | The golden model of the 32-opcode node |
|
||||
| Nucleus | `v4/capsule/*.v4`, built by `v4/tools/mkimage.c` | The assembled words, as a memory image linked into the binary |
|
||||
| Nucleus | `v4/capsule/*.v4`, built by `v4/tools/mkimage.c` into `capsules/v4/nucleus-64.f18` | The assembled words, as a capsule of F18 code: sent through its port to a node born empty |
|
||||
| Capsules | `capsules/v4/*.4th`, baked by `tools/mkcapsule.c` | FORTH source, loaded when the system comes up |
|
||||
| Boot | `v4/system/boot.c` | Starts the nucleus, checks and loads each capsule, prints the parity lines, gives the prompt |
|
||||
|
||||
@@ -59,7 +59,7 @@ State, 2026-10-05, after `ENGINE.md` step 1: all six builds start the nucleus,
|
||||
load `v4:forth79.4th`, pass POST and reach `ok>`, with the same lines:
|
||||
|
||||
```
|
||||
PARITY:V4_NUCLEUS words=298 image_hash=0xd982cf093aadb8b9
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=... capsule_hash=0xbcc90cc5a638a299 words=298
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=... dict_hash=...
|
||||
PARITY:V4_POST tests=550 pass=550 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=... dict_hash=...
|
||||
@@ -68,8 +68,8 @@ POST: PASSED
|
||||
ok>
|
||||
```
|
||||
|
||||
Bare-metal logs: `logs/20261006-074907/amd64/`, `logs/20261006-075150/aarch64/`,
|
||||
`logs/20261006-075532/riscv64/`. On each, seven lines were then typed at the
|
||||
Bare-metal logs: `logs/20261006-102421/amd64/`, `logs/20261006-102706/aarch64/`,
|
||||
`logs/20261006-103048/riscv64/`. On each, seven lines were then typed at the
|
||||
prompt through the serial port — a definition, its use with the capsule's
|
||||
`U*`, `COLD`, the capsule word again, a `FORGET` of it (refused), a kernel
|
||||
word no one serves, and `BYE` — and each was answered as the hosted binary
|
||||
@@ -105,7 +105,8 @@ everything about word records, the fleet and identity is its alone.
|
||||
making v4 equal v3 on bare metal. Its first part is built and tested in
|
||||
the engine: a node has `V4_PORTS` ports; a read from one blocks until the
|
||||
neighbour writes and a write until the neighbour reads; a node is born
|
||||
empty with `P` at its ports and executes what a neighbour sends it; and
|
||||
empty with `P` at its ports and executes what a neighbour sends it, which
|
||||
is how each product's node now gets its nucleus, as a capsule; and
|
||||
the fabric (`v4/src/fabric.c`) is the nodes there are and the table of how
|
||||
their ports are wired, both of which change while they run. The products
|
||||
below are still one node each; they become the five nodes at `MESH.md`
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* (kernel/src/v4/sk_v4.c) both call v4_boot_run and differ only in where
|
||||
* the text goes. So the two start the same way and print the same lines:
|
||||
*
|
||||
* PARITY:V4_NUCLEUS words=N image_hash=0x...
|
||||
* PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x... capsule_hash=0x... words=N
|
||||
* PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x... capsule_hash=0x... dict_hash=0x...
|
||||
* PARITY:V4_POST tests=N pass=N fail=N (printed by the POST capsule)
|
||||
* PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0x... capsule_hash=0x... dict_hash=0x...
|
||||
@@ -59,8 +59,8 @@ typedef struct {
|
||||
#define V4_BOOT_KEY_NONE (-1) /* not yet: it is asked again */
|
||||
#define V4_BOOT_KEY_END (-2) /* and there never will be */
|
||||
|
||||
/* Start the node from the nucleus image, with `disk` as its block storage
|
||||
* (see v4_image_boot), and load the capsules. Returns 1 with the node idle,
|
||||
/* 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,
|
||||
* 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);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/* capsule.h -- a capsule of F18 code: what a neighbour writes to a node's
|
||||
* port to put a programme into it and start it. docs/v4.0.0/MESH.md
|
||||
* section 5.
|
||||
*
|
||||
* A node newly born has nothing in it and executes what arrives at its port
|
||||
* (node.h). So a capsule of F18 code is not a format a node has to
|
||||
* understand: it is the words themselves, in the order they are sent. For
|
||||
* each stretch of memory that is not zero,
|
||||
*
|
||||
* @p a! @p push the node executes this from its port,
|
||||
* address takes these two as its literals,
|
||||
* count - 1
|
||||
* @p !+ unext and then this: each word that follows is fetched
|
||||
* the words ... from the port and stored
|
||||
*
|
||||
* and at the end
|
||||
*
|
||||
* jump start
|
||||
*
|
||||
* This is the F18's own way of loading a node through a port. Nothing here
|
||||
* knows what the programme is: StarForth's nucleus is one such capsule
|
||||
* (v4/tools/mkimage.c), and anything written for the node is another.
|
||||
*/
|
||||
#ifndef V4_CAPSULE_H
|
||||
#define V4_CAPSULE_H
|
||||
|
||||
#include "v4/node.h"
|
||||
|
||||
/* Write the capsule that makes a node's memory what `n`'s is, and starts it
|
||||
* at `start`: `out` is called with each word in turn. Words of `n` that
|
||||
* are zero are not sent -- a node is born with its memory zero. Returns
|
||||
* how many words were written. */
|
||||
unsigned v4_capsule_write(const v4_node *n, v4_cell start,
|
||||
void (*out)(void *self, v4_cell word), void *self);
|
||||
|
||||
#endif /* V4_CAPSULE_H */
|
||||
+21
-25
@@ -1,31 +1,24 @@
|
||||
/* image.h -- a capsule image: the host node's memory with the vocabulary in
|
||||
* it, and what a loader needs to know to start it.
|
||||
/* image.h -- the StarForth nucleus, as its host knows it: where things are
|
||||
* in a node that has taken it in, and how a line is handed to that node.
|
||||
*
|
||||
* The compiler capsule is text (capsule/ *.v4) and FORTH source (capsule/
|
||||
* *.fth). tools/mkimage.c assembles the one and has a node compile the
|
||||
* other, on the build machine, and writes what the node's memory then holds
|
||||
* as a C file. A system that is to run the vocabulary -- the hosted binary,
|
||||
* the bare-metal kernel -- links that file and calls v4_image_boot: it needs
|
||||
* no assembler, no files and no C library.
|
||||
* The nucleus is text (capsule/ *.v4). tools/mkimage.c assembles it on the
|
||||
* build machine and writes two things:
|
||||
*
|
||||
* An image is for one cell width, one node size and one pair of stack sizes;
|
||||
* v4_image_boot refuses an image the engine was not built for.
|
||||
* - the nucleus as a capsule of F18 code (capsule.h), which is put into
|
||||
* an empty node through its port. It is a capsule like any other: in
|
||||
* the capsule directory, with a name, a hash and a signature
|
||||
* (capsules/v4/nucleus-<width>.f18). docs/v4.0.0/MESH.md section 5.
|
||||
* - this description of it, a C file: the addresses a host needs.
|
||||
*
|
||||
* A description is for one cell width, one node size and one pair of stack
|
||||
* sizes; v4_image_born refuses one the engine was not built for.
|
||||
*/
|
||||
#ifndef V4_IMAGE_H
|
||||
#define V4_IMAGE_H
|
||||
|
||||
#include "v4/exec.h"
|
||||
|
||||
/* one word of memory that is not zero */
|
||||
typedef struct {
|
||||
v4_cell addr;
|
||||
v4_cell value;
|
||||
} v4_image_cell;
|
||||
|
||||
typedef struct {
|
||||
const v4_image_cell *cells; /* every non-zero word of memory, in address order */
|
||||
unsigned count;
|
||||
|
||||
/* what the image was built for */
|
||||
unsigned cell_bits, node_words, data_ring, ret_ring;
|
||||
|
||||
@@ -66,7 +59,8 @@ typedef struct {
|
||||
v4_cell boot_cells, fence;
|
||||
} v4_image;
|
||||
|
||||
/* The image built from v4/capsule (the generated file defines it). */
|
||||
/* The description of the nucleus built from v4/capsule (the generated file
|
||||
* defines it). */
|
||||
extern const v4_image v4_capsule_image;
|
||||
|
||||
/* The system is complete: what is in the dictionary now is what COLD comes
|
||||
@@ -78,11 +72,13 @@ void v4_image_seal(v4_node *n, const v4_image *im);
|
||||
#define V4_LINE_COMPLETED 1 /* the host says " ok" */
|
||||
#define V4_LINE_ERROR 2 /* the message is printed; the host says " ERROR" */
|
||||
|
||||
/* Reset `n`, load the image into it, attach its registers -- with `disk`,
|
||||
* `blocks` blocks of 1024 bytes, as its block storage, or none if `disk` is
|
||||
* 0 -- and leave it idle, waiting to be handed a line. Returns 1, or 0 if
|
||||
* the image is not for this build of the engine (nothing is then done). */
|
||||
int v4_image_boot(v4_node *n, v4_exec_state *es, v4_heat *h, 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
|
||||
* 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);
|
||||
|
||||
/* Hand the node `len` characters of text to interpret. Returns 1, or 0 if
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/* capsule.c -- a capsule of F18 code. See capsule.h.
|
||||
*
|
||||
* Nothing here uses the C library.
|
||||
*/
|
||||
#include "v4/capsule.h"
|
||||
#include "v4/iword.h"
|
||||
#include "v4/opcode.h"
|
||||
|
||||
/* A stretch of zeros shorter than this, between words that are not zero, is
|
||||
* sent with them: starting a new stretch costs four words. */
|
||||
#define GAP 4u
|
||||
|
||||
static v4_cell words_of(unsigned a, unsigned b, unsigned c, unsigned d)
|
||||
{
|
||||
unsigned op[V4_SLOT_COUNT];
|
||||
unsigned k;
|
||||
op[0] = a; op[1] = b; op[2] = c; op[3] = d;
|
||||
for (k = 4; k < V4_SLOT_COUNT; k++) op[k] = V4_OP_NOP;
|
||||
return (v4_cell)v4_iword_assemble(op, 0);
|
||||
}
|
||||
|
||||
/* `jump addr`, the jump in the first slot so that its address is as wide as
|
||||
* an address can be */
|
||||
static v4_cell jump_to(v4_cell addr)
|
||||
{
|
||||
v4_ucell mask = (v4_ucell)v4_iword_slot_mask(0);
|
||||
v4_ucell word = (v4_ucell)words_of(V4_OP_JUMP, V4_OP_NOP, V4_OP_NOP, V4_OP_NOP);
|
||||
return (v4_cell)((word & ~mask) | ((v4_ucell)addr & mask));
|
||||
}
|
||||
|
||||
unsigned v4_capsule_write(const v4_node *n, v4_cell start,
|
||||
void (*out)(void *self, v4_cell word), void *self)
|
||||
{
|
||||
unsigned k = 0, written = 0;
|
||||
|
||||
while (k < V4_NODE_WORDS) {
|
||||
unsigned first, last, zeros, i;
|
||||
|
||||
if (n->mem[k] == 0) { k++; continue; }
|
||||
first = last = k;
|
||||
zeros = 0;
|
||||
for (i = k + 1; i < V4_NODE_WORDS && zeros < GAP; i++) {
|
||||
if (n->mem[i] != 0) { last = i; zeros = 0; } else zeros++;
|
||||
}
|
||||
out(self, words_of(V4_OP_FETCH_P, V4_OP_BANG_A, V4_OP_FETCH_P, V4_OP_PUSH));
|
||||
out(self, (v4_cell)first);
|
||||
out(self, (v4_cell)(last - first));
|
||||
out(self, words_of(V4_OP_FETCH_P, V4_OP_STORE_INC, V4_OP_UNEXT, V4_OP_NOP));
|
||||
for (i = first; i <= last; i++) out(self, n->mem[i]);
|
||||
written += 4u + (last - first + 1u);
|
||||
k = last + 1;
|
||||
}
|
||||
out(self, jump_to(start));
|
||||
return written + 1u;
|
||||
}
|
||||
+3
-11
@@ -1,34 +1,26 @@
|
||||
/* image.c -- start a node from a capsule image. See image.h.
|
||||
/* image.c -- the StarForth nucleus, as its host knows it. See image.h.
|
||||
*
|
||||
* Nothing here uses the C library: the bare-metal kernel links this file.
|
||||
*/
|
||||
#include "v4/image.h"
|
||||
|
||||
int v4_image_boot(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im,
|
||||
int v4_image_born(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im,
|
||||
unsigned char *disk, unsigned blocks)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
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)
|
||||
return 0;
|
||||
|
||||
v4_node_reset(n);
|
||||
v4_node_born(n, im->port);
|
||||
v4_exec_reset(es);
|
||||
v4_heat_reset(h);
|
||||
for (i = 0; i < im->count; i++)
|
||||
if (v4_node_addr_ok(im->cells[i].addr)) n->mem[im->cells[i].addr] = im->cells[i].value;
|
||||
|
||||
v4_node_console_attach(n, im->console_tx);
|
||||
v4_node_console_input_attach(n, im->console_rx, im->console_status);
|
||||
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);
|
||||
v4_node_port_attach(n, im->port);
|
||||
if (disk && blocks) v4_node_storage_attach(n, im->storage_reg, disk, blocks);
|
||||
|
||||
n->mem[im->line_status] = V4_LINE_COMPLETED;
|
||||
n->p = im->idle;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+70
-16
@@ -80,17 +80,6 @@ uint64_t v4_boot_dict_hash(const v4_node *n, const v4_image *im)
|
||||
return hash_cell(h, n->mem[im->latest]);
|
||||
}
|
||||
|
||||
static uint64_t image_hash(const v4_image *im)
|
||||
{
|
||||
uint64_t h = FNV_OFFSET;
|
||||
unsigned i;
|
||||
for (i = 0; i < im->count; i++) {
|
||||
h = hash_cell(h, im->cells[i].addr);
|
||||
h = hash_cell(h, im->cells[i].value);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
/* how many words FORTH holds: the list from LATEST, each entry's link in the
|
||||
* cell before its code */
|
||||
static uint32_t word_count(const v4_node *n, const v4_image *im)
|
||||
@@ -258,6 +247,72 @@ static int load_capsule(const v4_boot *b, const char *name)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ---- the nucleus ----------------------------------------------------------- */
|
||||
|
||||
/* The nucleus is a capsule of F18 code (v4/include/v4/capsule.h): the words
|
||||
* themselves, to be written to the port of a node that has nothing in it.
|
||||
* The boot is that node's neighbour. It finds the capsule, checks its hash
|
||||
* and its signature as it does any capsule's, and gives the node a word
|
||||
* each time it reads its port, until the node has taken them all and is
|
||||
* where the nucleus waits to be handed a line. */
|
||||
#define NUCLEUS_NAME_(bits) "v4:nucleus-" #bits ".f18"
|
||||
#define NUCLEUS_NAME__(bits) NUCLEUS_NAME_(bits)
|
||||
#define NUCLEUS_NAME NUCLEUS_NAME__(V4_CELL_BITS)
|
||||
#define CELL_BYTES (V4_CELL_BITS / 8)
|
||||
|
||||
static int load_nucleus(const v4_boot *b)
|
||||
{
|
||||
const CapsuleDirHeader *dir = capsule_get_directory();
|
||||
const CapsuleDesc *descs = capsule_get_descriptors();
|
||||
const CapsuleNameEntry *names = capsule_get_names();
|
||||
const uint8_t *arena = capsule_get_arena();
|
||||
const CapsuleDesc *cap;
|
||||
const uint8_t *p;
|
||||
CapsuleValidateResult vr;
|
||||
CapsuleSigResult sr;
|
||||
v4_node *n = b->n;
|
||||
uint64_t words, at = 0, steps = 0;
|
||||
|
||||
cap = capsule_find_by_name(dir, descs, names, NUCLEUS_NAME);
|
||||
if (!cap) { say(b, "V4: capsule " NUCLEUS_NAME " is not in this binary\n"); return 0; }
|
||||
vr = capsule_validate(cap, arena, dir->arena_size, 1);
|
||||
if (vr != CAPSULE_VALID) {
|
||||
say(b, "V4: capsule " NUCLEUS_NAME ": "); say(b, capsule_validate_result_str(vr)); say(b, "\n");
|
||||
return 0;
|
||||
}
|
||||
sr = capsule_verify_signature(descs, names, capsule_get_signatures(), arena, dir->desc_count, (int)(cap - descs));
|
||||
if (sr != CAPSULE_SIG_OK) {
|
||||
say(b, "V4: capsule " NUCLEUS_NAME " signature: "); say(b, capsule_sig_result_str(sr)); say(b, "\n");
|
||||
if (sr == CAPSULE_SIG_INVALID) return 0;
|
||||
}
|
||||
p = capsule_get_payload(cap, arena);
|
||||
if (!p || cap->length % CELL_BYTES != 0) { say(b, "V4: capsule " NUCLEUS_NAME " is not whole words\n"); return 0; }
|
||||
words = cap->length / CELL_BYTES;
|
||||
|
||||
while (at < words || !v4_line_done(n, b->im)) {
|
||||
(void)v4_exec_step_word(n, b->es, b->h);
|
||||
if (n->stopped || n->asking || ++steps > STEP_LIMIT) {
|
||||
say(b, "V4: the node did not take the nucleus in\n");
|
||||
return 0;
|
||||
}
|
||||
if (n->reading && !n->given) {
|
||||
v4_ucell word = 0;
|
||||
unsigned i;
|
||||
if (at >= words) { say(b, "V4: the node wants more than the nucleus holds\n"); return 0; }
|
||||
for (i = 0; i < CELL_BYTES; i++) word |= (v4_ucell)p[at * CELL_BYTES + i] << (8 * i);
|
||||
v4_node_port_give(n, 0, (v4_cell)word);
|
||||
at++;
|
||||
}
|
||||
}
|
||||
|
||||
say(b, "PARITY:V4_NUCLEUS name=" NUCLEUS_NAME);
|
||||
say(b, " capsule_id="); say_hex(b, cap->capsule_id);
|
||||
say(b, " capsule_hash="); say_hex(b, cap->content_hash);
|
||||
say(b, " words="); say_dec(b, word_count(n, b->im));
|
||||
say(b, "\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ---- the whole boot ------------------------------------------------------ */
|
||||
|
||||
int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks)
|
||||
@@ -265,13 +320,12 @@ int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks)
|
||||
unsigned i;
|
||||
|
||||
post_len = 0; post_seen = 0; post_clean = 0;
|
||||
if (!v4_image_boot(b->n, b->es, b->h, b->im, disk, blocks)) {
|
||||
say(b, "V4: the nucleus image is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n");
|
||||
/* 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)) {
|
||||
say(b, "V4: the nucleus is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
say(b, "PARITY:V4_NUCLEUS words="); say_dec(b, word_count(b->n, b->im));
|
||||
say(b, " image_hash="); say_hex(b, image_hash(b->im));
|
||||
say(b, "\n");
|
||||
if (!load_nucleus(b)) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; }
|
||||
|
||||
/* the kernel's words, before any capsule that might use one */
|
||||
for (i = 0; i < b->word_count; i++) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* At the level of opcodes: no word of any vocabulary is defined here.
|
||||
*/
|
||||
#include "v4/asm.h"
|
||||
#include "v4/capsule.h"
|
||||
#include "v4/fabric.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -69,6 +70,13 @@ static int stream_take(void *self, v4_cell value)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* for v4_capsule_write: each word of the capsule, into the stream's own list */
|
||||
static void collect(void *self, v4_cell word)
|
||||
{
|
||||
stream *s = (stream *)self;
|
||||
((v4_cell *)s->words)[s->count++] = word;
|
||||
}
|
||||
|
||||
/* What a neighbour sends an empty node to put `count` words at `addr` and
|
||||
* start there (MESH.md section 5): for the F18, and for this node, code a
|
||||
* node executes straight from its port. */
|
||||
@@ -187,6 +195,42 @@ int main(void)
|
||||
CHECK(ND(b)->n.last_from == 0, "it was listening to any port, and knows which it heard from: %u", ND(b)->n.last_from);
|
||||
}
|
||||
|
||||
/* ---- a capsule of F18 code: any node's memory, sent to an empty node (capsule.h) ---- */
|
||||
{
|
||||
static v4_node src;
|
||||
static v4_cell sent[400];
|
||||
static stream s;
|
||||
static v4_device dev;
|
||||
unsigned k;
|
||||
|
||||
v4_node_reset(&src);
|
||||
v4_asm_begin(&as, &src, 40); /* a programme, not at the start of memory */
|
||||
LIT(123); LIT(OUT); O(BANG_A); O(STORE_A);
|
||||
wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as), "a programme for the capsule");
|
||||
src.mem[3] = 5; /* and words here and there: the first of memory, */
|
||||
src.mem[200] = -1; src.mem[202] = 7; /* two with one zero between them, */
|
||||
src.mem[300] = 9; src.mem[310] = 11; /* two with many, */
|
||||
src.mem[PB - 1] = 13; /* and the last before the ports */
|
||||
|
||||
memset(&s, 0, sizeof s);
|
||||
s.words = sent;
|
||||
dev.give = stream_give; dev.take = stream_take; dev.self = &s;
|
||||
s.count = 0;
|
||||
s.count = v4_capsule_write(&src, 40, collect, &s);
|
||||
CHECK(s.count > 0 && s.count < 400, "the capsule is %u words for a memory of %u", s.count, (unsigned)V4_NODE_WORDS);
|
||||
s.at = 0;
|
||||
|
||||
v4_fabric_init(&f, places, 4);
|
||||
b = (unsigned)v4_fabric_add(&f, &pool[0], PB);
|
||||
CHECK(v4_fabric_wire_device(&f, b, 5, &dev), "it is sent to a node newly born");
|
||||
steps = settle(5000);
|
||||
CHECK(s.at == s.count, "which takes all of it: %u of %u", s.at, s.count);
|
||||
for (k = 0; k < V4_NODE_WORDS && ND(b)->n.mem[k] == (k == (unsigned)OUT ? 123 : src.mem[k]); k++) { }
|
||||
CHECK(k == V4_NODE_WORDS, "its memory is then the same, word for word (first difference at %u)", k);
|
||||
CHECK(ND(b)->n.mem[OUT] == 123 && ND(b)->n.reading, "and it was started where the capsule says, and ran");
|
||||
}
|
||||
|
||||
/* ---- a word is passed on by a node in between ---- */
|
||||
v4_fabric_init(&f, places, 4);
|
||||
a = loaded(0);
|
||||
|
||||
+27
-14
@@ -1,19 +1,21 @@
|
||||
/* mkimage.c -- build the capsule image (include/v4/image.h).
|
||||
*
|
||||
* mkimage OUTPUT.c
|
||||
* mkimage DESCRIPTION.c CAPSULE.f18
|
||||
*
|
||||
* Runs on the build machine. It assembles capsule/ *.v4 -- the nucleus,
|
||||
* docs/v4.0.0/NUCLEUS.md -- onto a host node with the memory map of
|
||||
* tests/host_map.h. It runs nothing: it writes every
|
||||
* word of the node's memory that is not zero, and the addresses a loader
|
||||
* needs, as a C file. No FORTH source is compiled here: what is not in the
|
||||
* nucleus is loaded from capsules when the system boots (v4/system/boot.c).
|
||||
* tests/host_map.h. It runs nothing. It writes the nucleus as a capsule
|
||||
* of F18 code (v4/include/v4/capsule.h) -- what is sent to an empty node's
|
||||
* port to make it a StarForth node -- and the addresses a host needs, as a
|
||||
* C file (v4/include/v4/image.h). No FORTH source is compiled here: what
|
||||
* is not in the nucleus is loaded from capsules when the system boots.
|
||||
*
|
||||
* The image is the same on every machine it is built on: nothing in it
|
||||
* depends on the build machine but the cell width this tool was compiled
|
||||
* for.
|
||||
*/
|
||||
#include "v4/image.h"
|
||||
#include "v4/capsule.h"
|
||||
#include "v4/text.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -31,6 +33,14 @@ static void die(const char *what, const char *detail)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* one word of the capsule, to the file */
|
||||
static void put_word(void *self, v4_cell word)
|
||||
{
|
||||
v4_ucell u = (v4_ucell)word;
|
||||
unsigned i;
|
||||
for (i = 0; i < V4_CELL_BITS / 8; i++) fputc((int)((u >> (8 * i)) & 0xFFu), (FILE *)self);
|
||||
}
|
||||
|
||||
/* every entry from xt back: no access control fields set */
|
||||
static void clear_acl(v4_cell xt)
|
||||
{
|
||||
@@ -45,7 +55,7 @@ int main(int argc, char **argv)
|
||||
v4_cell k, line, idle, faults, voc;
|
||||
unsigned count = 0;
|
||||
|
||||
if (argc != 2) die("usage: mkimage OUTPUT.c", NULL);
|
||||
if (argc != 3) die("usage: mkimage DESCRIPTION.c CAPSULE.f18", NULL);
|
||||
|
||||
if (!host_load(&tx, &n, files, (unsigned)(sizeof files / sizeof files[0]))) die("the capsule does not assemble", NULL);
|
||||
if (!v4_text_finish(&tx)) die("not everything is defined", v4_text_error(&tx));
|
||||
@@ -89,16 +99,19 @@ int main(int argc, char **argv)
|
||||
for (voc = n.mem[VOC_LINK]; voc != 0; voc = n.mem[voc + 1]) clear_acl(n.mem[voc]);
|
||||
if (!v4_node_guards_intact(&n)) die("the node's guards are damaged", NULL);
|
||||
|
||||
n.mem[LINE_STATUS] = V4_LINE_COMPLETED; /* idle, with nothing to report */
|
||||
|
||||
/* the nucleus as a capsule of F18 code: the words a neighbour writes to
|
||||
* an empty node's port, each in V4_CELL_BITS / 8 bytes, low byte first */
|
||||
out = fopen(argv[2], "wb");
|
||||
if (!out) die("cannot write", argv[2]);
|
||||
count = v4_capsule_write(&n, idle, put_word, out);
|
||||
if (fclose(out) != 0) die("cannot write", argv[2]);
|
||||
|
||||
out = fopen(argv[1], "w");
|
||||
if (!out) die("cannot write", argv[1]);
|
||||
fprintf(out, "/* Generated by v4/tools/mkimage.c from v4/capsule -- do not edit. */\n#include \"v4/image.h\"\n\n");
|
||||
fprintf(out, "static const v4_image_cell cells[] = {\n");
|
||||
for (k = 0; k < (v4_cell)V4_NODE_WORDS; k++)
|
||||
if (n.mem[k] != 0) {
|
||||
fprintf(out, " { %ld, (v4_cell)0x%llxULL },\n", (long)k, (unsigned long long)(v4_ucell)n.mem[k]);
|
||||
count++;
|
||||
}
|
||||
fprintf(out, "};\n\nconst v4_image v4_capsule_image = {\n cells, %uu,\n %uu, %uu, %uu, %uu,\n", count,
|
||||
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, %ld, %ld,\n %ld, %uu, %ld,\n %ld, %ld,\n %ld, %ld,\n", (long)line, (long)idle, (long)LINE_STATUS,
|
||||
(long)TIB, 1025u, (long)SPAN, (long)faults, (long)PORT, (long)w_key, (long)w_key_end);
|
||||
@@ -106,6 +119,6 @@ int main(int argc, char **argv)
|
||||
(long)DSTACK_REG, (long)RSTACK_REG, (long)NODE_ERROR, (long)STORAGE_REG, (long)DP, (long)LATEST,
|
||||
(long)BOOT_CELLS, (long)FENCE);
|
||||
if (fclose(out) != 0) die("cannot write", argv[1]);
|
||||
fprintf(stderr, "mkimage: %u words of memory, dictionary to word %ld, %d-bit cells -> %s\n", count, (long)((n.mem[DP] + 3) / 4), V4_CELL_BITS, argv[1]);
|
||||
fprintf(stderr, "mkimage: a capsule of %u words, dictionary to word %ld, %d-bit cells -> %s, %s\n", count, (long)((n.mem[DP] + 3) / 4), V4_CELL_BITS, argv[2], argv[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user