190 lines
6.4 KiB
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) {}
|
|
}
|