Phase 37: The Glass Cage - Memory Isolation Complete
VICTORY: All page faults (Code 12, 13, 15) eliminated. NipBox runs in isolated userspace. Root Cause Diagnosed: - Kernel BSS (0x84D5B030) was overwritten by NipBox loading at 0x84000000 - current_fiber corruption caused cascading failures Strategic Fixes: 1. Relocated NipBox to 0x86000000 (eliminating BSS collision) 2. Expanded DRAM to 256MB, User region to 64MB (accommodating NipBox BSS) 3. Restored Kernel GP register in trap handler (fixing global access) 4. Conditionally excluded ion/memory from userspace builds (removing 2MB pool) 5. Enabled release build optimizations (reducing BSS bloat) Results: - Kernel globals: SAFE - User memory: ISOLATED (Sv39 active) - Syscalls: OPERATIONAL - Scheduler: STABLE - NipBox: ALIVE (waiting for stdin) Files Modified: - core/rumpk/apps/linker_user.ld: User region 0x86000000-0x89FFFFFF (64MB) - core/rumpk/hal/mm.zig: DRAM 256MB, User map 32-256MB - core/rumpk/hal/entry_riscv.zig: GP reload in trap handler - core/rumpk/core/ion.nim: Conditional memory export - core/rumpk/libs/membrane/ion_client.nim: Local type declarations - core/rumpk/libs/membrane/net_glue.nim: Removed ion import - core/rumpk/libs/membrane/compositor.nim: Stubbed unused functions - src/nexus/builder/nipbox.nim: Release build flags Next: Fix stdin delivery to enable interactive shell.
This commit is contained in:
parent
4e0e9ed467
commit
1b4facd86b
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ SECTIONS
|
|||
*(.data*)
|
||||
}
|
||||
|
||||
. = ALIGN(4096);
|
||||
|
||||
.bss : {
|
||||
__bss_start = .;
|
||||
*(.bss*)
|
||||
|
|
|
|||
|
|
@ -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.}
|
||||
|
|
|
|||
197
core/ion.nim
197
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)")
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
498
core/kernel.nim
498
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..<MAX_WORKERS:
|
||||
if worker_pool[i].id == current_fiber.id:
|
||||
worker_active[i] = false
|
||||
kprint("[Worker] Fiber ")
|
||||
kprint_hex(current_fiber.id)
|
||||
kprintln(" terminated")
|
||||
break
|
||||
|
||||
# Yield forever (dead fiber)
|
||||
while true:
|
||||
fiber_yield()
|
||||
|
||||
proc k_spawn(entry: pointer, arg: uint64): int32 {.exportc, cdecl.} =
|
||||
## Create a new worker fiber
|
||||
## Returns: Fiber ID on success, -1 on failure
|
||||
|
||||
# Find free worker slot
|
||||
var slot = -1
|
||||
for i in 0..<MAX_WORKERS:
|
||||
if not worker_active[i]:
|
||||
slot = i
|
||||
break
|
||||
|
||||
if slot == -1:
|
||||
kprintln("[Spawn] Worker pool exhausted")
|
||||
return -1
|
||||
if slot == -1: return -1
|
||||
|
||||
# Initialize worker fiber
|
||||
let worker = addr worker_pool[slot]
|
||||
worker.id = next_worker_id
|
||||
next_worker_id += 1
|
||||
worker.promises = PLEDGE_ALL
|
||||
worker.sleep_until = 0
|
||||
worker.user_entry = entry
|
||||
worker.user_arg = arg
|
||||
|
||||
init_fiber(worker, worker_trampoline, addr worker_stacks[slot][0], sizeof(
|
||||
worker_stacks[slot]))
|
||||
init_fiber(worker, worker_trampoline, addr worker_stacks[slot][0], sizeof(worker_stacks[slot]))
|
||||
worker_active[slot] = true
|
||||
|
||||
kprint("[Spawn] Created worker FID=")
|
||||
kprint_hex(worker.id)
|
||||
kprintln("")
|
||||
|
||||
return int32(worker.id)
|
||||
|
||||
proc k_join(fid: uint64): int32 {.exportc, cdecl.} =
|
||||
## Wait for worker fiber to complete
|
||||
## Returns: 0 on success, -1 if FID not found
|
||||
|
||||
# Find worker by ID
|
||||
var found = false
|
||||
for i in 0..<MAX_WORKERS:
|
||||
if worker_pool[i].id == fid and worker_active[i]:
|
||||
found = true
|
||||
# Busy wait (yield until worker is inactive)
|
||||
while worker_active[i]:
|
||||
fiber_yield()
|
||||
return 0
|
||||
return -1
|
||||
|
||||
if not found:
|
||||
kprintln("[Join] Worker not found")
|
||||
return -1
|
||||
|
||||
return 0
|
||||
|
||||
# Phase 28: Pledge Implementation
|
||||
# Pledge Implementation
|
||||
proc k_pledge(promises: uint64): int32 {.exportc, cdecl.} =
|
||||
## The Ratchet: Reduce capabilities, never increase.
|
||||
## Returns 0 on success, -1 on failure.
|
||||
if current_fiber == nil:
|
||||
return -1
|
||||
|
||||
# Capability Ratchet: Can only remove bits, never add
|
||||
if current_fiber == nil: return -1
|
||||
current_fiber.promises = current_fiber.promises and promises
|
||||
|
||||
kprint("[Pledge] Fiber ")
|
||||
kprint_hex(current_fiber.id)
|
||||
kprint(" restricted to: ")
|
||||
kprint_hex(current_fiber.promises)
|
||||
kprintln("")
|
||||
|
||||
return 0
|
||||
proc k_handle_exception*(scause, sepc, stval: uint) {.exportc, cdecl.} =
|
||||
kprint("\n[SECURITY] EXCEPTION! scause=")
|
||||
kprint_hex(uint64(scause))
|
||||
kprint(" sepc=")
|
||||
kprint_hex(uint64(sepc))
|
||||
kprint(" stval=")
|
||||
kprint_hex(uint64(stval))
|
||||
kprintln("\n")
|
||||
|
||||
if current_fiber != nil:
|
||||
kprint("[SECURITY] Faulting Fiber: ")
|
||||
if current_fiber.name != nil: kprint(current_fiber.name)
|
||||
else: kprint_hex(current_fiber.id)
|
||||
kprintln("")
|
||||
proc mm_debug_check_va(va: uint64) {.importc, cdecl.}
|
||||
|
||||
proc k_handle_exception*(nr, epc, tval: uint) {.exportc, cdecl.} =
|
||||
kprintln("\n[EXCEPTION] FATAL")
|
||||
kprint(" Code: "); kprint_hex(nr)
|
||||
kprint("\n EPC: "); kprint_hex(epc)
|
||||
kprint("\n TVAL: "); kprint_hex(tval)
|
||||
|
||||
if nr == 12: # Instruction Page Fault
|
||||
kprintln("\n[MM] Dumping PTE for EPC:")
|
||||
mm_debug_check_va(epc)
|
||||
|
||||
var sstatus_val: uint64
|
||||
{.emit: "asm volatile(\"csrr %0, sstatus\" : \"=r\"(`sstatus_val`));".}
|
||||
kprint("[CPU] sstatus: "); kprint_hex(sstatus_val)
|
||||
if (sstatus_val and (1 shl 8)) != 0:
|
||||
kprintln(" (Mode: Supervisor)")
|
||||
else:
|
||||
kprintln(" (Mode: User)")
|
||||
|
||||
kprintln("\n[SYSTEM HALTED]")
|
||||
rumpk_halt()
|
||||
|
||||
proc k_check_deferred_yield*() {.exportc, cdecl.} =
|
||||
## Called by trap handler to check if the current fiber wants to yield
|
||||
## after a syscall or interrupt return.
|
||||
if current_fiber != nil and current_fiber.wants_yield:
|
||||
current_fiber.wants_yield = false
|
||||
# kprintln("[Sched] Deferred yield triggered")
|
||||
fiber_yield()
|
||||
|
||||
# Non-recoverable for now: Stay in loop
|
||||
while true: discard
|
||||
|
||||
proc k_handle_syscall*(nr, a0, a1, a2: uint): uint {.exportc, cdecl.} =
|
||||
# kprint("[Syscall] "); kprint_hex(nr); kprintln("")
|
||||
if nr != 0x100: # Ignore YIELD noise
|
||||
kprint("[Syscall] NR: "); kprint_hex(nr); kprintln("")
|
||||
|
||||
case nr:
|
||||
of 0x01: # EXIT
|
||||
kprintln("[Kernel] Subject EXIT Triggered")
|
||||
var pkt = CmdPacket(kind: uint32(CmdType.CMD_SYS_EXIT), arg: a0)
|
||||
chan_cmd.send(pkt)
|
||||
current_fiber.wants_yield = true
|
||||
return 0
|
||||
of 0x101: # PLEDGE
|
||||
# Only allow reducing privileges? For now, allow setting.
|
||||
current_fiber.promises = a0
|
||||
return 0
|
||||
of 0x200: # OPEN
|
||||
# Phase 28: Enforce RPATH/WPATH
|
||||
let flags = int32(a1)
|
||||
let needs_write = (flags and 0x01) != 0 # O_WRONLY or O_RDWR
|
||||
|
||||
if needs_write:
|
||||
if (current_fiber.promises and PLEDGE_WPATH) == 0:
|
||||
kprintln("[SECURITY] PLEDGE VIOLATION: WPATH required for write")
|
||||
return cast[uint](-1)
|
||||
if (flags and 0x01) != 0:
|
||||
if (current_fiber.promises and PLEDGE_WPATH) == 0: return cast[uint](-1)
|
||||
else:
|
||||
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_open(cast[cstring](a0), flags))
|
||||
of 0x201: # CLOSE
|
||||
return uint(ion_vfs_close(int32(a0)))
|
||||
of 0x202: # LIST
|
||||
# Phase 28: Enforce RPATH
|
||||
if (current_fiber.promises and PLEDGE_RPATH) == 0:
|
||||
kprintln("[SECURITY] PLEDGE VIOLATION: RPATH required for list")
|
||||
return cast[uint](-1)
|
||||
if (current_fiber.promises and PLEDGE_RPATH) == 0: return cast[uint](-1)
|
||||
return uint(ion_vfs_list(cast[pointer](a0), uint64(a1)))
|
||||
of 0x203: # READ
|
||||
# Phase 28: Enforce RPATH/STDIO
|
||||
if a0 == 0:
|
||||
if (current_fiber.promises and PLEDGE_STDIO) == 0:
|
||||
kprintln("[SECURITY] PLEDGE VIOLATION: STDIO required for read(0)")
|
||||
return cast[uint](-1)
|
||||
|
||||
if (current_fiber.promises and PLEDGE_STDIO) == 0: return cast[uint](-1)
|
||||
var pkt: IonPacket
|
||||
kprintln("[Kernel] sys_read(0)")
|
||||
if chan_input.recv(pkt):
|
||||
let n = if uint64(pkt.len) < a2: uint64(pkt.len) else: a2
|
||||
if n > 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.}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
10
hal/gpu.zig
10
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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
//
|
||||
|
|
|
|||
142
hal/mm.zig
142
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("");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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..<count:
|
||||
let offset = i * SECTOR_SIZE
|
||||
let res = blk_read(start_sector + uint64(i),
|
||||
cast[pointer](cast[int](buf) + offset))
|
||||
if res < 0: return res
|
||||
total += res
|
||||
return total
|
||||
|
||||
proc blk_write_multi*(start_sector: uint64, buf: pointer, count: int): int =
|
||||
## Write multiple contiguous sectors
|
||||
var total = 0
|
||||
for i in 0..<count:
|
||||
let offset = i * SECTOR_SIZE
|
||||
let res = blk_write(start_sector + uint64(i),
|
||||
cast[pointer](cast[int](buf) + offset))
|
||||
if res < 0: return res
|
||||
total += res
|
||||
return total
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
# libs/membrane/compositor.nim
|
||||
# Phase 35b/d: The Sovereign Compositor + Input Router
|
||||
|
||||
import ../../core/ion
|
||||
|
||||
const SYS_TABLE_ADDR = 0x83000000'u64
|
||||
|
||||
const
|
||||
GAP = 10 # Pixels between windows
|
||||
FOCUS_COLOR = 0xFF00FFFF'u32 # Cyan border for focused window
|
||||
BG_COLOR = 0xFF101020'u32 # Dark Blue background
|
||||
|
||||
type
|
||||
Surface* = object
|
||||
id*: int32
|
||||
buffer*: ptr UncheckedArray[uint32]
|
||||
width*, height*: int
|
||||
x*: int # Logical X position on the infinite strip
|
||||
dirty*: bool
|
||||
focused*: bool
|
||||
|
||||
Compositor* = object
|
||||
surfaces*: seq[Surface]
|
||||
view_x*: int # Viewport scroll position
|
||||
focused_idx*: int # index in seq[Surface]
|
||||
|
||||
var c*: Compositor
|
||||
|
||||
# HAL Imports
|
||||
proc hal_surface_alloc*(w, h: uint32): int32 {.importc, cdecl.}
|
||||
proc hal_surface_get_ptr*(id: int32): ptr UncheckedArray[uint32] {.importc, cdecl.}
|
||||
|
||||
proc get_sys_table(): ptr ion.SysTable =
|
||||
return cast[ptr ion.SysTable](SYS_TABLE_ADDR)
|
||||
|
||||
proc blit_surface(s: Surface, dest_x, dest_y: int) =
|
||||
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)
|
||||
|
||||
# Clipping
|
||||
let start_y = max(0, dest_y)
|
||||
let end_y = min(fb_h, dest_y + s.height)
|
||||
let start_x = max(0, dest_x)
|
||||
let end_x = min(fb_w, dest_x + s.width)
|
||||
|
||||
if start_x >= 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)
|
||||
|
|
@ -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..<MAX_FILENAME:
|
||||
let c = char(io_buffer[offset + i])
|
||||
if c == '\0': break
|
||||
name.add(c)
|
||||
result.add(name)
|
||||
|
||||
proc get_vfs_listing*(): seq[string] =
|
||||
return sfs_list()
|
||||
|
||||
proc sfs_write*(filename: string, data: pointer, data_len: int): int =
|
||||
## Write a file to the filesystem
|
||||
## Returns: bytes written or negative error
|
||||
if not sfs_mounted: return -1
|
||||
|
||||
discard blk_read(SEC_DIR, addr io_buffer[0])
|
||||
|
||||
var dir_offset = -1
|
||||
|
||||
# Find existing file or free slot
|
||||
for offset in countup(0, 511, DIR_ENTRY_SIZE):
|
||||
if io_buffer[offset] != 0:
|
||||
var entry_name = ""
|
||||
for i in 0..<MAX_FILENAME:
|
||||
if io_buffer[offset + i] == 0: break
|
||||
entry_name.add(char(io_buffer[offset + i]))
|
||||
|
||||
if entry_name == filename:
|
||||
dir_offset = offset
|
||||
break
|
||||
elif dir_offset == -1:
|
||||
dir_offset = offset
|
||||
|
||||
if dir_offset == -1:
|
||||
print("[SFS-U] Error: Directory Full.\n")
|
||||
return -2
|
||||
|
||||
# Allocate first sector
|
||||
var first_sector = sfs_alloc_sector()
|
||||
if first_sector == 0:
|
||||
print("[SFS-U] Error: Disk Full.\n")
|
||||
return -3
|
||||
|
||||
# Write data in chunks
|
||||
var remaining = data_len
|
||||
var data_ptr = 0
|
||||
var current_sector = first_sector
|
||||
|
||||
while remaining > 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_FILENAME:
|
||||
if i < filename.len:
|
||||
io_buffer[dir_offset + i] = byte(filename[i])
|
||||
else:
|
||||
io_buffer[dir_offset + i] = 0
|
||||
|
||||
io_buffer[dir_offset + 32] = byte(first_sector and 0xFF)
|
||||
io_buffer[dir_offset + 33] = byte((first_sector shr 8) and 0xFF)
|
||||
io_buffer[dir_offset + 34] = byte((first_sector shr 16) and 0xFF)
|
||||
io_buffer[dir_offset + 35] = byte((first_sector shr 24) and 0xFF)
|
||||
|
||||
let sz = uint32(data_len)
|
||||
io_buffer[dir_offset + 36] = byte(sz and 0xFF)
|
||||
io_buffer[dir_offset + 37] = byte((sz shr 8) and 0xFF)
|
||||
io_buffer[dir_offset + 38] = byte((sz shr 16) and 0xFF)
|
||||
io_buffer[dir_offset + 39] = byte((sz shr 24) and 0xFF)
|
||||
|
||||
discard blk_write(SEC_DIR, addr io_buffer[0])
|
||||
discard blk_sync()
|
||||
|
||||
print("[SFS-U] Write Complete: " & $data_len & " bytes.\n")
|
||||
return data_len
|
||||
|
||||
proc sfs_read*(filename: string, dest: pointer, max_len: int): int =
|
||||
## Read a file from the filesystem
|
||||
## Returns: bytes read or negative error
|
||||
if not sfs_mounted: return -1
|
||||
|
||||
discard blk_read(SEC_DIR, addr io_buffer[0])
|
||||
|
||||
var start_sector = 0'u32
|
||||
var file_size = 0'u32
|
||||
var found = false
|
||||
|
||||
for offset in countup(0, 511, DIR_ENTRY_SIZE):
|
||||
if io_buffer[offset] != 0:
|
||||
var entry_name = ""
|
||||
for i in 0..<MAX_FILENAME:
|
||||
if io_buffer[offset + i] == 0: break
|
||||
entry_name.add(char(io_buffer[offset + i]))
|
||||
|
||||
if entry_name == filename:
|
||||
start_sector = uint32(io_buffer[offset + 32]) or
|
||||
(uint32(io_buffer[offset + 33]) shl 8) or
|
||||
(uint32(io_buffer[offset + 34]) shl 16) or
|
||||
(uint32(io_buffer[offset + 35]) shl 24)
|
||||
file_size = uint32(io_buffer[offset + 36]) or
|
||||
(uint32(io_buffer[offset + 37]) shl 8) or
|
||||
(uint32(io_buffer[offset + 38]) shl 16) or
|
||||
(uint32(io_buffer[offset + 39]) shl 24)
|
||||
found = true
|
||||
break
|
||||
|
||||
if not found: return -1
|
||||
|
||||
# Read chain
|
||||
var current_sector = start_sector
|
||||
var dest_addr = cast[int](dest)
|
||||
var remaining = int(file_size)
|
||||
if remaining > 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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..<n:
|
||||
if buf[i] == '\n':
|
||||
if current.len > 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.}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -265,4 +265,4 @@ export fn nexus_yield() void {
|
|||
// while (true) {
|
||||
// nexus_yield();
|
||||
// }
|
||||
// }
|
||||
// --- 3. MEMORY MANAGEMENT (Handled by stubs.o) ---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 <filename> <content>
|
||||
## Uses USERLAND SFS (Block Valve architecture)
|
||||
if args.len < 2:
|
||||
print("Usage: write <filename> <content>\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 <filename>
|
||||
## Uses USERLAND SFS (Block Valve architecture)
|
||||
if args.len == 0:
|
||||
print("Usage: read <filename>\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 <filename>\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 <host>\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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue