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
This commit is contained in:
Markus Maiwald 2025-12-30 07:21:27 +01:00
commit 694a753bed
14 changed files with 394 additions and 0 deletions

0
.zig-cache/h/timestamp Normal file
View File

View File

@ -0,0 +1,2 @@
pub const packages = struct {};
pub const root_deps: []const struct { []const u8, []const u8 } = &.{};

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

53
README.md Normal file
View File

@ -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
```

62
boot/header.zig Normal file
View File

@ -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
);
}

54
build.zig Normal file
View File

@ -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);
}

41
core/kernel.nim Normal file
View File

@ -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.}

58
core/ring.nim Normal file
View File

@ -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

63
hal/abi.zig Normal file
View File

@ -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();
}

61
io/governor.nim Normal file
View File

@ -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()