feat(rumpk): Phase 4 - NPL Loader
THE PLATFORM HAS PURPOSE ======================== Rumpk now has a payload loading system: NPL (Nexus Payload). OUTPUT ------ [NPL] ✅ Verification PASSED [NPL] Executing payload... [NPL] ✅ Payload returned! [NPL] ✅ Bad sig rejected NPL FORMAT (128-byte header) ---------------------------- - Magic: \x7fNPL (4 bytes) - Version: 1 (1 byte) - Arch: 0xAA=ARM64, 0xEE=x86_64, 0x55=RISC-V (1 byte) - Flags: 2 bytes - Signature: 64 bytes (Ed25519 placeholder) - Body Size: 8 bytes - Reserved: 48 bytes IMPLEMENTATION -------------- core/npl.nim: - NPLHeader struct (packed, 128 bytes) - loadNpl() - validates magic, version, arch, signature - buildTestPayload() - creates test NPL in memory - Signature verification (mock: rejects 0xFF) TESTS VERIFIED -------------- 1. Valid payload: Loads and executes RET instruction 2. Bad signature: Correctly rejected (0xFF in sig[0]) 3. Cross-arch: Would reject wrong arch code PHASE SUMMARY ------------- ✅ Phase 1: Documentation (SPEC-008/009/010) ✅ Phase 2: Pure Zig libc (Freestanding Doctrine) ✅ Phase 3: Cooperative Fibers (Ping Pong) ✅ Phase 4: NPL Loader (with mock signature) → Phase 4.2: Ed25519 verification (Monocypher) → Phase 5: VisionFive 2 RISC-V hardware The unikernel can now load and execute signed payloads. Next: Real Ed25519 verification.
This commit is contained in:
parent
4cc268683d
commit
ee594df8a7
|
|
@ -0,0 +1,8 @@
|
|||
0
|
||||
5537 9466034 1767079280889767999 0d029e05059c175b5ce754f57f213b48 0 /home/markus/zWork/_Git/Nexus/core/rumpk/build/nimcache/@mnpl.nim.c
|
||||
19164 69191110 1749873121000000000 fe5756ed84745fc96fd9dfb15050f599 0 /usr/lib/nim/nimbase.h
|
||||
639 9459383 1767076381899751290 1b9448bcfa47e3161459266750e8ded4 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/limits.h
|
||||
268 9459347 1767076422997272233 06a4c7da1c4987981a369ef3e003bda3 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/stddef.h
|
||||
155 9459777 1767076495338437553 9cc523d7a8a3a0bbc7c7af0fabeafc0b 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/stdbool.h
|
||||
924 9459799 1767076530759032485 73bc6834aef9958f6652470b63d7814b 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/stdint.h
|
||||
499 9459330 1767076360432003062 357ccd6329b0128cce0610c1443c600d 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/string.h
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
0
|
||||
15874 9465108 1767078402329925246 e5c92c416746964294e1e5d350d1d6c3 0 /home/markus/zWork/_Git/Nexus/core/rumpk/build/nimcache/@mkernel.nim.c
|
||||
23680 9466035 1767079280889767999 54c7101f99bb89eb7e9878f7b072ebef 0 /home/markus/zWork/_Git/Nexus/core/rumpk/build/nimcache/@mkernel.nim.c
|
||||
19164 69191110 1749873121000000000 fe5756ed84745fc96fd9dfb15050f599 0 /usr/lib/nim/nimbase.h
|
||||
639 9459383 1767076381899751290 1b9448bcfa47e3161459266750e8ded4 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/limits.h
|
||||
268 9459347 1767076422997272233 06a4c7da1c4987981a369ef3e003bda3 0 /home/markus/zWork/_Git/Nexus/core/rumpk/core/include/stddef.h
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
{.push stackTrace: off, lineTrace: off.}
|
||||
|
||||
import fiber
|
||||
import npl
|
||||
|
||||
# =========================================================
|
||||
# HAL Imports from Zig (Layer 0)
|
||||
|
|
@ -43,16 +44,6 @@ proc nimPanic(msg: cstring) {.exportc: "panic", cdecl, noreturn.} =
|
|||
kprint("\n")
|
||||
rumpk_halt()
|
||||
|
||||
# =========================================================
|
||||
# Memory Allocator - Provided by Zig L0 (hal/stubs.zig)
|
||||
# =========================================================
|
||||
|
||||
# Zig exports: malloc, free, realloc, calloc
|
||||
# We just import them for any explicit usage
|
||||
proc malloc(size: csize_t): pointer {.importc, cdecl.}
|
||||
proc free(p: pointer) {.importc, cdecl.}
|
||||
proc realloc(p: pointer, size: csize_t): pointer {.importc, cdecl.}
|
||||
|
||||
# =========================================================
|
||||
# Fiber Test: Ping Pong
|
||||
# =========================================================
|
||||
|
|
@ -81,6 +72,77 @@ proc thread_b_entry() {.cdecl.} =
|
|||
while true:
|
||||
{.emit: "asm volatile(\"wfi\");".}
|
||||
|
||||
# =========================================================
|
||||
# NPL Loader Test
|
||||
# =========================================================
|
||||
|
||||
var npl_test_buffer {.align: 128.}: array[256, uint8]
|
||||
|
||||
proc test_npl_loader() =
|
||||
kprintln("")
|
||||
kprintln("╔═══════════════════════════════════════╗")
|
||||
kprintln("║ NPL Loader Test (Phase 4) ║")
|
||||
kprintln("╚═══════════════════════════════════════╝")
|
||||
kprintln("")
|
||||
|
||||
# Build test payload with architecture-specific RET instruction
|
||||
when defined(arm64) or defined(aarch64):
|
||||
let retCode = [0xC0'u8, 0x03'u8, 0x5F'u8, 0xD6'u8]
|
||||
let archCode = ARCH_ARM64
|
||||
kprintln("[NPL] Building ARM64 test payload...")
|
||||
elif defined(amd64) or defined(x86_64):
|
||||
let retCode = [0xC3'u8]
|
||||
let archCode = ARCH_X86_64
|
||||
kprintln("[NPL] Building x86_64 test payload...")
|
||||
elif defined(riscv64):
|
||||
let retCode = [0x67'u8, 0x80'u8, 0x00'u8, 0x00'u8]
|
||||
let archCode = ARCH_RISCV64
|
||||
kprintln("[NPL] Building RISC-V test payload...")
|
||||
else:
|
||||
kprintln("[NPL] Unsupported architecture!")
|
||||
return
|
||||
|
||||
buildTestPayload(addr npl_test_buffer, archCode, retCode)
|
||||
kprintln("[NPL] Loading...")
|
||||
|
||||
let entry = loadNpl(addr npl_test_buffer[0], 256)
|
||||
|
||||
kprintln("[NPL] loadNpl returned")
|
||||
|
||||
if entry != nil:
|
||||
kprintln("[NPL] ✅ Verification PASSED")
|
||||
kprintln("[NPL] Executing payload...")
|
||||
entry()
|
||||
kprintln("[NPL] ✅ Payload returned!")
|
||||
else:
|
||||
kprintln("[NPL] ❌ Load failed")
|
||||
|
||||
proc test_npl_rejection() =
|
||||
kprintln("")
|
||||
kprintln("[NPL] Testing rejection...")
|
||||
|
||||
when defined(arm64) or defined(aarch64):
|
||||
let retCode = [0xC0'u8, 0x03'u8, 0x5F'u8, 0xD6'u8]
|
||||
let archCode = ARCH_ARM64
|
||||
elif defined(amd64) or defined(x86_64):
|
||||
let retCode = [0xC3'u8]
|
||||
let archCode = ARCH_X86_64
|
||||
elif defined(riscv64):
|
||||
let retCode = [0x67'u8, 0x80'u8, 0x00'u8, 0x00'u8]
|
||||
let archCode = ARCH_RISCV64
|
||||
else:
|
||||
return
|
||||
|
||||
buildTestPayload(addr npl_test_buffer, archCode, retCode)
|
||||
npl_test_buffer[8] = 0xFF'u8 # Trigger rejection
|
||||
|
||||
let entry = loadNpl(addr npl_test_buffer[0], 256)
|
||||
|
||||
if entry == nil:
|
||||
kprintln("[NPL] ✅ Bad sig rejected")
|
||||
else:
|
||||
kprintln("[NPL] ❌ Should reject!")
|
||||
|
||||
# =========================================================
|
||||
# Kernel Main Entry
|
||||
# =========================================================
|
||||
|
|
@ -96,6 +158,11 @@ proc kmain() {.exportc, cdecl.} =
|
|||
kprintln("")
|
||||
kprintln("[Rumpk L1] The Rubicon is crossed.")
|
||||
kprintln("[Rumpk L1] Zig + Nim = Sovereign Metal.")
|
||||
|
||||
# Phase 4: NPL Loader Test
|
||||
test_npl_loader()
|
||||
test_npl_rejection()
|
||||
|
||||
kprintln("")
|
||||
kprintln("[Rumpk L1] Initializing Fibers...")
|
||||
|
||||
|
|
@ -106,12 +173,10 @@ proc kmain() {.exportc, cdecl.} =
|
|||
init_fiber(addr fiber_a, thread_a_entry, addr stack_a[0])
|
||||
init_fiber(addr fiber_b, thread_b_entry, addr stack_b[0])
|
||||
|
||||
# Jump into the matrix
|
||||
kprintln("[Kernel] Switching to Fiber A...")
|
||||
kprintln("")
|
||||
switch(addr fiber_a)
|
||||
|
||||
# We should never get here unless Fiber A switches back to Main
|
||||
nimPanic("Main thread returned!")
|
||||
|
||||
{.pop.}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,136 @@
|
|||
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
||||
# RUMPK CORE // NPL FORMAT
|
||||
# The Contract of Execution.
|
||||
|
||||
{.push stackTrace: off, lineTrace: off.}
|
||||
|
||||
# =========================================================
|
||||
# Constants
|
||||
# =========================================================
|
||||
|
||||
const NPL_MAGIC*: array[4, uint8] = [0x7F'u8, 0x4E'u8, 0x50'u8, 0x4C'u8]
|
||||
const NPL_VERSION* = 1'u8
|
||||
const ARCH_ARM64* = 0xAA'u8
|
||||
const ARCH_X86_64* = 0xEE'u8
|
||||
const ARCH_RISCV64* = 0x55'u8
|
||||
const NPL_HEADER_SIZE* = 128
|
||||
|
||||
# =========================================================
|
||||
# Types
|
||||
# =========================================================
|
||||
|
||||
type
|
||||
NPLHeader* {.packed.} = object
|
||||
magic*: array[4, uint8]
|
||||
version*: uint8
|
||||
arch*: uint8
|
||||
flags*: uint16
|
||||
signature*: array[64, uint8]
|
||||
body_size*: uint64
|
||||
reserved*: array[48, uint8]
|
||||
|
||||
PayloadEntry* = proc() {.cdecl.}
|
||||
|
||||
# Global last error (avoids struct return issues)
|
||||
var nplLastError*: cstring = nil
|
||||
|
||||
# =========================================================
|
||||
# Architecture Detection
|
||||
# =========================================================
|
||||
|
||||
proc currentArch*(): uint8 =
|
||||
when defined(arm64) or defined(aarch64):
|
||||
return ARCH_ARM64
|
||||
elif defined(amd64) or defined(x86_64):
|
||||
return ARCH_X86_64
|
||||
elif defined(riscv64):
|
||||
return ARCH_RISCV64
|
||||
else:
|
||||
return 0x00'u8
|
||||
|
||||
# =========================================================
|
||||
# Debug output
|
||||
# =========================================================
|
||||
|
||||
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
|
||||
|
||||
proc nplPrint(s: cstring) =
|
||||
var i = 0
|
||||
while s[i] != '\0':
|
||||
inc i
|
||||
console_write(cast[pointer](s), csize_t(i))
|
||||
|
||||
# =========================================================
|
||||
# NPL Loader (returns nil on failure, sets nplLastError)
|
||||
# =========================================================
|
||||
|
||||
proc loadNpl*(rawPtr: pointer, maxLen: uint64): PayloadEntry =
|
||||
nplLastError = nil
|
||||
|
||||
nplPrint("[NPL] loadNpl\n")
|
||||
|
||||
if rawPtr == nil:
|
||||
nplLastError = "null pointer"
|
||||
return nil
|
||||
|
||||
let header = cast[ptr NPLHeader](rawPtr)
|
||||
|
||||
# Check Magic
|
||||
if header.magic[0] != NPL_MAGIC[0] or
|
||||
header.magic[1] != NPL_MAGIC[1] or
|
||||
header.magic[2] != NPL_MAGIC[2] or
|
||||
header.magic[3] != NPL_MAGIC[3]:
|
||||
nplLastError = "bad magic"
|
||||
return nil
|
||||
|
||||
# Check Version
|
||||
if header.version != NPL_VERSION:
|
||||
nplLastError = "bad version"
|
||||
return nil
|
||||
|
||||
# Check Architecture
|
||||
if header.arch != currentArch():
|
||||
nplLastError = "bad arch"
|
||||
return nil
|
||||
|
||||
# Check Bounds
|
||||
let totalSize = NPL_HEADER_SIZE.uint64 + header.body_size
|
||||
if totalSize > maxLen:
|
||||
nplLastError = "truncated"
|
||||
return nil
|
||||
|
||||
# Get body pointer
|
||||
let bodyPtr = cast[pointer](cast[uint64](rawPtr) + NPL_HEADER_SIZE.uint64)
|
||||
|
||||
# Verify Signature (dev mode: reject 0xFF)
|
||||
if header.signature[0] == 0xFF'u8:
|
||||
nplLastError = "sig fail"
|
||||
return nil
|
||||
|
||||
nplPrint("[NPL] valid\n")
|
||||
|
||||
# Cast body to function pointer
|
||||
return cast[PayloadEntry](bodyPtr)
|
||||
|
||||
# =========================================================
|
||||
# Helper: Build Test Payload
|
||||
# =========================================================
|
||||
|
||||
proc buildTestPayload*(buffer: ptr array[256, uint8], arch: uint8,
|
||||
codeBytes: openArray[uint8]) =
|
||||
# Clear buffer
|
||||
for i in 0..<256:
|
||||
buffer[i] = 0
|
||||
|
||||
let header = cast[ptr NPLHeader](buffer)
|
||||
header.magic = NPL_MAGIC
|
||||
header.version = NPL_VERSION
|
||||
header.arch = arch
|
||||
header.flags = 1
|
||||
header.body_size = codeBytes.len.uint64
|
||||
|
||||
# Copy code to body
|
||||
for i, b in codeBytes:
|
||||
buffer[NPL_HEADER_SIZE + i] = b
|
||||
|
||||
{.pop.}
|
||||
Loading…
Reference in New Issue