From 8f05ea44f69cb796e7b1f581ba1b8045c98d615b Mon Sep 17 00:00:00 2001 From: salmonik Date: Sat, 1 Aug 2026 17:38:57 +0200 Subject: rsnix: preemptive kernel tasks (ADR 0003) — sched, IST fault stacks, 176-byte frames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - sched: 16-slot static table, shell=task 0, idle=task 1, spawn with guard page + 8 KiB stack, RR on the 100 Hz tick, int 0x60 yield, sleep_ms, lazy reap, jobs/bg/sleep/kill shell commands - gdt: own GDT mirroring UEFI selectors + TSS with IST1/IST2 (guard-page overflows die gracefully instead of triple-faulting) - interrupts: isr_dispatch returns next frame in rax; IntFrame grows rsp/ss fields — QEMU's 64-bit iretq always pops all five slots from a 176-byte frame, so synthetic task frames must carry rsp=top+ss (a 160-byte frame made the first iretq pop rsp/ss from the unmapped page beyond the frame) - panic handler: task panics abort the task, shell keeps running - main: sched::init moved after page allocator + adopted tables (the idle's stack+guard need both; it was silently not spawning) --- rsdos/src/main.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'rsdos') diff --git a/rsdos/src/main.rs b/rsdos/src/main.rs index a528810..7ca031c 100644 --- a/rsdos/src/main.rs +++ b/rsdos/src/main.rs @@ -8,9 +8,44 @@ use rsnix::uefi::*; // --------------------------------------------------------------------------- // Panic handler // --------------------------------------------------------------------------- + +/// Small fmt::Write over a byte slice (no allocation, bounded). +struct PanicBuf<'a> { + b: &'a mut [u8], + n: usize, +} + +impl core::fmt::Write for PanicBuf<'_> { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + let m = s.len().min(self.b.len() - self.n); + self.b[self.n..self.n + m].copy_from_slice(&s.as_bytes()[..m]); + self.n += m; + Ok(()) + } +} + #[panic_handler] fn panic(info: &PanicInfo) -> ! { use core::fmt::Write; + + // A background task (id >= 2) panicked: die gracefully — log it, + // mark the task done, switch away. The shell keeps running and + // `jobs` shows the corpse. Panics in the shell, idle, ISR context, + // or before the scheduler exists still halt the whole system. + if rsnix::sched::is_ready() && !rsnix::sched::in_isr() && rsnix::sched::current() >= 2 { + let mut buf = [0u8; 192]; + let n = { + let mut w = PanicBuf { b: &mut buf, n: 0 }; + let _ = write!(&mut w, "{}", info.message()); + if let Some(loc) = info.location() { + let _ = write!(&mut w, " at {}:{}", loc.file(), loc.line()); + } + w.n + }; + let msg = core::str::from_utf8(&buf[..n]).unwrap_or("task panic"); + rsnix::sched::abort_current(msg); + } + rsnix::serial::write_str("\r\n*** PANIC ***\r\n"); let _ = write!(&mut rsnix::serial::SerialWriter, "{}\r\n", info.message()); if let Some(loc) = info.location() { @@ -114,6 +149,11 @@ pub unsafe extern "efiapi" fn efi_main(image_handle: Handle, system_table: *mut rsnix::serial_println!("serial: COM1 115200 8N1"); rsnix::ps2::init(); rsnix::serial_println!("ps2: keyboard initialized"); + if rsnix::gdt::init() { + rsnix::serial_println!("gdt: own GDT + TSS (IST: page-fault/double-fault)"); + } else { + rsnix::serial_println!("gdt: kept UEFI GDT (IST fault stacks off)"); + } rsnix::interrupts::init(); rsnix::serial_println!("intr: IDT loaded, PIC remapped, timer ~100 Hz"); @@ -151,6 +191,12 @@ pub unsafe extern "efiapi" fn efi_main(image_handle: Handle, system_table: *mut rsnix::serial_println!("pg: self-test FAILED"); } + // Scheduler init needs the page allocator (idle task's stack + guard + // page) and the adopted page tables, so it runs after the memory + // subsystem is fully up. The tick (interrupts::init) is already + // live, which is what `sleep`/wake scheduling depends on. + rsnix::sched::init(); + let t = rsnix::rtc::now(); rsnix::serial_println!( "rtc: {}-{:02}-{:02} {:02}:{:02}:{:02}", -- cgit v1.2.3