From a183dc7adf65e3afef9379b8d4aef15e49930060 Mon Sep 17 00:00:00 2001 From: Markus Maiwald Date: Thu, 1 Jan 2026 18:26:43 +0100 Subject: [PATCH] Phase 14-15: Nexus Forge - Software Defined OS Build System MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/nipbox.nim | 392 +++++++++++++++++-------------------------------- 1 file changed, 132 insertions(+), 260 deletions(-) diff --git a/src/nipbox.nim b/src/nipbox.nim index d1407af..5052956 100644 --- a/src/nipbox.nim +++ b/src/nipbox.nim @@ -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 ") +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 | dd write ") - -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()