# SPDX-License-Identifier: LSL-1.0 # Copyright (c) 2026 Markus Maiwald # Stewardship: Self Sovereign Society Foundation # # This file is part of the Nexus Sovereign Core. # See legal/LICENSE_SOVEREIGN.md for license terms. ## Nexus Membrane: POSIX Core Shim # Markus Maiwald (Architect) | Voxis Forge (AI) # libc.nim - Sovereign Libc for Nexus # (C) 2026 Markus Maiwald import ion_client import net_glue # memcpy removed to avoid C header conflict proc syscall*(nr: int, a0: uint64 = 0, a1: uint64 = 0, a2: uint64 = 0): int = var res: int let n = cast[uint64](nr) let v0 = a0 let v1 = a1 let v2 = a2 {.emit: """ register unsigned long a7 __asm__("a7") = `n`; register unsigned long a0_ __asm__("a0") = `v0`; register unsigned long a1_ __asm__("a1") = `v1`; register unsigned long a2_ __asm__("a2") = `v2`; __asm__ volatile("ecall" : "+r"(a0_) : "r"(a7), "r"(a1_), "r"(a2_) : "memory"); `res` = (int)a0_; """.} return res # --- LIBC IO SHIMS --- when not defined(RUMPK_KERNEL): proc write*(fd: int, buf: pointer, count: uint64): int {.exportc, cdecl.} = # Always use syscall, even for stdout/stderr. Kernel handles it. return int(syscall(0x204, uint64(fd), cast[uint64](buf), count)) proc read*(fd: int, buf: pointer, count: uint64): int {.exportc, cdecl.} = # DIAGNOSTIC: Trace read() calls if fd == 0: var msg = "[LIBC] read(0) called\n" discard write(1, unsafeAddr msg[0], uint64(msg.len)) return int(syscall(0x203, uint64(fd), cast[uint64](buf), count)) proc open*(path: cstring, flags: int = 0): int {.exportc, cdecl.} = return int(syscall(0x200, cast[uint64](path), uint64(flags))) proc close*(fd: int): int {.exportc, cdecl.} = return int(syscall(0x201, uint64(fd))) proc print*(s: string) = if s.len > 0: discard write(1, unsafeAddr s[0], uint64(s.len)) proc readdir*(buf: pointer, max_len: uint64): int {.exportc, cdecl.} = return int(syscall(0x202, cast[uint64](buf), max_len)) proc exit*(status: int) {.exportc, cdecl.} = discard syscall(0x01, uint64(status)) while true: discard proc yield_fiber*() {.exportc: "yield", cdecl.} = discard syscall(0x100, 0) proc pump_membrane_stack*() {.importc, cdecl.} proc pledge*(promises: uint64): int {.exportc, cdecl.} = return int(syscall(0x101, promises)) proc spawn*(entry: pointer, arg: uint64): int {.exportc, cdecl.} = return int(syscall(0x500, cast[uint64](entry), arg)) proc join*(fid: int): int {.exportc, cdecl.} = return int(syscall(0x501, uint64(fid))) proc kexec*(entry: pointer): int {.exportc, cdecl.} = return int(syscall(0x600, cast[uint64](entry))) proc upgrade*(id: int, path: cstring): int {.exportc, cdecl.} = # Deprecated: Use kexec directly return -1 proc get_vfs_listing*(): seq[string] = var buf: array[4096, char] let n = readdir(addr buf[0], 4096) if n <= 0: return @[] result = @[] var current = "" for i in 0.. 0: result.add(current) current = "" else: current.add(buf[i]) if current.len > 0: result.add(current) # Surface API (Glyph) proc sys_surface_create*(width, height: int): int {.exportc, cdecl.} = return int(syscall(0x300, uint64(width), uint64(height))) proc sys_surface_flip*(surf_id: int = 0) {.exportc, cdecl.} = discard syscall(0x301, uint64(surf_id)) proc sys_surface_get_ptr*(surf_id: int): pointer {.exportc, cdecl.} = return cast[pointer](syscall(0x302, uint64(surf_id))) # --- NETWORK SHIMS (Membrane) --- const MAX_SOCKS = 32 FD_OFFSET = 3 # Syscalls SYS_SOCK_SOCKET = 0x900 SYS_SOCK_BIND = 0x901 SYS_SOCK_CONNECT= 0x902 SYS_SOCK_LISTEN = 0x903 SYS_SOCK_ACCEPT = 0x904 when defined(RUMPK_KERNEL): # KERNEL IMPLEMENTATION type SockState = enum CLOSED, LISTEN, CONNECTING, ESTABLISHED, FIN_WAIT NexusSock = object fd: int pcb: pointer state: SockState rx_buf: array[4096, byte] rx_len: int accepted_pcb: pointer accepted_pending: int32 var g_sockets: array[MAX_SOCKS, NexusSock] var g_sock_used: array[MAX_SOCKS, bool] proc membrane_init*() {.importc, cdecl.} proc pump_membrane_stack*() {.importc, cdecl.} proc rumpk_yield_internal() {.importc, cdecl.} proc glue_connect(sock: ptr NexusSock, ip: uint32, port: uint16): int {.importc, cdecl.} proc glue_bind(sock: ptr NexusSock, port: uint16): int {.importc, cdecl.} proc glue_listen(sock: ptr NexusSock): int {.importc, cdecl.} proc glue_accept_peek(sock: ptr NexusSock): pointer {.importc, cdecl.} proc glue_setup_socket(sock: ptr NexusSock, pcb: pointer) {.importc, cdecl.} proc glue_write(sock: ptr NexusSock, buf: pointer, len: int): int {.importc, cdecl.} proc glue_read(sock: ptr NexusSock, buf: pointer, len: int): int {.importc, cdecl.} proc glue_close(sock: ptr NexusSock): int {.importc, cdecl.} const MAX_FILES = 16 FILE_FD_START = 100 type FDType = enum FD_NONE, FD_SOCKET, FD_FILE FD_Entry = object kind: FDType path: array[64, char] # For files var g_fd_table: array[256, FD_Entry] proc alloc_sock(): int = for i in 0..= MAX_SOCKS: return nil if not g_sock_used[idx]: return nil if g_fd_table[fd].kind != FD_SOCKET: return nil return addr g_sockets[idx] proc libc_is_socket_fd*(fd: int): bool {.exportc: "libc_is_socket_fd", cdecl.} = if fd < 0 or fd >= 256: return false return g_fd_table[fd].kind == FD_SOCKET proc libc_impl_socket*(domain, sock_type, proto: int): int {.exportc: "libc_impl_socket", cdecl.} = let idx = alloc_sock() if idx < 0: return -1 return idx + FD_OFFSET proc libc_impl_bind*(fd: int, addr_ptr: pointer, len: int): int {.exportc: "libc_impl_bind", cdecl.} = let sock = get_sock(fd) if sock == nil: return -1 let port_ptr = cast[ptr uint16](cast[uint64](addr_ptr) + 2) return glue_bind(sock, port_ptr[]) proc libc_impl_listen*(fd: int, backlog: int): int {.exportc: "libc_impl_listen", cdecl.} = let sock = get_sock(fd) if sock == nil: return -1 return glue_listen(sock) proc libc_impl_accept*(fd: int, addr_ptr: pointer, len_ptr: pointer): int {.exportc: "libc_impl_accept", cdecl.} = let listener = get_sock(fd) if listener == nil: return -1 while true: pump_membrane_stack() let client_pcb = glue_accept_peek(listener) if client_pcb != nil: let client_idx = alloc_sock() if client_idx < 0: return -1 let client = addr g_sockets[client_idx] glue_setup_socket(client, client_pcb) client.state = ESTABLISHED return client.fd rumpk_yield_internal() proc libc_impl_connect*(fd: int, addr_ptr: pointer, len: int): int {.exportc: "libc_impl_connect", cdecl.} = let sock = get_sock(fd) if sock == nil: return -1 let ip_ptr = cast[ptr uint32](cast[uint64](addr_ptr) + 4) let port_ptr = cast[ptr uint16](cast[uint64](addr_ptr) + 2) let res = glue_connect(sock, ip_ptr[], port_ptr[]) while sock.state == CONNECTING: pump_membrane_stack() rumpk_yield_internal() if sock.state == ESTABLISHED: return 0 return -1 proc libc_impl_recv*(fd: int, buf: pointer, count: uint64): int {.exportc: "libc_impl_recv", cdecl.} = let sock = get_sock(fd) if sock == nil: return -1 while true: pump_membrane_stack() let n = glue_read(sock, buf, int(count)) if n > 0: return n if sock.state == CLOSED: return 0 rumpk_yield_internal() proc libc_impl_send*(fd: int, buf: pointer, count: uint64): int {.exportc: "libc_impl_send", cdecl.} = let sock = get_sock(fd) if sock == nil: return -1 let res = glue_write(sock, buf, int(count)) pump_membrane_stack() return res proc libc_impl_close_socket*(fd: int): int {.exportc: "libc_impl_close_socket", cdecl.} = let sock = get_sock(fd) if sock == nil: return -1 discard glue_close(sock) let idx = fd - FD_OFFSET g_sock_used[idx] = false return 0 # --- VFS SHIMS --- # These route POSIX file calls to our Sovereign File System (SFS) proc sfs_open_file*(path: cstring, flags: int): int32 {.importc, cdecl.} proc sfs_read_file*(path: cstring, dest: pointer, max_len: int): int {.importc, cdecl.} proc sfs_write_file*(name: cstring, data: pointer, data_len: int) {.importc, cdecl.} proc libc_impl_open*(path: cstring, flags: int): int {.exportc: "libc_impl_open", cdecl.} = # Find free File FD for i in FILE_FD_START..<255: if g_fd_table[i].kind == FD_NONE: g_fd_table[i].kind = FD_FILE let p_str = $path let to_copy = min(p_str.len, 63) for j in 0..= 0 and fd < 256: if g_fd_table[fd].kind == FD_FILE: let path = cast[cstring](addr g_fd_table[fd].path[0]) return sfs_read_file(path, buf, int(count)) elif g_fd_table[fd].kind == FD_SOCKET: return libc_impl_recv(fd, buf, count) return -1 proc libc_impl_write*(fd: int, buf: pointer, count: uint64): int {.exportc: "libc_impl_write", cdecl.} = if fd == 1 or fd == 2: return int(syscall(0x204, uint64(fd), cast[uint64](buf), count)) if fd >= 0 and fd < 256: if g_fd_table[fd].kind == FD_FILE: let path = cast[cstring](addr g_fd_table[fd].path[0]) sfs_write_file(path, buf, int(count)) return int(count) elif g_fd_table[fd].kind == FD_SOCKET: return libc_impl_send(fd, buf, count) return -1 proc libc_impl_close*(fd: int): int {.exportc: "libc_impl_close", cdecl.} = if fd < 0 or fd >= 256: return -1 if g_fd_table[fd].kind == FD_SOCKET: discard libc_impl_close_socket(fd) g_fd_table[fd].kind = FD_NONE return 0 else: # USER WRAPPERS proc socket*(domain, sock_type, protocol: int): int {.exportc, cdecl.} = return int(syscall(SYS_SOCK_SOCKET, uint64(domain), uint64(sock_type), uint64(protocol))) proc bind_socket*(fd: int, addr_ptr: pointer, len: int): int {.exportc: "bind", cdecl.} = return int(syscall(SYS_SOCK_BIND, uint64(fd), cast[uint64](addr_ptr), uint64(len))) proc connect*(fd: int, addr_ptr: pointer, len: int): int {.exportc, cdecl.} = return int(syscall(SYS_SOCK_CONNECT, uint64(fd), cast[uint64](addr_ptr), uint64(len))) proc listen*(fd: int, backlog: int): int {.exportc, cdecl.} = return int(syscall(SYS_SOCK_LISTEN, uint64(fd), uint64(backlog))) proc accept*(fd: int, addr_ptr: pointer, len_ptr: pointer): int {.exportc, cdecl.} = return int(syscall(SYS_SOCK_ACCEPT, uint64(fd), cast[uint64](addr_ptr), cast[uint64](len_ptr))) proc send*(fd: int, buf: pointer, count: uint64, flags: int): int {.exportc, cdecl.} = return int(syscall(0x204, uint64(fd), cast[uint64](buf), count)) proc recv*(fd: int, buf: pointer, count: uint64, flags: int): int {.exportc, cdecl.} = return int(syscall(0x203, uint64(fd), cast[uint64](buf), count)) # ========================================================= # lwIP Syscall Bridge (SPEC-400, SPEC-401) # ========================================================= # The Graft: These C-compatible exports provide the kernel interface # required by sys_arch.c without pulling in kernel-only code. proc syscall_get_time_ns*(): uint64 {.exportc, cdecl.} = ## Get monotonic time in nanoseconds from kernel ## Used by lwIP's sys_now() for timer management # TODO: Add dedicated syscall 0x700 for TIME # For now, use rdtime directly (architecture-specific) var ticks: uint64 {.emit: """ #if defined(__riscv) __asm__ volatile ("rdtime %0" : "=r"(`ticks`)); // RISC-V QEMU virt: 10MHz timer -> 100ns per tick `ticks` = `ticks` * 100; #elif defined(__aarch64__) __asm__ volatile ("mrs %0, cntvct_el0" : "=r"(`ticks`)); // ARM64: Assume 1GHz for now (should read cntfrq_el0) // `ticks` = `ticks`; #else `ticks` = 0; #endif """.} return ticks proc syscall_get_random*(): uint32 {.exportc, cdecl.} = ## Generate cryptographically strong random number for TCP ISN ## Implementation: SipHash-2-4(MonolithKey, Time || CycleCount) ## Per SPEC-401: Hash Strategy let sys = get_sys_table() # Get high-resolution time let time_ns = syscall_get_time_ns() # Mix time with itself (upper/lower bits) var mix_data: array[16, byte] copyMem(addr mix_data[0], unsafeAddr time_ns, 8) # Add cycle counter for additional entropy var cycles: uint64 {.emit: """ #if defined(__riscv) __asm__ volatile ("rdcycle %0" : "=r"(`cycles`)); #else `cycles` = 0; #endif """.} copyMem(addr mix_data[8], unsafeAddr cycles, 8) # Use SipHash with system key (SPEC-401) # TODO: Use actual Monolith key when available var key: array[16, byte] for i in 0..<16: key[i] = byte(i xor 0xAA) # Temporary key (Phase 39: Use Monolith) var hash_out: array[16, byte] if sys.fn_siphash != nil: sys.fn_siphash(addr key, addr mix_data[0], 16, addr hash_out) # Return first 32 bits var rnd: uint32 copyMem(addr rnd, addr hash_out[0], 4) return rnd else: # Fallback: XOR mixing if SipHash unavailable return uint32(time_ns xor (time_ns shr 32) xor cycles) proc syscall_panic*() {.exportc, cdecl, noreturn.} = ## Trigger kernel panic from lwIP assertion failure ## Routes to kernel's EXIT syscall discard syscall(0x01, 255) # EXIT with error code 255 while true: discard # noreturn