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:
commit
694a753bed
|
|
@ -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.
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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.}
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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()
|
||||
Loading…
Reference in New Issue