118 lines
3.6 KiB
Zig
118 lines
3.6 KiB
Zig
// SPDX-License-Identifier: LCL-1.0
|
|
// Copyright (c) 2026 Markus Maiwald
|
|
// Stewardship: Self Sovereign Society Foundation
|
|
//
|
|
// This file is part of the Nexus Commonwealth.
|
|
// See legal/LICENSE_COMMONWEALTH.md for license terms.
|
|
|
|
//! Rumpk HAL: Sovereign Channels (Lock-Free Ring Buffers)
|
|
//!
|
|
//! Implements atomic lock-free ring buffers for ION packet communication.
|
|
//! Provides the "pipes" between kernel and userspace with invariant protection.
|
|
//!
|
|
//! SAFETY: All operations use atomic loads/stores with proper memory fences.
|
|
|
|
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
|
|
pub const IonPacket = extern struct {
|
|
data: u64,
|
|
phys: u64,
|
|
len: u16,
|
|
id: u16,
|
|
_pad: u32, // Match Nim's 24-byte alignment
|
|
};
|
|
|
|
pub const CmdPacket = extern struct {
|
|
kind: u32,
|
|
_pad: u32, // Explicit Padding for 8-byte alignment
|
|
arg: u64,
|
|
id: u128, // SipHash Provenance (16 bytes)
|
|
};
|
|
// Size: 32 bytes (matches libs/membrane/ion.zig and core/ion.nim)
|
|
|
|
pub fn Ring(comptime T: type) type {
|
|
return extern struct {
|
|
head: u32,
|
|
tail: u32,
|
|
mask: u32,
|
|
data: [256]T,
|
|
};
|
|
}
|
|
|
|
// INVARIANT 1: The Handle Barrier
|
|
fn validate_ring_ptr(ptr: u64) void {
|
|
const min_valid: u64 = if (builtin.cpu.arch == .aarch64) 0x4000_0000 else 0x8000_0000;
|
|
if (ptr < min_valid) {
|
|
@panic("HAL: Invariant Violation - Invalid Ring Pointer");
|
|
}
|
|
}
|
|
|
|
fn pushGeneric(comptime T: type, ring: *Ring(T), pkt: T) bool {
|
|
const head = @atomicLoad(u32, &ring.head, .monotonic);
|
|
const tail = @atomicLoad(u32, &ring.tail, .monotonic);
|
|
|
|
// INVARIANT 2: Overflow Protection
|
|
const next = (head + 1) & ring.mask;
|
|
if (next == tail) {
|
|
return false;
|
|
}
|
|
|
|
ring.data[head & ring.mask] = pkt;
|
|
@atomicStore(u32, &ring.head, next, .release);
|
|
return true;
|
|
}
|
|
|
|
fn popGeneric(comptime T: type, ring: *Ring(T), out_pkt: *T) bool {
|
|
const head = @atomicLoad(u32, &ring.head, .monotonic);
|
|
const tail = @atomicLoad(u32, &ring.tail, .monotonic);
|
|
|
|
// INVARIANT 3: Underflow Protection
|
|
if (head == tail) {
|
|
return false;
|
|
}
|
|
|
|
// Ensure we see data written by producer before reading it
|
|
switch (builtin.cpu.arch) {
|
|
.riscv64 => asm volatile ("fence r, rw" ::: .{ .memory = true }),
|
|
.aarch64 => asm volatile ("dmb ld" ::: .{ .memory = true }),
|
|
else => @compileError("unsupported arch"),
|
|
}
|
|
|
|
out_pkt.* = ring.data[tail & ring.mask];
|
|
const next = (tail + 1) & ring.mask;
|
|
@atomicStore(u32, &ring.tail, next, .release);
|
|
return true;
|
|
}
|
|
|
|
// Exported ABI Functions (Hardened)
|
|
export fn hal_channel_push(handle: u64, pkt: IonPacket) bool {
|
|
validate_ring_ptr(handle);
|
|
const ring: *Ring(IonPacket) = @ptrFromInt(handle);
|
|
return pushGeneric(IonPacket, ring, pkt);
|
|
}
|
|
|
|
export fn hal_channel_pop(handle: u64, out_pkt: *IonPacket) bool {
|
|
validate_ring_ptr(handle);
|
|
const ring: *Ring(IonPacket) = @ptrFromInt(handle);
|
|
return popGeneric(IonPacket, ring, out_pkt);
|
|
}
|
|
|
|
export fn hal_cmd_push(handle: u64, pkt: CmdPacket) bool {
|
|
validate_ring_ptr(handle);
|
|
const ring: *Ring(CmdPacket) = @ptrFromInt(handle);
|
|
// uart.print("[HAL] Pushing CMD to "); uart.print_hex(handle); uart.print("\n");
|
|
return pushGeneric(CmdPacket, ring, pkt);
|
|
}
|
|
|
|
export fn hal_cmd_pop(handle: u64, out_pkt: *CmdPacket) bool {
|
|
validate_ring_ptr(handle);
|
|
const ring: *Ring(CmdPacket) = @ptrFromInt(handle);
|
|
// uart.print("[HAL] Popping CMD from "); uart.print_hex(handle); uart.print("\n");
|
|
return popGeneric(CmdPacket, ring, out_pkt);
|
|
}
|
|
// Stub for term.nim compatibility
|
|
export fn fiber_can_run_on_channels() bool {
|
|
return true;
|
|
}
|