146 lines
4.3 KiB
Nim
146 lines
4.3 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.
|
|
|
|
## Rumpk Layer 1: ION Slab Allocator
|
|
|
|
# ION Memory Manager
|
|
# The "Slab Allocator" for Zero-Copy IO
|
|
|
|
import ../ring
|
|
|
|
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}
|
|
proc dbg(s: string) =
|
|
console_write(unsafeAddr s[0], csize_t(s.len))
|
|
var nl = "\n"
|
|
console_write(unsafeAddr nl[0], csize_t(1))
|
|
|
|
const
|
|
SLAB_SIZE* = 2048 # Max Packet Size (Ethernet Frame + Headroom)
|
|
POOL_COUNT* = 1024 # Number of packets in the pool (2MB total RAM)
|
|
POOL_ALIGN* = 4096 # VirtIO/Page Alignment
|
|
|
|
type
|
|
# The Physical Token representing a packet
|
|
IonPacket* = object
|
|
data*: ptr UncheckedArray[byte] # Virtual Address of payload
|
|
phys*: uint64 # Physical Address (For VirtIO/DMA)
|
|
len*: uint16 # Actual data length
|
|
id*: uint16 # The Token ID (Slab Index)
|
|
|
|
# The Monolithic Memory Pool
|
|
PacketPool = object
|
|
# We use 'align' to ensure the buffer starts on a page boundary for VirtIO
|
|
buffer {.align: POOL_ALIGN.}: array[SLAB_SIZE * POOL_COUNT, byte]
|
|
free_ring: RingBuffer[uint16, POOL_COUNT] # Stores IDs of free slabs
|
|
base_phys: uint64
|
|
|
|
var global_pool: PacketPool
|
|
|
|
proc ion_pool_init*() {.exportc.} =
|
|
## Initialize the DMA Pool with REAL Physical Address
|
|
dbg("[ION] Initializing Pool...")
|
|
|
|
# 1. Get the VIRTUAL address of the static buffer
|
|
let virt_addr = cast[uint64](addr global_pool.buffer[0])
|
|
|
|
# 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()
|
|
|
|
# Fill the free ring with all indices [0..1023]
|
|
dbg("[ION] Filling Slabs...")
|
|
var count = 0
|
|
for i in 0 ..< POOL_COUNT:
|
|
if global_pool.free_ring.push(uint16(i)):
|
|
inc count
|
|
|
|
dbg("[ION] Pool Ready.")
|
|
|
|
proc ion_alloc*(): IonPacket {.exportc.} =
|
|
## O(1) Allocation. Returns an empty packet struct.
|
|
## If OOM, returns packet with data = nil
|
|
var pkt: IonPacket
|
|
|
|
let (ok, idx) = global_pool.free_ring.pop()
|
|
if not ok:
|
|
dbg("[ION] ALLOC FAILED (Empty Ring?)")
|
|
pkt.data = nil
|
|
return pkt
|
|
|
|
pkt.id = idx
|
|
pkt.len = 0
|
|
|
|
# Calculate Virtual Address
|
|
let offset = int(idx) * SLAB_SIZE
|
|
pkt.data = cast[ptr UncheckedArray[byte]](addr global_pool.buffer[offset])
|
|
|
|
# Calculate Physical Address (1:1 map for Phase 7)
|
|
pkt.phys = global_pool.base_phys + uint64(offset)
|
|
|
|
return pkt
|
|
|
|
proc ion_free*(pkt: IonPacket) {.exportc.} =
|
|
## O(1) Free. Returns the token to the ring.
|
|
if pkt.data == nil: return
|
|
|
|
discard global_pool.free_ring.push(pkt.id)
|
|
|
|
# Helper for C/Zig Interop (Pure Pointers)
|
|
# Return physical address of a allocated block, put ID in out_id
|
|
proc ion_alloc_raw*(out_id: ptr uint16): uint64 {.exportc, cdecl.} =
|
|
let pkt = ion_alloc()
|
|
if pkt.data == nil: return 0
|
|
out_id[] = pkt.id
|
|
return pkt.phys
|
|
|
|
proc ion_free_raw*(id: uint16) {.exportc, cdecl.} =
|
|
var pkt: IonPacket
|
|
pkt.id = id
|
|
# We don't reconstruct data/phys for free, just push ID
|
|
# But for safety we might check bounds? Ring handles it.
|
|
pkt.data = cast[ptr UncheckedArray[byte]](1) # Dummy non-nil
|
|
ion_free(pkt)
|
|
|
|
proc ion_get_virt*(id: uint16): ptr byte {.exportc.} =
|
|
let offset = int(id) * SLAB_SIZE
|
|
return addr global_pool.buffer[offset]
|
|
|
|
proc ion_get_phys*(id: uint16): uint64 {.exportc.} =
|
|
let offset = int(id) * SLAB_SIZE
|
|
return global_pool.base_phys + uint64(offset)
|
|
|
|
# =========================================================
|
|
# The Global TX Ring (Multiplexing)
|
|
# =========================================================
|
|
|
|
var global_tx_ring*: RingBuffer[IonPacket, 256]
|
|
|
|
proc ion_tx_init*() {.exportc.} =
|
|
global_tx_ring.init()
|
|
|
|
proc ion_tx_push*(pkt: IonPacket): bool {.exportc.} =
|
|
global_tx_ring.push(pkt)
|
|
|
|
proc ion_tx_pop*(out_id: ptr uint16, out_len: ptr uint16): bool {.exportc.} =
|
|
if global_tx_ring.isEmpty:
|
|
return false
|
|
|
|
let (ok, pkt) = global_tx_ring.pop()
|
|
if not ok: return false
|
|
|
|
out_id[] = pkt.id
|
|
out_len[] = pkt.len
|
|
return true
|