feat(rumpk): implement Invariant Shield and Blink Recovery

- Implement Design by Contract in HAL and Kernel (Phase 2 Task 1)
- Add invariant checks to Sovereign Channels (pointer validation, bounds)
- Create invariant.nim for secure Logic-to-HAL transitions
- Codify Silence Doctrine in DOCTRINE.md and SPEC files
- Finalize Blink Recovery confirmation via Saboteur test
- Update SPEC-008, SPEC-009, SPEC-010, SPEC-011 with architectural refinements
- Sync Website vision with new technical milestones
This commit is contained in:
Markus Maiwald 2025-12-30 21:35:00 +01:00
parent 46e7be6837
commit 061a2ff56b
14 changed files with 851 additions and 235 deletions

185
build.sh
View File

@ -71,6 +71,28 @@ zig build-obj \
mv stubs.o "$BUILD_DIR/stubs.o" mv stubs.o "$BUILD_DIR/stubs.o"
echo "$BUILD_DIR/stubs.o" echo "$BUILD_DIR/stubs.o"
zig build-obj \
-target $ZIG_TARGET \
$ZIG_OBJ_FLAGS \
-O ReleaseFast \
"$RUMPK_DIR/hal/channel.zig" \
--name channel
mv channel.o "$BUILD_DIR/channel.o"
echo "$BUILD_DIR/channel.o"
# Compile NexShell NPL (Immune System Voice)
echo "[1.1/8] Compiling NexShell NPL..."
zig build-obj \
-target $ZIG_TARGET \
$ZIG_OBJ_FLAGS \
-O ReleaseFast \
"$RUMPK_DIR/src/npl/system/nexshell.zig" \
--name nexshell
mv nexshell.o "$BUILD_DIR/nexshell.o"
echo "$BUILD_DIR/nexshell.o"
# ========================================================= # =========================================================
# Step 2: Compile context switch assembly # Step 2: Compile context switch assembly
# ========================================================= # =========================================================
@ -100,52 +122,52 @@ echo " → $BUILD_DIR/monocypher.o"
# ========================================================= # =========================================================
# Step 2.2: Compile LwIP (Kernel Stack) # Step 2.2: Compile LwIP (Kernel Stack)
# ========================================================= # =========================================================
echo "[2.2/8] Compiling LwIP (Kernel)..." # echo "[2.2/8] Compiling LwIP (Kernel)..."
LWIP_CORE_FILES=( # LWIP_CORE_FILES=(
"src/core/init.c" # "src/core/init.c"
"src/core/def.c" # "src/core/def.c"
"src/core/dns.c" # "src/core/dns.c"
"src/core/inet_chksum.c" # "src/core/inet_chksum.c"
"src/core/ip.c" # "src/core/ip.c"
"src/core/mem.c" # "src/core/mem.c"
"src/core/memp.c" # "src/core/memp.c"
"src/core/netif.c" # "src/core/netif.c"
"src/core/pbuf.c" # "src/core/pbuf.c"
"src/core/raw.c" # "src/core/raw.c"
"src/core/stats.c" # "src/core/stats.c"
"src/core/sys.c" # "src/core/sys.c"
"src/core/tcp.c" # "src/core/tcp.c"
"src/core/tcp_in.c" # "src/core/tcp_in.c"
"src/core/tcp_out.c" # "src/core/tcp_out.c"
"src/core/timeouts.c" # "src/core/timeouts.c"
"src/core/udp.c" # "src/core/udp.c"
"src/core/ipv4/autoip.c" # "src/core/ipv4/autoip.c"
"src/core/ipv4/dhcp.c" # "src/core/ipv4/dhcp.c"
"src/core/ipv4/etharp.c" # "src/core/ipv4/etharp.c"
"src/core/ipv4/icmp.c" # "src/core/ipv4/icmp.c"
"src/core/ipv4/igmp.c" # "src/core/ipv4/igmp.c"
"src/core/ipv4/ip4.c" # "src/core/ipv4/ip4.c"
"src/core/ipv4/ip4_addr.c" # "src/core/ipv4/ip4_addr.c"
"src/core/ipv4/ip4_frag.c" # "src/core/ipv4/ip4_frag.c"
"src/netif/ethernet.c" # "src/netif/ethernet.c"
) # )
#
for cfile in "${LWIP_CORE_FILES[@]}"; do # for cfile in "${LWIP_CORE_FILES[@]}"; do
cfile_path="$RUMPK_DIR/vendor/lwip/$cfile" # cfile_path="$RUMPK_DIR/vendor/lwip/$cfile"
objname=$(basename "$cfile" .c) # objname=$(basename "$cfile" .c)
zig cc \ # zig cc \
-target $ZIG_TARGET \ # -target $ZIG_TARGET \
$ARCH_FLAGS \ # $ARCH_FLAGS \
-ffreestanding \ # -ffreestanding \
-fno-stack-protector \ # -fno-stack-protector \
-fno-builtin \ # -fno-builtin \
-O2 \ # -O2 \
-I"$RUMPK_DIR/core/include" \ # -I"$RUMPK_DIR/core/include" \
-I"$RUMPK_DIR/core/net" \ # -I"$RUMPK_DIR/core/net" \
-I"$RUMPK_DIR/vendor/lwip/src/include" \ # -I"$RUMPK_DIR/vendor/lwip/src/include" \
-c "$cfile_path" \ # -c "$cfile_path" \
-o "$BUILD_DIR/lwip_kernel_$objname.o" # -o "$BUILD_DIR/lwip_kernel_$objname.o"
done # done
# ========================================================= # =========================================================
# Step 2.3: Compile LwIP (Membrane Stack) # Step 2.3: Compile LwIP (Membrane Stack)
@ -169,7 +191,23 @@ for cfile in "${LWIP_CORE_FILES[@]}"; do
-o "$BUILD_DIR/lwip_membrane_$objname.o" -o "$BUILD_DIR/lwip_membrane_$objname.o"
done done
LWIP_MEMBRANE_OBJS=$(ls $BUILD_DIR/lwip_membrane_*.o) LWIP_MEMBRANE_OBJS=$(ls $BUILD_DIR/lwip_membrane_*.o)
echo " → LwIP objects compiled."
# Compile sys_arch.c (LwIP Platform Abstraction)
echo "[2.4/8] Compiling LwIP Platform Layer (sys_arch.c)..."
# zig cc \
# -target $ZIG_TARGET \
# $ARCH_FLAGS \
# -ffreestanding \
# -fno-stack-protector \
# -fno-builtin \
# -O2 \
# -I"$RUMPK_DIR/core/include" \
# -I"$RUMPK_DIR/core/net" \
# -I"$RUMPK_DIR/vendor/lwip/src/include" \
# -c "$RUMPK_DIR/core/net/sys_arch.c" \
# -o "$BUILD_DIR/sys_arch.o"
#
# echo " → LwIP objects compiled."
# ========================================================= # =========================================================
# Step 3: Compile Nim L1 (Kernel + Fibers) # Step 3: Compile Nim L1 (Kernel + Fibers)
@ -265,6 +303,21 @@ zig cc \
-c "$RUMPK_DIR/libs/membrane/clib.c" \ -c "$RUMPK_DIR/libs/membrane/clib.c" \
-o "$BUILD_DIR/clib.o" -o "$BUILD_DIR/clib.o"
# Compile sys_arch.c (Membrane LwIP Platform Layer)
zig cc \
-target $ZIG_TARGET \
$ARCH_FLAGS \
-ffreestanding \
-fno-stack-protector \
-fno-builtin \
-O2 \
-I"$RUMPK_DIR/core/include" \
-I"$RUMPK_DIR/core/net" \
-I"$RUMPK_DIR/libs/membrane/include" \
-I"$RUMPK_DIR/vendor/lwip/src/include" \
-c "$RUMPK_DIR/libs/membrane/sys_arch.c" \
-o "$BUILD_DIR/membrane_sys_arch.o"
nim c \ nim c \
--cpu:$NIM_CPU \ --cpu:$NIM_CPU \
--os:any \ --os:any \
@ -307,19 +360,20 @@ for cfile in "$BUILD_DIR/membrane_nimcache"/*.c; do
done done
# ========================================================= # =========================================================
# Step 5.1: Compile Subject Zero Object (Before Patching) # Step 5.1: Compile Subject Zig Object (Diamond Glass)
# ========================================================= # =========================================================
echo "[5.1/8] Compiling Subject Zero Object..." echo "[5.1/8] Compiling Subject Zig Object..."
zig cc \
SUBJECT_SRC=${SUBJECT_SRC:-"$RUMPK_DIR/apps/subject_zig/main.zig"}
zig build-obj \
-target $ZIG_TARGET \ -target $ZIG_TARGET \
$ARCH_FLAGS \ $ZIG_OBJ_FLAGS \
-ffreestanding \ -O ReleaseSmall \
-fno-stack-protector \ "$SUBJECT_SRC" \
-fno-builtin \ --name subject_zig
-O2 \
-I"$RUMPK_DIR/libs/membrane/include" \ mv subject_zig.o "$BUILD_DIR/subject_zig.o"
-c "$RUMPK_DIR/apps/subject_zero/main.c" \
-o "$BUILD_DIR/subject_zero.o"
# ========================================================= # =========================================================
# Step 5.5: Patch Atomic References & Nuke Cache (GLOBAL) # Step 5.5: Patch Atomic References & Nuke Cache (GLOBAL)
@ -366,6 +420,7 @@ zig ar rc "$BUILD_DIR/libnexus.a" \
$MEMBRANE_NIM_OBJS \ $MEMBRANE_NIM_OBJS \
$LWIP_MEMBRANE_OBJS \ $LWIP_MEMBRANE_OBJS \
"$BUILD_DIR/clib.o" \ "$BUILD_DIR/clib.o" \
"$BUILD_DIR/membrane_sys_arch.o" \
"$BUILD_DIR/switch.o" "$BUILD_DIR/switch.o"
echo "$BUILD_DIR/libnexus.a" echo "$BUILD_DIR/libnexus.a"
@ -377,14 +432,19 @@ echo "[6/8] Linking Subject Zero (Canary)..."
# Note: subject_zero.o and libnexus.a are already compiled and patched. # Note: subject_zero.o and libnexus.a are already compiled and patched.
# =========================================================
# Step 6: Link Subject Zig
# =========================================================
echo "[6/8] Linking Subject Zig..."
zig cc \ zig cc \
-target $ZIG_TARGET \ -target $ZIG_TARGET \
-nostdlib \ -nostdlib \
-rtlib=none \
-T "$RUMPK_DIR/apps/linker_user.ld" \ -T "$RUMPK_DIR/apps/linker_user.ld" \
"$BUILD_DIR/subject_zero.o" \ "$BUILD_DIR/subject_zig.o" \
"$BUILD_DIR/libnexus.a" \ "$BUILD_DIR/libnexus.a" \
-o "$BUILD_DIR/subject.elf" -o "$BUILD_DIR/subject.elf" \
-lgcc
echo "$BUILD_DIR/subject.elf" echo "$BUILD_DIR/subject.elf"
zig objcopy -O binary "$BUILD_DIR/subject.elf" "$BUILD_DIR/subject.bin" zig objcopy -O binary "$BUILD_DIR/subject.elf" "$BUILD_DIR/subject.bin"
@ -446,13 +506,14 @@ $LINKER \
-T "$RUMPK_DIR/hal/arch/$ARCH/linker.ld" \ -T "$RUMPK_DIR/hal/arch/$ARCH/linker.ld" \
"$BUILD_DIR/hal.o" \ "$BUILD_DIR/hal.o" \
"$BUILD_DIR/stubs.o" \ "$BUILD_DIR/stubs.o" \
"$BUILD_DIR/channel.o" \
"$BUILD_DIR/switch.o" \ "$BUILD_DIR/switch.o" \
"$BUILD_DIR/monocypher.o" \ "$BUILD_DIR/monocypher.o" \
"$BUILD_DIR/cstubs.o" \ "$BUILD_DIR/cstubs.o" \
"$BUILD_DIR/overrides.o" \ "$BUILD_DIR/overrides.o" \
"$BUILD_DIR/loader.o" \ "$BUILD_DIR/loader.o" \
"$BUILD_DIR/nexshell.o" \
$NIM_OBJS \ $NIM_OBJS \
$BUILD_DIR/lwip_kernel_*.o \
-o "$BUILD_DIR/rumpk-$ARCH.elf" -o "$BUILD_DIR/rumpk-$ARCH.elf"
echo "$BUILD_DIR/rumpk-$ARCH.elf" echo "$BUILD_DIR/rumpk-$ARCH.elf"

View File

@ -24,7 +24,7 @@ elif defined(arm64) or defined(aarch64):
const ARCH_NAME = "aarch64" const ARCH_NAME = "aarch64"
elif defined(riscv64): elif defined(riscv64):
const CONTEXT_SIZE = 112 const CONTEXT_SIZE = 128
const RET_ADDR_INDEX = 0 # ra at [sp + 0] const RET_ADDR_INDEX = 0 # ra at [sp + 0]
const ARCH_NAME = "riscv64" const ARCH_NAME = "riscv64"
@ -105,11 +105,11 @@ proc fiber_trampoline() {.cdecl, exportc, noreturn.} =
# Fiber Initialization (Arch-Specific) # Fiber Initialization (Arch-Specific)
# ========================================================= # =========================================================
proc init_fiber*(f: Fiber, entry: proc() {.cdecl.}, stack_base: pointer) = proc init_fiber*(f: Fiber, entry: proc() {.cdecl.}, stack_base: pointer, size: int) =
f.state.entry = entry f.state.entry = entry
# Start at top of stack # Start at top of stack (using actual size)
var sp = cast[uint64](stack_base) + STACK_SIZE var sp = cast[uint64](stack_base) + cast[uint64](size)
# 1. Align to 16 bytes (Universal requirement) # 1. Align to 16 bytes (Universal requirement)
sp = sp and not 15'u64 sp = sp and not 15'u64

69
core/include/arch/cc.h Normal file
View File

@ -0,0 +1,69 @@
/*
* Nexus Membrane: Userspace LwIP Configuration
* Provides compiler-specific definitions for NPL networking.
*/
#ifndef LWIP_ARCH_CC_H
#define LWIP_ARCH_CC_H
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define LWIP_NO_INTTYPES_H 1
/* Byte Order */
#ifndef BYTE_ORDER
#define LITTLE_ENDIAN 1234
#define BIG_ENDIAN 4321
#define BYTE_ORDER LITTLE_ENDIAN
#endif
/* Types */
typedef uint8_t u8_t;
typedef int8_t s8_t;
typedef uint16_t u16_t;
typedef int16_t s16_t;
typedef uint32_t u32_t;
typedef int32_t s32_t;
typedef uintptr_t mem_ptr_t;
/* Sys Prot Type (Critical Section) */
typedef u32_t sys_prot_t;
/* Error code */
#define LWIP_ERR_T s8_t
/* Printf formatters */
#define U16_F "u"
#define S16_F "d"
#define X16_F "x"
#define U32_F "u"
#define S32_F "d"
#define X32_F "x"
#define SZT_F "zu"
/* Structure packing */
#if defined(__GNUC__) || defined(__clang__)
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_STRUCT __attribute__((packed))
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_END
#else
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_END
#endif
/* Diagnostic output */
#define LWIP_PLATFORM_DIAG(x) do { printf x; } while(0)
#define LWIP_PLATFORM_ASSERT(x) do { \
printf("[LwIP] Assertion \"%s\" failed at line %d in %s\n", x, __LINE__, __FILE__); \
while(1) asm("wfi"); \
} while(0)
#define LWIP_RAND() ((u32_t)rand())
#endif /* LWIP_ARCH_CC_H */

5
core/include/arch/perf.h Normal file
View File

@ -0,0 +1,5 @@
#ifndef LWIP_ARCH_PERF_H
#define LWIP_ARCH_PERF_H
#define PERF_START
#define PERF_STOP(x)
#endif

View File

@ -0,0 +1,18 @@
/*
* Nexus Rumpk: LwIP System Architecture
* Minimal definitions for critical sections.
*/
#ifndef LWIP_ARCH_SYS_ARCH_H
#define LWIP_ARCH_SYS_ARCH_H
#include "arch/cc.h"
/* Critical section protection type */
typedef u32_t sys_prot_t;
/* Stub out threading primitives for NO_SYS or Cooperative */
#define SYS_MBOX_NULL NULL
#define SYS_SEM_NULL NULL
#endif /* LWIP_ARCH_SYS_ARCH_H */

11
core/include/inttypes.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef _INTTYPES_H
#define _INTTYPES_H
#include <stdint.h>
/* Minimal format specifiers */
#define PRId32 "d"
#define PRIu32 "u"
#define PRIx32 "x"
#endif

38
core/invariant.nim Normal file
View File

@ -0,0 +1,38 @@
# import ion # Included in ion.nim
# Error type for future use
type
FiberPanic* = object of CatchableError
# Forward declarations for utilities defined in kernel.nim (included there)
proc kernel_panic*(msg: cstring) {.importc: "panic", cdecl.}
proc kprintln*(s: cstring) {.importc: "kprintln", cdecl.}
template secure_send*(ring_ptr: pointer, data: uint64) =
## Verifies invariants before pushing to a command ring.
# 1. PRE-CONDITION: Alignment
if (cast[uint](ring_ptr) and 0b11) != 0:
kernel_panic("Invariant Violation: Unaligned Ring Pointer")
# 2. OPERATION: Try to push via HAL
# We cast uint64 back to CmdPacket for the FFI call
let success = hal_cmd_push(cast[uint64](ring_ptr), cast[CmdPacket](data))
# 3. POST-CONDITION: Flow Control Warning
if not success:
kprintln("[Invariant] Warning: Command Ring Full, Drop.")
template secure_push_packet*(ring_ptr: pointer, pkt: IonPacket) =
## Verifies invariants for IonPacket transfers.
if (cast[uint](ring_ptr) and 0b11) != 0:
kernel_panic("Invariant Violation: Unaligned Ring Pointer")
if not hal_channel_push(cast[uint64](ring_ptr), pkt):
kprintln("[Invariant] Warning: Packet Ring Full, Drop.")
template secure_recv_cmd*(ring_ptr: pointer, out_pkt: var CmdPacket): bool =
if (cast[uint](ring_ptr) and 0b11) != 0:
kernel_panic("Invariant Violation: Unaligned Ring Pointer")
hal_cmd_pop(cast[uint64](ring_ptr), addr out_pkt)

64
core/ion.nim Normal file
View File

@ -0,0 +1,64 @@
# Nexus Rumpk: ION Control Plane
# Markus Maiwald (Architect) | Voxis Forge (AI)
import ion/memory
export memory
type
CmdType* = enum
CMD_NONE = 0
CMD_NET_STOP = 1
CMD_NET_START = 2
CMD_DROP_ALL = 3
CmdPacket* = object
kind*: uint32
arg*: uint32
# Binary compatible with hal/channel.zig
HAL_Ring*[T] = object
head*: uint32
tail*: uint32
mask*: uint32
data*: array[256, T]
SovereignChannel*[T] = object
ring*: ptr HAL_Ring[T]
SysTable* = object
magic*: uint32 # 0x4E585553
s_rx*: ptr HAL_Ring[IonPacket] # Kernel -> App
s_tx*: ptr HAL_Ring[IonPacket] # App -> Kernel
s_event*: ptr HAL_Ring[IonPacket] # Telemetry
s_cmd*: ptr HAL_Ring[CmdPacket] # Command Ring (Control Plane)
include invariant
const SYSTABLE_BASE* = 0x83000000'u64
# HAL Imports (Hardened ABI - Handle Based)
proc hal_channel_push*(handle: uint64,
pkt: IonPacket): bool {.importc: "hal_channel_push", cdecl.}
proc hal_channel_pop*(handle: uint64,
out_pkt: ptr IonPacket): bool {.importc: "hal_channel_pop", cdecl.}
proc hal_cmd_push*(handle: uint64,
pkt: CmdPacket): bool {.importc: "hal_cmd_push", cdecl.}
proc hal_cmd_pop*(handle: uint64,
out_pkt: ptr CmdPacket): bool {.importc: "hal_cmd_pop", cdecl.}
proc send*(chan: var SovereignChannel[IonPacket], pkt: IonPacket) =
secure_push_packet(chan.ring, pkt)
proc recv*(chan: var SovereignChannel[IonPacket],
out_pkt: var IonPacket): bool =
if (cast[uint](chan.ring) and 0b11) != 0:
return false # Or panic
return hal_channel_pop(cast[uint64](chan.ring), addr out_pkt)
proc send*(chan: var SovereignChannel[CmdPacket], pkt: CmdPacket) =
secure_send(chan.ring, cast[uint64](pkt))
proc recv*(chan: var SovereignChannel[CmdPacket],
out_pkt: var CmdPacket): bool =
return secure_recv_cmd(chan.ring, out_pkt)

View File

@ -1,191 +1,204 @@
# Rumpk Layer 1: The Logic Core # Rumpk Layer 1: The Logic Core (Autonomous Immune System)
# Markus Maiwald (Architect) | Voxis Forge (AI) # Markus Maiwald (Architect) | Voxis Forge (AI)
{.push stackTrace: off, lineTrace: off.} {.push stackTrace: off, lineTrace: off.}
import fiber import fiber
import ion/memory import ion
import ion/ion_switch
import ring
import net
# HAL Imports from Zig (Layer 0)
var net_paused*: bool = false
var pause_start*: uint64 = 0
# =========================================================
# Fiber Management (Forward Declared)
# =========================================================
var fiber_net: FiberObject
var fiber_nexshell: FiberObject
var fiber_subject: FiberObject
var fiber_watchdog: FiberObject
# POSIX-like exports for Zig NPLs
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.} proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
proc write*(fd: cint, p: pointer, len: csize_t): csize_t {.exportc, cdecl.} =
console_write(p, len)
return len
# Utility for Logic Core
proc kprint*(s: cstring) {.exportc, cdecl.} =
if s != nil:
let length = len(s)
if length > 0:
console_write(s, csize_t(length))
proc kprintln*(s: cstring) {.exportc, cdecl.} =
kprint(s); kprint("\n")
proc rumpk_yield_internal() {.cdecl, exportc.} =
if current_fiber == addr fiber_net:
switch(addr fiber_nexshell)
elif current_fiber == addr fiber_nexshell:
switch(addr fiber_subject)
elif current_fiber == addr fiber_subject:
switch(addr fiber_watchdog)
else:
switch(addr fiber_net)
proc fiber_yield*() {.exportc, cdecl.} =
rumpk_yield_internal()
# Utility moved up
# Channel API (The Valve) - Wrappers for ION
# Channel API is imported from ion.nim
const SYSTABLE_BASE = 0x83000000'u64
# Global Rings (The Pipes - L0 Physics)
var guest_rx_hal: HAL_Ring[IonPacket]
var guest_tx_hal: HAL_Ring[IonPacket]
var guest_event_hal: HAL_Ring[IonPacket]
var guest_cmd_hal: HAL_Ring[CmdPacket]
# Shared Channels (The Valves - L1 Logic)
var chan_rx*: SovereignChannel[IonPacket]
var chan_tx*: SovereignChannel[IonPacket]
var chan_event*: SovereignChannel[IonPacket]
var chan_cmd*: SovereignChannel[CmdPacket]
# HAL/NPL Entry points
proc rumpk_halt() {.importc, cdecl, noreturn.} proc rumpk_halt() {.importc, cdecl, noreturn.}
proc virtio_net_init() {.importc, cdecl.} proc virtio_net_init() {.importc, cdecl.}
proc nexshell_main() {.importc, cdecl.}
proc launch_subject() {.importc, cdecl.}
# Kernel I/O # Hardware Ingress (Zig -> Nim)
proc kprint(s: string) = proc ion_ingress*(id: uint16, len: uint16) {.exportc, cdecl.} =
if s.len > 0: ## Intercept raw hardware packet and push to Sovereign RX Channel
console_write(unsafeAddr s[0], csize_t(s.len)) let data = ion_get_virt(id)
var pkt = IonPacket(data: cast[ptr UncheckedArray[byte]](data), len: len, id: id)
proc kprintln(s: string) = chan_rx.send(pkt)
kprint(s)
kprint("\n")
# Panic Handler # Panic Handler
proc nimPanic(msg: cstring) {.exportc: "panic", cdecl, noreturn.} = proc nimPanic(msg: cstring) {.exportc: "panic", cdecl, noreturn.} =
kprint("\n[PANIC] ") kprint("\n[PANIC] "); kprintln(msg)
if msg != nil:
var i = 0
while msg[i] != '\0':
var buf: array[1, char]
buf[0] = msg[i]
console_write(addr buf[0], 1)
inc i
kprint("\n")
rumpk_halt() rumpk_halt()
# Networking Fiber
var fiber_net: FiberObject
var stack_net: array[8192, uint8]
var fiber_sniff: FiberObject
var stack_sniff: array[4096, uint8]
var fast_ring: RingBuffer[IonPacket, 256]
var sniff_flow: NetFlow
# ========================================================= # =========================================================
# The Reflex (Zero-Copy Headers Swap) # Fiber Entries
# ========================================================= # =========================================================
proc swap_macs(frame: ptr UncheckedArray[byte]) = var stack_net: array[32768, uint8]
# Dst (0..5) <-> Src (6..11) var stack_nexshell: array[32768, uint8]
for i in 0..5: var stack_subject: array[65536, uint8]
let tmp = frame[i] var stack_watchdog: array[4096, uint8]
frame[i] = frame[6+i]
frame[6+i] = tmp
proc swap_ips(frame: ptr UncheckedArray[byte]) =
# IPv4 (Eth 14). Src=12, Dst=16. Total Offset = 14+12=26, 14+16=30
# Swap 4 bytes
for i in 0..3:
let tmp = frame[26+i]
frame[26+i] = frame[30+i]
frame[30+i] = tmp
proc swap_ports(frame: ptr UncheckedArray[byte]) =
# UDP (Eth 14 + IP 20). Src=0, Dst=2. Total Offset = 34, 36
# Swap 2 bytes
for i in 0..1:
let tmp = frame[34+i]
frame[34+i] = frame[36+i]
frame[36+i] = tmp
proc fast_path_consumer() {.cdecl.} =
var aliveMsg = "[ECHO] Fiber Alive! Ready to Bounce.\n"
console_write(addr aliveMsg[0], csize_t(aliveMsg.len))
while true:
var work = false
if not fast_ring.isEmpty:
let (ok, pkt) = fast_ring.pop()
if ok:
work = true
# Zero-Copy In-Place Modification
let frame = cast[ptr UncheckedArray[byte]](pkt.data)
# 1. Swap Headers
swap_macs(frame)
swap_ips(frame)
swap_ports(frame)
# 2. Log
var msg = "[ECHO] Bouncing Packet...\n"
console_write(addr msg[0], csize_t(msg.len))
# 3. Send (Pass Ownership to Driver)
ion_egress(pkt)
# Do NOT free here. Driver frees after TX.
if not work:
switch(addr fiber_net)
proc sniffer_init() =
fast_ring.init()
ion_tx_init() # Initialize the Global TX Ring
sniff_flow.port = 8080
sniff_flow.ring = addr fast_ring
sniff_flow.fType = FLOW_DIRECT
ion_register(8080, addr sniff_flow)
init_fiber(addr fiber_sniff, fast_path_consumer, addr stack_sniff[0])
proc launch_subject() {.importc, cdecl.}
var stack_subject: array[16384, byte] # Matches STACK_SIZE in fiber.nim if 16k is preferred
var fiber_subject: FiberObject
proc subject_fiber_entry() {.cdecl.} = proc subject_fiber_entry() {.cdecl.} =
kprintln("[Membrane] Launching Subject Zero Canary...")
launch_subject() launch_subject()
# Include Watchdog Logic (Access to Kernel Globals)
include watchdog
proc net_fiber_entry() {.cdecl.} = proc net_fiber_entry() {.cdecl.} =
kprintln("[Net] Fiber started. Initializing stack...") kprint("[Net] Fiber 1 Reporting for Duty.")
ion_pool_init() if net_paused: kprintln(" (PAUSED)") else: kprintln("")
virtio_net_init()
net_init() var pkt: IonPacket
sniffer_init() var cmd: CmdPacket
kprintln("[Net] Interface UP (10.0.2.15)")
while true: while true:
net_loop_cycle(addr rumpk_netif) # 1. Process Commands
# Pump the Membrane Stack too if chan_cmd.recv(cmd):
# (In a real scenario, this would be a separate thread or triggered by ION) if cmd.kind == uint32(CMD_NET_STOP):
# pump_membrane_stack() # Import this? kprintln("[Net] STOP received. Suspending IO.")
net_paused = true
pause_start = cpu_ticks()
elif cmd.kind == uint32(CMD_NET_START):
kprintln("[Net] START received. Resuming IO.")
net_paused = false
switch(addr fiber_subject) # 2. Process Data (if not paused)
switch(addr fiber_sniff) if not net_paused:
if chan_tx.recv(pkt):
kprintln("[Net] Packet intercepted. Generating Telemetry...")
var alert = IonPacket(id: 777, len: 42)
chan_event.send(alert)
kprintln("[Net] Event dispatched.")
fiber_yield()
# Sovereign Syscall Table (Fixed Address: 0x801FFF00) # =========================================================
type # kmain: The Orchestrator
SysTable = object # =========================================================
s_yield*: pointer
s_alloc*: pointer
s_free*: pointer
s_tx*: pointer
s_rx*: pointer # Address of the shared RX Ring (Flow)
var guest_rx_ring: RingBuffer[IonPacket, 256]
var guest_flow: NetFlow
proc rumpk_yield_internal() {.cdecl.} =
switch(addr fiber_net)
proc rumpk_alloc_internal(): IonPacket {.cdecl.} =
return ion_alloc()
proc rumpk_free_internal(pkt: IonPacket) {.cdecl.} =
ion_free(pkt)
proc rumpk_tx_internal(pkt: IonPacket): bool {.cdecl.} =
return ion_tx_push(pkt)
proc kmain() {.exportc, cdecl.} = proc kmain() {.exportc, cdecl.} =
# Initialize the Guest Flow kprintln("\n\n")
guest_rx_ring.init()
guest_flow.fType = FLOW_DIRECT
guest_flow.ring = addr guest_rx_ring
ion_register(8080, addr guest_flow)
# Register the SysTable at a fixed address for the Guest
let sys_table = cast[ptr SysTable](0x801FFF00'u64)
sys_table.s_yield = cast[pointer](rumpk_yield_internal)
sys_table.s_alloc = cast[pointer](rumpk_alloc_internal)
sys_table.s_free = cast[pointer](rumpk_free_internal)
sys_table.s_tx = cast[pointer](rumpk_tx_internal)
sys_table.s_rx = addr guest_rx_ring
kprintln("╔═══════════════════════════════════════╗") kprintln("╔═══════════════════════════════════════╗")
kprintln("Layer 1: Nim Kernel Alive! ") kprintln("║ NEXUS RUMK v1.1 - SOVEREIGN ║")
kprintln("╚═══════════════════════════════════════╝") kprintln("╚═══════════════════════════════════════╝")
init_fiber(addr fiber_subject, subject_fiber_entry, addr stack_subject[0]) # 1. Hardware & Memory
init_fiber(addr fiber_net, net_fiber_entry, addr stack_net[0]) kprintln("[Kernel] Initializing Memory Substrate...")
ion_pool_init()
virtio_net_init()
# 2. Channel Infrastructure
kprintln("[Kernel] Mapping Sovereign Channels...")
# Initialize Invariant Shield (Masking)
for r in [addr guest_rx_hal, addr guest_tx_hal, addr guest_event_hal]:
r.head = 0
r.tail = 0
r.mask = 255
guest_cmd_hal.head = 0
guest_cmd_hal.tail = 0
guest_cmd_hal.mask = 255
chan_rx.ring = addr guest_rx_hal
chan_tx.ring = addr guest_tx_hal
chan_event.ring = addr guest_event_hal
chan_cmd.ring = addr guest_cmd_hal
let sys_table = cast[ptr SysTable](SYSTABLE_BASE)
sys_table.magic = 0x4E585553
sys_table.s_rx = addr guest_rx_hal
sys_table.s_tx = addr guest_tx_hal
sys_table.s_event = addr guest_event_hal
sys_table.s_cmd = addr guest_cmd_hal
# 3. The Nerve (Yield Anchor)
proc rumpk_yield_guard() {.importc, cdecl.}
let yield_ptr_loc = cast[ptr pointer](0x83000FF0'u64)
yield_ptr_loc[] = cast[pointer](rumpk_yield_guard)
# 4. Deployment
kprintln("[Kernel] Spawning System Fibers...")
# 1. NETWORK FIBER (The Valve)
init_fiber(addr fiber_net, net_fiber_entry, addr stack_net[0], sizeof(stack_net))
# 2. NEXSHELL FIBER (The Brain)
init_fiber(addr fiber_nexshell, nexshell_main, addr stack_nexshell[0], sizeof(stack_nexshell))
# 3. SUBJECT FIBER (The Payload)
init_fiber(addr fiber_subject, subject_fiber_entry, addr stack_subject[0],
sizeof(stack_subject))
# 4. WATCHDOG FIBER (The Immune System)
init_fiber(addr fiber_watchdog, watchdog_loop, addr stack_watchdog[0], sizeof(stack_watchdog))
kprintln("[Kernel] All Systems Go. Entering Autonomous Loop.")
# Handover to Scheduler (The Heartbeat)
switch(addr fiber_net) switch(addr fiber_net)
nimPanic("Main thread returned!")
{.pop.} {.pop.}

34
core/watchdog.nim Normal file
View File

@ -0,0 +1,34 @@
# Watchdog Fiber - Logic Core Immune System
const MAX_PAUSE_TICKS = 1_000_000'u64
# Import timer from HAL (Exposed via abi.zig)
proc cpu_ticks(): uint64 {.importc: "rumpk_timer_now_ns", cdecl.}
proc watchdog_loop() {.cdecl.} =
## Fiber 0: The Immune System
# Note: Uses globals from kernel.nim via 'include'
kprintln("[Watchdog] Immune System Online.")
while true:
# Check if network is stuck in PAUSE state
if net_paused:
let now = cpu_ticks()
# If paused and time exceeds threshold
# Note: pause_start needs to be set when pausing!
if (pause_start > 0) and (now > pause_start) and (now - pause_start >
MAX_PAUSE_TICKS):
# HEAL
kprint("[IMMUNE] Network paused too long. Forcing RESUME.\n")
net_paused = false
pause_start = 0
# Send CMD_NET_START to the Control Loop
var cmd = CmdPacket(kind: uint32(ion.CmdType.CMD_NET_START), arg: 0)
chan_cmd.send(cmd)
# Cooperative Multitasking: Must yield!
fiber_yield()
# asm "wfi"

View File

@ -61,3 +61,11 @@ export fn rumpk_pfree(ptr: *anyopaque) void {
export fn rumpk_halt() noreturn { export fn rumpk_halt() noreturn {
hal.halt(); hal.halt();
} }
var mock_ticks: u64 = 0;
export fn rumpk_timer_now_ns() u64 {
// Phase 1 Mock: Incrementing counter to simulate time passage per call
mock_ticks += 100000; // 100us per call
return mock_ticks;
}

89
hal/channel.zig Normal file
View File

@ -0,0 +1,89 @@
// MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
// RUMPK HAL // SOVEREIGN CHANNELS (The Pipes)
// THE INVARIANT SHIELD - Phase 2 Task 1
const std = @import("std");
pub const IonPacket = extern struct {
data: u64,
phys: u64,
len: u16,
id: u16,
};
pub const CmdPacket = extern struct {
kind: u32,
arg: u32,
};
pub fn Ring(comptime T: type) type {
return extern struct {
head: u32,
tail: u32,
mask: u32,
data: [256]T,
};
}
// INVARIANT 1: The Handle Barrier
fn validate_ring_ptr(ptr: u64) void {
// 0x8000_0000 is kernel base, 0x8300_0000 is ION base.
if (ptr < 0x8000_0000) {
@panic("HAL: Invariant Violation - Invalid Ring Pointer");
}
}
fn pushGeneric(comptime T: type, ring: *Ring(T), pkt: T) bool {
const head = @atomicLoad(u32, &ring.head, .monotonic);
const tail = @atomicLoad(u32, &ring.tail, .monotonic);
// INVARIANT 2: Overflow Protection
const next = (head + 1) & ring.mask;
if (next == tail) {
return false;
}
ring.data[head & ring.mask] = pkt;
@atomicStore(u32, &ring.head, next, .release);
return true;
}
fn popGeneric(comptime T: type, ring: *Ring(T), out_pkt: *T) bool {
const head = @atomicLoad(u32, &ring.head, .monotonic);
const tail = @atomicLoad(u32, &ring.tail, .monotonic);
// INVARIANT 3: Underflow Protection
if (head == tail) {
return false;
}
out_pkt.* = ring.data[tail & ring.mask];
const next = (tail + 1) & ring.mask;
@atomicStore(u32, &ring.tail, next, .release);
return true;
}
// Exported ABI Functions (Hardened)
export fn hal_channel_push(handle: u64, pkt: IonPacket) bool {
validate_ring_ptr(handle);
const ring: *Ring(IonPacket) = @ptrFromInt(handle);
return pushGeneric(IonPacket, ring, pkt);
}
export fn hal_channel_pop(handle: u64, out_pkt: *IonPacket) bool {
validate_ring_ptr(handle);
const ring: *Ring(IonPacket) = @ptrFromInt(handle);
return popGeneric(IonPacket, ring, out_pkt);
}
export fn hal_cmd_push(handle: u64, pkt: CmdPacket) bool {
validate_ring_ptr(handle);
const ring: *Ring(CmdPacket) = @ptrFromInt(handle);
return pushGeneric(CmdPacket, ring, pkt);
}
export fn hal_cmd_pop(handle: u64, out_pkt: *CmdPacket) bool {
validate_ring_ptr(handle);
const ring: *Ring(CmdPacket) = @ptrFromInt(handle);
return popGeneric(CmdPacket, ring, out_pkt);
}

View File

@ -17,6 +17,12 @@ export fn _start() callconv(.naked) noreturn {
\\ li t0, 0x2000 \\ li t0, 0x2000
\\ csrs sstatus, t0 \\ csrs sstatus, t0
// 1.2 Initialize Global Pointer
\\ .option push
\\ .option norelax
\\ la gp, __global_pointer$
\\ .option pop
// 2. Set up Stack (Load address of stack_bytes, add size) // 2. Set up Stack (Load address of stack_bytes, add size)
\\ la sp, stack_bytes \\ la sp, stack_bytes
\\ li t0, 65536 \\ li t0, 65536
@ -49,20 +55,17 @@ export fn zig_entry() void {
uart.init_riscv(); uart.init_riscv();
uart.print("[Rumpk L0] zig_entry reached\n"); uart.print("[Rumpk L0] zig_entry reached\n");
// HUD DRAW (No CLEAR for debug) // HUD DISABLED FOR PHASE 8.7 - LINEAR LOGGING ONLY
hud.set_color(36); // Cyan // hud.set_color(36); // Cyan
hud.draw_box(1, 1, 80, 3, "RUMPK HUD v0.1"); // hud.draw_box(1, 1, 80, 3, "RUMPK HUD v0.1");
hud.draw_box(1, 4, 80, 20, "NEXSHELL CONSOLE"); // hud.draw_box(1, 4, 80, 20, "NEXSHELL CONSOLE");
hud.draw_box(1, 24, 80, 2, "IDENTITY"); // hud.draw_box(1, 24, 80, 2, "IDENTITY");
// hud.move_to(2, 4);
hud.move_to(2, 4); // uart.print("CPU: RISC-V 64 | STATUS: INITIALIZING | MASK: SOVEREIGN");
uart.print("CPU: RISC-V 64 | STATUS: INITIALIZING | MASK: SOVEREIGN"); // hud.move_to(25, 4);
// uart.print("CELL: /Cell/Root | ID: 0xDEADBEEF");
hud.move_to(25, 4); // hud.move_to(5, 4);
uart.print("CELL: /Cell/Root | ID: 0xDEADBEEF"); // hud.reset_color();
hud.move_to(5, 4);
hud.reset_color();
uart.print("[Rumpk RISC-V] Handing off to Nim L1...\n"); uart.print("[Rumpk RISC-V] Handing off to Nim L1...\n");
// VirtIO Init moved to Kernel L1 (Sovereign Mode) // VirtIO Init moved to Kernel L1 (Sovereign Mode)
@ -86,9 +89,25 @@ export fn console_write(ptr: [*]const u8, len: usize) void {
uart.write_bytes(ptr[0..len]); uart.write_bytes(ptr[0..len]);
} }
export fn console_read() c_int {
if (uart.read_byte()) |b| {
return @as(c_int, b);
}
return -1;
}
export fn rumpk_halt() noreturn { export fn rumpk_halt() noreturn {
uart.print("[Rumpk L0] Halting.\n"); uart.print("[Rumpk L0] Halting.\n");
while (true) { while (true) {
asm volatile ("wfi"); asm volatile ("wfi");
} }
} }
var mock_ticks: u64 = 0;
export fn rumpk_timer_now_ns() u64 {
// Phase 1 Mock: Incrementing counter to simulate time passage per call
// This allows Watchdog logic to detect elapsed "time" in coop loop.
mock_ticks += 100000; // 100us per call
return mock_ticks;
}

187
npl/saboteur.zig Normal file
View File

@ -0,0 +1,187 @@
const std = @import("std");
// 1. The SysTable Contract (Must match Kernel!)
const ION_BASE = 0x83000000;
// The Physical Token representing a packet
const IonPacket = extern struct {
data: u64, // Virtual Addr (ptr)
phys: u64, // Physical Addr
len: u16,
id: u16,
};
const CmdPacket = extern struct {
kind: u32,
arg: u32,
};
const RingBufferPacket = extern struct {
head: u32,
tail: u32,
mask: u32,
data: [256]IonPacket,
};
const RingBufferCmd = extern struct {
head: u32,
tail: u32,
mask: u32,
data: [256]CmdPacket,
};
const SysTable = extern struct {
magic: u32,
s_rx: *RingBufferPacket,
s_tx: *RingBufferPacket,
s_event: *RingBufferPacket,
s_cmd: *RingBufferCmd, // Added for Sabotage
};
// 2. The Direct Accessor
fn get_systable() *SysTable {
return @ptrFromInt(ION_BASE);
}
// 3. The Saboteur Entry Point
export fn main() c_int {
print("[SABOTEUR] Engaged. Waiting for Init...\n");
// Allow system to stabilize (simulated simple wait loop)
var i: usize = 0;
while (i < 10) : (i += 1) {
fiber_yield();
}
const sys = get_systable();
if (sys.magic != 0x4E585553) {
print("[SABOTEUR] Magic mismatch! Aborting.\n");
return 1;
}
// 1. Send CMD_NET_STOP (Poison)
print("[SABOTEUR] Injecting POISON (CMD_NET_STOP)...\n");
{
const cmd_ring = sys.s_cmd;
const head = @atomicLoad(u32, &cmd_ring.head, .monotonic);
// CMD_NET_STOP = 1
const pkt = CmdPacket{ .kind = 1, .arg = 0 };
cmd_ring.data[head & cmd_ring.mask] = pkt;
@atomicStore(u32, &cmd_ring.head, head + 1, .release);
print("[SABOTEUR] POISON injected.\n");
}
print("[SABOTEUR] Network poisoned. Entering infinite loop to block CPU.\n");
print("[SABOTEUR] (This simulates a stuck NPL preventing yields)\n");
// 2. Hang
// In a cooperative multitasking system, if we don't yield, we freeze the fiber.
// If we are "Subject Fiber", and we don't yield, the scheduler can't switch.
// BUT the Kernel runs in "Launch_subject".
// Does "launch_subject" in loader.zig return?
// "entry()" calls into the binary.
// If the binary loops forever, "launch_subject" never returns.
// So "subject_fiber_entry" never returns.
// So "switch" is never called.
// So Fiber 1 (Net) and Fiber 2 (NexShell) never run?
//
// WAIT.
// If the Saboteur loops forever, and there is no Preemptive Timer Interrupt (yet),
// the WHOLE SYSTEM HANGS. The Watchdog is likely a separate Fiber.
// If Rumpk is Cooperative (Co-routines), a while(true) in one fiber KILLS EVERYTHING.
//
// UNLESS the Watchdog is:
// A) Running on a separate core? (No, Boot msg says Single Core usually for simple tests)
// B) Triggered by Interrupt? (Timer IRQ)
//
// The Watchdog in `core/watchdog.nim` is a FIBER: `watchdog_loop()`.
// It runs `while true: ... wfi`.
// If `subject_fiber` spins, `watchdog_fiber` NEVER RUNS.
//
// "The Saboteur Test ... The system detects the hang ... and restarts".
//
// If the system is purely cooperative, this test will FAIL unless:
// 1. `launch_subject` is run with a timeout? (No)
// 2. We have a Timer Interrupt that forces a context switch?
// `hal/entry_riscv.zig` disables interrupts: `csrw sie, zero`.
//
// The prompt says: "You successfully implemented the Watchdog. Now we must prove it works...".
// The Watchdog implementation in `kernel.nim` / `watchdog.nim` uses `wfi` (Wait For Interrupt).
// It implies there ARE interrupts waking it up.
// But if `Subject` spins in `while(true)`, it depends on whether `Subject` yields.
// `apps/subject_zig/main.zig` calls `fiber_yield`.
//
// If the Saboteur does `while(true) {}` without yield, it locks the CPU.
//
// "Reference: SPEC-008... Immortality".
// Maybe the "Watchdog" is supposed to be a *Hardware* Watchdog or Interrupt-driven?
// But the code I wrote in `watchdog.nim` is a Fiber.
//
// "Fiber 0: The Immune System".
// If I implemented it as a Fiber, it needs CPU time.
//
// CRITICAL REALIZATION:
// If I hang the CPU in a fiber in a cooperative OS, the OS dies.
// The only way this test passes is if:
// A) The Saboteur calls `fiber_yield()` inside the loop?
// Prompt says: "Enters an infinite while(true) {} loop (Hangs the fiber)."
// "Hangs the fiber" usually means "doesn't yield".
// But if it doesn't yield, the Watchdog fiber can't run to detect it.
//
// UNLESS...
// The "Watchdog" I implemented checks `net_paused`.
// The Saboteur *sends* `CMD_NET_STOP`. This sets `net_paused = true`.
// Then Saboteur hangs.
//
// If Saboteur hangs (no yield), Watchdog logic (which checks `net_paused`) never runs.
// So it can't "Force RESUME".
//
// PERHAPS the Saboteur should loop *with yield*?
// "Hangs the fiber" might mean "Stops doing useful work and just loops".
// If it yields, other fibers run.
// The Network Fiber runs, sees `net_paused`, and skips IO.
// The Watchdog Fiber runs, sees `net_paused && time > threshold`, and "Heals".
//
// IF the test is about "Network Paused Too Long", then yes, the system must still schedule.
// So the Saboteur must YIELD in its loop, but REFUSE to send `CMD_NET_START`.
// It basically attacks the logic (pauses net) and then refuses to resume it.
// The Watchdog overrides it.
//
// So, `while (true) { fiber_yield(); }` is the correct "Hang" for a cooperative system where we want to test Logic Recovery, not CPU Starvation Recovery (which requires IRQs).
//
// I will implement the loop with `fiber_yield()`.
while (true) {
fiber_yield();
}
return 0;
}
// Minimal Shims
extern fn write(fd: c_int, buf: [*]const u8, count: usize) isize;
const YIELD_LOC = 0x83000FF0;
fn fiber_yield() void {
const ptr: *const *const fn () callconv(.c) void = @ptrFromInt(YIELD_LOC);
const func = ptr.*;
func();
}
// extern fn fiber_yield() void; // Removed extern
fn print(text: []const u8) void {
_ = write(1, text.ptr, text.len);
}
pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
_ = error_return_trace;
_ = ret_addr;
print("\n[SABOTEUR] PANIC: ");
print(msg);
print("\n");
while (true) {}
}