269 lines
8.2 KiB
Zig
269 lines
8.2 KiB
Zig
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 write(fd: i32, buf: [*]const u8, count: usize) isize;
|
|
|
|
// Helper to bridge naming if needed, but `write` is the symbol name.
|
|
fn write_extern(fd: i32, buf: [*]const u8, count: usize) isize {
|
|
return write(fd, 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) {
|
|
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;
|
|
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();
|
|
// }
|
|
// }
|