// 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 Immune System (NPL): The Voice & Command Plane //! //! Implemented as an NPL fiber, NexShell provides the interactive kernel shell. //! Handles telemetry events, user input, and command dispatch to the ION layer. //! //! SAFETY: Interacts with the shared SysTable via volatile pointers and atomic operations. const std = @import("std"); const ION_BASE = 0x83000000; const IonPacket = extern struct { data: u64, phys: u64, len: u16, id: u16, _pad: u32, // Match Nim's 24-byte alignment }; const CmdPacket = extern struct { kind: u32, reserved: u32, arg: u64, id: [16]u8, }; fn RingBuffer(comptime T: type) type { return extern struct { head: u32, tail: u32, mask: u32, data: [256]T, }; } const SysTable = extern struct { magic: u32, reserved: u32, s_rx: *RingBuffer(IonPacket), s_tx: *RingBuffer(IonPacket), s_event: *RingBuffer(IonPacket), s_cmd: *RingBuffer(CmdPacket), s_input: *RingBuffer(IonPacket), }; const CMD_ION_STOP = 1; const CMD_ION_START = 2; const CMD_GPU_MATRIX = 0x100; const CMD_GET_GPU_STATUS = 0x102; // The Main Loop for the NexShell Fiber export fn nexshell_main() void { const sys = @as(*SysTable, @ptrFromInt(ION_BASE)); print("\n╔═══════════════════════════════════════╗\n"); print("║ NEXSHELL IMMUNE SYSTEM ACTIVE ║\n"); print("║ Command Plane: READY ║\n"); print("╚═══════════════════════════════════════╝\n"); // TEMP: event_ring disabled due to NULL pointer issue // const event_ring = sys.s_event; const cmd_ring = sys.s_cmd; // SAFETY(NexShell): Input buffer initialized to `undefined` for performance. // Populated by char-by-char console reads before use. var input_buffer: [64]u8 = undefined; var input_idx: usize = 0; var loop_count: usize = 0; var poll_pulse: usize = 0; var last_lsr: u8 = 0; print("[NexShell] Entering main loop...\n"); while (true) { loop_count += 1; poll_pulse += 1; // First iteration diagnostic if (loop_count == 1) { print("[NexShell] First iteration\n"); } // Polling pulse every 100 to show activity if (poll_pulse >= 100) { print("."); poll_pulse = 0; } // 1. Process Telemetry Events // TEMPORARILY DISABLED: event_ring causes page fault (NULL pointer?) // const head = @atomicLoad(u32, &event_ring.head, .acquire); // const tail = @atomicLoad(u32, &event_ring.tail, .monotonic); // // if (head != tail) { // const pkt = event_ring.data[tail & event_ring.mask]; // print("\n[NexShell] ALERT | EventID: "); // if (pkt.id == 777) { // print("777 (SECURITY_HEARTBEAT)\n"); // } else { // print("GENERIC\n"); // } // @atomicStore(u32, &event_ring.tail, tail + 1, .release); // } // 2. Process User Input (Non-blocking) console_poll(); const current_lsr = debug_uart_lsr(); if (current_lsr != last_lsr) { print("[LSR:"); print_hex(current_lsr); print("]"); last_lsr = current_lsr; } if ((loop_count % 20) == 0) { print("."); // Alive heartbeat } const c = console_read(); if (c != -1) { print("[GOT]"); const byte = @as(u8, @intCast(c)); // print("[NexShell] Got char\n"); if (forward_mode) { // Check for escape: Ctrl+K (11) if (byte == 11) { forward_mode = false; print("\n[NexShell] RESUMING KERNEL CONTROL.\n"); } else { const bs = [1]u8{byte}; ion_push_stdin(&bs, 1); } } else { if (byte == '\r' or byte == '\n') { print("\n"); process_command(input_buffer[0..input_idx], cmd_ring); input_idx = 0; } else if (byte == 0x7F or byte == 0x08) { if (input_idx > 0) { input_idx -= 1; print("\x08 \x08"); // Backspace } } else if (input_idx < 63) { input_buffer[input_idx] = byte; input_idx += 1; const bs = [1]u8{byte}; print(&bs); } } } else { fiber_sleep(20); // 50Hz poll is plenty for keyboard (Wait... fiber_sleep takes milliseconds in Nim wrapper!) // Re-checking kernel.nim: fiber_sleep(ms) multiplies by 1_000_000. // So 20 is Correct for 20ms. // Wait. If kernel.nim multiplies by 1M, then passing 20 = 20M ns = 20ms. // My analysis in Thought Process was confused. // kernel.nim: // proc fiber_sleep*(ms: uint64) = current_fiber.sleep_until = now + (ms * 1_000_000) // So nexshell.zig calling fiber_sleep(20) -> 20ms. // THIS IS CORRECT. // I will NOT change this to 20_000_000. That would be 20,000 seconds! // I will restore the comment to be accurate. fiber_sleep(20); } fiber_yield(); } } var forward_mode: bool = false; fn process_command(cmd_text: []const u8, cmd_ring: *RingBuffer(CmdPacket)) void { if (cmd_text.len == 0) return; if (forward_mode) { const is_toggle = std.mem.eql(u8, cmd_text, "kernel") or std.mem.eql(u8, cmd_text, "exit"); // ALWAYS FORWARD TO USERLAND first so it can process its own exit print("[NexShell] Forwarding to Subject...\n"); // Combine command + newline to avoid fragmentation if (cmd_text.len > 0) { // SAFETY(NexShell): Local forward buffer initialized to `undefined`. // Immediately populated by `@memcpy` and newline before pushing to ION. var forward_buf: [128]u8 = undefined; const copy_len = if (cmd_text.len > 126) 126 else cmd_text.len; @memcpy(forward_buf[0..copy_len], cmd_text[0..copy_len]); forward_buf[copy_len] = '\n'; ion_push_stdin(forward_buf[0 .. copy_len + 1].ptr, copy_len + 1); } if (is_toggle) { forward_mode = false; print("[NexShell] Dropping to Kernel Debug Mode.\n"); } } else { if (std.mem.eql(u8, cmd_text, "subject") or std.mem.eql(u8, cmd_text, "nipbox")) { forward_mode = true; print("[NexShell] Resuming Subject Pipe.\n"); return; } if (std.mem.eql(u8, cmd_text, "io stop") or std.mem.eql(u8, cmd_text, "ion stop")) { print("[NexShell] Pushing CMD_ION_STOP...\n"); push_cmd(cmd_ring, CMD_ION_STOP, 0); } else if (std.mem.eql(u8, cmd_text, "matrix on")) { print("[NexShell] Engaging Matrix Protocol (Emergency Override)...\n"); push_cmd(cmd_ring, CMD_GPU_MATRIX, 1); } else if (std.mem.eql(u8, cmd_text, "matrix off")) { print("[NexShell] Disengaging Matrix Protocol...\n"); push_cmd(cmd_ring, CMD_GPU_MATRIX, 0); } else if (std.mem.eql(u8, cmd_text, "matrix status")) { push_cmd(cmd_ring, CMD_GET_GPU_STATUS, 0); } else if (std.mem.eql(u8, cmd_text, "ps") or std.mem.eql(u8, cmd_text, "fibers")) { print("[NexShell] Active Fibers:\n"); print(" - ION (Packet Engine)\n"); print(" - NexShell (Command Plane)\n"); print(" - Compositor (Render Pipeline)\n"); print(" - NetSwitch (Traffic Engine)\n"); print(" - Subject (Userland Loader)\n"); print(" - Kernel (Main)\n"); } else if (std.mem.eql(u8, cmd_text, "stl summary")) { stl_print_summary(); } else if (std.mem.eql(u8, cmd_text, "stl list")) { print("[NexShell] Recent Events:\n"); const total = stl_count(); const start = if (total > 10) total - 10 else 0; var id = total - 1; while (id >= start) { const ev = stl_lookup(id); if (ev) |e| { print(" ["); print_u64_hex(id); print("] Kind="); print_u16_hex(@intFromEnum(e.kind)); print(" Fiber="); print_u16_hex(@as(u16, @intCast(e.fiber_id))); print("\n"); } if (id == 0) break; id -= 1; } } else if (std.mem.startsWith(u8, cmd_text, "stl graph") or std.mem.eql(u8, cmd_text, "stl tree")) { var lineage: LineageResult = undefined; const total = stl_count(); if (total == 0) { print("[NexShell] No events to graph.\n"); } else { // Default to last event const last_id = @as(u64, total - 1); stl_trace_lineage(last_id, &lineage); print("[NexShell] Causal Graph for Event "); print_u64_hex(last_id); print(":\n\n"); var i: u32 = 0; while (i < lineage.count) : (i += 1) { const eid = lineage.event_ids[i]; const ev = stl_lookup(eid); if (i > 0) print(" |\n ▼\n"); print("["); print_u64_hex(eid); print("] "); if (ev) |e| { switch (e.kind) { .SystemBoot => print("SystemBoot"), .FiberSpawn => print("FiberSpawn"), .CapabilityGrant => print("CapGrant"), .AccessDenied => print("AccessDenied"), else => { print("Kind="); print_u16_hex(@intFromEnum(e.kind)); }, } } else { print("???"); } print("\n"); } print("\n"); } } else if (std.mem.eql(u8, cmd_text, "stl dump")) { var dump_buf: [4096]u8 = undefined; const written = stl_export_binary(&dump_buf, dump_buf.len); if (written > 0) { print("[NexShell] STL Binary Dump ("); print_u64_hex(written); print(" bytes):\n"); var i: usize = 0; while (i < written) : (i += 1) { print_hex(dump_buf[i]); if ((i + 1) % 32 == 0) print("\n"); } print("\n[NexShell] Dump Complete.\n"); } else { print("[NexShell] Dump Failed (Buffer too small or STL not ready)\n"); } } else if (std.mem.eql(u8, cmd_text, "mem")) { print("[NexShell] Memory Status:\n"); print(" Ion Pool: 32MB allocated\n"); print(" Surface: 32MB framebuffer pool\n"); print(" Stack Usage: ~512KB (6 fibers)\n"); } else if (std.mem.eql(u8, cmd_text, "uptime")) { print("[NexShell] System Status: OPERATIONAL\n"); print(" Architecture: RISC-V (Virt)\n"); print(" Timer: SBI Extension\n"); print(" Input: Interrupt-Driven (IRQ 10)\n"); } else if (std.mem.eql(u8, cmd_text, "reboot")) { print("[NexShell] Initiating system reboot...\n"); // SBI shutdown extension (EID=0x53525354, FID=0) asm volatile ( \\ li a7, 0x53525354 \\ li a6, 0 \\ li a0, 0 \\ ecall ); } else if (std.mem.eql(u8, cmd_text, "clear")) { print("\x1b[2J\x1b[H"); // ANSI clear screen + cursor home } else if (std.mem.eql(u8, cmd_text, "help")) { print("[NexShell] Kernel Commands:\n"); print(" System: ps, fibers, mem, uptime, reboot, clear\n"); print(" STL: stl summary, stl list, stl dump, stl graph\n"); print(" IO: io stop, ion stop\n"); print(" Matrix: matrix on/off/status\n"); print(" Shell: subject, nipbox, help\n"); } else { print("[NexShell] Unknown Kernel Command: "); print(cmd_text); print("\n"); } } } fn push_cmd(ring: *RingBuffer(CmdPacket), kind: u32, arg: u64) void { const head = @atomicLoad(u32, &ring.head, .acquire); const tail = @atomicLoad(u32, &ring.tail, .monotonic); const next = (head + 1) & ring.mask; if (next == tail) { print("[NexShell] CMD RING FULL!\n"); return; } ring.data[head & ring.mask] = .{ .kind = kind, .reserved = 0, .arg = arg, .id = [_]u8{0} ** 16 }; @atomicStore(u32, &ring.head, next, .release); } // OS Shims extern fn k_handle_syscall(nr: usize, a0: usize, a1: usize, a2: usize) usize; extern fn console_read() c_int; extern fn console_poll() void; extern fn ion_push_stdin(ptr: [*]const u8, len: usize) void; extern fn fiber_sleep(ms: u64) void; extern fn fiber_yield() void; extern fn debug_uart_lsr() u8; // STL Externs extern fn stl_count() u32; extern fn stl_print_summary() void; extern fn stl_get_recent(max_count: u32, result: *QueryResult) void; extern fn stl_export_binary(dest: [*]u8, max_size: usize) usize; extern fn stl_trace_lineage(event_id: u64, result: *LineageResult) void; extern fn stl_lookup(event_id: u64) ?*const Event; const LineageResult = extern struct { count: u32, event_ids: [16]u64, }; const EventKind = enum(u16) { Null = 0, SystemBoot = 1, SystemShutdown = 2, FiberSpawn = 3, FiberTerminate = 4, CapabilityGrant = 10, CapabilityRevoke = 11, CapabilityDelegate = 12, ChannelOpen = 20, ChannelClose = 21, ChannelRead = 22, ChannelWrite = 23, MemoryAllocate = 30, MemoryFree = 31, MemoryMap = 32, NetworkPacketRx = 40, NetworkPacketTx = 41, AccessDenied = 50, PolicyViolation = 51, }; const Event = packed struct { kind: EventKind, _reserved: u8 = 0, timestamp_ns: u64, fiber_id: u64, entity_id: u64, cause_id: u64, data0: u64, data1: u64, data2: u64, }; const QueryResult = extern struct { count: u32, events: [64]*const Event, }; fn print_u64_hex(val: u64) void { const chars = "0123456789ABCDEF"; var buf: [16]u8 = undefined; var v = val; var i: usize = 0; while (i < 16) : (i += 1) { buf[15 - i] = chars[v & 0xF]; v >>= 4; } print(&buf); } fn print_u16_hex(val: u16) void { const chars = "0123456789ABCDEF"; const buf = [4]u8{ chars[(val >> 12) & 0xF], chars[(val >> 8) & 0xF], chars[(val >> 4) & 0xF], chars[val & 0xF], }; print(&buf); } fn print_hex(val: u8) void { const chars = "0123456789ABCDEF"; const hi = chars[(val >> 4) & 0xF]; const lo = chars[val & 0xF]; const buf = [_]u8{ hi, lo }; print(&buf); } fn kernel_write(fd: c_int, buf: [*]const u8, count: usize) isize { // 0x204 = SYS_WRITE return @as(isize, @bitCast(k_handle_syscall(0x204, @as(usize, @intCast(fd)), @intFromPtr(buf), count))); } fn print(text: []const u8) void { _ = kernel_write(1, text.ptr, text.len); }