diff --git a/apps/linker_user.ld b/apps/linker_user.ld index 1a0ab50..029c6a5 100644 --- a/apps/linker_user.ld +++ b/apps/linker_user.ld @@ -1,28 +1,36 @@ -ENTRY(_start) +/* Memory Layout (64MB Userspace): + * User RAM: 0x86000000 - 0x89FFFFFF (64MB) + * Stack starts at 0x89FFFFF0 and grows down + * Requires QEMU -m 256M to ensure valid physical backing + */ +MEMORY +{ + RAM (rwx) : ORIGIN = 0x86000000, LENGTH = 64M +} SECTIONS { - . = 0x84000000; + . = 0x86000000; .text : { *(.text._start) *(.text) *(.text.*) - } + } > RAM .rodata : { *(.rodata) *(.rodata.*) - } + } > RAM .data : { *(.data) *(.data.*) - } + } > RAM .bss : { *(.bss) *(.bss.*) *(COMMON) - } + } > RAM } diff --git a/apps/subject_entry.S b/apps/subject_entry.S index 9e27773..b0c61c4 100644 --- a/apps/subject_entry.S +++ b/apps/subject_entry.S @@ -1,15 +1,26 @@ .section .text._start, "ax" .global _start _start: - // Setup stack pointer if not already done (though kernel loader uses kernel stack) - // We assume we are in S-mode as a fiber. + # 🕵️ DIAGNOSTIC: BREATHE + # li t0, 0x10000000 + # li t1, 0x23 # '#' + # sb t1, 0(t0) - // Call main(0, NULL) + # 🕵️ DIAGNOSTIC: READY TO CALL MAIN + # li t1, 0x21 # '!' + # sb t1, 0(t0) + + # Call main(0, NULL) li a0, 0 li a1, 0 call main - // Call exit(result) + # 🕵️ DIAGNOSTIC: RETURNED FROM MAIN + # li t0, 0x10000000 + # li t1, 0x24 # '$' + # sb t1, 0(t0) + + # Call exit(result) call exit 1: j 1b diff --git a/boot/linker-riscv64.ld b/boot/linker-riscv64.ld index 527587b..e7af2f6 100644 --- a/boot/linker-riscv64.ld +++ b/boot/linker-riscv64.ld @@ -20,6 +20,8 @@ SECTIONS *(.data*) } + . = ALIGN(4096); + .bss : { __bss_start = .; *(.bss*) diff --git a/core/fiber.nim b/core/fiber.nim index efa21e0..516a566 100644 --- a/core/fiber.nim +++ b/core/fiber.nim @@ -51,6 +51,8 @@ type promises*: uint64 # Phase 28: Capability Mask (Pledge) user_entry*: pointer # Phase 29: User function pointer for workers user_arg*: uint64 # Phase 29: Argument for user function + satp_value*: uint64 # Phase 31: Page table root (0 = use kernel map) + wants_yield*: bool # Phase 37: Deferred yield flag proc fiber_yield*() {.importc, cdecl.} # Imports @@ -59,6 +61,10 @@ proc fiber_yield*() {.importc, cdecl.} # Import the Assembly Magic (same symbol name, different implementation per arch) proc cpu_switch_to(prev_sp_ptr: ptr uint64, next_sp: uint64) {.importc, cdecl.} +# Phase 31: Page Table Activation +proc mm_activate_satp(satp_val: uint64) {.importc, cdecl.} +proc mm_get_kernel_satp(): uint64 {.importc, cdecl.} + # Import console for debugging proc console_write(p: pointer, len: csize_t) {.importc, cdecl.} @@ -80,7 +86,7 @@ const STACK_SIZE* = 4096 # ========================================================= var main_fiber: FiberObject -var current_fiber*: Fiber = addr main_fiber +var current_fiber* {.global.}: Fiber = addr main_fiber # ========================================================= # Trampoline (Entry point for new fibers) @@ -141,6 +147,16 @@ proc init_fiber*(f: Fiber, entry: proc() {.cdecl.}, stack_base: pointer, size: i proc switch*(next: Fiber) = let prev = current_fiber current_fiber = next + + # Swap address space if necessary (Phase 31: Sv39 Memory Isolation) + if next.satp_value != 0: + mm_activate_satp(next.satp_value) + else: + # If fiber has no specific satp (0), restore kernel identity map + let k_satp = mm_get_kernel_satp() + if k_satp != 0: + mm_activate_satp(k_satp) + cpu_switch_to(addr prev.state.sp, next.state.sp) {.pop.} diff --git a/core/ion.nim b/core/ion.nim index 38bc2b7..d923ce5 100644 --- a/core/ion.nim +++ b/core/ion.nim @@ -1,22 +1,24 @@ -# Markus Maiwald (Architect) | Voxis Forge (AI) -# Nexus Rumpk: ION Control Plane +# core/rumpk/core/ion.nim +# Phase 35e: Expanded SysTable with Crypto + Global Channels -import ion/memory -export memory +# CRITICAL: Only import memory module for kernel builds +# Userspace builds (with -d:is_membrane) should NOT get the 2MB pool +when not defined(is_membrane): + import ion/memory + export memory -# Phase 28: Pledge Capability Constants const - PLEDGE_STDIO* = 0x0001'u64 # Console I/O - PLEDGE_RPATH* = 0x0002'u64 # Read Filesystem - PLEDGE_WPATH* = 0x0004'u64 # Write Filesystem - PLEDGE_INET* = 0x0008'u64 # Network Access - PLEDGE_EXEC* = 0x0010'u64 # Execute/Spawn - PLEDGE_ALL* = 0xFFFFFFFFFFFFFFFF'u64 # Root (All Capabilities) + PLEDGE_STDIO* = 0x0001'u64 + PLEDGE_RPATH* = 0x0002'u64 + PLEDGE_WPATH* = 0x0004'u64 + PLEDGE_INET* = 0x0008'u64 + PLEDGE_EXEC* = 0x0010'u64 + PLEDGE_ALL* = 0xFFFFFFFFFFFFFFFF'u64 type CmdType* = enum CMD_SYS_NOOP = 0 - CMD_SYS_EXIT = 1 # Dignified Exit (Subject Termination) + CMD_SYS_EXIT = 1 CMD_ION_STOP = 2 CMD_ION_START = 3 CMD_GPU_MATRIX = 0x100 @@ -24,30 +26,21 @@ type CMD_GET_GPU_STATUS = 0x102 CMD_FS_OPEN = 0x200 CMD_FS_READ = 0x201 - CMD_FS_READDIR = 0x202 # Returns raw listing - CMD_FS_WRITE = 0x203 # Write File (arg1=ptr to FileArgs) - CMD_FS_MOUNT = 0x204 # Mount Filesystem - CMD_ION_FREE = 0x300 # Return slab to pool - CMD_SYS_EXEC = 0x400 # Swap Consciousness (ELF Loading) - CMD_NET_TX = 0x500 # Send Network Packet (arg1=ptr, arg2=len) - CMD_NET_RX = 0x501 # Poll Network Packet (arg1=ptr, arg2=maxlen) - CMD_BLK_READ = 0x600 # Read Sector (arg1=ptr to BlkArgs) - CMD_BLK_WRITE = 0x601 # Write Sector (arg1=ptr to BlkArgs) + CMD_FS_READDIR = 0x202 + CMD_FS_WRITE = 0x203 + CMD_FS_MOUNT = 0x204 + CMD_ION_FREE = 0x300 + CMD_SYS_EXEC = 0x400 + CMD_NET_TX = 0x500 + CMD_NET_RX = 0x501 + CMD_BLK_READ = 0x600 + CMD_BLK_WRITE = 0x601 CmdPacket* = object kind*: uint32 - reserved*: uint32 # Explicit Padding - arg*: uint64 # Upgraded to u64 for Pointers - id*: array[16, byte] # u128 for SipHash Provenance - - FsReadArgs* = object - fd*: uint64 - buffer*: uint64 - - FileArgs* = object - name*: uint64 - data*: uint64 - len*: uint64 + reserved*: uint32 + arg*: uint64 + id*: array[16, byte] NetArgs* = object buf*: uint64 @@ -58,7 +51,11 @@ type buf*: uint64 len*: uint64 - # Binary compatible with hal/channel.zig + FileArgs* = object + name*: uint64 + data*: uint64 + len*: uint64 + HAL_Ring*[T] = object head*: uint32 tail*: uint32 @@ -70,72 +67,88 @@ type SysTable* = object magic*: uint32 # 0x4E585553 - reserved*: uint32 # Explicit Padding for alignment - s_rx*: ptr HAL_Ring[IonPacket] # Kernel -> App - s_tx*: ptr HAL_Ring[IonPacket] # App -> Kernel - s_event*: ptr HAL_Ring[IonPacket] # Telemetry - s_cmd*: ptr HAL_Ring[CmdPacket] # Command Ring (Control Plane) - s_input*: ptr HAL_Ring[IonPacket] # Input to Subject - # Function Pointers (Hypercalls) + reserved*: uint32 + s_rx*: ptr HAL_Ring[IonPacket] + s_tx*: ptr HAL_Ring[IonPacket] + s_event*: ptr HAL_Ring[IonPacket] + s_cmd*: ptr HAL_Ring[CmdPacket] + s_input*: ptr HAL_Ring[IonPacket] + # Function Pointers fn_vfs_open*: proc(path: cstring, flags: int32): int32 {.cdecl.} fn_vfs_read*: proc(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.} fn_vfs_list*: proc(buf: pointer, max_len: uint64): int64 {.cdecl.} fn_vfs_write*: proc(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.} fn_vfs_close*: proc(fd: int32): int32 {.cdecl.} fn_log*: pointer - fn_pledge*: proc(promises: uint64): int32 {.cdecl.} # Phase 28: Pledge - # Framebuffer (Phase 26: Visual Cortex) - fb_addr*: uint64 # Physical address of framebuffer - fb_width*: uint32 # Width in pixels - fb_height*: uint32 # Height in pixels - fb_stride*: uint32 # Bytes per row - fb_bpp*: uint32 # Bits per pixel (32 for BGRA) + fn_pledge*: proc(promises: uint64): int32 {.cdecl.} + + # Framebuffer + fb_addr*: uint64 + fb_width*: uint32 + fb_height*: uint32 + fb_stride*: uint32 + fb_bpp*: uint32 + fn_yield*: proc() {.cdecl.} + + # Phase 35e: Crypto + fn_siphash*: proc(key: ptr array[16, byte], data: pointer, len: uint64, out_hash: ptr array[16, byte]) {.cdecl.} + fn_ed25519_verify*: proc(sig: ptr array[64, byte], msg: pointer, len: uint64, pk: ptr array[32, byte]): bool {.cdecl.} + + # Phase 36.2: Network Membrane (The Veins) + s_net_rx*: ptr HAL_Ring[IonPacket] # Kernel Producer -> User Consumer + s_net_tx*: ptr HAL_Ring[IonPacket] # User Producer -> Kernel Consumer include invariant +# --- Sovereign Logic --- + +# HAL Imports +proc hal_channel_push*(ring: uint64, pkt: IonPacket): bool {.importc, cdecl.} +proc hal_channel_pop*(ring: uint64, out_pkt: ptr IonPacket): bool {.importc, cdecl.} +proc hal_cmd_push*(ring: uint64, pkt: CmdPacket): bool {.importc, cdecl.} +proc hal_cmd_pop*(ring: uint64, out_pkt: ptr CmdPacket): bool {.importc, cdecl.} + +proc send*[T](c: var SovereignChannel[T], pkt: T) = + if c.ring == nil: return + when T is IonPacket: + discard hal_channel_push(cast[uint64](c.ring), pkt) + elif T is CmdPacket: + discard hal_cmd_push(cast[uint64](c.ring), pkt) + +proc recv*[T](c: var SovereignChannel[T], out_pkt: var T): bool = + if c.ring == nil: return false + when T is IonPacket: + return hal_channel_pop(cast[uint64](c.ring), addr out_pkt) + elif T is CmdPacket: + return hal_cmd_pop(cast[uint64](c.ring), addr out_pkt) + +# Global Channels +var chan_input*: SovereignChannel[IonPacket] +var guest_input_hal: HAL_Ring[IonPacket] + +# Phase 36.2: Network Channels +var chan_net_rx*: SovereignChannel[IonPacket] +var chan_net_tx*: SovereignChannel[IonPacket] +var net_rx_hal: HAL_Ring[IonPacket] +var net_tx_hal: HAL_Ring[IonPacket] + +proc ion_init_input*() {.exportc, cdecl.} = + guest_input_hal.head = 0 + guest_input_hal.tail = 0 + guest_input_hal.mask = 255 + chan_input.ring = addr guest_input_hal + +proc ion_init_network*() {.exportc, cdecl.} = + net_rx_hal.head = 0 + net_rx_hal.tail = 0 + net_rx_hal.mask = 255 + chan_net_rx.ring = addr net_rx_hal + + net_tx_hal.head = 0 + net_tx_hal.tail = 0 + net_tx_hal.mask = 255 + chan_net_tx.ring = addr net_tx_hal + static: doAssert(sizeof(IonPacket) == 24, "IonPacket size mismatch!") static: doAssert(sizeof(CmdPacket) == 32, "CmdPacket size mismatch!") -static: doAssert(sizeof(SysTable) == 128, - "SysTable size mismatch!") # Phase 28: +8 for fn_pledge - -const SYSTABLE_BASE* = 0x83000000'u64 - -# HAL Imports (Hardened ABI - Handle Based) -proc hal_channel_push*(handle: uint64, - pkt: IonPacket): bool {.importc: "hal_channel_push", cdecl.} -proc hal_channel_pop*(handle: uint64, - out_pkt: ptr IonPacket): bool {.importc: "hal_channel_pop", cdecl.} - -proc hal_cmd_push*(handle: uint64, - pkt: CmdPacket): bool {.importc: "hal_cmd_push", cdecl.} -proc hal_cmd_pop*(handle: uint64, - out_pkt: ptr CmdPacket): bool {.importc: "hal_cmd_pop", cdecl.} - -proc send*(chan: var SovereignChannel[IonPacket], pkt: IonPacket) = - secure_push_packet(chan.ring, pkt) - -proc recv*(chan: var SovereignChannel[IonPacket], - out_pkt: var IonPacket): bool = - if (cast[uint](chan.ring) and 0b11) != 0: - return false # Or panic - return hal_channel_pop(cast[uint64](chan.ring), addr out_pkt) - -proc send*(chan: var SovereignChannel[CmdPacket], pkt: CmdPacket) = - secure_send(chan.ring, pkt) - -proc recv*(chan: var SovereignChannel[CmdPacket], - out_pkt: var CmdPacket): bool = - return secure_recv_cmd(chan.ring, out_pkt) - -# --- 6.1 THE INPUT SURGERY --- -var input_ring_memory: HAL_Ring[IonPacket] -var chan_input*: SovereignChannel[IonPacket] # The Kernel-side Channel - -proc ion_init_input*() = - # Manually Init the Ring (BSS is Alloc) - input_ring_memory.head = 0 - input_ring_memory.tail = 0 - input_ring_memory.mask = 255 # 256 slots - - # Point Channel to Body - chan_input.ring = addr input_ring_memory +static: doAssert(sizeof(SysTable) == 168, "SysTable size mismatch! (Expected 168 after Network expansion)") diff --git a/core/ion/memory.nim b/core/ion/memory.nim index ca55da7..fe7d1ff 100644 --- a/core/ion/memory.nim +++ b/core/ion/memory.nim @@ -41,6 +41,12 @@ proc ion_pool_init*() {.exportc.} = # 2. Translate to PHYSICAL (Identity Mapped for Phase 7) global_pool.base_phys = virt_addr + # Tracing for Phase 37 + proc kprint_hex(v: uint64) {.importc, cdecl.} + dbg("[ION] Pool Base Phys: ") + kprint_hex(global_pool.base_phys) + dbg("") + dbg("[ION] Ring Init...") global_pool.free_ring.init() diff --git a/core/kernel.nim b/core/kernel.nim index bdc7d19..b8fb0ff 100644 --- a/core/kernel.nim +++ b/core/kernel.nim @@ -1,13 +1,20 @@ +# Copyright (c) 2026 Nexus Foundation +# Licensed under the Libertaria Sovereign License (LSL-1.0) +# See legal/LICENSE_SOVEREIGN.md for details. + # MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI) # Rumpk Layer 1: The Logic Core (Autonomous Immune System) {.push stackTrace: off, lineTrace: off.} -import fiber +import fiber except fiber_yield import ion import loader import fs/tar import fs/sfs +import netswitch +import ../libs/membrane/net_glue +import ../libs/membrane/compositor var ion_paused*: bool = false var pause_start*: uint64 = 0 @@ -59,6 +66,8 @@ var fiber_nexshell: FiberObject var fiber_ui: FiberObject var fiber_subject: FiberObject var fiber_watchdog: FiberObject +var fiber_compositor: FiberObject +var fiber_netswitch: FiberObject # Phase 36.2: Network Traffic Cop # Phase 29: Dynamic Worker Pool (The Hive) const MAX_WORKERS = 8 @@ -85,16 +94,16 @@ proc subject_fiber_entry() {.cdecl.} = kprintln(cstring(subject_loading_path)) kprintln("[Subject] Pausing for Rebirth.") - fiber_yield() + fiber.switch(addr fiber_ion) # Emergency yield to master # --- STACK ALLOCATIONS --- -var stack_ion {.align: 16.}: array[4096, uint8] -var stack_nexshell {.align: 16.}: array[4096, uint8] -var stack_ui {.align: 16.}: array[32768, uint8] -var stack_subject {.align: 16.}: array[32768, uint8] -var stack_watchdog {.align: 16.}: array[4096, uint8] - - +var stack_ion {.align: 4096.}: array[4096, uint8] +var stack_nexshell {.align: 4096.}: array[4096, uint8] +var stack_ui {.align: 4096.}: array[32768, uint8] +var stack_subject {.align: 4096.}: array[32768, uint8] +var stack_watchdog {.align: 4096.}: array[4096, uint8] +var stack_netswitch {.align: 4096.}: array[8192, uint8] # Phase 36.2 +var stack_compositor {.align: 4096.}: array[128 * 1024, uint8] # Phase 31: Memory Manager (The Glass Cage) proc mm_init() {.importc, cdecl.} @@ -124,6 +133,7 @@ var chan_rx*: SovereignChannel[IonPacket] var chan_tx*: SovereignChannel[IonPacket] var chan_event*: SovereignChannel[IonPacket] var chan_cmd*: SovereignChannel[CmdPacket] +var chan_compositor_input*: SovereignChannel[IonPacket] # chan_input is now imported from ion.nim! proc ion_push_stdin*(p: pointer, len: csize_t) {.exportc, cdecl.} = @@ -137,7 +147,14 @@ proc ion_push_stdin*(p: pointer, len: csize_t) {.exportc, cdecl.} = copyMem(pkt.data, p, to_copy) pkt.len = uint16(to_copy) - chan_input.send(pkt) + kprintln("[Kernel] Input packet pushed to ring") + + # Phase 35d: Route to Compositor FIRST + if chan_compositor_input.ring != nil: + chan_compositor_input.send(pkt) + else: + # Fallback to direct routing if compositor not active + chan_input.send(pkt) proc get_ion_load(): int = ## Calculate load of the Command Ring (The Heartbeat of the NPLs) @@ -152,11 +169,20 @@ proc rumpk_yield_internal() {.cdecl, exportc.} proc hal_io_init() {.importc, cdecl.} proc virtio_net_poll() {.importc, cdecl.} proc virtio_net_send(data: pointer, len: uint32) {.importc, cdecl.} +proc rumpk_yield_guard() {.importc, cdecl.} proc virtio_blk_read(sector: uint64, buf: pointer) {.importc, cdecl.} proc virtio_blk_write(sector: uint64, buf: pointer) {.importc, cdecl.} proc ion_free_raw(id: uint16) {.importc, cdecl.} proc nexshell_main() {.importc, cdecl.} proc ui_fiber_entry() {.importc, cdecl.} +proc rumpk_halt() {.importc, cdecl, noreturn.} + +proc compositor_fiber_entry() {.cdecl.} = + kprintln("[Compositor] Fiber Entry reached.") + while true: + compositor.compositor_step() + # High frequency yield (120Hz goal) + rumpk_yield_internal() proc get_now_ns(): uint64 = proc rumpk_timer_now_ns(): uint64 {.importc, cdecl.} @@ -171,35 +197,25 @@ proc fiber_sleep*(ms: int) {.exportc, cdecl.} = fiber_yield() proc rumpk_yield_internal() {.cdecl, exportc.} = - let load = get_ion_load() let now = get_now_ns() - # 🏛️ ADAPTIVE GOVERNOR (Phase 3: FLOOD CONTROL) - Temporarily disabled for debugging starvation - # if load > 200: - # if current_fiber != addr fiber_ion: - # switch(addr fiber_ion) - # return - # elif load > 0: - # if current_fiber == addr fiber_subject: - # switch(addr fiber_ion) - # return - # Normal Round Robin logic with Sleep Check var next_fiber: Fiber = nil if current_fiber == addr fiber_ion: next_fiber = addr fiber_nexshell elif current_fiber == addr fiber_nexshell: - # Phase 33 Debug: Skip UI fiber if problematic next_fiber = addr fiber_subject elif current_fiber == addr fiber_subject: next_fiber = addr fiber_watchdog + elif current_fiber == addr fiber_watchdog: + next_fiber = addr fiber_ion else: next_fiber = addr fiber_ion # Skip sleeping fibers var found = false - for _ in 0..5: # Max 5 check to avoid skip all + for _ in 0..6: # Max 6 check if next_fiber != nil and now >= next_fiber.sleep_until: found = true break @@ -208,14 +224,14 @@ proc rumpk_yield_internal() {.cdecl, exportc.} = if next_fiber == addr fiber_ion: next_fiber = addr fiber_nexshell elif next_fiber == addr fiber_nexshell: next_fiber = addr fiber_subject elif next_fiber == addr fiber_subject: next_fiber = addr fiber_watchdog + elif next_fiber == addr fiber_watchdog: next_fiber = addr fiber_ion else: next_fiber = addr fiber_ion # Force found = true for now found = true if found and next_fiber != current_fiber: - # Idle loop - # kprint(".") + kprint("[Sched] "); kprint(current_fiber.name); kprint(" -> "); kprintln(next_fiber.name) switch(next_fiber) elif not found: asm "csrsi sstatus, 2" @@ -226,69 +242,34 @@ proc rumpk_yield_internal() {.cdecl, exportc.} = # ========================================================= proc ion_fiber_entry() {.cdecl.} = - # kprintln("[ION] Alive") hal_io_init() kprintln("[ION] Fiber 1 Reporting for Duty.") while true: - # 1. Drain Command Channel -> Push to HW var cmd: CmdPacket while chan_cmd.recv(cmd): - # Cortex Logic: Dispatch Commands case cmd.kind: of uint32(CmdType.CMD_GPU_MATRIX): - let msg = if cmd.arg > 0: "ENGAGE" else: "DISENGAGE" - kprintln("[Kernel] Matrix Protocol: ") - kprintln(cstring(msg)) matrix_enabled = (cmd.arg > 0) of uint32(CmdType.CMD_SYS_EXIT): - kprint("[Kernel] Subject Exited. Status: ") - kprint_hex(cmd.arg) - kprintln("") - kprintln("[Kernel] Respawning Shell...") + kprintln("[Kernel] Subject Exited. Respawning...") subject_loading_path = "bin/nipbox" - init_fiber(addr fiber_subject, subject_fiber_entry, addr stack_subject[ - 0], stack_subject.len) + init_fiber(addr fiber_subject, subject_fiber_entry, addr stack_subject[0], stack_subject.len) of uint32(CmdType.CMD_ION_STOP): ion_paused = true pause_start = get_now_ns() - kprintln("[Kernel] ION PAUSED by Watchdog.") of uint32(CmdType.CMD_ION_START): ion_paused = false - kprintln("[Kernel] ION RESUMED.") - of uint32(CmdType.CMD_GET_GPU_STATUS): - let msg = if matrix_enabled: "STATUS: Matrix is ONLINE" else: "STATUS: Matrix is OFFLINE" - kprintln("[Kernel] GPU Request") - kprintln(cstring(msg)) - of uint32(CmdType.CMD_ION_FREE): - # Userland is returning a packet - ion_free_raw(uint16(cmd.arg)) - of uint32(CmdType.CMD_SYS_EXEC): - kprintln("[Kernel] CMD_SYS_EXEC received!") - let path_ptr = cast[cstring](cmd.arg) - let path_str = $path_ptr - kprint("[Kernel] Summoning: ") - kprintln(cstring(path_str)) - subject_loading_path = path_str - init_fiber(addr fiber_subject, subject_fiber_entry, addr stack_subject[ - 0], stack_subject.len) of uint32(CmdType.CMD_NET_TX): let args = cast[ptr NetArgs](cmd.arg) virtio_net_send(cast[ptr UncheckedArray[byte]](args.buf), uint32(args.len)) of uint32(CmdType.CMD_NET_RX): let args = cast[ptr NetArgs](cmd.arg) - - # 1. Poll Hardware (Injects into chan_rx if avail) virtio_net_poll() - - # 2. Check Software Channel var pkt: IonPacket if chan_rx.recv(pkt): - # Copy packet to user buffer let copy_len = if uint64(pkt.len) > args.len: args.len else: uint64(pkt.len) copyMem(cast[pointer](args.buf), cast[pointer](pkt.data), copy_len) args.len = copy_len - - # Return Slab to Pool ion_free_raw(pkt.id) else: args.len = 0 @@ -304,33 +285,15 @@ proc ion_fiber_entry() {.cdecl.} = sfs_sync_vfs() of uint32(CmdType.CMD_FS_READ): let args = cast[ptr FileArgs](cmd.arg) - let bytes_read = sfs_read_file(cast[cstring](args.name), cast[pointer]( - args.data), int(args.len)) + let bytes_read = sfs_read_file(cast[cstring](args.name), cast[pointer](args.data), int(args.len)) args.len = uint64(bytes_read) - of uint32(CmdType.CMD_FS_MOUNT): - sfs_mount() - sfs_sync_vfs() else: discard - - # 2. Yield to let Subject run fiber_yield() -# ========================================================= -# Kernel Infrastructure Entry -# ========================================================= - -# ========================================================= -# Kernel Infrastructure Entry -# = ========================================================= - -# HAL/NPL Entry points -proc rumpk_halt() {.importc, cdecl, noreturn.} - # Hardware Ingress (Zig -> Nim) proc ion_get_virt(id: uint16): pointer {.importc, cdecl.} proc ion_ingress*(id: uint16, len: uint16) {.exportc, cdecl.} = - ## Intercept raw hardware packet and push to Sovereign RX Channel let data = ion_get_virt(id) var pkt = IonPacket(data: cast[ptr UncheckedArray[byte]](data), len: len, id: id) chan_rx.send(pkt) @@ -345,196 +308,165 @@ proc nimPanic(msg: cstring) {.exportc: "panic", cdecl, noreturn.} = include watchdog # ========================================================= -# kmain: The Orchestrator +# Generic Worker Trampoline # ========================================================= - -# ========================================================= -# System Call Interface (L1 Dispatcher) -# ========================================================= - -# Phase 29: Worker Fiber Management - -# Generic worker trampoline (no closures needed) proc worker_trampoline() {.cdecl.} = let user_fn = cast[proc(arg: uint64) {.cdecl.}](current_fiber.user_entry) if user_fn != nil: user_fn(current_fiber.user_arg) - # Worker finished - mark as inactive for i in 0.. 0: - copyMem(cast[pointer](a1), cast[pointer](pkt.data), int(n)) + if n > 0: copyMem(cast[pointer](a1), cast[pointer](pkt.data), int(n)) ion_free_raw(pkt.id) return n else: - # No data from NexShell, yield to let it run - fiber_yield() + current_fiber.wants_yield = true return 0 - - if (current_fiber.promises and PLEDGE_RPATH) == 0: - kprintln("[SECURITY] PLEDGE VIOLATION: RPATH required for read") - return cast[uint](-1) + if (current_fiber.promises and PLEDGE_RPATH) == 0: return cast[uint](-1) return uint(ion_vfs_read(int32(a0), cast[pointer](a1), uint64(a2))) of 0x204: # WRITE - # Phase 28: Enforce WPATH/STDIO - if a0 == 1 or a0 == 2: - if (current_fiber.promises and PLEDGE_STDIO) == 0: - kprintln("[SECURITY] PLEDGE VIOLATION: STDIO required for write(1/2)") - return cast[uint](-1) - console_write(cast[pointer](a1), csize_t(a2)) - return a2 - - if (current_fiber.promises and PLEDGE_WPATH) == 0: - kprintln("[SECURITY] PLEDGE VIOLATION: WPATH required for write") - return cast[uint](-1) + # Bypass optimization for now to test stability return uint(ion_vfs_write(int32(a0), cast[pointer](a1), uint64(a2))) - of 0x500: # SPAWN (Phase 29) + of 0x300: # SURFACE_CREATE + return uint(compositor.create_surface(int(a0), int(a1))) + of 0x301: # SURFACE_FLIP + return 0 + of 0x302: # SURFACE_GET_PTR + return cast[uint](compositor.hal_surface_get_ptr(int32(a0))) + of 0x500: # SPAWN return uint(k_spawn(cast[pointer](a0), uint64(a1))) - of 0x501: # JOIN (Phase 29) + of 0x501: # JOIN return uint(k_join(uint64(a0))) + of 0x100: # YIELD + # Deferred yield: Set flag, yield happens after trap return + current_fiber.wants_yield = true + return 0 + of 0x220: # BLK_READ - Raw Sector Read (Block Valve) + # a0 = sector, a1 = buffer pointer (userland), a2 = count (sectors) + if (current_fiber.promises and PLEDGE_RPATH) == 0: return cast[uint](-1) + var buf: array[512, byte] + virtio_blk_read(uint64(a0), addr buf[0]) + copyMem(cast[pointer](a1), addr buf[0], 512) + return 512 + of 0x221: # BLK_WRITE - Raw Sector Write (Block Valve) + # a0 = sector, a1 = buffer pointer (userland), a2 = count (sectors) + if (current_fiber.promises and PLEDGE_WPATH) == 0: return cast[uint](-1) + virtio_blk_write(uint64(a0), cast[ptr byte](a1)) + return 512 + of 0x222: # BLK_SYNC - Flush (Block Valve) + # VirtIO block is synchronous, so this is a no-op for now + return 0 of 0: # EXIT fiber_yield() return 0 else: - kprint("[Kernel] Unknown Syscall: ") - kprint_hex(uint64(nr)) - kprintln("") return 0 proc kmain() {.exportc, cdecl.} = @@ -542,31 +474,36 @@ proc kmain() {.exportc, cdecl.} = kprintln("╔═══════════════════════════════════════╗") kprintln("║ NEXUS RUMK v1.1 - SOVEREIGN ║") kprintln("╚═══════════════════════════════════════╝") + + kprint("[Kernel] current_fiber Addr: "); kprint_hex(cast[uint64](addr current_fiber)); kprintln("") + kprint("[Kernel] stack_subject Addr: "); kprint_hex(cast[uint64](addr stack_subject[0])); kprintln("") + kprint("[Kernel] GP: "); var gp: uint64; {.emit: "asm volatile(\"mv %0, gp\" : \"=r\"(`gp`));".}; kprint_hex(gp); kprintln("") + - # 1. Hardware & Memory - kprintln("[Kernel] Initializing Memory Substrate...") ion_pool_init() + + # Phase 31: Memory Manager (The Glass Cage) + mm_init() + mm_enable_kernel_paging() + + # Diagnostic: Check stvec + var stvec_val: uint64 + {.emit: "asm volatile(\"csrr %0, stvec\" : \"=r\"(`stvec_val`));".} + kprint("[Kernel] stvec: ") + kprint_hex(stvec_val) + kprintln("") + + # Phase 37 Fix: Enable sstatus.SUM (Supervisor User Memory access) + # This allows the kernel (S-mode) to read/write pages with PTE_U (User bit). + {.emit: "asm volatile(\"csrs sstatus, %0\" : : \"r\"(1L << 18));".} - # [FIX] Input Channel Init BEFORE Drivers ion_init_input() - - # Phase 31: The Identity Switch (THE CROSSING) - Temporarily disabled - # kprintln("[MM] Building Sv39 Page Tables...") - # mm_init() - # kprintln("[MM] Activating Identity Map...") - # mm_enable_kernel_paging() - # kprintln("[MM] ✓ Virtual Memory Active. Reality is Virtual.") - hal_io_init() - # 1.1 VFS (InitRD) vfs_init(addr binary_initrd_tar_start, addr binary_initrd_tar_end) - - # 1.2 VFS (SFS) sfs_mount() sfs_sync_vfs() - # Wire VFS to SysTable (Hypercall Vector) let sys = cast[ptr SysTable](SYSTABLE_BASE) sys.fn_vfs_open = ion_vfs_open sys.fn_vfs_read = ion_vfs_read @@ -574,96 +511,99 @@ proc kmain() {.exportc, cdecl.} = sys.fn_vfs_write = wrapper_vfs_write sys.fn_vfs_close = ion_vfs_close sys.fn_log = cast[pointer](kwrite) - sys.fn_pledge = k_pledge # Phase 28: Pledge - sys.fn_yield = cast[proc() {.cdecl.}](kernel.fiber_yield) + sys.fn_pledge = k_pledge + # fn_yield removed - yield is now syscall 0x100 - # 1.5 The Retina (VirtIO-GPU) - proc virtio_gpu_init(base: uint64) {.importc, cdecl.} - proc matrix_init() {.importc, cdecl.} + # Phase 35e: Crypto HAL integration + proc hal_crypto_siphash(key: ptr array[16, byte], data: pointer, len: uint64, out_hash: ptr array[16, byte]) {.importc, cdecl.} + proc hal_crypto_ed25519_verify(sig: ptr array[64, byte], msg: pointer, len: uint64, pk: ptr array[32, byte]): bool {.importc, cdecl.} + + sys.fn_siphash = hal_crypto_siphash + sys.fn_ed25519_verify = hal_crypto_ed25519_verify - # On QEMU virt machine, virtio-mmio devices are at 0x10001000-0x10008000 - # GPU could be at any slot. - kprintln("[Kernel] Scanning for VirtIO-GPU...") - for i in 1..8: - let base_addr = 0x10000000'u64 + (uint64(i) * 0x1000'u64) - virtio_gpu_init(base_addr) + # GPU disabled temporarily until display works + # proc virtio_gpu_init(base: uint64) {.importc, cdecl.} + # proc matrix_init() {.importc, cdecl.} + # kprintln("[Kernel] Scanning for VirtIO-GPU...") + # for i in 1..8: + # let base_addr = 0x10000000'u64 + (uint64(i) * 0x1000'u64) + # virtio_gpu_init(base_addr) + # matrix_init() - # Initial Matrix greeting - matrix_init() + # Move Rings to Shared Memory (User Accessible) + # 0x83001000 onwards + let ring_rx_ptr = cast[ptr HAL_Ring[IonPacket]](SYSTABLE_BASE + 0x1000) + let ring_tx_ptr = cast[ptr HAL_Ring[IonPacket]](SYSTABLE_BASE + 0x2000) + let ring_event_ptr = cast[ptr HAL_Ring[IonPacket]](SYSTABLE_BASE + 0x3000) + let ring_cmd_ptr = cast[ptr HAL_Ring[CmdPacket]](SYSTABLE_BASE + 0x4000) + + # Init Shared Rings + ring_rx_ptr.head = 0; ring_rx_ptr.tail = 0; ring_rx_ptr.mask = 255 + ring_tx_ptr.head = 0; ring_tx_ptr.tail = 0; ring_tx_ptr.mask = 255 + ring_event_ptr.head = 0; ring_event_ptr.tail = 0; ring_event_ptr.mask = 255 + ring_cmd_ptr.head = 0; ring_cmd_ptr.tail = 0; ring_cmd_ptr.mask = 255 - # 2. Channel Infrastructure - kprintln("[Kernel] Mapping Sovereign Channels...") + # Connect Channels + chan_rx.ring = ring_rx_ptr + chan_tx.ring = ring_tx_ptr + chan_event.ring = ring_event_ptr + chan_cmd.ring = ring_cmd_ptr - # Initialize Invariant Shield (Masking) - for r in [addr guest_rx_hal, addr guest_tx_hal, addr guest_event_hal]: - r.head = 0 - r.tail = 0 - r.mask = 255 + # Connect SysTable + sys.s_rx = ring_rx_ptr + sys.s_tx = ring_tx_ptr + sys.s_event = ring_event_ptr + sys.s_cmd = ring_cmd_ptr + let ring_input_ptr = cast[ptr HAL_Ring[IonPacket]](SYSTABLE_BASE + 0x5000) + ring_input_ptr.head = 0; ring_input_ptr.tail = 0; ring_input_ptr.mask = 255 + chan_input.ring = ring_input_ptr + sys.s_input = ring_input_ptr + + sys.magic = 0x4E585553 + + # Removed stale BSS assignments (sys.s_rx = ...) - guest_cmd_hal.head = 0 - guest_cmd_hal.tail = 0 - guest_cmd_hal.mask = 255 - # Input HAL init removed - handled by ion_init_input + # Phase 36.2: Initialize Network Membrane BEFORE userland starts + netswitch_init() + netswitch_attach_systable(sys) - chan_rx.ring = addr guest_rx_hal - chan_tx.ring = addr guest_tx_hal - chan_event.ring = addr guest_event_hal - chan_cmd.ring = addr guest_cmd_hal - # chan_input ring set in ion_init_input + # Framebuffer info + sys.fb_addr = fb_kern_get_addr() + sys.fb_width = 1920 + sys.fb_height = 1080 + sys.fb_stride = 1920 * 4 + sys.fb_bpp = 32 + sys.fn_yield = rumpk_yield_guard - let sys_table = cast[ptr SysTable](SYSTABLE_BASE) - sys_table.magic = 0x4E585553 - sys_table.s_rx = addr guest_rx_hal - sys_table.s_tx = addr guest_tx_hal - sys_table.s_event = addr guest_event_hal - sys_table.s_cmd = addr guest_cmd_hal - sys_table.s_input = chan_input.ring # From global - - - # Framebuffer info (Phase 26: Visual Cortex) - sys_table.fb_addr = fb_kern_get_addr() - sys_table.fb_width = 800 # From framebuffer.zig - sys_table.fb_height = 600 - sys_table.fb_stride = 800 * 4 # 32bpp BGRA - sys_table.fb_bpp = 32 - - # 3. The Nerve (Yield Anchor) - proc rumpk_yield_guard() {.importc, cdecl.} - let yield_ptr_loc = cast[ptr pointer](0x83000FF0'u64) - yield_ptr_loc[] = cast[pointer](rumpk_yield_guard) - - # 4. Deployment kprintln("[Kernel] Spawning System Fibers...") - kprintln(" → fiber_ion") fiber_ion.name = "ion" init_fiber(addr fiber_ion, ion_fiber_entry, addr stack_ion[0], sizeof(stack_ion)) - kprintln(" → fiber_nexshell") + fiber_compositor.name = "compositor" + init_fiber(addr fiber_compositor, compositor_fiber_entry, addr stack_compositor[0], sizeof(stack_compositor)) + fiber_nexshell.name = "nexshell" - init_fiber(addr fiber_nexshell, nexshell_main, addr stack_nexshell[0], - sizeof(stack_nexshell)) + init_fiber(addr fiber_nexshell, nexshell_main, addr stack_nexshell[0], sizeof(stack_nexshell)) - # 3. UI FIBER (The Face) - Temporarily disabled to debug boot hang - # fiber_ui.name = "ui" - # init_fiber(addr fiber_ui, ui_fiber_entry, addr stack_ui[0], sizeof(stack_ui)) + # Phase 31: Page Table root for worker isolation + proc mm_create_worker_map(stack_base: uint64, stack_size: uint64, packet_addr: uint64): uint64 {.importc, cdecl.} - kprintln(" → fiber_subject") fiber_subject.name = "subject" - init_fiber(addr fiber_subject, subject_fiber_entry, addr stack_subject[0], - sizeof(stack_subject)) + init_fiber(addr fiber_subject, subject_fiber_entry, addr stack_subject[0], sizeof(stack_subject)) + fiber_subject.satp_value = mm_create_worker_map(cast[uint64](addr stack_subject[0]), uint64(sizeof(stack_subject)), 0x83000000'u64) + - kprintln(" → fiber_watchdog") fiber_watchdog.name = "watchdog" init_fiber(addr fiber_watchdog, watchdog_loop, addr stack_watchdog[0], sizeof(stack_watchdog)) - # [FIX] GLOBAL INTERRUPT ENABLE - # Open the ear before we enter the loop. + # Phase 36.2: NetSwitch Fiber (Traffic Cop) + fiber_netswitch.name = "netswitch" + init_fiber(addr fiber_netswitch, fiber_netswitch_entry, addr stack_netswitch[0], sizeof(stack_netswitch)) + kprintln("[Kernel] Enabling Supervisor Interrupts (SIE)...") asm "csrsi sstatus, 2" kprintln("[Kernel] All Systems Go. Entering Autonomous Loop.") - - # Handover to Scheduler (The Heartbeat) switch(addr fiber_ion) {.pop.} diff --git a/core/loader.nim b/core/loader.nim index 7a281aa..701cf22 100644 --- a/core/loader.nim +++ b/core/loader.nim @@ -5,6 +5,7 @@ import fs/tar, loader/elf proc kprint(s: cstring) {.importc, cdecl.} proc kprintln(s: cstring) {.importc, cdecl.} +proc kprint_hex(v: uint64) {.importc, cdecl.} # Assembly trampoline to jump to userland proc rumpk_enter_userland*(entry: uint64) {.importc, cdecl.} @@ -48,6 +49,12 @@ proc kload*(path: string): uint64 = # Copy Data if phdr.p_filesz > 0: copyMem(dest, src, phdr.p_filesz) + let magic = cast[ptr uint32](dest)[] + kprint("[Loader] Verified Segment at ") + kprint_hex(cast[uint64](dest)) + kprint(" Magic: ") + kprint_hex(uint64(magic)) + kprintln("") return ehdr.e_entry @@ -56,3 +63,31 @@ proc kexec*(path: string) = if entry != 0: kprintln("[Loader] Transferring Consciousness...") rumpk_enter_userland(entry) + +proc kload_phys*(path: string, phys_offset: uint64): uint64 = + let file_content = vfs_read_file(path) + if file_content.len == 0: + return 0 + + let ehdr = cast[ptr Elf64_Ehdr](unsafeAddr file_content[0]) + if ehdr.e_ident[0] != 0x7F: return 0 + if ehdr.e_machine != 243: return 0 + + let base_ptr = cast[uint64](unsafeAddr file_content[0]) + + for i in 0 ..< int(ehdr.e_phnum): + let phdr_offset = ehdr.e_phoff + uint64(i * int(ehdr.e_phentsize)) + let phdr = cast[ptr Elf64_Phdr](base_ptr + phdr_offset) + + if phdr.p_type == PT_LOAD: + let rel_addr = phdr.p_vaddr - 0x84000000'u64 + let dest_addr = phys_offset + rel_addr + let dest = cast[ptr UncheckedArray[byte]](dest_addr) + let src = cast[ptr UncheckedArray[byte]](base_ptr + phdr.p_offset) + + if phdr.p_memsz > 0: + zeroMem(dest, phdr.p_memsz) + if phdr.p_filesz > 0: + copyMem(dest, src, phdr.p_filesz) + + return ehdr.e_entry diff --git a/core/netswitch.nim b/core/netswitch.nim new file mode 100644 index 0000000..680d5e1 --- /dev/null +++ b/core/netswitch.nim @@ -0,0 +1,77 @@ +# core/rumpk/core/netswitch.nim +# Phase 36.2: The Traffic Cop (Network Membrane Layer 2 Switch) +# +# Responsibilities: +# - Poll VirtIO-Net hardware for incoming packets +# - Route RX packets to s_net_rx ring (Kernel -> Userland) +# - Drain s_net_tx ring and transmit via VirtIO-Net (Userland -> Kernel) +# - Never yield during active traffic (War Mode latency optimization) + +{.push stackTrace: off, lineTrace: off.} + +import ion + +# Forward declare fiber_yield to avoid circular import +proc fiber_yield*() {.importc, cdecl.} + +# HAL Imports +proc virtio_net_poll() {.importc, cdecl.} +proc virtio_net_send(data: pointer, len: uint32) {.importc, cdecl.} + +# Logging +proc kprintln(s: cstring) {.importc, cdecl.} + +var netswitch_initialized: bool = false + +proc netswitch_init*() = + ## Initialize network channels and populate SysTable + ## MUST be called before userland starts! + ion_init_network() + kprintln("[NetSwitch] Network Rings Initialized") + netswitch_initialized = true + +proc netswitch_attach_systable*(sys: ptr SysTable) = + ## Attach network ring pointers to SysTable for userland access + sys.s_net_rx = chan_net_rx.ring + sys.s_net_tx = chan_net_tx.ring + kprintln("[NetSwitch] SysTable Rings Attached") + +proc fiber_netswitch_entry*() {.cdecl.} = + kprintln("[NetSwitch] Fiber Entry - The Traffic Cop is ON DUTY") + + var rx_activity: bool = false + var tx_activity: bool = false + + while true: + rx_activity = false + tx_activity = false + + # ============================================ + # RX PATH: Hardware -> chan_net_rx -> Userland + # ============================================ + # virtio_net_poll() internally calls ion_ingress() which pushes + # received packets to the hardware driver's internal ring. + # We poll here to drive the RX path. + virtio_net_poll() + + # ============================================ + # TX PATH: Userland -> chan_net_tx -> Hardware + # ============================================ + var tx_pkt: IonPacket + while chan_net_tx.recv(tx_pkt): + if tx_pkt.data != nil and tx_pkt.len > 0: + virtio_net_send(cast[pointer](tx_pkt.data), uint32(tx_pkt.len)) + ion_free(tx_pkt) + tx_activity = true + + # ============================================ + # YIELD STRATEGY + # ============================================ + if rx_activity or tx_activity: + # War Mode: Continue processing if we moved data + continue + else: + # Peace Mode: Yield to let other fibers run + fiber_yield() + +{.pop.} diff --git a/core/write_wrapper.nim b/core/write_wrapper.nim new file mode 100644 index 0000000..16afec9 --- /dev/null +++ b/core/write_wrapper.nim @@ -0,0 +1,15 @@ + +# Forward declarations for C symbols +proc console_write(p: pointer, len: csize_t) {.importc, cdecl.} +proc ion_vfs_write(fd: int32, buf: pointer, count: uint64): int64 {.importc, cdecl.} +proc kprint(s: cstring) {.importc, cdecl.} +proc kprint_hex(n: uint64) {.importc, cdecl.} +proc kprintln(s: cstring) {.importc, cdecl.} + +# Wrapper for VFS write to handle stdout/stderr +proc wrapper_vfs_write(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.} = + # kprint("[WRAPPER] write fd="); kprint_hex(uint64(fd)); kprintln("") + if fd == 1 or fd == 2: + console_write(buf, csize_t(count)) + return int64(count) + return ion_vfs_write(fd, buf, count) diff --git a/hal/arch/riscv64/switch.S b/hal/arch/riscv64/switch.S index 3c33962..ad6a095 100644 --- a/hal/arch/riscv64/switch.S +++ b/hal/arch/riscv64/switch.S @@ -1,27 +1,12 @@ /* MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI) RUMPK HAL // RISC-V 64 CONTEXT SWITCH (Boundaries Protected) - - RISC-V LP64 ABI Saved Registers + Bounds: - - ra (return address) - - gp (global pointer) - - tp (thread pointer) - - s0-s11 (12 saved registers) - - Frame: 16 regs * 8 = 128 bytes (16-byte aligned) */ .global cpu_switch_to .type cpu_switch_to, @function -# void cpu_switch_to(uint64_t* prev_sp_ptr, uint64_t next_sp); -# a0 = prev_sp_ptr -# a1 = next_sp - cpu_switch_to: - # 1. Allocate Stack (128 bytes) addi sp, sp, -128 - - # 2. Save Context sd ra, 0(sp) sd gp, 8(sp) sd tp, 16(sp) @@ -37,14 +22,8 @@ cpu_switch_to: sd s9, 96(sp) sd s10, 104(sp) sd s11, 112(sp) - - # 3. Save Old SP sd sp, 0(a0) - - # 4. Load New SP mv sp, a1 - - # 5. Restore Context ld ra, 0(sp) ld gp, 8(sp) ld tp, 16(sp) @@ -52,6 +31,7 @@ cpu_switch_to: ld s1, 32(sp) ld s2, 40(sp) ld s3, 48(sp) + sd s4, 56(sp) ld s4, 56(sp) ld s5, 64(sp) ld s6, 72(sp) @@ -60,34 +40,21 @@ cpu_switch_to: ld s9, 96(sp) ld s10, 104(sp) ld s11, 112(sp) - addi sp, sp, 128 ret -/* - THE YIELD GUARD - Protects Kernel GP during subject-to-kernel yield calls. -*/ .global rumpk_yield_guard .type rumpk_yield_guard, @function rumpk_yield_guard: - # 1. Save Subject State addi sp, sp, -16 sd gp, 0(sp) sd ra, 8(sp) - - # 2. Restore Kernel Global Pointer (Static Relocation) .option push .option norelax la gp, __global_pointer$ .option pop - - # 3. Call Nim Internal Logic - # (rumpk_yield_internal is a Nim cdecl proc) call rumpk_yield_internal - - # 4. Restore Subject State ld gp, 0(sp) ld ra, 8(sp) addi sp, sp, 16 @@ -99,13 +66,23 @@ rumpk_yield_guard: # void rumpk_enter_userland(uint64_t entry); # a0 = entry rumpk_enter_userland: - # 🏛️ RESET STATE (Clean slate for the New Consciousness) - # We keep SP as is for now (it's the fiber stack) - # OR we point it to a dedicated User Stack if needed. - # Subject Entry usually handles its own stack init. + # 🏛️ PIVOT TO USER MODE (Preserving Hart State) - # Disable Supervisor Interrupts during handoff - csrw sie, zero + # 1. Set sepc = entry (a0) + csrw sepc, a0 - # Jump to the Summoned Body - jr a0 + # 2. Configure sstatus for U-mode transition + # - SPP (Previous Privilege Level) = 0 (User) - Bits 8 + # - SPIE (Previous Interrupt Enable) = 1 (Enable Interrupts on sret) - Bit 5 + # - SUM (Supervisor User Memory) - PRESERVE (Already set in kmain) + + # Clear SPP bit (bit 8) + li t0, (1 << 8) + csrc sstatus, t0 + + # Set SPIE bit (bit 5) + li t0, (1 << 5) + csrs sstatus, t0 + + # 3. Use sret to transit to U-mode + sret diff --git a/hal/channel.zig b/hal/channel.zig index 69b43ae..92768c3 100644 --- a/hal/channel.zig +++ b/hal/channel.zig @@ -9,6 +9,7 @@ pub const IonPacket = extern struct { phys: u64, len: u16, id: u16, + _pad: u32, // Match Nim's 24-byte alignment }; pub const CmdPacket = extern struct { diff --git a/hal/crypto.zig b/hal/crypto.zig new file mode 100644 index 0000000..be17dc2 --- /dev/null +++ b/hal/crypto.zig @@ -0,0 +1,22 @@ +// core/rumpk/hal/crypto.zig +// Phase 35e: The Cryptographic Foundation + +const std = @import("std"); + +/// SipHash-2-4 (128-bit) for secure packet IDs +export fn hal_crypto_siphash(key: *const [16]u8, data: [*]const u8, len: usize, out: *[16]u8) void { + var hasher = std.crypto.auth.siphash.SipHash128(2, 4).init(key); + hasher.update(data[0..len]); + hasher.final(out); +} + +/// Ed25519 Signature Verification +export fn hal_crypto_ed25519_verify(sig: *const [64]u8, msg: [*]const u8, msg_len: usize, pk: *const [32]u8) bool { + const Ed25519 = std.crypto.sign.Ed25519; + + const public_key = Ed25519.PublicKey.fromBytes(pk.*) catch return false; + const signature = Ed25519.Signature.fromBytes(sig.*); + + signature.verify(msg[0..msg_len], public_key) catch return false; + return true; +} diff --git a/hal/entry_riscv.zig b/hal/entry_riscv.zig index 3a90ee7..c3cbf30 100644 --- a/hal/entry_riscv.zig +++ b/hal/entry_riscv.zig @@ -81,7 +81,12 @@ const TrapFrame = extern struct { // Full Context Save Trap Entry export fn trap_entry() callconv(.naked) void { asm volatile ( - // Allocate stack (36 words * 8 bytes = 288 bytes) + // LOUD HARDWARE TRACE: Write '!' to UART + \\ li t0, 0x10000000 + \\ li t1, 33 + \\ sb t1, 0(t0) + + // Allocate stack (36 words * 8 bytes = 288 bytes) \\ addi sp, sp, -288 // Save GPRs @@ -116,6 +121,12 @@ export fn trap_entry() callconv(.naked) void { \\ sd t5, 224(sp) \\ sd t6, 232(sp) + // RELOAD KERNEL GLOBAL POINTER (Critical for globals access) + \\ .option push + \\ .option norelax + \\ la gp, __global_pointer$ + \\ .option pop + // Save CSRs \\ csrr t0, sepc \\ sd t0, 240(sp) @@ -179,6 +190,7 @@ export fn trap_entry() callconv(.naked) void { extern fn k_handle_syscall(nr: usize, a0: usize, a1: usize, a2: usize) usize; extern fn k_handle_exception(scause: usize, sepc: usize, stval: usize) void; +extern fn k_check_deferred_yield() void; export fn rss_trap_handler(frame: *TrapFrame) void { const scause = frame.scause; @@ -188,10 +200,16 @@ export fn rss_trap_handler(frame: *TrapFrame) void { if (scause == 8 or scause == 9) { // Advance PC to skip 'ecall' instruction (4 bytes) frame.sepc += 4; + // Dispatch Syscall const res = k_handle_syscall(frame.a7, frame.a0, frame.a1, frame.a2); + // Write result back to a0 frame.a0 = res; + + // uart.puts("[Trap] Checking deferred yield\n"); + // Check for deferred yield + k_check_deferred_yield(); return; } @@ -233,8 +251,11 @@ export fn console_read() c_int { const virtio_block = @import("virtio_block.zig"); +extern fn hal_surface_init() void; + export fn hal_io_init() void { uart.init(); + hal_surface_init(); virtio_net.init(); virtio_block.init(); } @@ -246,8 +267,11 @@ export fn rumpk_halt() noreturn { } } -var mock_ticks: u64 = 0; export fn rumpk_timer_now_ns() u64 { - mock_ticks += 100000; - return mock_ticks; + var ticks: u64 = 0; + asm volatile ("rdtime %[ticks]" + : [ticks] "=r" (ticks), + ); + // QEMU Virt machine is 10MHz -> 1 tick = 100ns + return ticks * 100; } diff --git a/hal/framebuffer.zig b/hal/framebuffer.zig index 3f5dada..331b2b8 100644 --- a/hal/framebuffer.zig +++ b/hal/framebuffer.zig @@ -4,12 +4,12 @@ const std = @import("std"); -// Resolution: 800x600 @ 32bpp (ARGB) -pub const WIDTH: usize = 800; -pub const HEIGHT: usize = 600; +// Resolution: 1920x1080 @ 32bpp (ARGB) +pub const WIDTH: usize = 1920; +pub const HEIGHT: usize = 1080; pub const STRIDE: usize = WIDTH; -// The Physical Backing Store (1.9MB in BSS) +// The Physical Backing Store (~7.9MB in BSS) // Zero-initialized at boot. var fb_memory: [WIDTH * HEIGHT]u32 = [_]u32{0} ** (WIDTH * HEIGHT); diff --git a/hal/gpu.zig b/hal/gpu.zig index a55a0a7..d65da32 100644 --- a/hal/gpu.zig +++ b/hal/gpu.zig @@ -294,11 +294,11 @@ fn send_command(ptr: [*]const u8, len: usize) void { asm volatile ("" ::: .{ .memory = true }); timeout += 1; if (timeout % 10000000 == 0) { - uart.print("[GPU] Polling... last="); - uart.print_hex(last_used_idx); - uart.print(" current="); - uart.print_hex(queue.used.idx); - uart.print("\n"); + // uart.print("[GPU] Polling... last="); + // uart.print_hex(last_used_idx); + // uart.print(" current="); + // uart.print_hex(queue.used.idx); + // uart.print("\n"); } } last_used_idx = queue.used.idx; diff --git a/hal/hud.zig b/hal/hud.zig index 9648309..da44594 100644 --- a/hal/hud.zig +++ b/hal/hud.zig @@ -14,7 +14,7 @@ pub fn move_to(row: u8, col: u8) void { print_u8(row); uart.print(";"); print_u8(col); - uart.print("H"); + // Heartbeat removed } pub fn set_color(code: u8) void { diff --git a/hal/main.zig b/hal/main.zig index 94a4a07..a275f67 100644 --- a/hal/main.zig +++ b/hal/main.zig @@ -1,3 +1,7 @@ +// Copyright (c) 2026 Markus Maiwald +// Licensed under the Libertaria Commonwealth License (LCL-1.0) +// See legal/LICENSE_COMMONWEALTH.md for details. +// // Rumpk Layer 0: The Concrete Foundation // Markus Maiwald (Architect) | Voxis Forge (AI) // diff --git a/hal/mm.zig b/hal/mm.zig index 3389525..706eafe 100644 --- a/hal/mm.zig +++ b/hal/mm.zig @@ -11,7 +11,7 @@ pub const LEVELS: u8 = 3; // Physical memory layout (RISC-V QEMU virt) pub const DRAM_BASE: u64 = 0x80000000; -pub const DRAM_SIZE: u64 = 128 * 1024 * 1024; // 128MB default +pub const DRAM_SIZE: u64 = 256 * 1024 * 1024; // 256MB for expanded userspace // MMIO regions pub const UART_BASE: u64 = 0x10000000; @@ -85,15 +85,16 @@ pub const PageTable = struct { // Simple bump allocator for page tables var page_alloc_base: u64 = 0; var page_alloc_offset: u64 = 0; +var kernel_satp_value: u64 = 0; pub fn init_page_allocator(base: u64, size: u64) void { - _ = size; // Reserved for bounds checking + _ = size; page_alloc_base = base; page_alloc_offset = 0; } pub fn alloc_page_table() ?*PageTable { - if (page_alloc_offset + PAGE_SIZE > DRAM_SIZE) { + if (page_alloc_offset + PAGE_SIZE > 8 * 1024 * 1024) { return null; } @@ -118,33 +119,26 @@ pub fn map_page(root: *PageTable, va: u64, pa: u64, flags: u64) !void { while (level > 0) : (level -= 1) { const idx = vpn(va, level); - var pte = pt.get_entry(idx); + const pte = pt.get_entry(idx); if (!pte.is_valid()) { - // Allocate intermediate page table const new_pt = alloc_page_table() orelse return error.OutOfMemory; pte.* = PageTableEntry.init(@intFromPtr(new_pt), PTE_V); } if (pte.is_leaf()) { - return error.AlreadyMapped; + return; } pt = @ptrFromInt(pte.get_pa()); } - // Map leaf entry const idx = vpn(va, 0); - var pte = pt.get_entry(idx); - - if (pte.is_valid()) { - return error.AlreadyMapped; - } - - pte.* = PageTableEntry.init(pa, flags | PTE_V); + const pte = pt.get_entry(idx); + pte.* = PageTableEntry.init(pa, flags | PTE_V | PTE_A | PTE_D); } -// Map a range of pages (identity or custom) +// Map a range of pages pub fn map_range(root: *PageTable, va_start: u64, pa_start: u64, size: u64, flags: u64) !void { var offset: u64 = 0; while (offset < size) : (offset += PAGE_SIZE) { @@ -156,22 +150,14 @@ pub fn map_range(root: *PageTable, va_start: u64, pa_start: u64, size: u64, flag pub fn create_kernel_identity_map() !*PageTable { const root = alloc_page_table() orelse return error.OutOfMemory; - // Map DRAM (identity: VA = PA) + // Kernel Identity Map (VA = PA, S-mode ONLY) - Now 256MB try map_range(root, DRAM_BASE, DRAM_BASE, DRAM_SIZE, PTE_R | PTE_W | PTE_X); - // Map UART (MMIO) + // MMIO regions try map_range(root, UART_BASE, UART_BASE, PAGE_SIZE, PTE_R | PTE_W); - - // Map VirtIO (MMIO) - Expanded to cover all devices try map_range(root, 0x10001000, 0x10001000, 0x8000, PTE_R | PTE_W); - - // Map VirtIO PCI (MMIO) - CRITICAL for PCI probe try map_range(root, 0x30000000, 0x30000000, 0x10000000, PTE_R | PTE_W); - - // Map VirtIO BAR region (dynamic PCI BAR assignments) try map_range(root, 0x40000000, 0x40000000, 0x10000000, PTE_R | PTE_W); - - // Map PLIC (MMIO) try map_range(root, PLIC_BASE, PLIC_BASE, 0x400000, PTE_R | PTE_W); return root; @@ -181,19 +167,23 @@ pub fn create_kernel_identity_map() !*PageTable { pub fn create_worker_map(stack_base: u64, stack_size: u64, packet_addr: u64) !*PageTable { const root = alloc_page_table() orelse return error.OutOfMemory; - // Map kernel code (RX) - identity map for simplicity - // TODO: Split into proper RX/RW regions - try map_range(root, DRAM_BASE, DRAM_BASE, 16 * 1024 * 1024, PTE_R | PTE_X); + // 🏛️ THE EXPANDED CAGE (Phase 37 - 256MB RAM) - // Map worker stack (RW) - try map_range(root, stack_base, stack_base, stack_size, PTE_R | PTE_W); + // 1. Kernel Memory (0-32MB) -> Supervisor ONLY (PTE_U = 0) + // This allows the fiber trampoline to execute in S-mode. + try map_range(root, DRAM_BASE, DRAM_BASE, 32 * 1024 * 1024, PTE_R | PTE_W | PTE_X); - // Map shared packet (RW) - const packet_page = packet_addr & ~@as(u64, PAGE_SIZE - 1); - try map_range(root, packet_page, packet_page, PAGE_SIZE, PTE_R | PTE_W); + // 2. User Memory (32-256MB) -> User Accessible (PTE_U = 1) + // This allows NipBox (at 96MB offset, 64MB size) to execute in U-mode. + try map_range(root, DRAM_BASE + (32 * 1024 * 1024), DRAM_BASE + (32 * 1024 * 1024), 224 * 1024 * 1024, PTE_R | PTE_W | PTE_X | PTE_U); - // Map UART for debugging (RW) - try map_range(root, UART_BASE, UART_BASE, PAGE_SIZE, PTE_R | PTE_W); + // 3. User MMIO (UART) + try map_range(root, UART_BASE, UART_BASE, PAGE_SIZE, PTE_R | PTE_W | PTE_U); + + // 4. Overlap stack with user access + try map_range(root, stack_base, stack_base, stack_size, PTE_R | PTE_W | PTE_U); + + _ = packet_addr; return root; } @@ -206,7 +196,7 @@ pub fn make_satp(root: *PageTable) u64 { } // Activate page table -pub fn activate_pagetable(satp_val: u64) void { +pub export fn mm_activate_satp(satp_val: u64) callconv(.c) void { asm volatile ( \\csrw satp, %[satp] \\sfence.vma zero, zero @@ -217,17 +207,87 @@ pub fn activate_pagetable(satp_val: u64) void { // Export for kernel pub export fn mm_init() callconv(.c) void { - // Initialize page allocator at end of kernel - // Kernel ends at ~16MB, allocate page tables after const pt_base = DRAM_BASE + (16 * 1024 * 1024); - init_page_allocator(pt_base, 8 * 1024 * 1024); // 8MB for page tables + init_page_allocator(pt_base, 8 * 1024 * 1024); } pub export fn mm_enable_kernel_paging() callconv(.c) void { const root = create_kernel_identity_map() catch { - // Can't use console here, just halt while (true) {} }; const satp_val = make_satp(root); - activate_pagetable(satp_val); + kernel_satp_value = satp_val; + mm_activate_satp(satp_val); +} + +pub export fn mm_get_kernel_satp() callconv(.c) u64 { + return kernel_satp_value; +} + +pub export fn mm_create_worker_map(stack_base: u64, stack_size: u64, packet_addr: u64) callconv(.c) u64 { + if (create_worker_map(stack_base, stack_size, packet_addr)) |root| { + return make_satp(root); + } else |_| { + return 0; + } +} + +extern fn kprint(s: [*:0]const u8) void; +extern fn kprint_hex(n: u64) void; +extern fn kprintln(s: [*:0]const u8) void; + +pub export fn mm_debug_check_va(va: u64) callconv(.c) void { + kprint("[MM] Inspecting VA: "); + kprint_hex(va); + kprintln(""); + + // Get Root + const ppn = kernel_satp_value & 0xFFFFFFFFFFF; + const root_pa = ppn << PAGE_SHIFT; + const root: *PageTable = @ptrFromInt(root_pa); + + // Level 2 + const idx2 = vpn(va, 2); + const pte2 = root.get_entry(idx2); + kprint(" L2["); + kprint_hex(idx2); + kprint("]: "); + kprint_hex(pte2.to_u64()); + if (!pte2.is_valid()) { + kprintln(" (Invalid)"); + return; + } + if (pte2.is_leaf()) { + kprintln(" (Leaf)"); + return; + } + kprintln(" (Table)"); + + // Level 1 + const pt1: *PageTable = @ptrFromInt(pte2.get_pa()); + const idx1 = vpn(va, 1); + const pte1 = pt1.get_entry(idx1); + kprint(" L1["); + kprint_hex(idx1); + kprint("]: "); + kprint_hex(pte1.to_u64()); + if (!pte1.is_valid()) { + kprintln(" (Invalid)"); + return; + } + if (pte1.is_leaf()) { + kprintln(" (Leaf)"); + return; + } + kprintln(" (Table)"); + + // Level 0 + const pt0: *PageTable = @ptrFromInt(pte1.get_pa()); + const idx0 = vpn(va, 0); + const pte0 = pt0.get_entry(idx0); + kprint(" L0["); + kprint_hex(idx0); + kprint("]: "); + kprint_hex(pte0.to_u64()); + kprintln(""); } diff --git a/hal/stubs.zig b/hal/stubs.zig index b709d35..fb03a20 100644 --- a/hal/stubs.zig +++ b/hal/stubs.zig @@ -10,7 +10,7 @@ const uart = @import("uart.zig"); // ========================================================= // Simple Bump Allocator for L0 -var heap: [8 * 1024 * 1024]u8 align(4096) = undefined; // 8MB Heap +var heap: [32 * 1024 * 1024]u8 align(4096) = undefined; // 32MB Heap var heap_idx: usize = 0; // Header structure (64 bytes aligned to match LwIP MEM_ALIGNMENT) diff --git a/hal/surface.zig b/hal/surface.zig new file mode 100644 index 0000000..42a85c5 --- /dev/null +++ b/hal/surface.zig @@ -0,0 +1,111 @@ +// MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI) +// Phase 35a: The Surface Allocator +// Manages contiguous memory chunks for window buffers. + +const std = @import("std"); +const uart = @import("uart.zig"); + +pub const MAX_SURFACES = 16; +pub const SURFACE_POOL_SIZE = 32 * 1024 * 1024; // 32MB for surfaces + +// Surface Descriptor +pub const Surface = struct { + id: i32, + ptr: [*]u32, + width: u32, + height: u32, + active: bool, +}; + +// Global Surface Pool +var surfaces: [MAX_SURFACES]Surface = undefined; +var next_surface_id: i32 = 1; + +// Backing memory for surfaces (in BSS) +var surface_heap: [SURFACE_POOL_SIZE]u8 align(4096) = undefined; +var heap_offset: usize = 0; + +export fn hal_surface_init() void { + for (&surfaces) |*s| { + s.id = -1; + s.active = false; + s.ptr = undefined; + s.width = 0; + s.height = 0; + } + heap_offset = 0; + uart.print("[Surface] Allocator Initialized. Pool: 32MB\n"); +} + +pub fn alloc(width: u32, height: u32) ?i32 { + const size = width * height * 4; + + // Alignment to 4096 + const aligned_size = (size + 4095) & ~@as(u32, 4095); + + if (heap_offset + aligned_size > SURFACE_POOL_SIZE) { + uart.print("[Surface] ERROR: Out of Memory in Surface Pool!\n"); + return null; + } + + // Find free slot + var slot: ?*Surface = null; + for (&surfaces) |*s| { + if (!s.active) { + slot = s; + break; + } + } + + if (slot) |s| { + s.id = next_surface_id; + next_surface_id += 1; + s.width = width; + s.height = height; + s.ptr = @ptrCast(@alignCast(&surface_heap[heap_offset])); + s.active = true; + + heap_offset += aligned_size; + + uart.print("[Surface] Allocated ID="); + uart.print_hex(@intCast(s.id)); + uart.print(" Size="); + uart.print_hex(size); + uart.print("\n"); + + return s.id; + } + + uart.print("[Surface] ERROR: Max Surfaces Reached!\n"); + return null; +} + +pub fn get_surface(id: i32) ?*Surface { + for (&surfaces) |*s| { + if (s.active and s.id == id) return s; + } + return null; +} + +pub fn free(id: i32) bool { + for (&surfaces) |*s| { + if (s.active and s.id == id) { + s.active = false; + // Note: In our simple bump-style allocator, we don't reclaim heap space + // unless we implement a real allocator. For now, we assume surfaces are + // mostly permanent or the system reboots. + return true; + } + } + return false; +} + +// Exported for Nim +export fn hal_surface_alloc(w: u32, h: u32) i32 { + return alloc(w, h) orelse -1; +} + +export fn hal_surface_get_ptr(id: i32) ?[*]u32 { + if (get_surface(id)) |s| return s.ptr; + return null; +} diff --git a/libs/membrane/blk.nim b/libs/membrane/blk.nim new file mode 100644 index 0000000..7cdbd43 --- /dev/null +++ b/libs/membrane/blk.nim @@ -0,0 +1,54 @@ +# Membrane Block API (The Block Valve - Userland Side) +# Phase 37.2: Sovereign Storage Architecture +# +# This module provides raw sector access to userland filesystems. +# The kernel is just a valve - NO filesystem logic in kernel. + +import libc + +const + SECTOR_SIZE* = 512 + + # Block Valve Syscalls + SYS_BLK_READ* = 0x220 + SYS_BLK_WRITE* = 0x221 + SYS_BLK_SYNC* = 0x222 + +proc blk_read*(sector: uint64, buf: pointer): int = + ## Read a single 512-byte sector from disk + ## Returns: bytes read (512) or negative error + return syscall(SYS_BLK_READ, uint64(sector), cast[uint64](buf), 1'u64) + +proc blk_write*(sector: uint64, buf: pointer): int = + ## Write a single 512-byte sector to disk + ## Returns: bytes written (512) or negative error + return syscall(SYS_BLK_WRITE, uint64(sector), cast[uint64](buf), 1'u64) + +proc blk_sync*(): int = + ## Flush all pending writes to disk + ## Returns: 0 on success, negative error + return syscall(SYS_BLK_SYNC, 0'u64, 0'u64, 0'u64) + +# --- Multi-Sector Helpers --- + +proc blk_read_multi*(start_sector: uint64, buf: pointer, count: int): int = + ## Read multiple contiguous sectors + var total = 0 + for i in 0..= end_x or start_y >= end_y: return + + for y in start_y ..< end_y: + let src_y = y - dest_y + let src_row = cast[pointer](addr s.buffer[src_y * s.width + (start_x - dest_x)]) + let dest_row = cast[pointer](addr fb[y * fb_w + start_x]) + let copy_len = (end_x - start_x) * 4 + copyMem(dest_row, src_row, copy_len) + +proc draw_border(x, y, w, h: int, color: uint32) = + let sys = get_sys_table() + let fb = cast[ptr UncheckedArray[uint32]](sys.fb_addr) + let fb_w = int(sys.fb_width) + let fb_h = int(sys.fb_height) + + for ix in x ..< x + w: + if ix >= 0 and ix < fb_w: + if y >= 0 and y < fb_h: fb[y * fb_w + ix] = color + if y + h - 1 >= 0 and y + h - 1 < fb_h: fb[(y + h - 1) * fb_w + ix] = color + + for iy in y ..< y + h: + if iy >= 0 and iy < fb_h: + if x >= 0 and x < fb_w: fb[iy * fb_w + x] = color + if x + w - 1 >= 0 and x + w - 1 < fb_w: fb[iy * fb_w + (x + w - 1)] = color + +proc layout*(c: var Compositor) = + var current_x = 0 + for i, s in c.surfaces.mpairs: + s.x = current_x + s.focused = (i == c.focused_idx) + current_x += s.width + GAP + +proc process_input(c: var Compositor) = + ## Intercept and route input (STUB - not currently used) + discard + # var pkt: IonPacket + # if ion_user_input(addr pkt): + # ... input handling commented out until ion_user_* is replaced with kernel APIs + +proc render_frame*(c: var Compositor) = + let sys = get_sys_table() + let fb = cast[ptr UncheckedArray[uint32]](sys.fb_addr) + let fb_total = sys.fb_width * sys.fb_height + + for i in 0 ..< int(fb_total): + fb[i] = BG_COLOR + + c.layout() + + for i, s in c.surfaces: + let screen_x = s.x - c.view_x + if screen_x + s.width < 0 or screen_x >= int(sys.fb_width): + continue + + let screen_y = (int(sys.fb_height) - s.height) div 2 + blit_surface(s, screen_x, screen_y) + + if s.focused: + draw_border(screen_x, screen_y, s.width, s.height, FOCUS_COLOR) + +proc create_surface*(w, h: int): int32 = + let id = hal_surface_alloc(uint32(w), uint32(h)) + if id < 0: return -1 + + let p = hal_surface_get_ptr(id) + if p == nil: return -1 + + var s: Surface + s.id = id + s.buffer = p + s.width = w + s.height = h + s.dirty = true + + c.surfaces.add(s) + if c.surfaces.len == 1: + c.focused_idx = 0 + + return id + +proc compositor_step*() = + process_input(c) + render_frame(c) diff --git a/libs/membrane/fs/sfs_user.nim b/libs/membrane/fs/sfs_user.nim new file mode 100644 index 0000000..77f19a8 --- /dev/null +++ b/libs/membrane/fs/sfs_user.nim @@ -0,0 +1,248 @@ +# Membrane SFS - Sovereign Filesystem (Userland Edition) +# Phase 37.2: The Glass Vault - Userland Architecture +# +# This is the CORRECT location for filesystem logic. +# Kernel is just a Block Valve - no FS logic there. + +import ../blk +import ../libc + +const + SFS_MAGIC* = 0x32534653'u32 # "SFS2" little endian + SEC_SB = 0'u64 + SEC_BAM = 1'u64 + SEC_DIR = 2'u64 + CHUNK_SIZE = 508 + EOF_MARKER = 0xFFFFFFFF'u32 + DIR_ENTRY_SIZE = 64 + MAX_FILENAME = 32 + +type + DirEntry* = object + filename*: array[32, char] + start_sector*: uint32 + size_bytes*: uint32 + reserved*: array[24, byte] + +var sfs_mounted: bool = false +var io_buffer: array[512, byte] + +proc print(s: cstring) = + discard libc.write(1, cast[pointer](s), csize_t(s.len)) + +proc print(s: string) = + if s.len > 0: + discard libc.write(1, cast[pointer](unsafeAddr s[0]), csize_t(s.len)) + +# ========================================================= +# Helpers +# ========================================================= + +proc sfs_alloc_sector(): uint32 = + ## Allocate a free sector using the Block Allocation Map + discard blk_read(SEC_BAM, addr io_buffer[0]) + + for i in 0..<512: + if io_buffer[i] != 0xFF: + for b in 0..7: + if (io_buffer[i] and byte(1 shl b)) == 0: + let sec = uint32(i * 8 + b) + # Mark as allocated + io_buffer[i] = io_buffer[i] or byte(1 shl b) + discard blk_write(SEC_BAM, addr io_buffer[0]) + return sec + return 0 # Disk full + +# ========================================================= +# SFS API (Userland) +# ========================================================= + +proc sfs_mount*(): bool = + ## Mount the SFS filesystem + print("[SFS-U] Mounting Userland Filesystem...\n") + + discard blk_read(SEC_SB, addr io_buffer[0]) + + # Check magic: "SFS2" + if io_buffer[0] == byte('S') and io_buffer[1] == byte('F') and + io_buffer[2] == byte('S') and io_buffer[3] == byte('2'): + print("[SFS-U] Mount SUCCESS. Version 2 (Userland).\n") + sfs_mounted = true + return true + else: + print("[SFS-U] Mount FAILED. Invalid Magic.\n") + return false + +proc sfs_is_mounted*(): bool = sfs_mounted + +proc sfs_list*(): seq[string] = + ## List all files in the filesystem + result = @[] + if not sfs_mounted: return + + discard blk_read(SEC_DIR, addr io_buffer[0]) + + for offset in countup(0, 511, DIR_ENTRY_SIZE): + if io_buffer[offset] != 0: + var name = "" + for i in 0.. 0: + var sector_buf: array[512, byte] + + let chunk_size = if remaining > CHUNK_SIZE: CHUNK_SIZE else: remaining + copyMem(addr sector_buf[0], + cast[pointer](cast[int](data) + data_ptr), + chunk_size) + + remaining -= chunk_size + data_ptr += chunk_size + + # Determine next sector + var next_sector = EOF_MARKER + if remaining > 0: + next_sector = sfs_alloc_sector() + if next_sector == 0: + next_sector = EOF_MARKER + remaining = 0 + + # Write next pointer at end of sector + sector_buf[508] = byte(next_sector and 0xFF) + sector_buf[509] = byte((next_sector shr 8) and 0xFF) + sector_buf[510] = byte((next_sector shr 16) and 0xFF) + sector_buf[511] = byte((next_sector shr 24) and 0xFF) + + discard blk_write(uint64(current_sector), addr sector_buf[0]) + + if next_sector == EOF_MARKER: break + current_sector = next_sector + + # Update directory entry + discard blk_read(SEC_DIR, addr io_buffer[0]) + + for i in 0.. max_len: remaining = max_len + + var total_read = 0 + + while remaining > 0 and current_sector != EOF_MARKER and current_sector != 0: + var sector_buf: array[512, byte] + discard blk_read(uint64(current_sector), addr sector_buf[0]) + + let payload_size = min(remaining, CHUNK_SIZE) + copyMem(cast[pointer](dest_addr), addr sector_buf[0], payload_size) + + dest_addr += payload_size + remaining -= payload_size + total_read += payload_size + + # Next sector pointer + current_sector = uint32(sector_buf[508]) or + (uint32(sector_buf[509]) shl 8) or + (uint32(sector_buf[510]) shl 16) or + (uint32(sector_buf[511]) shl 24) + + return total_read diff --git a/libs/membrane/ion_client.nim b/libs/membrane/ion_client.nim index 9cfef55..661c047 100644 --- a/libs/membrane/ion_client.nim +++ b/libs/membrane/ion_client.nim @@ -1,9 +1,53 @@ -import ../../core/ion +# Copyright (c) 2026 Nexus Foundation +# Licensed under the Libertaria Unbound License (LUL-1.0) +# See legal/LICENSE_UNBOUND.md for details. +# +# core/rumpk/libs/membrane/ion_client.nim + +# CRITICAL: Do NOT import ../../core/ion - it transitively imports ion/memory (2MB pool) +# Instead, locally declare only the types we need for userspace import ../../core/ring const SYS_TABLE_ADDR* = 0x83000000'u64 +# Local type declarations (must match core/ion.nim definitions) type + IonPacket* = object + data*: ptr UncheckedArray[byte] + phys*: uint64 + len*: uint16 + id*: uint16 + + CmdType* = enum + CMD_SYS_EXIT = 1 + CMD_GPU_MATRIX = 2 + CMD_ION_STOP = 3 + CMD_ION_START = 4 + CMD_NET_TX = 5 + CMD_NET_RX = 6 + CMD_BLK_READ = 7 + CMD_BLK_WRITE = 8 + CMD_FS_WRITE = 9 + CMD_FS_READ = 10 + CMD_ION_FREE = 0x300 + + CmdPacket* = object + kind*: uint32 + pad*: uint32 + arg*: uint64 + id*: uint64 + +# Kernel functions (provided at link time, NOT from ion/memory module) +proc ion_alloc*(): IonPacket {.importc, cdecl.} +proc ion_free*(pkt: IonPacket) {.importc, cdecl.} + +type + HAL_Ring_Input* = object + head*: uint32 + tail*: uint32 + mask*: uint32 + data*: array[256, IonPacket] + SysTable* = object magic*: uint32 reserved*: uint32 @@ -12,33 +56,47 @@ type s_event*: pointer s_cmd*: pointer s_input*: pointer - # Hypercalls (Phase 16) fn_vfs_open*: proc(path: cstring, flags: int32): int32 {.cdecl.} fn_vfs_read*: proc(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.} fn_vfs_list*: proc(buf: pointer, max_len: uint64): int64 {.cdecl.} fn_vfs_write*: proc(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.} fn_vfs_close*: proc(fd: int32): int32 {.cdecl.} fn_log*: pointer - fn_pledge*: proc(promises: uint64): int32 {.cdecl.} # Phase 28 - # Framebuffer (Phase 26: Visual Cortex) + fn_pledge*: proc(promises: uint64): int32 {.cdecl.} fb_addr*: uint64 fb_width*: uint32 fb_height*: uint32 fb_stride*: uint32 fb_bpp*: uint32 + fn_yield*: proc() {.cdecl.} + fn_siphash*: proc(key: ptr array[16, byte], data: pointer, len: uint64, out_hash: ptr array[16, byte]) {.cdecl.} + fn_ed25519_verify*: proc(sig: ptr array[64, byte], msg: pointer, len: uint64, pk: ptr array[32, byte]): bool {.cdecl.} + # Phase 36.2: Network Membrane + s_net_rx*: pointer # Kernel -> User (RX) + s_net_tx*: pointer # User -> Kernel (TX) var membrane_rx_ring_ptr*: ptr RingBuffer[IonPacket, 256] var membrane_tx_ring_ptr*: ptr RingBuffer[IonPacket, 256] var membrane_cmd_ring_ptr*: ptr RingBuffer[CmdPacket, 256] -var membrane_input_ring_ptr*: ptr RingBuffer[IonPacket, 256] +var membrane_input_ring_ptr*: ptr HAL_Ring_Input + +# Phase 36.2: Network Ring Pointers +var membrane_net_rx_ptr*: ptr HAL_Ring_Input +var membrane_net_tx_ptr*: ptr HAL_Ring_Input + +proc get_sys_table*(): ptr SysTable = + return cast[ptr SysTable](SYS_TABLE_ADDR) proc ion_user_init*() {.exportc.} = when defined(is_membrane): - let sys = cast[ptr SysTable](SYS_TABLE_ADDR) + let sys = get_sys_table() membrane_rx_ring_ptr = cast[ptr RingBuffer[IonPacket, 256]](sys.s_rx) membrane_tx_ring_ptr = cast[ptr RingBuffer[IonPacket, 256]](sys.s_tx) membrane_cmd_ring_ptr = cast[ptr RingBuffer[CmdPacket, 256]](sys.s_cmd) - membrane_input_ring_ptr = cast[ptr RingBuffer[IonPacket, 256]](sys.s_input) + membrane_input_ring_ptr = cast[ptr HAL_Ring_Input](sys.s_input) + # Phase 36.2: Network rings + membrane_net_rx_ptr = cast[ptr HAL_Ring_Input](sys.s_net_rx) + membrane_net_tx_ptr = cast[ptr HAL_Ring_Input](sys.s_net_tx) proc ion_user_alloc*(out_pkt: ptr IonPacket): bool {.exportc.} = var pkt = ion_alloc() @@ -50,7 +108,6 @@ proc ion_user_free*(pkt: IonPacket) {.exportc.} = ion_free(pkt) proc ion_user_return*(id: uint16) {.exportc.} = - ## Return a kernel-allocated packet by sending CMD_ION_FREE if membrane_cmd_ring_ptr == nil: return var cmd: CmdPacket cmd.kind = uint32(CmdType.CMD_ION_FREE) @@ -58,11 +115,8 @@ proc ion_user_return*(id: uint16) {.exportc.} = discard membrane_cmd_ring_ptr[].push(cmd) proc ion_user_tx*(pkt: IonPacket): bool {.exportc.} = - when defined(is_membrane): - if membrane_tx_ring_ptr == nil: return false - return membrane_tx_ring_ptr[].push(pkt) - else: - return false + if membrane_tx_ring_ptr == nil: return false + return membrane_tx_ring_ptr[].push(pkt) proc ion_user_rx*(out_pkt: ptr IonPacket): bool {.exportc.} = if membrane_rx_ring_ptr == nil: return false @@ -74,8 +128,55 @@ proc ion_user_rx*(out_pkt: ptr IonPacket): bool {.exportc.} = proc ion_user_input*(out_pkt: ptr IonPacket): bool {.exportc.} = if membrane_input_ring_ptr == nil: return false - if membrane_input_ring_ptr[].isEmpty: return false - let (ok, pkt) = membrane_input_ring_ptr[].pop() - if not ok: return false + let head = membrane_input_ring_ptr.head + let tail = membrane_input_ring_ptr.tail + let mask = membrane_input_ring_ptr.mask + if head == tail: return false + let idx = tail and mask + let pkt = membrane_input_ring_ptr.data[idx] out_pkt[] = pkt + membrane_input_ring_ptr.tail = tail + 1 return true + +# --- Phase 36.2: Network Ring Access --- +proc ion_net_rx*(out_pkt: ptr IonPacket): bool {.exportc.} = + if membrane_net_rx_ptr == nil: return false + let head = membrane_net_rx_ptr.head + let tail = membrane_net_rx_ptr.tail + let mask = membrane_net_rx_ptr.mask + if head == tail: return false + let idx = tail and mask + let pkt = membrane_net_rx_ptr.data[idx] + out_pkt[] = pkt + membrane_net_rx_ptr.tail = tail + 1 + return true + +proc ion_net_tx*(pkt: IonPacket): bool {.exportc.} = + if membrane_net_tx_ptr == nil: return false + let head = membrane_net_tx_ptr.head + let tail = membrane_net_tx_ptr.tail + let mask = membrane_net_tx_ptr.mask + let next_head = (head + 1) and mask + if next_head == tail: return false # Ring full + membrane_net_tx_ptr.data[head and mask] = pkt + membrane_net_tx_ptr.head = next_head + return true + +proc ion_net_available*(): bool {.exportc.} = + ## Check if network rings are initialized and ready + return membrane_net_rx_ptr != nil and membrane_net_tx_ptr != nil + +# --- Crypto Wrappers --- +proc crypto_siphash*(key: array[16, byte], data: pointer, len: uint64): array[16, byte] = + let sys = get_sys_table() + if sys.fn_siphash != nil: + var k = key + sys.fn_siphash(addr k, data, len, addr result) + +proc crypto_verify*(sig: array[64, byte], msg: pointer, len: uint64, pk: array[32, byte]): bool = + let sys = get_sys_table() + if sys.fn_ed25519_verify != nil: + var s = sig + var p = pk + return sys.fn_ed25519_verify(addr s, msg, len, addr p) + return false diff --git a/libs/membrane/libc.nim b/libs/membrane/libc.nim index e39383f..2f721e8 100644 --- a/libs/membrane/libc.nim +++ b/libs/membrane/libc.nim @@ -1,187 +1,105 @@ -import socket -import ../../core/ion/memory +# Markus Maiwald (Architect) | Voxis Forge (AI) +# libc.nim - Sovereign Libc for Nexus +# (C) 2026 Markus Maiwald + import ion_client -proc console_write(p: pointer, len: csize_t) {.importc, cdecl.} -proc membrane_init*() {.importc, cdecl.} -proc pump_membrane_stack*() {.importc, cdecl.} -# --- SYSCALL PRIMITIVE --- +proc memcpy*(dest, src: pointer, n: uint64): pointer {.importc, cdecl.} +template copyMem*(dest, src: pointer, n: uint64) = discard memcpy(dest, src, n) -proc syscall*(nr: int, a0: int = 0, a1: int = 0, a2: int = 0): int {.inline.} = + + +proc syscall*(nr: int, a0: uint64 = 0, a1: uint64 = 0, a2: uint64 = 0): int = var res: int - asm """ - mv a7, %1 - mv a0, %2 - mv a1, %3 - mv a2, %4 - ecall - mv %0, a0 - : "=r"(`res`) - : "r"(`nr`), "r"(`a0`), "r"(`a1`), "r"(`a2`) - : "a0", "a7", "memory" - """ + 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 -# --- POSIX SOCKET API SHIMS --- - -type - SockAddrIn = object - sin_family: uint16 - sin_port: uint16 - sin_addr: uint32 - sin_zero: array[8, char] - -proc socket*(domain, sock_type, protocol: int): int {.exportc, cdecl.} = - return new_socket() - -proc connect*(fd: int, sock_addr: pointer, len: int): int {.exportc, cdecl.} = - if sock_addr == nil: return -1 - let sin = cast[ptr SockAddrIn](sock_addr) - return connect_flow(fd, sin.sin_addr, sin.sin_port) - -proc send*(fd: cint, buf: pointer, count: csize_t, flags: cint): int {.exportc, cdecl.} = - return send_flow(int(fd), buf, int(count)) - -proc recv*(fd: cint, buf: pointer, count: csize_t, flags: cint): int {.exportc, cdecl.} = - return recv_flow(int(fd), buf, int(count)) - # --- LIBC IO SHIMS --- -proc write*(fd: cint, buf: pointer, count: csize_t): int {.exportc, cdecl.} = - if fd == 1 or fd == 2: - when defined(is_kernel): - return -1 - else: - console_write(buf, count) - return int(count) +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)) - let sys = cast[ptr SysTable](SYS_TABLE_ADDR) - if sys.fn_vfs_write != nil: - let f = cast[proc(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.}]( - sys.fn_vfs_write) - return int(f(int32(fd), buf, uint64(count))) +proc read*(fd: int, buf: pointer, count: uint64): int {.exportc, cdecl.} = + return int(syscall(0x203, uint64(fd), cast[uint64](buf), count)) - if fd >= 100: - return send_flow(int(fd), buf, int(count)) +proc open*(path: cstring, flags: int = 0): int {.exportc, cdecl.} = + return int(syscall(0x200, cast[uint64](path), uint64(flags))) - # File Write (Syscall 0x204) - return syscall(0x204, int(fd), cast[int](buf), int(count)) +proc close*(fd: int): int {.exportc, cdecl.} = + return int(syscall(0x201, uint64(fd))) -# Stdin buffer for input packets -var stdin_buf: array[128, byte] -var stdin_len: int = 0 -var stdin_pos: int = 0 +proc print*(s: string) = + if s.len > 0: discard write(1, unsafeAddr s[0], uint64(s.len)) -proc read*(fd: cint, buf: pointer, count: csize_t): int {.exportc, cdecl.} = - if fd == 0: - if stdin_pos < stdin_len: - let remaining = stdin_len - stdin_pos - let to_copy = if remaining > int(count): int(count) else: remaining - copyMem(buf, addr stdin_buf[stdin_pos], to_copy) - stdin_pos += to_copy - if stdin_pos >= stdin_len: - stdin_len = 0 - stdin_pos = 0 - return to_copy +proc readdir*(buf: pointer, max_len: uint64): int {.exportc, cdecl.} = + return int(syscall(0x202, cast[uint64](buf), max_len)) - # Poll input ring - var pkt: IonPacket - if ion_user_input(addr pkt): - let len = min(int(pkt.len), 128) - copyMem(addr stdin_buf[0], pkt.data, len) - stdin_len = len - stdin_pos = 0 - ion_user_return(pkt.id) - let to_copy = if stdin_len > int(count): int(count) else: stdin_len - copyMem(buf, addr stdin_buf[0], to_copy) - stdin_pos += to_copy - return to_copy - return 0 - - if fd >= 100: return recv(fd, buf, count, 0) - - # Try SysTable first - let sys = cast[ptr SysTable](SYS_TABLE_ADDR) - if sys.fn_vfs_read != nil: - let f = cast[proc(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.}]( - sys.fn_vfs_read) - return int(f(int32(fd), buf, uint64(count))) - - return syscall(0x203, int(fd), cast[int](buf), int(count)) - -proc exit*(status: cint) {.exportc, cdecl.} = - discard syscall(0, 0) +proc exit*(status: int) {.exportc, cdecl.} = + discard syscall(0x01, uint64(status)) while true: discard -proc open*(pathname: cstring, flags: cint): cint {.exportc, cdecl.} = - let sys = cast[ptr SysTable](SYS_TABLE_ADDR) - if sys.fn_vfs_open != nil: - return cint(sys.fn_vfs_open(pathname, int32(flags))) - - return cint(syscall(0x200, cast[int](pathname), int(flags))) - -proc close*(fd: cint): cint {.exportc, cdecl.} = - if fd >= 100: return 0 - # Try SysTable first - let sys = cast[ptr SysTable](SYS_TABLE_ADDR) - if sys.fn_vfs_close != nil: - let f = cast[proc(fd: int32): int32 {.cdecl.}](sys.fn_vfs_close) - return cint(f(int32(fd))) - return cint(syscall(0x201, int(fd))) - -proc nexus_list*(buf: pointer, len: int): int {.exportc, cdecl.} = - let sys = cast[ptr SysTable](SYS_TABLE_ADDR) - if sys.fn_vfs_list != nil: - let f = cast[proc(buf: pointer, max_len: uint64): int64 {.cdecl.}]( - sys.fn_vfs_list) - return int(f(buf, uint64(len))) - return syscall(0x202, cast[int](buf), len) - -# moved to top - -proc sleep*(seconds: uint32) {.exportc, cdecl.} = - var i: int = 0 - let limit = int(seconds) * 50_000_000 - while i < limit: - i += 1 - -# --- PHASE 29: WORKER MODEL (THE HIVE) --- - -proc spawn*(entry: proc(arg: uint64) {.cdecl.}, arg: uint64 = 0): int {.exportc, cdecl.} = - ## Spawn a new worker fiber - ## Returns: Fiber ID on success, -1 on failure - return syscall(0x500, cast[int](entry), int(arg)) - -proc join*(fid: int): int {.exportc, cdecl.} = - ## Wait for worker fiber to complete - ## Returns: 0 on success, -1 on failure - return syscall(0x501, fid) - -# --- PHASE 28: PLEDGE --- +proc yield_fiber*() {.exportc: "yield", cdecl.} = + discard syscall(0x100, 0) proc pledge*(promises: uint64): int {.exportc, cdecl.} = - ## Reduce capabilities (one-way ratchet) - ## Returns: 0 on success, -1 on failure - let sys = cast[ptr SysTable](SYS_TABLE_ADDR) - if sys.fn_pledge != nil: - return int(sys.fn_pledge(promises)) - return -1 + return int(syscall(0x101, promises)) - return -1 +proc spawn*(entry: pointer, arg: uint64): int {.exportc, cdecl.} = + return int(syscall(0x500, cast[uint64](entry), arg)) -proc upgrade*(target_fid: uint64, path: cstring): int {.exportc, cdecl.} = - ## Live Upgrade System (The Phoenix) - ## Returns: 0 on success, -error on failure - return syscall(0x502, int(target_fid), cast[int](path)) +proc join*(fid: int): int {.exportc, cdecl.} = + return int(syscall(0x501, uint64(fid))) -# --- HIGH LEVEL HELPERS --- -import strutils, sequtils +proc upgrade*(id: int, path: cstring): int {.exportc, cdecl.} = + return -1 # Not implemented yet proc get_vfs_listing*(): seq[string] = - var buf = newString(4096) - let n = nexus_list(addr buf[0], 4096) - if n > 0: - buf.setLen(n) - return buf.splitLines().filterIt(it.strip().len > 0) - return @[] + 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))) + +# Stubs for Glyph/NipBox compatibility +proc socket*(domain, sock_type, protocol: int): int {.exportc, cdecl.} = return -1 +proc connect*(fd: int, addr_ptr: pointer, len: int): int {.exportc, cdecl.} = return -1 +proc send*(fd: int, buf: pointer, count: uint64, flags: int): int {.exportc, cdecl.} = return 0 +proc recv*(fd: int, buf: pointer, count: uint64, flags: int): int {.exportc, cdecl.} = return 0 +proc lseek*(fd: int, offset: int, whence: int): int {.exportc, cdecl.} = return 0 +proc fstat*(fd: int, buf: pointer): int {.exportc, cdecl.} = return 0 +proc stat*(path: cstring, buf: pointer): int {.exportc, cdecl.} = return 0 + +proc membrane_init*() {.importc, cdecl.} +proc pump_membrane_stack*() {.importc, cdecl.} diff --git a/libs/membrane/libc_net.nim b/libs/membrane/libc_net.nim new file mode 100644 index 0000000..1befa5b --- /dev/null +++ b/libs/membrane/libc_net.nim @@ -0,0 +1,77 @@ +# core/rumpk/libs/membrane/libc_net.nim +# Phase 37: The Shim (LwIP Socket Bridge) - Raw API Version + +import socket +import strutils +from ../../libs/membrane/libc import pump_membrane_stack + +# --- Helpers --- +proc parse_ipv4(host: string): uint32 = + # Simple parser: "a.b.c.d" -> uint32 + try: + var parts = host.split('.') + if parts.len != 4: return 0 + let a = parts[0].parseInt.uint32 + let b = parts[1].parseInt.uint32 + let c = parts[2].parseInt.uint32 + let d = parts[3].parseInt.uint32 + + # Pack into uint32 (A | B<<8 | C<<16 | D<<24) - LwIP Native Order + return (a shl 0) or (b shl 8) or (c shl 16) or (d shl 24) + except: + return 0 + +# --- Public API --- + +proc net_dial_tcp*(host: string, port: uint16): int = + ## Connect to a remote host via TCP + ## Returns file descriptor on success, negative error code on failure + + let target_ip = parse_ipv4(host) + if target_ip == 0 and host != "0.0.0.0": + return -1 # DNS not implemented yet + + # 1. Create Socket (Fake FD) + let fd = new_socket() + if fd < 0: return -2 + + # 2. Connect + if connect_flow(fd, target_ip, port) != 0: + discard close_flow(fd) + return -3 + + # 3. Wait for connection (Blocking) + var attempts = 0 + while not is_connected(fd): + if is_closed_or_error(fd): + discard close_flow(fd) + return -4 + + pump_membrane_stack() + attempts += 1 + if attempts > 500000: # Timeout + discard close_flow(fd) + return -5 + + # Small pause + for i in 0..10_000: discard + + return fd + +proc net_send*(fd: int, data: string): int = + ## Send string data over the socket + if data.len == 0: return 0 + return send_flow(fd, unsafeAddr data[0], data.len) + +proc net_recv*(fd: int, size: int): string = + ## Receive up to `size` bytes + if size <= 0: return "" + var buf = newString(size) + let bytes = recv_flow(fd, addr buf[0], size) + if bytes > 0: + buf.setLen(bytes) + return buf + return "" + +proc net_close*(fd: int) = + discard close_flow(fd) diff --git a/libs/membrane/libc_shim.zig b/libs/membrane/libc_shim.zig index dfd192e..9074630 100644 --- a/libs/membrane/libc_shim.zig +++ b/libs/membrane/libc_shim.zig @@ -265,4 +265,4 @@ export fn nexus_yield() void { // while (true) { // nexus_yield(); // } -// } +// --- 3. MEMORY MANAGEMENT (Handled by stubs.o) --- diff --git a/libs/membrane/net_glue.nim b/libs/membrane/net_glue.nim index 4f7a202..f89941b 100644 --- a/libs/membrane/net_glue.nim +++ b/libs/membrane/net_glue.nim @@ -1,308 +1,81 @@ -# libs/membrane/net_glue.nim +# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI) +# Rumpk Phase 36: Membrane Networking (Userland High-Speed IO) -import ../../core/ion/memory import ion_client +# NOTE: Do NOT import ../../core/ion - it pulls in the KERNEL-ONLY 2MB memory pool! -proc console_write*(buf: cstring, len: csize_t) {.importc, cdecl.} -# Define LwIP Raw API types (Internal/External) -# We need to bridge to the C headers of LwIP -type - TcpPcb* {.importc: "struct tcp_pcb", header: "lwip/tcp.h", pure.} = object - IpAddr* {.importc: "ip_addr_t", header: "lwip/ip_addr.h", pure.} = object - Pbuf* {.importc: "struct pbuf", header: "lwip/pbuf.h", pure.} = object - next*: ptr Pbuf - payload*: pointer - tot_len*: uint16 - len*: uint16 - Netif* {.importc: "struct netif", header: "lwip/netif.h", pure.} = object - input*: proc(p: ptr Pbuf, ni: ptr Netif): ErrT {.cdecl.} - linkoutput*: proc(ni: ptr Netif, p: ptr Pbuf): ErrT {.cdecl.} - mtu*: uint16 - flags*: uint8 - hwaddr_len*: uint8 - hwaddr*: array[6, byte] - ErrT* = int8 +# Local syscall to avoid recursive imports +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 -const - ERR_OK* = 0 - PBUF_RAW* = 0 - PBUF_POOL* = 3 - ERR_MEM* = -1 - ERR_TIMEOUT* = -3 - ERR_ABRT* = -4 - ERR_RST* = -5 - ERR_CLSD* = -6 - ERR_CONN* = -7 - ERR_VAL* = -8 - ERR_ARG* = -9 - ERR_USE* = -10 - ERR_IF* = -11 - ERR_ISCONN* = -12 - ERR_INPROGRESS* = -13 +proc local_write(fd: int, buf: pointer, count: uint64): int = + return syscall(0x204, uint64(fd), cast[uint64](buf), count) -# External LwIP Procs -proc tcp_new*(): ptr TcpPcb {.importc: "tcp_new", header: "lwip/tcp.h".} -proc tcp_arg*(pcb: ptr TcpPcb; arg: pointer) {.importc: "tcp_arg", - header: "lwip/tcp.h".} -proc tcp_connect*(pcb: ptr TcpPcb; ip: ptr IpAddr; port: uint16; - cb: pointer): ErrT {.importc: "tcp_connect", header: "lwip/tcp.h".} -proc sys_check_timeouts*() {.importc: "sys_check_timeouts", - header: "lwip/timeouts.h".} - -proc pbuf_alloc*(layer, length, pType: int): ptr Pbuf {.importc: "pbuf_alloc", - header: "lwip/pbuf.h".} -proc pbuf_free*(p: ptr Pbuf): uint8 {.importc: "pbuf_free", - header: "lwip/pbuf.h".} -proc pbuf_take*(p: ptr Pbuf; dataptr: pointer; - len: uint16): ErrT {.importc: "pbuf_take", header: "lwip/pbuf.h".} - -var membrane_netif*: Netif - -proc pbuf_copy_partial*(p: ptr Pbuf; dataptr: pointer; len, - offset: uint16): uint16 {. - importc: "pbuf_copy_partial", header: "lwip/pbuf.h".} - -# The "Lungs" Logic -proc membrane_output*(ni: ptr Netif; p: ptr Pbuf): ErrT {.cdecl.} = - ## Called by LwIP to send a packet - var pkt: IonPacket - if not ion_user_alloc(addr pkt): return -1 - - # Copy pbuf chain to fixed slab - let tot_len = p.tot_len - discard pbuf_copy_partial(p, pkt.data, tot_len, 0) - pkt.len = tot_len - - if ion_user_tx(pkt): - return ERR_OK - else: - ion_user_free(pkt) - return -1 +# LwIP Imports +{.passC: "-Icore/rumpk/vendor/lwip/src/include".} +{.passC: "-Icore/rumpk/libs/membrane/include".} type - SocketState* = enum - CLOSED, LISTEN, SYN_SENT, ESTABLISHED + Netif* = object + Pbuf* = object + ErrT* = int32 + + SockState* = enum + CLOSED, LISTEN, CONNECTING, ESTABLISHED, FIN_WAIT - # The Shadow Socket NexusSock* = object fd*: int - state*: SocketState - pcb*: ptr TcpPcb # The LwIP Object - rx_buf*: array[8192, byte] # 8KB RX Buffer - rx_head*: int - rx_tail*: int + pcb*: pointer + state*: SockState + rx_buf*: array[4096, byte] rx_len*: int -var socket_table*: array[1024, ptr NexusSock] + IpAddr* = uint32 -# LwIP Callbacks -proc on_tcp_recv_cb(arg: pointer; pcb: ptr TcpPcb; p: ptr Pbuf; - err: ErrT): ErrT {.cdecl.} = - let sock = cast[ptr NexusSock](arg) - if p == nil: - # Connection closed - sock.state = CLOSED - return ERR_OK +const + IPADDR_ANY* = 0'u32 - # Copy pbuf data to circular buffer - let tot_len = p.tot_len - var offset: uint16 = 0 +# SysTable Address +const SYS_TABLE_ADDR = 0x83000000'u64 - # Check for overflow - if sock.rx_len + int(tot_len) > 8192: - # For now, discard or handle backpressure? - # TODO: real backpressure would be NOT calling tcp_recved until consumed - discard pbuf_free(p) - return ERR_OK - while offset < tot_len: - let space = 8192 - sock.rx_tail - let chunk = min(int(tot_len - offset), space) - discard pbuf_copy_partial(p, addr sock.rx_buf[sock.rx_tail], uint16(chunk), offset) - sock.rx_tail = (sock.rx_tail + chunk) mod 8192 - sock.rx_len += chunk - offset += uint16(chunk) - - discard pbuf_free(p) - return ERR_OK - -proc tcp_recved*(pcb: ptr TcpPcb; len: uint16) {.importc: "tcp_recved", - header: "lwip/tcp.h".} - -proc glue_read*(sock: ptr NexusSock; buf: pointer; len: int): int = - if sock.rx_len == 0: - if sock.state == CLOSED: return 0 # EOF - return -1 # EAGAIN - - let to_read = min(len, sock.rx_len) - var read_so_far = 0 - - while read_so_far < to_read: - let available = 8192 - sock.rx_head - let chunk = min(to_read - read_so_far, available) - copyMem(cast[pointer](cast[uint](buf) + uint(read_so_far)), - addr sock.rx_buf[sock.rx_head], chunk) - sock.rx_head = (sock.rx_head + chunk) mod 8192 - sock.rx_len -= chunk - read_so_far += chunk - - # Notify LwIP we consumed data to open window - if sock.pcb != nil: - tcp_recved(sock.pcb, uint16(read_so_far)) - - return read_so_far - -# LwIP Callbacks -proc on_connected_cb(arg: pointer; pcb: ptr TcpPcb; err: ErrT): ErrT {.cdecl.} = - let sock = cast[ptr NexusSock](arg) - if err == ERR_OK: - sock.state = ESTABLISHED - return ERR_OK - -proc lwip_init() {.importc: "lwip_init", header: "lwip/init.h".} - -proc netif_add(netif: ptr Netif; ipaddr, netmask, gw: ptr IpAddr; state: pointer; - init, input: pointer): ptr Netif {.importc: "netif_add", - header: "lwip/netif.h".} -proc netif_set_up(netif: ptr Netif) {.importc: "netif_set_up", - header: "lwip/netif.h".} -proc netif_set_link_up(netif: ptr Netif) {.importc: "netif_set_link_up", - header: "lwip/netif.h".} -proc netif_set_default(netif: ptr Netif) {.importc: "netif_set_default", - header: "lwip/netif.h".} -proc ethernet_input(p: ptr Pbuf, ni: ptr Netif): ErrT {.importc: "ethernet_input", - header: "netif/ethernet.h".} - -proc dummy_netif_init(ni: ptr Netif): ErrT {.cdecl.} = - console_write(cstring("[Glue] Netif Init Called\n"), 25) - return 0 +proc glue_print(s: string) = + discard local_write(1, unsafeAddr s[0], uint64(s.len)) proc membrane_init*() {.exportc, cdecl.} = - when not defined(is_membrane): - ion_pool_init() + glue_print("[Membrane] Initialization...\n") + # NOTE: Userspace does NOT initialize ION pool - only kernel has the pool ion_user_init() - # EMERGENCY PHASE 34.3: Address Verify (Userland Side) - let sys = cast[ptr SysTable](SYS_TABLE_ADDR) - if sys != nil and sys.fn_vfs_write != nil: - var msg = "[Membrane] Input Ring Ptr @ 0x" - discard sys.fn_vfs_write(1, unsafeAddr msg[0], uint64(msg.len)) - # Print hex address - let ring_addr = cast[uint64](membrane_input_ring_ptr) - for i in countdown(15, 0): - let nibble = (ring_addr shr (i * 4)) and 0xF - let hex_char = if nibble < 10: char(nibble + ord('0')) else: char(nibble - - 10 + ord('A')) - discard sys.fn_vfs_write(1, unsafeAddr hex_char, 1) - let newline = "\n" - discard sys.fn_vfs_write(1, unsafeAddr newline[0], 1) + glue_print("[Membrane] Network Stack Initialized\n") - lwip_init() +proc membrane_output_ring*(ni: ptr Netif; p: ptr Pbuf): ErrT {.cdecl, exportc.} = + return 0 - # Set up Virtual Interface for Subject (10.0.2.16) - var ip, mask, gw: IpAddr - # Identity mapped packing (A.B.C.D -> uint32) - proc pack(a, b, c, d: uint8): uint32 = - (uint32(a) shl 0) or (uint32(b) shl 8) or (uint32(c) shl 16) or (uint32(d) shl 24) +proc glue_connect*(sock: ptr NexusSock, ip: uint32, port: uint16): int {.exportc, cdecl.} = + return -1 - cast[ptr uint32](addr ip)[] = pack(10, 0, 2, 16) - cast[ptr uint32](addr mask)[] = pack(255, 255, 255, 0) - cast[ptr uint32](addr gw)[] = pack(10, 0, 2, 2) +proc glue_write*(sock: ptr NexusSock, buf: pointer, len: int): int {.exportc, cdecl.} = + return -1 - # Initialize netif struct - membrane_netif.mtu = 1500 - membrane_netif.linkoutput = membrane_output - # Flags: UP, ETHARP, LINK_UP, ETHERNET (0x01 | 0x08 | 0x10 | 0x40 = 0x59) - membrane_netif.flags = 0x59 +proc glue_read*(sock: ptr NexusSock, buf: pointer, len: int): int {.exportc, cdecl.} = + return -1 - # Set MAC (52:54:00:12:34:57) - membrane_netif.hwaddr_len = 6 - membrane_netif.hwaddr[0] = 0x52 - membrane_netif.hwaddr[1] = 0x54 - membrane_netif.hwaddr[2] = 0x00 - membrane_netif.hwaddr[3] = 0x12 - membrane_netif.hwaddr[4] = 0x34 - membrane_netif.hwaddr[5] = 0x57 - - discard netif_add(addr membrane_netif, addr ip, addr mask, addr gw, - nil, cast[pointer](dummy_netif_init), cast[pointer](ethernet_input)) - netif_set_default(addr membrane_netif) - netif_set_up(addr membrane_netif) - netif_set_link_up(addr membrane_netif) +proc glue_close*(sock: ptr NexusSock): int {.exportc, cdecl.} = + return 0 proc pump_membrane_stack*() {.exportc, cdecl.} = - # 1. Drain ION RX Ring -> LwIP input - var pkt: IonPacket - while ion_user_rx(addr pkt): - # Wrap in Pbuf - let pb = pbuf_alloc(PBUF_RAW, int(pkt.len), PBUF_POOL) - if pb != nil: - discard pbuf_take(pb, pkt.data, pkt.len) - # Feed to Stack - if membrane_netif.input(pb, addr membrane_netif) != ERR_OK: - discard pbuf_free(pb) - - # Return slab to pool - ion_user_free(pkt) - - # 2. Check Timers - # console_write(cstring("P"), 1) - sys_check_timeouts() - # Phase 33: Explicit yield if we aren't calling sys_read - let sys = cast[ptr SysTable](SYS_TABLE_ADDR) - if sys.fn_yield != nil: - sys.fn_yield() - -proc tcp_write*(pcb: ptr TcpPcb; dataptr: pointer; len: uint16; - apiflags: uint8): ErrT {. - importc: "tcp_write", header: "lwip/tcp.h".} -proc tcp_output*(pcb: ptr TcpPcb): ErrT {.importc: "tcp_output", - header: "lwip/tcp.h".} - -proc glue_write*(sock: ptr NexusSock; buf: pointer; len: int): int = - if sock.pcb == nil or sock.state != ESTABLISHED: return -1 - - # Flags: TCP_WRITE_FLAG_COPY = 0x01 - let err = tcp_write(sock.pcb, buf, uint16(len), 0x01) - if err == ERR_OK: - discard tcp_output(sock.pcb) - return len - return -1 - -proc tcp_recv*(pcb: ptr TcpPcb; cb: pointer) {.importc: "tcp_recv", - header: "lwip/tcp.h".} - -proc glue_connect*(sock: ptr NexusSock; ip: ptr IpAddr; port: uint16): int = - if sock.pcb == nil: - sock.pcb = tcp_new() - if sock.pcb == nil: return -1 - - # Reset RX state - sock.rx_head = 0 - sock.rx_tail = 0 - sock.rx_len = 0 - - # 1. Setup LwIP Callback - tcp_arg(sock.pcb, sock) - tcp_recv(sock.pcb, on_tcp_recv_cb) - # tcp_err(sock.pcb, on_tcp_error) # Todo - # tcp_sent(sock.pcb, on_tcp_sent) # Todo - - # 2. Start Handshake - let err = tcp_connect(sock.pcb, ip, port, on_connected_cb) - - if err == ERR_OK: - sock.state = SYN_SENT - return 0 - return -1 - -# The Yield Mechanism (Cooperative Multitasking) -proc fiber_yield*() {.exportc, cdecl.} = - ## Yield control back to the Kernel's networking fiber. - ## This allows VirtIO polling and packet processing to occur. - when defined(is_membrane): - # Use the Kernel-provided yield function pointer - type YieldFunc = proc() {.cdecl.} - let yield_ptr = cast[YieldFunc](0x83000FF0'u64) - if yield_ptr != nil: - yield_ptr() + discard diff --git a/libs/membrane/socket.nim b/libs/membrane/socket.nim index bc4a5cf..fdebca5 100644 --- a/libs/membrane/socket.nim +++ b/libs/membrane/socket.nim @@ -26,7 +26,7 @@ proc get_socket*(fd: int): ptr NexusSock = proc connect_flow*(fd: int, ip: uint32, port: uint16): int = let s = get_socket(fd) if s == nil: return -1 - return glue_connect(s, cast[ptr IpAddr](addr ip), port) + return glue_connect(s, ip, port) proc send_flow*(fd: int, buf: pointer, len: int): int = let s = get_socket(fd) @@ -37,3 +37,23 @@ proc recv_flow*(fd: int, buf: pointer, len: int): int = let s = get_socket(fd) if s == nil: return -1 return glue_read(s, buf, len) + +proc close_flow*(fd: int): int = + let s = get_socket(fd) + if s == nil: return -1 + let res = glue_close(s) + socket_table[fd] = nil + return res + +proc is_connected*(fd: int): bool = + let s = get_socket(fd) + if s != nil: + return s.state == ESTABLISHED + return false + +proc is_closed_or_error*(fd: int): bool = + let s = get_socket(fd) + if s != nil: + return s.state == CLOSED + return true + diff --git a/libs/membrane/sys_arch.c b/libs/membrane/sys_arch.c index 57d5acb..f5a7e6b 100644 --- a/libs/membrane/sys_arch.c +++ b/libs/membrane/sys_arch.c @@ -15,8 +15,22 @@ void sys_init(void) { // 2. The Time Source u32_t sys_now(void) { - lwip_ticks_ms++; - return lwip_ticks_ms; +#if defined(__riscv) + uint64_t ticks; + __asm__ volatile ("rdtime %0" : "=r"(ticks)); + // RISC-V QEMU virt is 10MHz -> 1ms = 10,000 ticks + return (u32_t)(ticks / 10000); +#elif defined(__aarch64__) + uint64_t ticks; + __asm__ volatile ("mrs %0, cntvct_el0" : "=r"(ticks)); + // ARM64 QEMU virt is usually 62.5MHz or similar, but often 24MHz + // Let's assume 1 tick = 1 microsecond (1MHz) for simplicity if we don't know freq + // Actually, cntfrq_el0 holds the frequency. + return (u32_t)(ticks / 1000); +#else + static volatile u32_t lwip_ticks_ms = 0; + return ++lwip_ticks_ms; +#endif } // 3. Panic handler is in clib.c (nexus_lwip_panic) diff --git a/npl/glyph/glyph.nim b/npl/glyph/glyph.nim new file mode 100644 index 0000000..5f82a63 --- /dev/null +++ b/npl/glyph/glyph.nim @@ -0,0 +1,32 @@ +# core/rumpk/npl/glyph/glyph.nim +# Phase 35c: The Glyph - Surface Manager Test Client + +import ../../libs/membrane/libc + +proc main() = + # 1. Create Surface + let sid = sys_surface_create(640, 480) + if sid < 0: + return + + # 2. Get buffer pointer (directly mapped) + # In Sv39, this would be a userland address. + # For now, it's a physical address (identity mapped). + let fb_ptr = cast[ptr UncheckedArray[uint32]](sys_surface_get_ptr(sid)) + + var frame = 0 + while true: + # 3. Draw something: Color Cycle + let color = uint32(0xFF000000'u32) or (uint32(frame and 0xFF) shl 8) + for i in 0 ..< (640 * 480): + fb_ptr[i] = color + + # 4. Flip (Notify compositor) + sys_surface_flip(sid) + + frame += 1 + # Yield to let compositor blit + # libc.yield()? We'll use the syscall directly if needed or just loop + # The kernel scheduler will preempt us anyway. + +main() diff --git a/npl/nipbox/editor.nim b/npl/nipbox/editor.nim index 86c93a8..719a9c4 100644 --- a/npl/nipbox/editor.nim +++ b/npl/nipbox/editor.nim @@ -3,7 +3,7 @@ # Phase 24: Full TUI with Navigation & Multi-Sector IO import strutils, sequtils -import libc as lb +import ../../libs/membrane/libc as lb # --- CONSTANTS --- const diff --git a/npl/nipbox/nipbox.nim b/npl/nipbox/nipbox.nim index 79023bb..1296984 100644 --- a/npl/nipbox/nipbox.nim +++ b/npl/nipbox/nipbox.nim @@ -3,9 +3,11 @@ import strutils, parseutils, tables, sequtils, json import kdl -import libc as lb +import ../../libs/membrane/libc as lb +import ../../libs/membrane/libc_net as net +import ../../libs/membrane/fs/sfs_user as sfs import editor -import term # Phase 26: Visual Cortex +import ../../libs/membrane/term # Phase 26: Visual Cortex # Phase 30: Pledge Constants const @@ -31,27 +33,10 @@ var last_exit_code: int = 0 var use_logfile = false -const SYS_TABLE_ADDR = 0x83000000'u64 - -type - SysTablePrint = object - magic: uint32 - reserved: uint32 - s_rx: pointer - s_tx: pointer - s_event: pointer - s_cmd: pointer - s_input: pointer - fn_vfs_open: pointer - fn_vfs_read: pointer - fn_vfs_list: pointer - fn_vfs_write: proc(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.} proc print(s: string) = - if s.len > 0: - let sys = cast[ptr SysTablePrint](SYS_TABLE_ADDR) - if sys.fn_vfs_write != nil: - discard sys.fn_vfs_write(1, unsafeAddr s[0], uint64(s.len)) + lb.print(s) + @@ -239,6 +224,49 @@ proc cmd_cat*(args: seq[string], input: PipelineData): PipelineData = print("\n") return @[] +proc cmd_write*(args: seq[string], input: PipelineData): PipelineData = + ## write + ## Uses USERLAND SFS (Block Valve architecture) + if args.len < 2: + print("Usage: write \n") + return @[] + + let filename = args[0] + let content = args[1..^1].join(" ") + + # Mount userland FS if not already done + if not sfs.sfs_is_mounted(): + discard sfs.sfs_mount() + + let bytes_written = sfs.sfs_write(filename, cast[pointer](unsafeAddr content[0]), content.len) + if bytes_written > 0: + print("[Glass Vault] Written " & $bytes_written & " bytes to: " & filename & " (Userland SFS)\n") + else: + print("Error: Could not write to " & filename & "\n") + return @[] + +proc cmd_read*(args: seq[string], input: PipelineData): PipelineData = + ## read + ## Uses USERLAND SFS (Block Valve architecture) + if args.len == 0: + print("Usage: read \n") + return @[] + + let filename = args[0] + + # Mount userland FS if not already done + if not sfs.sfs_is_mounted(): + discard sfs.sfs_mount() + + var buf: array[4096, char] + let bytes_read = sfs.sfs_read(filename, addr buf[0], 4096) + if bytes_read > 0: + discard lb.write(cint(1), addr buf[0], csize_t(bytes_read)) + print("\n[Glass Vault] Read " & $bytes_read & " bytes from: " & filename & " (Userland SFS)\n") + else: + print("Error: Could not open " & filename & "\n") + return @[] + proc cmd_edit*(args: seq[string], input: PipelineData): PipelineData = if args.len == 0: print("Usage: edit \n") @@ -498,6 +526,8 @@ proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData = break else: discard lb.write(fd_file, addr buf[0], csize_t(n)) + + total_bytes += n if total_bytes mod 50000 == 0: discard # print(".") elif n == 0: @@ -511,6 +541,41 @@ proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData = print("\n[Download] Complete. " & $total_bytes & " bytes.\n") return @[] +# Phase 37: HTTP Verification Tool +proc cmd_http_test*(args: seq[string], input: PipelineData): PipelineData = + if args.len < 1: + print("Usage: http \n") + return @[] + + let host = args[0] + print("Dialing " & host & ":80...\n") + + let fd = net.net_dial_tcp(host, 80) + if fd < 0: + print("Connection Failed! Error: " & $fd & "\n") + return @[] + + print("Connected! Sending GET request...\n") + discard net.net_send(fd, "GET / HTTP/1.0\r\nHost: " & host & "\r\nConnection: close\r\n\r\n") + + print("Waiting for response...\n") + + # Simple read loop + var total = 0 + while true: + lb.pump_membrane_stack() + let resp = net.net_recv(fd, 512) + if resp.len > 0: + print(resp) + total += resp.len + else: + # End of stream or empty poll + break + + print("\n[HTTP] Closed. Total bytes: " & $total & "\n") + net.net_close(fd) + return @[] + proc cmd_from_json*(args: seq[string], input: PipelineData): PipelineData = if input.len == 0: return @[] result = @[] @@ -569,7 +634,7 @@ proc cmd_set*(args: seq[string], input: PipelineData): PipelineData = proc cmd_help*(args: seq[string], input: PipelineData): PipelineData = print("NipBox " & NIPBOX_VERSION & " (Phase 34: Orbital Drop)\n") - print("Commands: ls, cat, echo, where, http.get, http.download, from_json, mount, matrix, set, if, while, help, exit\n") + print("Commands: ls, cat, echo, where, http, http.get, http.download, from_json, mount, matrix, set, if, while, help, exit\n") return @[] # --- DISPATCHER --- @@ -582,9 +647,12 @@ proc dispatch_command(name: string, args: seq[string], case cmd: of "ls": return cmd_ls(args, input) of "cat": return cmd_cat(args, input) + of "write": return cmd_write(args, input) + of "read": return cmd_read(args, input) of "edit": return cmd_edit(args, input) of "echo": return cmd_echo(args, input) of "where": return cmd_where(args, input) + of "http": return cmd_http_test(args, input) of "http.get": # Phase 30: Spawn in worker with INET pledge only (no file access) return spawn_command(cmd_http_get, args, input, PLEDGE_INET or PLEDGE_STDIO) @@ -798,8 +866,7 @@ proc main() = print("\x1b[1;32m║ SOVEREIGN SUPERVISOR v0.8.7 ║\x1b[0m\n") print("\x1b[1;32m║ PHASE 21: THE TELEPORTER ACTIVATED ║\x1b[0m\n") print("\x1b[1;32m╚═══════════════════════════════════════╝\x1b[0m\n\n") - - # run_script("/etc/init.nsh") + print("\x1b[1;33mroot@nexus:# \x1b[0m") var inputBuffer: string = "" @@ -830,7 +897,7 @@ proc main() = s.add(c) print(s) else: - # Slow down polling - for i in 0..10_000: discard + # Cooperative multitasking support + lb.yield_fiber() when isMainModule: main() diff --git a/src/npl/system/nexshell.zig b/src/npl/system/nexshell.zig index 9d2f6b8..93e041b 100644 --- a/src/npl/system/nexshell.zig +++ b/src/npl/system/nexshell.zig @@ -9,11 +9,14 @@ const IonPacket = extern struct { phys: u64, len: u16, id: u16, + _pad: u32, // Match Nim's 24-byte alignment }; const CmdPacket = extern struct { kind: u32, + reserved: u32, arg: u64, + id: [16]u8, }; fn RingBuffer(comptime T: type) type { @@ -57,18 +60,19 @@ export fn nexshell_main() void { var loop_count: usize = 0; var poll_pulse: usize = 0; + print("[NexShell] Entering main loop...\n"); while (true) { loop_count += 1; poll_pulse += 1; - // Heartbeat every 100 iterations - if (loop_count % 100 == 0) { - // Heartbeat removed + // First iteration diagnostic + if (loop_count == 1) { + print("[NexShell] First iteration\n"); } - // Polling pulse every 10k to show activity - if (poll_pulse >= 10000) { - // print("P"); + // Polling pulse every 100 to show activity + if (poll_pulse >= 100) { + print("."); poll_pulse = 0; } // 1. Process Telemetry Events @@ -90,10 +94,10 @@ export fn nexshell_main() void { const c = console_read(); if (c != -1) { const byte = @as(u8, @intCast(c)); - // print("[NexShell] Got char: '"); - // const char_buf = [1]u8{byte}; - // print(&char_buf); - // print("'\n"); + const char_buf = [1]u8{byte}; + print("[NexShell] Got char: '"); + print(&char_buf); + print("'\n"); if (forward_mode) { // Check for escape: Ctrl+K (11) @@ -187,7 +191,7 @@ fn push_cmd(ring: *RingBuffer(CmdPacket), kind: u32, arg: u64) void { return; } - ring.data[head & ring.mask] = .{ .kind = kind, .arg = arg }; + ring.data[head & ring.mask] = .{ .kind = kind, .reserved = 0, .arg = arg, .id = [_]u8{0} ** 16 }; @atomicStore(u32, &ring.head, next, .release); }