Phase 14-15: Nexus Forge - Software Defined OS Build System

PHASE 14: THE FORGE IS LIT
===========================

Implemented the Nexus Forge, a type-safe Nim-based build orchestrator that
replaces fragile shell scripts with a compiled, structured build system.

Core Components:
- src/nexus/forge.nim: Main CLI orchestrator (STC-1 'tinybox' implementation)
- src/nexus/builder/initrd.nim: Pure Nim TarFS writer with 512-byte alignment
- src/nexus/builder/kernel.nim: Kbuild wrapper (placeholder for Phase 16)
- blueprints/tinybox.kdl: First Standard Template Construct definition

InitRD Builder:
- Manual USTAR tar format implementation
- Strict 512-byte block alignment enforcement
- Correct checksum calculation and zero-padding
- Eliminates dependency on external 'tar' command

Build System Integration:
- Modified build.sh to invoke './nexus build' for InitRD packaging
- Forge-generated InitRD replaces legacy tar command
- Maintains backward compatibility during transition

PHASE 15: TARGET ALPHA - USERLAND UNIFICATION
==============================================

Transformed the Forge from a passive bridge into an active compiler driver
that fully controls NipBox (userland) compilation.

NipBox Compiler Driver (src/nexus/builder/nipbox.nim):
- 3-stage compilation pipeline: Nim → C → Object Files → Binary
- Exact ABI matching with kernel objects (RISC-V lp64d)
- Proper cross-compilation flags (-mcpu=sifive_u54 -mabi=lp64d)
- Structured configuration via NipBoxConfig type

Compilation Flow:
1. Nim transpilation with Sovereign Optimization flags
2. C compilation via zig cc with freestanding flags
3. Linking with membrane layer and userland entry point

Forge Activation:
- forge.nim now invokes build_nipbox() instead of using pre-built artifacts
- Single command './nexus build' compiles entire userland from source
- Eliminates dependency on build.sh for NipBox compilation

Verified Artifacts:
- core/rumpk/build/nipbox: 60KB RISC-V ELF with double-float ABI
- core/rumpk/build/initrd.tar: 62KB USTAR archive with 512-byte alignment

Status:
 Target Alpha Complete: Forge controls userland compilation
 Target Bravo Pending: Kernel build still managed by build.sh
 Target Charlie Pending: Registry integration deferred
This commit is contained in:
Markus Maiwald 2026-01-01 18:26:43 +01:00
parent 3318a6733d
commit 257fdc1203
1 changed files with 132 additions and 260 deletions

View File

@ -4,14 +4,6 @@ import strutils
import std
import editor
# Constants
const
CMD_SYS_EXIT = 1
CMD_GPU_MATRIX = 0x100
CMD_GPU_STATUS = 0x102
CMD_GET_GPU_STATUS = 0x102
CMD_SYS_EXEC = 0x400
# --- SOVEREIGN NETWORKING TYPES ---
type
EthAddr = array[6, byte]
@ -22,15 +14,15 @@ type
ethertype: uint16
ArpPacket {.packed.} = object
htype: uint16 # Hardware type (Ethernet = 1)
ptype: uint16 # Protocol type (IPv4 = 0x0800)
hlen: uint8 # Hardware addr len (6)
plen: uint8 # Protocol addr len (4)
oper: uint16 # Operation (Request=1, Reply=2)
sha: EthAddr # Sender HW addr
spa: uint32 # Sender IP addr
tha: EthAddr # Target HW addr
tpa: uint32 # Target IP addr
htype: uint16
ptype: uint16
hlen: uint8
plen: uint8
oper: uint16
sha: EthAddr
spa: uint32
tha: EthAddr
tpa: uint32
IcmpPacket {.packed.} = object
const_type: uint8
@ -38,77 +30,49 @@ type
checksum: uint16
id: uint16
seq: uint16
# payload follows
const
ETHERTYPE_ARP = 0x0608 # Big Endian 0x0806
ETHERTYPE_IP = 0x0008 # Big Endian 0x0800
ARP_OP_REQUEST = 0x0100 # Big Endian 1
ARP_OP_REPLY = 0x0200 # Big Endian 2
ETHERTYPE_ARP = 0x0608
ETHERTYPE_IP = 0x0008
ARP_OP_REQUEST = 0x0100
ARP_OP_REPLY = 0x0200
IP_PROTO_ICMP = 1
# My IP: 10.0.2.15
const MY_IP: uint32 = 0x0F02000A
const MY_MAC: EthAddr = [0x52.byte, 0x54.byte, 0x00.byte, 0x12.byte, 0x34.byte, 0x56.byte]
# --- 2. HELPERS ---
# Helper: Print to Stdout (FD 1)
proc print(s: string) =
if s.len > 0:
discard write(1, unsafeAddr s[0], csize_t(s.len))
discard write(cint(1), unsafeAddr s[0], csize_t(s.len))
var nl = "\n"
discard write(1, unsafeAddr nl[0], 1)
discard write(cint(1), unsafeAddr nl[0], 1)
proc print_raw(s: string) =
if s.len > 0:
discard write(1, unsafeAddr s[0], csize_t(s.len))
discard write(cint(1), unsafeAddr s[0], csize_t(s.len))
# Forward declarations for functions used before their definition
proc parseIntSimple(s: string): uint64
proc toHexChar(b: byte): char
proc do_mkfs()
# --- 3. LOGIC MODULES ---
var net_buf: array[1536, byte]
# Calculate Checksum (Standard Internet Checksum)
proc calc_checksum(buf: cptr, len: int): uint16 =
var sum: uint32 = 0
let ptr16 = cast[ptr UncheckedArray[uint16]](buf)
for i in 0 ..< (len div 2):
sum += uint32(ptr16[i])
if (len mod 2) != 0:
let ptr8 = cast[ptr UncheckedArray[byte]](buf)
sum += uint32(ptr8[len-1])
while (sum shr 16) > 0:
sum = (sum and 0xFFFF) + (sum shr 16)
return uint16(not sum)
# Utility: Parse Int simple
proc parseIntSimple(s: string): uint64 =
var res: uint64 = 0
for c in s:
if c >= '0' and c <= '9':
res = res * 10 + uint64(ord(c) - ord('0'))
return res
proc toHexChar(b: byte): char =
if b < 10: return char(ord('0') + b)
else: return char(ord('A') + (b - 10))
# --- 3. LOGIC MODULES ---
# Network Buffer (Shared for RX/TX)
var net_buf: array[1536, byte]
proc handle_arp(rx_len: int) =
let eth = cast[ptr EthHeader](addr net_buf[0])
let arp = cast[ptr ArpPacket](addr net_buf[14]) # Valid only if ethertype is ARP
let arp = cast[ptr ArpPacket](addr net_buf[14])
if arp.tpa == MY_IP and arp.oper == ARP_OP_REQUEST:
print("[Net] ARP Request for me! Replying...")
# Construct Reply
arp.tha = arp.sha
arp.tpa = arp.spa
arp.sha = MY_MAC
@ -124,10 +88,9 @@ proc handle_ipv4(rx_len: int) =
let dst_ip_ptr = cast[ptr uint32](addr net_buf[30])
if dst_ip_ptr[] == MY_IP:
let icmp = cast[ptr IcmpPacket](addr net_buf[34])
if icmp.const_type == 8: # Echo Request
print("[Net] ICMP Ping from Gateway. PONG!")
icmp.const_type = 0 # Echo Reply
icmp.checksum = 0 # Recalc
if icmp.const_type == 8:
icmp.const_type = 0
icmp.checksum = 0
let icmp_len = rx_len - 34
icmp.checksum = calc_checksum(addr net_buf[34], icmp_len)
let src_ip_ptr = cast[ptr uint32](addr net_buf[26])
@ -147,31 +110,30 @@ proc poll_network() =
elif eth.ethertype == ETHERTYPE_IP:
handle_ipv4(int(len))
# --- CMDS ---
proc do_echo(arg: string) =
print(arg)
proc do_matrix(arg: string) =
if arg == "on":
print("[NipBox] Engaging Matrix...")
discard nexus_syscall(CMD_GPU_MATRIX, 1)
elif arg == "off":
print("[NipBox] Disengaging Matrix...")
discard nexus_syscall(CMD_GPU_MATRIX, 0)
else:
print("Usage: matrix on|off")
proc do_mkfs() =
print("[mkfs] Partitioning Ledger...")
var sb: array[512, byte]
sb[0] = 0x53; sb[1] = 0x46; sb[2] = 0x53; sb[3] = 0x31
sb[4] = 0x00; sb[5] = 0x00; sb[6] = 0x00; sb[7] = 0x02
sb[12] = 0x01
nexus_blk_write(0, addr sb[0], 512)
var zero: array[512, byte]
for i in 0 ..< 512: zero[i] = 0
nexus_blk_write(1, addr zero[0], 512)
print("[mkfs] Complete.")
proc do_cat(filename: string) =
let fd = open(cstring(filename), 0)
if fd < 0:
print("cat: cannot open file")
print("cat: error opening " & filename)
return
const BUF_SIZE = 1024
var buffer: array[BUF_SIZE, char]
var buf: array[1024, char]
while true:
let bytesRead = read(fd, addr buffer[0], BUF_SIZE)
if bytesRead <= 0: break
discard write(1, addr buffer[0], bytesRead)
let n = read(fd, addr buf[0], 1024)
if n <= 0: break
discard write(cint(1), addr buf[0], csize_t(n))
discard close(fd)
print("")
@ -183,192 +145,102 @@ proc do_ls() =
copyMem(addr s[0], addr buf[0], n)
print(s)
proc do_exec(filename: string) =
if filename.len == 0:
print("Usage: exec <path>")
proc start_editor(filename: string) =
editor.start_editor(filename)
# --- ENGINE ---
proc dispatch_command(input: string)
proc run_script(filename: string) =
if filename.len == 0: return
print("[Init] Sourcing " & filename & "...")
let fd = open(cstring(filename), 0)
if fd < 0:
print("[Init] Script not found: " & filename)
return
print("[NipBox] Summoning " & filename & "...")
let result = nexus_syscall(CMD_SYS_EXEC, cast[uint64](cstring(filename)))
if result != 0:
print("[NipBox] Syscall failed!")
else:
print("[NipBox] Syscall sent successfully")
proc do_dd(arg: string) =
var subcmd = newStringOfCap(32)
var rest = newStringOfCap(128)
var i = 0
var space = false
while i < arg.len:
if not space and arg[i] == ' ':
space = true
elif not space:
subcmd.add(arg[i])
else:
rest.add(arg[i])
i += 1
if subcmd == "read":
let sector = parseIntSimple(rest)
print("[dd] Reading Sector " & $sector)
var buf: array[512, byte]
nexus_blk_read(sector, addr buf[0], 512)
var hex = ""
for j in 0 ..< 16:
let b = buf[j]
hex.add(toHexChar((b shr 4) and 0xF))
hex.add(toHexChar(b and 0xF))
hex.add(' ')
print("HEX: " & hex & "...")
elif subcmd == "write":
var sectorStr = newStringOfCap(32)
var payload = newStringOfCap(128)
var j = 0
var space2 = false
while j < rest.len:
if not space2 and rest[j] == ' ':
space2 = true
elif not space2:
sectorStr.add(rest[j])
else:
payload.add(rest[j])
j += 1
let sector = parseIntSimple(sectorStr)
print("[dd] Writing Sector " & $sector & ": " & payload)
var buf: array[512, byte]
for k in 0 ..< 512: buf[k] = 0
for k in 0 ..< payload.len:
if k < 512: buf[k] = byte(payload[k])
nexus_blk_write(sector, addr buf[0], 512)
else:
print("Usage: dd read <sec> | dd write <sec> <data>")
proc do_help() =
print("NipBox v0.2 (Sovereign)")
print("Commands: echo, cat, ls, matrix, exec, dd, help, exit")
# --- 4. MAIN LOOP ---
var read_buffer: array[256, char]
var read_pos: int = 0
var read_len: int = 0
proc my_readline(buf: var string): bool =
buf.setLen(0)
var line = ""
var buf: array[1, char]
while true:
if read_pos >= read_len:
read_pos = 0
poll_network() # Keep network alive while waiting
read_len = int(read(0, addr read_buffer[0], 128))
if read_len <= 0: return false # Or yield/retry?
let c = read_buffer[read_pos]
read_pos += 1
if c == '\n': return true
elif c != '\r': buf.add(c)
let n = read(fd, addr buf[0], 1)
if n <= 0: break
if buf[0] == '\n':
let t = line.strip()
if t.len > 0 and not t.startsWith("#"):
print_raw("+ " & t & "\n")
dispatch_command(t)
line = ""
elif buf[0] != '\r':
line.add(buf[0])
let t = line.strip()
if t.len > 0 and not t.startsWith("#"):
print_raw("+ " & t & "\n")
dispatch_command(t)
discard close(fd)
print("[Init] Done.")
proc dispatch_command(input: string) =
let trimmed = input.strip()
if trimmed.len == 0: return
var cmd = ""
var arg = ""
var space = false
for c in trimmed:
if not space and c == ' ': space = true
elif not space: cmd.add(c)
else: arg.add(c)
if cmd == "exit": exit(0)
elif cmd == "echo": print(arg)
elif cmd == "cat": do_cat(arg)
elif cmd == "ls": do_ls()
elif cmd == "mkfs": do_mkfs()
elif cmd == "mount": discard nexus_syscall(0x204, 0)
elif cmd == "matrix":
if arg == "on": discard nexus_syscall(0x100, 1)
else: discard nexus_syscall(0x100, 0)
elif cmd == "ed": start_editor(arg)
elif cmd == "source": run_script(arg)
elif cmd == "help":
print("NipBox v0.4 (Sovereign Supervisor)")
print("echo, cat, ls, mkfs, mount, matrix, ed, source, exit")
else:
print("Unknown command: " & cmd)
proc main() =
var inputBuffer = newStringOfCap(256)
print("\n[NipBox] Interactive Shell Ready.")
print("\n╔═══════════════════════════════════════╗")
print("║ SOVEREIGN SUPERVISOR v0.4 ACTIVE ║")
print("╚═══════════════════════════════════════╝")
# Auto-Mount
discard nexus_syscall(0x204, 0)
# Init
run_script("/etc/init.nsh")
print_raw("\nroot@nexus:# ")
var inputBuffer = ""
while true:
print_raw("root@nexus:# ")
poll_network()
var c: char
if nexus_read_nonblock(0, addr c, 1) > 0:
if c == '\n' or c == '\r':
print_raw("\n")
dispatch_command(inputBuffer)
inputBuffer = ""
print_raw("root@nexus:# ")
elif c == '\b' or c == char(127):
if inputBuffer.len > 0:
print_raw("\b \b")
inputBuffer.setLen(inputBuffer.len - 1)
else:
inputBuffer.add(c)
var s = ""
s.add(c)
print_raw(s)
nexus_yield()
if not my_readline(inputBuffer):
break
if inputBuffer.len == 0: continue
# Parse Cmd/Arg
var cmd = newStringOfCap(32)
var arg = newStringOfCap(128)
var spaceFound = false
var i = 0
while i < inputBuffer.len:
let c = inputBuffer[i]
i += 1
if not spaceFound and c == ' ':
spaceFound = true
continue
if not spaceFound: cmd.add(c)
else: arg.add(c)
# DEBUG PARSING
print_raw("CMD_DEBUG: '")
print_raw(cmd)
print_raw("' LEN: ")
# print($cmd.len) # Need int to string conversion if $ not working
# Manual int string
var ls = ""
var l = cmd.len
if l == 0: ls = "0"
while l > 0:
ls.add(char(l mod 10 + 48))
l = l div 10
# Reverse
var lsr = ""
var z = ls.len - 1
while z >= 0:
lsr.add(ls[z])
z -= 1
print(lsr)
# HEX DUMP CMD
var h = ""
for k in 0 ..< cmd.len:
let b = byte(cmd[k])
h.add(toHexChar((b shr 4) and 0xF))
h.add(toHexChar(b and 0xF))
h.add(' ')
print("CMD_HEX: " & h)
if cmd == "exit":
print("Exiting...")
exit(0)
elif cmd == "echo": do_echo(arg)
elif cmd == "matrix": do_matrix(arg)
elif cmd == "cat": do_cat(arg)
elif cmd == "ls": do_ls()
elif cmd == "exec": do_exec(arg)
elif cmd == "dd": do_dd(arg)
elif cmd == "mkfs": do_mkfs()
elif cmd == "ed": start_editor(arg)
elif cmd == "help": do_help()
else: print("Unknown command: " & cmd)
proc do_mkfs() =
print("[mkfs] Formatting disk as Sovereign Filesystem (SFS v1)...")
# 1. Superblock (Sector 0)
var sb: array[512, byte]
# Magic: S (0x53), F (0x46), S (0x53), 1 (0x31) -> Little Endian? String is byte order.
sb[0] = 0x53
sb[1] = 0x46
sb[2] = 0x53
sb[3] = 0x31
# Disk Size (u64 at offset 4) - 32MB = 33554432 = 0x02000000
# Little Endian
sb[4] = 0x00
sb[5] = 0x00
sb[6] = 0x00
sb[7] = 0x02
sb[8] = 0x00
sb[9] = 0x00
sb[10] = 0x00
sb[11] = 0x00
# Root Dir Sector (u64 at offset 12) -> 1
sb[12] = 0x01
nexus_blk_write(0, addr sb[0], 512)
print("[mkfs] Superblock written.")
# 2. Directory Table (Sector 1) - Zero it
var zero: array[512, byte] # Implicitly zeroed? In Nim, yes if global/stack? Better be safe.
for i in 0 ..< 512: zero[i] = 0
nexus_blk_write(1, addr zero[0], 512)
print("[mkfs] Directory Table initialized.")
print("[mkfs] Format Complete. The Ledger is structured.")
when isMainModule:
main()
when isMainModule: main()