commit 694a753bedbc65703145b3fccbe3ac0b802461be Author: Markus Maiwald Date: Tue Dec 30 07:21:27 2025 +0100 feat: Initialize Rumpk Modular Unikernel STRATEGIC PIVOT: From Project to Doctrine ========================================== This commit initializes Rumpk - a ground-zero Zig+Nim unikernel with POSIX-hostile design, hard ABI barriers, and military-grade security. DOCUMENTATION (3 New Specs) --------------------------- • SPEC-008-RUMPK-ARCHITECTURE.md - L0 (Zig): Boot, PMM, IRQ, HAL - L1 (Nim): LWKT Scheduler, Fibers, Disruptor Ring - L2 (ABI): struct HAL function pointers (future Janus socket) - L3 (Payload): NPL/NPK loaders, optional POSIX shim - SipHash IDs + Ed25519 signed execution • SPEC-009-RUMPK-IO.md - Disruptor Ring: Lock-free O(1) inter-fiber communication - Adaptive Governor: War Mode (polling) ↔ Peace Mode (interrupts) - Zero VM-exit design (Rumkv does NOT touch packets) • SPEC-010-SOVEREIGN-HIERARCHY-V2.md - /Cas: Immutable Content-Addressable Storage - /Cell: Active Containers (Driver/, App/, Sensor/) - /Bus: Active Interfaces (replaces /dev) - /Data: Mutable Persistence (User/, Volume/) - 'The Unix Lie' compatibility layer for legacy apps VISION.MD UPDATE ---------------- • Added dedicated Rumpk section differentiating from Rumk • Documented 4-layer architecture with ASCII diagram • Listed key innovations: Adaptive I/O, Disruptor, SipHash, Ed25519 REPOSITORY STRUCTURE (core/rumpk/) ---------------------------------- core/rumpk/ ├── boot/header.zig # Multiboot2/EFI entry ├── hal/abi.zig # L0→L1 ABI contract (struct HAL) ├── core/kernel.nim # kmain() entry point ├── core/ring.nim # Disruptor ring buffer ├── io/governor.nim # Adaptive War/Peace I/O ├── build.zig # Zig build orchestration └── README.md # Feature index DESIGN DECISIONS ---------------- • Hard ABI barrier: Zig exports C-compatible struct to Nim • Language-agnostic: L1 can be swapped for Janus later • No shared state: Fibers communicate via Channels only • No JIT, No W^X violations: Code sections immutable NEXT STEPS ---------- • Phase 1: Boot on QEMU (print 'Hello Rumpk') • Phase 2: Nim runtime on bare metal • Phase 3: Two fibers switching (Ping/Pong) • Phase 4: NPL loading with signature verification • Phase 5: VisionFive 2 hardware validation This is the foundation for the 'OS Factory' vision. Rumpk + Rumkv + NPL = Independent from Unix/Linux. Tested: Directory structure validated Status: ✅ SCAFFOLD COMPLETE diff --git a/.zig-cache/h/5185954eff7f7c6c82121d2fd2e2d4fd.txt b/.zig-cache/h/5185954eff7f7c6c82121d2fd2e2d4fd.txt new file mode 100644 index 0000000..e69de29 diff --git a/.zig-cache/h/timestamp b/.zig-cache/h/timestamp new file mode 100644 index 0000000..e69de29 diff --git a/.zig-cache/o/f1ea4330472a4c6ebcbfed6922919020/dependencies.zig b/.zig-cache/o/f1ea4330472a4c6ebcbfed6922919020/dependencies.zig new file mode 100644 index 0000000..72e4e83 --- /dev/null +++ b/.zig-cache/o/f1ea4330472a4c6ebcbfed6922919020/dependencies.zig @@ -0,0 +1,2 @@ +pub const packages = struct {}; +pub const root_deps: []const struct { []const u8, []const u8 } = &.{}; diff --git a/.zig-cache/z/0de2bc7b8ecf843738d5e3b13139ce2e b/.zig-cache/z/0de2bc7b8ecf843738d5e3b13139ce2e new file mode 100644 index 0000000..ccfe6c2 Binary files /dev/null and b/.zig-cache/z/0de2bc7b8ecf843738d5e3b13139ce2e differ diff --git a/.zig-cache/z/4cdd191b80afdf78c75a2dbd44f113ff b/.zig-cache/z/4cdd191b80afdf78c75a2dbd44f113ff new file mode 100644 index 0000000..4b43278 Binary files /dev/null and b/.zig-cache/z/4cdd191b80afdf78c75a2dbd44f113ff differ diff --git a/.zig-cache/z/6f1a1195a5c1b6e16f67c3dd3fe19ab5 b/.zig-cache/z/6f1a1195a5c1b6e16f67c3dd3fe19ab5 new file mode 100644 index 0000000..561e3a3 Binary files /dev/null and b/.zig-cache/z/6f1a1195a5c1b6e16f67c3dd3fe19ab5 differ diff --git a/.zig-cache/z/a36ff463ebe2b895b642ea13063bf31d b/.zig-cache/z/a36ff463ebe2b895b642ea13063bf31d new file mode 100644 index 0000000..fc377a7 Binary files /dev/null and b/.zig-cache/z/a36ff463ebe2b895b642ea13063bf31d differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..21c8358 --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +# Rumpk: The Modular Unikernel + +> **"The Kernel is a Library. The App is the OS."** + +**Status:** EXPERIMENTAL +**Languages:** Zig (L0) + Nim (L1) +**Design:** POSIX-hostile, Military-grade + +## Directory Structure + +``` +rumpk/ +├── boot/ [L0] Entry & Architecture (Zig/Asm) +│ ├── start.S Multiboot2/EFI entry point +│ └── arch/ Architecture-specific code +├── hal/ [L0] Hardware Abstraction (Zig) +│ ├── mm.zig Physical/Virtual Memory +│ ├── irq.zig Interrupt handling +│ ├── serial.zig UART/Early logging +│ └── abi.zig C-ABI export to Nim +├── core/ [L1] Logic (Nim) +│ ├── kernel.nim kmain() entry +│ ├── sched.nim LWKT Scheduler +│ ├── fiber.nim Fiber/Context management +│ └── ring.nim Disruptor buffer +├── sys/ [L2] ABI Glue +│ └── syscall.zig System call handlers +├── payload/ [L3] NPL/NPK Loaders +│ └── loader.nim Signature verification +└── io/ I/O Subsystem + └── governor.nim Adaptive War/Peace mode +``` + +## Key Features + +- **Adaptive I/O**: War Mode (polling) ↔ Peace Mode (interrupts) +- **Disruptor Ring**: Lock-free inter-fiber communication +- **SipHash IDs**: Collision-resistant process identification +- **Ed25519**: Only signed code executes + +## Specifications + +- [SPEC-008: Architecture](/.agents/specs/SPEC-008-RUMPK-ARCHITECTURE.md) +- [SPEC-009: I/O Subsystem](/.agents/specs/SPEC-009-RUMPK-IO.md) +- [SPEC-010: Sovereign Hierarchy](/.agents/specs/SPEC-010-SOVEREIGN-HIERARCHY-V2.md) + +## Build (Coming Soon) + +```bash +cd core/rumpk +zig build # Build L0 HAL +nimble build # Build L1 Logic +``` diff --git a/boot/header.zig b/boot/header.zig new file mode 100644 index 0000000..f837fdc --- /dev/null +++ b/boot/header.zig @@ -0,0 +1,62 @@ +// Rumpk Boot Header +// Multiboot2 / EFI entry point definition + +const std = @import("std"); + +// ========================================================= +// Multiboot2 Header (for GRUB/QEMU) +// ========================================================= + +const MULTIBOOT2_MAGIC: u32 = 0xe85250d6; +const MULTIBOOT2_ARCH_I386: u32 = 0; +const MULTIBOOT2_HEADER_LENGTH: u32 = @sizeOf(Multiboot2Header); + +const Multiboot2Header = extern struct { + magic: u32 = MULTIBOOT2_MAGIC, + architecture: u32 = MULTIBOOT2_ARCH_I386, + header_length: u32 = MULTIBOOT2_HEADER_LENGTH, + checksum: u32, + + // End tag + end_tag_type: u16 = 0, + end_tag_flags: u16 = 0, + end_tag_size: u32 = 8, +}; + +fn computeChecksum() u32 { + return @bitCast(-%@as(i32, @bitCast(MULTIBOOT2_MAGIC +% MULTIBOOT2_ARCH_I386 +% MULTIBOOT2_HEADER_LENGTH))); +} + +export const multiboot2_header linksection(".multiboot2") = Multiboot2Header{ + .checksum = computeChecksum(), +}; + +// ========================================================= +// Entry Point +// ========================================================= + +extern fn kmain() noreturn; + +export fn _start() callconv(.Naked) noreturn { + // Clear BSS, set up stack, then jump to Nim + asm volatile ( + \\ // Set up stack + \\ la sp, __stack_top + \\ + \\ // Clear BSS + \\ la t0, __bss_start + \\ la t1, __bss_end + \\1: + \\ bge t0, t1, 2f + \\ sd zero, (t0) + \\ addi t0, t0, 8 + \\ j 1b + \\2: + \\ // Jump to Nim kmain + \\ call kmain + \\ + \\ // Should never return + \\ wfi + \\ j 2b + ); +} diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..70f8911 --- /dev/null +++ b/build.zig @@ -0,0 +1,54 @@ +// Rumpk Build System +// Orchestrates L0 (Zig) and L1 (Nim) compilation + +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + // ========================================================= + // L0: Hardware Abstraction Layer (Zig) + // ========================================================= + + const hal = b.addStaticLibrary(.{ + .name = "rumpk_hal", + .root_source_file = b.path("hal/abi.zig"), + .target = target, + .optimize = optimize, + }); + + // Freestanding kernel - no libc + hal.root_module.red_zone = false; + hal.root_module.stack_check = .none; + + b.installArtifact(hal); + + // ========================================================= + // Boot: Entry Point (Assembly + Zig) + // ========================================================= + + const boot = b.addObject(.{ + .name = "boot", + .root_source_file = b.path("boot/header.zig"), + .target = target, + .optimize = optimize, + }); + + boot.root_module.red_zone = false; + boot.root_module.stack_check = .none; + + // ========================================================= + // Tests + // ========================================================= + + const hal_tests = b.addTest(.{ + .root_source_file = b.path("hal/abi.zig"), + .target = target, + .optimize = optimize, + }); + + const run_tests = b.addRunArtifact(hal_tests); + const test_step = b.step("test", "Run Rumpk HAL tests"); + test_step.dependOn(&run_tests.step); +} diff --git a/core/kernel.nim b/core/kernel.nim new file mode 100644 index 0000000..50bc84b --- /dev/null +++ b/core/kernel.nim @@ -0,0 +1,41 @@ +# Rumpk Kernel Core (Nim) +# kmain() entry point - called from L0 Zig boot + +{.push stackTrace: off.} + +# ========================================================= +# FFI Imports from L0 (Zig HAL) +# ========================================================= + +proc rumpk_console_write(p: pointer, len: csize_t) {.importc, cdecl.} +proc rumpk_halt() {.importc, cdecl, noreturn.} + +# ========================================================= +# Kernel I/O +# ========================================================= + +proc kprint(s: string) = + if s.len > 0: + rumpk_console_write(unsafeAddr s[0], csize_t(s.len)) + +proc kprintln(s: string) = + kprint(s) + kprint("\n") + +# ========================================================= +# Kernel Main +# ========================================================= + +proc kmain() {.exportc, cdecl, noreturn.} = + kprintln("╔═══════════════════════════════════════╗") + kprintln("║ RUMPK UNIKERNEL v0.0 ║") + kprintln("║ Zig+Nim • Zero POSIX • Modular ║") + kprintln("╚═══════════════════════════════════════╝") + kprintln("") + kprintln("[rumpk] L0 HAL initialized (Zig)") + kprintln("[rumpk] L1 Logic running (Nim)") + kprintln("[rumpk] Entering halt...") + + rumpk_halt() + +{.pop.} diff --git a/core/ring.nim b/core/ring.nim new file mode 100644 index 0000000..02c3b5e --- /dev/null +++ b/core/ring.nim @@ -0,0 +1,58 @@ +# Rumpk Disruptor Ring Buffer +# Lock-free O(1) inter-fiber communication + +import std/atomics + +type + Cursor* = object + value*: Atomic[int64] + + RingBuffer*[T; N: static[int]] = object + ## Lock-free circular buffer for zero-copy I/O + ## N must be power of 2 for masking + data*: array[N, T] + head*: Cursor ## Written by producer + tail*: Cursor ## Read by consumer + mask: int64 ## N - 1 for fast modulo + +proc init*[T; N: static[int]](ring: var RingBuffer[T, N]) = + ## Initialize the ring buffer + ring.head.value.store(0) + ring.tail.value.store(0) + ring.mask = N - 1 + +proc push*[T; N: static[int]](ring: var RingBuffer[T, N]; item: T): bool = + ## Push item to ring. Returns false if full. + let head = ring.head.value.load(moRelaxed) + let tail = ring.tail.value.load(moAcquire) + + if head - tail >= N: + return false # Full + + ring.data[head and ring.mask] = item + ring.head.value.store(head + 1, moRelease) + return true + +proc pop*[T; N: static[int]](ring: var RingBuffer[T, N]): tuple[ok: bool; item: T] = + ## Pop item from ring. Returns (false, default) if empty. + let tail = ring.tail.value.load(moRelaxed) + let head = ring.head.value.load(moAcquire) + + if tail >= head: + return (false, default(T)) # Empty + + let item = ring.data[tail and ring.mask] + ring.tail.value.store(tail + 1, moRelease) + return (true, item) + +proc len*[T; N: static[int]](ring: RingBuffer[T, N]): int = + ## Current number of items in ring + let head = ring.head.value.load(moRelaxed) + let tail = ring.tail.value.load(moRelaxed) + return int(head - tail) + +proc isFull*[T; N: static[int]](ring: RingBuffer[T, N]): bool = + ring.len >= N + +proc isEmpty*[T; N: static[int]](ring: RingBuffer[T, N]): bool = + ring.len == 0 diff --git a/hal/abi.zig b/hal/abi.zig new file mode 100644 index 0000000..9cd9ae7 --- /dev/null +++ b/hal/abi.zig @@ -0,0 +1,63 @@ +// HAL ABI - The Contract between L0 (Zig) and L1 (Nim) +// This struct is the "socket" for future language integration + +pub const HAL = extern struct { + /// Write to console/serial + console_write: *const fn ([*]const u8, usize) void, + + /// Allocate physical pages + palloc: *const fn (usize) ?*anyopaque, + + /// Free physical pages + pfree: *const fn (*anyopaque) void, + + /// Register interrupt handler + irq_register: *const fn (u8, *const fn () void) void, + + /// Get current time in nanoseconds + timer_now_ns: *const fn () u64, + + /// Halt the CPU + halt: *const fn () noreturn, +}; + +/// Global HAL instance - initialized by boot code +pub var hal: HAL = undefined; + +/// Initialize the HAL with platform-specific implementations +pub fn init(console: anytype, allocator: anytype) void { + hal = HAL{ + .console_write = console.write, + .palloc = allocator.alloc, + .pfree = allocator.free, + .irq_register = undefined, // TODO + .timer_now_ns = undefined, // TODO + .halt = halt_impl, + }; +} + +fn halt_impl() noreturn { + while (true) { + asm volatile ("wfi"); + } +} + +// ========================================================= +// Exports for Nim FFI +// ========================================================= + +export fn rumpk_console_write(ptr: [*]const u8, len: usize) void { + hal.console_write(ptr, len); +} + +export fn rumpk_palloc(pages: usize) ?*anyopaque { + return hal.palloc(pages); +} + +export fn rumpk_pfree(ptr: *anyopaque) void { + hal.pfree(ptr); +} + +export fn rumpk_halt() noreturn { + hal.halt(); +} diff --git a/io/governor.nim b/io/governor.nim new file mode 100644 index 0000000..6ed2295 --- /dev/null +++ b/io/governor.nim @@ -0,0 +1,61 @@ +# Rumpk Adaptive I/O Governor +# War Mode (polling) ↔ Peace Mode (interrupts) + +import ring + +const + POLL_BUDGET* = 2000 ## Cycles before switching to peace mode + PEACE_TIMEOUT_NS* = 1_000_000 ## 1ms before checking again + +type + GovernorMode* = enum + War, ## High load: 100% CPU polling + Peace ## Low load: Interrupt-driven + + IoGovernor*[T; N: static[int]] = object + mode*: GovernorMode + budget: int + ring*: RingBuffer[T, N] + + # Callbacks (set by driver) + hw_has_data*: proc(): bool {.nimcall.} + hw_fetch*: proc(): T {.nimcall.} + hw_enable_irq*: proc() {.nimcall.} + hw_disable_irq*: proc() {.nimcall.} + lwkt_yield*: proc() {.nimcall.} + +proc init*[T; N: static[int]](gov: var IoGovernor[T, N]) = + gov.mode = Peace + gov.budget = POLL_BUDGET + gov.ring.init() + +proc tick*[T; N: static[int]](gov: var IoGovernor[T, N]) = + ## Main driver loop iteration + + if gov.hw_has_data(): + # WAR MODE: Data available, stay aggressive + let data = gov.hw_fetch() + discard gov.ring.push(data) + gov.budget = POLL_BUDGET + gov.mode = War + + else: + # Speculation window + if gov.budget > 0: + dec gov.budget + # cpu_relax() equivalent - let other fibers run briefly + + else: + # PEACE MODE: Budget exhausted, sleep + gov.mode = Peace + gov.hw_enable_irq() + gov.lwkt_yield() + + # Woke up from interrupt + gov.hw_disable_irq() + gov.budget = POLL_BUDGET + +proc run*[T; N: static[int]](gov: var IoGovernor[T, N]) {.noreturn.} = + ## Infinite driver loop + while true: + gov.tick()