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