// 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: C Standard Library Shim //! //! Provides Zig-based stubs and bridges for expected C functions (e.g. fputs, memchr) //! and implements the "Sovereign Syscall" interface for ION communication. //! //! SAFETY: Bridges Userland C/Nim calls to Kernel Space via the SysTable. //! Local buffers used for char-by-char I/O are non-persistent. const std = @import("std"); // --- 1. IO PRIMITIVES --- // REPLACED BY MEMBRANE (libc.nim) // export fn write(fd: i32, buf: [*]const u8, count: usize) isize { // // Forward stdout (1) to Kernel Log // if (fd == 1) { // const sys = @as(*const ion.SysTable, @ptrFromInt(0x83000000)); // if (sys.fn_log != 0) { // const func = @as(*const fn ([*]const u8, u64) void, @ptrFromInt(sys.fn_log)); // func(buf, count); // } // } // return @intCast(count); // } // REPLACED BY MEMBRANE (libc.nim) // export fn close(fd: i32) i32 { // _ = fd; // return 0; // Success stub // } export fn fputc(c: i32, stream: ?*anyopaque) i32 { _ = stream; const char = @as(u8, @intCast(c)); const buf = [1]u8{char}; // _ = write(1, &buf, 1); // Use raw syscall or let standard lib handle it? // Wait, fputc calls write. If write is gone, this breaks. // libc.nim exports `write`. So if we link, `write` symbol exists! // So we can declare it extern? // Or simpler: fputc in libc.nim? No. // If fputc allows linking to `write` from `libc.nim`, we need `extern fn write`. _ = write_extern(1, &buf, 1); return c; } extern fn k_handle_syscall(nr: usize, a0: usize, a1: usize, a2: usize) usize; // Helper for fputc/fputs internal use in Kernel fn write_extern(fd: i32, buf: [*]const u8, count: usize) isize { // 0x204 = SYS_WRITE return @as(isize, @bitCast(k_handle_syscall(0x204, @as(usize, @intCast(fd)), @intFromPtr(buf), count))); } export fn fputs(s: [*]const u8, stream: ?*anyopaque) i32 { _ = stream; var len: usize = 0; while (s[len] != 0) : (len += 1) {} _ = write_extern(1, s, len); return 1; } // --- 2. THE MISSING SYMBOLS (STUBS) --- // Nim checks for errors on streams. We say "No error". export fn ferror(stream: ?*anyopaque) i32 { _ = stream; return 0; } export fn clearerr(stream: ?*anyopaque) void { _ = stream; } // Nim looks for chars in memory (optimized scans). export fn memchr(s: ?*const anyopaque, c: i32, n: usize) ?*anyopaque { if (s) |src_ptr| { const src: [*]const u8 = @ptrCast(src_ptr); const target = @as(u8, @intCast(c)); var i: usize = 0; while (i < n) : (i += 1) { if (src[i] == target) return @ptrCast(@constCast(src + i)); } } return null; } // 2. File I/O (VFS Bridge - via SysTable Hypercall Vector) // We cannot link directly, so we use the SysTable function pointers. // REPLACED BY MEMBRANE (libc.nim) // export fn open(path: [*]const u8, flags: i32) i32 { // _ = flags; // const sys = @as(*const ion.SysTable, @ptrFromInt(0x83000000)); // if (sys.fn_vfs_open != 0) { // const func = @as(*const fn ([*]const u8) i32, @ptrFromInt(sys.fn_vfs_open)); // return func(path); // } // return -1; // } // REPLACED BY MEMBRANE (libc.nim) // export fn list_files(buf: [*]u8, len: u64) i64 { // const sys = @as(*const ion.SysTable, @ptrFromInt(0x83000000)); // if (sys.fn_vfs_list != 0) { // const func = @as(*const fn ([*]u8, u64) i64, @ptrFromInt(sys.fn_vfs_list)); // return func(buf, len); // } // return 0; // } // Stdin Buffering (to prevent data loss on character-by-character reads) var current_stdin_pkt: ?ion.IonPacket = null; var stdin_offset: u16 = 0; // REPLACED BY MEMBRANE (libc.nim) - libc.nim IMPLEMENTS BUFFERING TOO! // export fn read(fd: i32, buf: [*]u8, count: usize) isize { // ... // } extern fn read(fd: i32, buf: [*]u8, count: usize) isize; export fn nexus_read_nonblock(fd: i32, buf: [*]u8, count: usize) isize { _ = fd; _ = buf; _ = count; // This logic relies on `current_stdin_pkt` which is local here. // If libc.nim handles stdin, we shouldn't mix. // NipBox previously used read(0). // If we use libc.nim's read, we rely on IT. // So this function might not be needed or should call read non-block if available? // For now, disable or stub? // NipBox 0.7 doesn't seem to call `nexus_read_nonblock` (I commented it out in favor of `read(0)`). // So safe to remove/comment. return 0; } // Nim tries to read lines. export fn fgets(s: [*]u8, size: i32, stream: ?*anyopaque) ?[*]u8 { _ = stream; if (size <= 0) return null; // Use linked read // But we need a char-by-char loop or similar if read is raw? // libc.nim read is blocking? // Let's implement fgets using 'read' extern. var idx: usize = 0; const max = @as(usize, @intCast(size - 1)); while (idx < max) { // SAFETY(Shim): Buffer populated by `read()` syscall before use. var buf: [1]u8 = undefined; const n = read(0, &buf, 1); if (n <= 0) break; s[idx] = buf[0]; idx += 1; if (buf[0] == '\n') break; } s[idx] = 0; if (idx == 0) return null; return s; } export fn fgetc(stream: ?*anyopaque) i32 { _ = stream; // SAFETY(Shim): Buffer populated by `read()` syscall before use. var buf: [1]u8 = undefined; const n = read(0, &buf, 1); if (n <= 0) return -1; return @intCast(buf[0]); } const CMD_ION_FREE = 0x300; // REPLACED BY MEMBRANE (libc.nim imports ion_client which likely has it or libc.nim itself?) // Ensure libc.nim handles ion_user_free or if we need it here. // But `ion_user_free` was duplicate. So remove export. // export fn ion_user_free(pkt: ion.IonPacket) void { // _ = nexus_syscall(CMD_ION_FREE, pkt.id); // } // Math stubs (sometimes needed) export fn dlopen() void {} export fn dlsym() void {} export fn strerror(errnum: i32) [*]const u8 { _ = errnum; return "Unknown Error"; } extern fn main(argc: i32, argv: [*]const [*]const u8) i32; // _start relocated to subject_entry.S const ion = @import("ion.zig"); // Sovereign Syscall: Push to CMD Ring export fn nexus_syscall(cmd_id: u32, arg: u64) c_int { // Construct Packet var pkt = ion.CmdPacket{ .kind = cmd_id, ._pad = 0, .arg = arg, .id = 0 }; // Compute Provenance (SipHash) const key = "\xde\xad\xbe\xef\xca\xfe\xba\xbe\x00\x01\x02\x03\x04\x05\x06\x07"; var hasher = std.crypto.auth.siphash.SipHash128(1, 3).init(key); hasher.update(std.mem.asBytes(&pkt.kind)); hasher.update(std.mem.asBytes(&pkt.arg)); const hash_int = hasher.finalInt(); pkt.id = hash_int; // Push to High-Priority CMD Ring if (!ion.sys_cmd_push(pkt)) { return -1; // Error: Ring full/backpressure } return 0; // Success } const NetArgs = ion.NetArgs; // Network TX: Send Raw Frame export fn nexus_net_tx(buf: [*]const u8, len: u64) void { var args = NetArgs{ .buf = @intFromPtr(buf), .len = len }; _ = nexus_syscall(ion.CMD_NET_TX, @intFromPtr(&args)); } // Network RX: Poll Raw Frame export fn nexus_net_rx(buf: [*]u8, max_len: u64) u64 { var args = NetArgs{ .buf = @intFromPtr(buf), .len = max_len }; _ = nexus_syscall(ion.CMD_NET_RX, @intFromPtr(&args)); // The kernel updates args.len with the actual received length // Wait... args is local stack variable. Kernel writes to it? // Userland and Kernel share address space in this unikernel model. // So yes, kernel writes to &args. return args.len; } const BlkArgs = ion.BlkArgs; export fn nexus_blk_read(sector: u64, buf: [*]u8, len: u64) void { var args = BlkArgs{ .sector = sector, .buf = @intFromPtr(buf), .len = len }; _ = nexus_syscall(ion.CMD_BLK_READ, @intFromPtr(&args)); nexus_yield(); // Block until Kernel processes it } export fn nexus_blk_write(sector: u64, buf: [*]const u8, len: u64) void { var args = BlkArgs{ .sector = sector, .buf = @intFromPtr(buf), .len = len }; _ = nexus_syscall(ion.CMD_BLK_WRITE, @intFromPtr(&args)); nexus_yield(); // Block until Kernel processes it } // Sovereign Yield: Return control to Kernel Scheduler export fn nexus_yield() void { const yield_ptr = @as(*const *const fn () void, @ptrFromInt(0x83000FF0)); yield_ptr.*(); } // REPLACED BY MEMBRANE (libc.nim) // export fn exit(status: c_int) noreturn { // const dbg_msg = "[Shim] exit() called. Signal termination.\n"; // //_ = write(1, dbg_msg, dbg_msg.len); // _ = write_extern(1, dbg_msg, dbg_msg.len); // const CMD_SYS_EXIT: u32 = 1; // _ = nexus_syscall(CMD_SYS_EXIT, @as(u64, @intCast(status))); // // The Void: Termination signaled. // const msg = "[Termination] Signaled. Waiting for System.\n"; // // _ = write(1, msg, msg.len); // _ = write_extern(1, msg, msg.len); // while (true) { // nexus_yield(); // } // --- 3. MEMORY MANAGEMENT (Handled by stubs.o) ---