288 lines
9.7 KiB
Nim
288 lines
9.7 KiB
Nim
# SPDX-License-Identifier: LSL-1.0
|
|
# Copyright (c) 2026 Markus Maiwald
|
|
# Stewardship: Self Sovereign Society Foundation
|
|
#
|
|
# This file is part of the Nexus Sovereign Core.
|
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
|
|
|
## Nexus Membrane: ION High-Level Client
|
|
|
|
# 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* = when defined(arm64): 0x50000000'u64
|
|
else: 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
|
|
s_rx*: pointer
|
|
s_tx*: pointer
|
|
s_event*: pointer
|
|
s_cmd*: pointer
|
|
s_input*: pointer
|
|
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_vfs_dup*: proc(fd: int32, min_fd: int32): int32 {.cdecl.}
|
|
fn_vfs_dup2*: proc(old_fd, new_fd: int32): int32 {.cdecl.}
|
|
fn_log*: pointer
|
|
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.}
|
|
# SPEC-503: Monolith Key Derivation
|
|
fn_blake3*: proc(data: pointer, len: uint64, out_hash: ptr array[32, byte]) {.cdecl.}
|
|
# Phase 36.2: Network Membrane
|
|
s_net_rx*: pointer # Kernel -> User (RX)
|
|
s_net_tx*: pointer # User -> Kernel (TX)
|
|
|
|
# Phase 36.3: Shared ION (16 bytes)
|
|
fn_ion_alloc*: proc(out_id: ptr uint16): uint64 {.cdecl.}
|
|
fn_ion_free*: proc(id: uint16) {.cdecl.}
|
|
|
|
# Phase 36.4: I/O Multiplexing (8 bytes)
|
|
fn_wait_multi*: proc(mask: uint64): int32 {.cdecl.}
|
|
|
|
# Phase 36.5: Network Hardware Info (8 bytes)
|
|
net_mac*: array[6, byte]
|
|
reserved_mac*: array[2, byte]
|
|
|
|
# Project LibWeb: LWF Sovereign Channel (16 bytes)
|
|
s_lwf_rx*: pointer # Kernel -> User (LWF frames)
|
|
s_lwf_tx*: pointer # User -> Kernel (LWF frames)
|
|
|
|
static:
|
|
doAssert sizeof(SysTable) == 240
|
|
|
|
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 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.} =
|
|
let sys = get_sys_table()
|
|
discard sys
|
|
# Use raw C write to avoid Nim string issues before init
|
|
proc console_write(p: pointer, len: uint) {.importc, cdecl.}
|
|
var msg = "[ION-Client] Initializing...\n"
|
|
console_write(addr msg[0], uint(msg.len))
|
|
|
|
when defined(is_membrane) or defined(RUMPK_KERNEL):
|
|
if sys.magic != 0x4E585553:
|
|
var err = "[ION-Client] ERROR: Invalid SysTable Magic!\n"
|
|
console_write(addr err[0], uint(err.len))
|
|
return
|
|
|
|
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 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)
|
|
|
|
var ok = "[ION-Client] Rings Mapped.\n"
|
|
console_write(addr ok[0], uint(ok.len))
|
|
else:
|
|
var err = "[ION-Client] ERROR: is_membrane NOT DEFINED!\n"
|
|
console_write(addr err[0], uint(err.len))
|
|
|
|
# --- ION CLIENT LOGIC ---
|
|
# Pure shared-memory slab allocator - NO kernel function calls!
|
|
|
|
const
|
|
USER_SLAB_BASE = SYS_TABLE_ADDR + 0x10000'u64 # Start of user packet slab in SysTable region
|
|
USER_SLAB_COUNT = 512 # Number of packet slots
|
|
USER_PKT_SIZE = 2048 # Size of each packet buffer
|
|
USER_BITMAP_ADDR = SYS_TABLE_ADDR + 0x100'u64 # Bitmap stored in SysTable region (after SysTable struct)
|
|
|
|
# Get pointer to shared bitmap (512 bits = 64 bytes for 512 slots)
|
|
proc get_user_bitmap(): ptr array[64, byte] =
|
|
return cast[ptr array[64, byte]](USER_BITMAP_ADDR)
|
|
|
|
proc ion_user_alloc*(out_pkt: ptr IonPacket): bool {.exportc.} =
|
|
## Allocate packet from shared slab - pure userland, no kernel call
|
|
let bitmap = get_user_bitmap()
|
|
|
|
# Find first free slot
|
|
for byteIdx in 0 ..< 64:
|
|
if bitmap[byteIdx] != 0xFF: # At least one bit free
|
|
for bitIdx in 0 ..< 8:
|
|
let slotIdx = byteIdx * 8 + bitIdx
|
|
if slotIdx >= USER_SLAB_COUNT:
|
|
return false
|
|
let mask = byte(1 shl bitIdx)
|
|
if (bitmap[byteIdx] and mask) == 0:
|
|
# Found free slot - mark as used
|
|
bitmap[byteIdx] = bitmap[byteIdx] or mask
|
|
|
|
let addr_val = USER_SLAB_BASE + uint64(slotIdx) * USER_PKT_SIZE
|
|
out_pkt.id = uint16(slotIdx) or 0x8000
|
|
out_pkt.phys = addr_val
|
|
out_pkt.len = 0
|
|
out_pkt.data = cast[ptr UncheckedArray[byte]](addr_val)
|
|
return true
|
|
return false
|
|
|
|
proc ion_user_free*(pkt: IonPacket) {.exportc.} =
|
|
## Free packet back to shared slab - pure userland, no kernel call
|
|
if pkt.data == nil:
|
|
return
|
|
let slotIdx = pkt.id and 0x7FFF
|
|
if slotIdx >= USER_SLAB_COUNT:
|
|
return
|
|
let bitmap = get_user_bitmap()
|
|
let byteIdx = int(slotIdx) div 8
|
|
let bitIdx = int(slotIdx) mod 8
|
|
let mask = byte(1 shl bitIdx)
|
|
bitmap[byteIdx] = bitmap[byteIdx] and (not mask)
|
|
|
|
proc ion_user_return*(id: uint16) {.exportc.} =
|
|
if membrane_cmd_ring_ptr == nil: return
|
|
var cmd: CmdPacket
|
|
cmd.kind = uint32(CmdType.CMD_ION_FREE)
|
|
cmd.arg = uint64(id)
|
|
discard membrane_cmd_ring_ptr[].push(cmd)
|
|
|
|
proc ion_user_tx*(pkt: IonPacket): bool {.exportc.} =
|
|
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
|
|
if membrane_rx_ring_ptr[].isEmpty: return false
|
|
let (ok, pkt) = membrane_rx_ring_ptr[].pop()
|
|
if not ok: return false
|
|
out_pkt[] = pkt
|
|
return true
|
|
|
|
proc ion_user_input*(out_pkt: ptr IonPacket): bool {.exportc.} =
|
|
if membrane_input_ring_ptr == nil: 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
|
|
|
|
proc ion_user_wait_multi*(mask: uint64): int32 {.exportc.} =
|
|
let sys = get_sys_table()
|
|
if sys.fn_wait_multi != nil:
|
|
return sys.fn_wait_multi(mask)
|
|
return -1
|
|
|
|
# --- 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
|
|
|
|
proc crypto_blake3*(data: pointer, len: uint64): array[32, byte] =
|
|
let sys = get_sys_table()
|
|
if sys.fn_blake3 != nil:
|
|
sys.fn_blake3(data, len, addr result)
|
|
|
|
proc ion_get_mac*(): array[6, byte] =
|
|
let sys = get_sys_table()
|
|
return sys.net_mac
|