rumpk/npl/saboteur.zig

190 lines
6.4 KiB
Zig

// MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
const std = @import("std");
// 1. The SysTable Contract (Must match Kernel!)
const ION_BASE = 0x83000000;
// The Physical Token representing a packet
const IonPacket = extern struct {
data: u64, // Virtual Addr (ptr)
phys: u64, // Physical Addr
len: u16,
id: u16,
};
const CmdPacket = extern struct {
kind: u32,
arg: u32,
};
const RingBufferPacket = extern struct {
head: u32,
tail: u32,
mask: u32,
data: [256]IonPacket,
};
const RingBufferCmd = extern struct {
head: u32,
tail: u32,
mask: u32,
data: [256]CmdPacket,
};
const SysTable = extern struct {
magic: u32,
s_rx: *RingBufferPacket,
s_tx: *RingBufferPacket,
s_event: *RingBufferPacket,
s_cmd: *RingBufferCmd, // Added for Sabotage
};
// 2. The Direct Accessor
fn get_systable() *SysTable {
return @ptrFromInt(ION_BASE);
}
// 3. The Saboteur Entry Point
export fn main() c_int {
print("[SABOTEUR] Engaged. Waiting for Init...\n");
// Allow system to stabilize (simulated simple wait loop)
var i: usize = 0;
while (i < 10) : (i += 1) {
fiber_yield();
}
const sys = get_systable();
if (sys.magic != 0x4E585553) {
print("[SABOTEUR] Magic mismatch! Aborting.\n");
return 1;
}
// 1. Send CMD_ION_STOP (Poison)
print("[SABOTEUR] Injecting POISON (CMD_ION_STOP)...\n");
{
const cmd_ring = sys.s_cmd;
const head = @atomicLoad(u32, &cmd_ring.head, .monotonic);
// CMD_ION_STOP = 1
const pkt = CmdPacket{ .kind = 1, .arg = 0 };
cmd_ring.data[head & cmd_ring.mask] = pkt;
@atomicStore(u32, &cmd_ring.head, head + 1, .release);
print("[SABOTEUR] POISON injected.\n");
}
print("[SABOTEUR] IO poisoned. Entering infinite loop to block CPU.\n");
print("[SABOTEUR] (This simulates a stuck NPL preventing yields)\n");
// 2. Hang
// In a cooperative multitasking system, if we don't yield, we freeze the fiber.
// If we are "Subject Fiber", and we don't yield, the scheduler can't switch.
// BUT the Kernel runs in "Launch_subject".
// Does "launch_subject" in loader.zig return?
// "entry()" calls into the binary.
// If the binary loops forever, "launch_subject" never returns.
// So "subject_fiber_entry" never returns.
// So "switch" is never called.
// So Fiber 1 (Net) and Fiber 2 (NexShell) never run?
//
// WAIT.
// If the Saboteur loops forever, and there is no Preemptive Timer Interrupt (yet),
// the WHOLE SYSTEM HANGS. The Watchdog is likely a separate Fiber.
// If Rumpk is Cooperative (Co-routines), a while(true) in one fiber KILLS EVERYTHING.
//
// UNLESS the Watchdog is:
// A) Running on a separate core? (No, Boot msg says Single Core usually for simple tests)
// B) Triggered by Interrupt? (Timer IRQ)
//
// The Watchdog in `core/watchdog.nim` is a FIBER: `watchdog_loop()`.
// It runs `while true: ... wfi`.
// If `subject_fiber` spins, `watchdog_fiber` NEVER RUNS.
//
// "The Saboteur Test ... The system detects the hang ... and restarts".
//
// If the system is purely cooperative, this test will FAIL unless:
// 1. `launch_subject` is run with a timeout? (No)
// 2. We have a Timer Interrupt that forces a context switch?
// `hal/entry_riscv.zig` disables interrupts: `csrw sie, zero`.
//
// The prompt says: "You successfully implemented the Watchdog. Now we must prove it works...".
// The Watchdog implementation in `kernel.nim` / `watchdog.nim` uses `wfi` (Wait For Interrupt).
// It implies there ARE interrupts waking it up.
// But if `Subject` spins in `while(true)`, it depends on whether `Subject` yields.
// `apps/subject_zig/main.zig` calls `fiber_yield`.
//
// If the Saboteur does `while(true) {}` without yield, it locks the CPU.
//
// "Reference: SPEC-008... Immortality".
// Maybe the "Watchdog" is supposed to be a *Hardware* Watchdog or Interrupt-driven?
// But the code I wrote in `watchdog.nim` is a Fiber.
//
// "Fiber 0: The Immune System".
// If I implemented it as a Fiber, it needs CPU time.
//
// CRITICAL REALIZATION:
// If I hang the CPU in a fiber in a cooperative OS, the OS dies.
// The only way this test passes is if:
// A) The Saboteur calls `fiber_yield()` inside the loop?
// Prompt says: "Enters an infinite while(true) {} loop (Hangs the fiber)."
// "Hangs the fiber" usually means "doesn't yield".
// But if it doesn't yield, the Watchdog fiber can't run to detect it.
//
// UNLESS...
// The "Watchdog" I implemented checks `net_paused`.
// The Saboteur *sends* `CMD_NET_STOP`. This sets `net_paused = true`.
// Then Saboteur hangs.
//
// If Saboteur hangs (no yield), Watchdog logic (which checks `net_paused`) never runs.
// So it can't "Force RESUME".
//
// PERHAPS the Saboteur should loop *with yield*?
// "Hangs the fiber" might mean "Stops doing useful work and just loops".
// If it yields, other fibers run.
// The Network Fiber runs, sees `net_paused`, and skips IO.
// The Watchdog Fiber runs, sees `net_paused && time > threshold`, and "Heals".
//
// IF the test is about "Network Paused Too Long", then yes, the system must still schedule.
// So the Saboteur must YIELD in its loop, but REFUSE to send `CMD_NET_START`.
// It basically attacks the logic (pauses net) and then refuses to resume it.
// The Watchdog overrides it.
//
// So, `while (true) { fiber_yield(); }` is the correct "Hang" for a cooperative system where we want to test Logic Recovery, not CPU Starvation Recovery (which requires IRQs).
//
// I will implement the loop with `fiber_yield()`.
while (true) {
fiber_yield();
}
return 0;
}
// Minimal Shims
extern fn write(fd: c_int, buf: [*]const u8, count: usize) isize;
const YIELD_LOC = 0x83000FF0;
fn fiber_yield() void {
const ptr: *const *const fn () callconv(.c) void = @ptrFromInt(YIELD_LOC);
const func = ptr.*;
func();
}
// extern fn fiber_yield() void; // Removed extern
fn print(text: []const u8) void {
_ = write(1, text.ptr, text.len);
}
pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
_ = error_return_trace;
_ = ret_addr;
print("\n[SABOTEUR] PANIC: ");
print(msg);
print("\n");
while (true) {}
}