# 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 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.} = let pkt = ion_alloc() if pkt.data == nil: return 0 out_id[] = pkt.id return pkt.phys proc ion_free_raw*(id: uint16) {.exportc.} = 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