From e5c2889acca73b887b640a280933ab400add7468 Mon Sep 17 00:00:00 2001 From: Zoe Date: Mon, 7 Sep 2026 13:52:07 -0500 Subject: [PATCH] feat: userspace faults --- src/arch/x86_64/interrupts/exceptions.rs | 96 +++++++++++++++--------- src/syscall/mod.rs | 11 ++- src/syscall/table.rs | 4 +- src/task/scheduler.rs | 4 +- userspace/omega3/src/main.rs | 4 +- 5 files changed, 76 insertions(+), 43 deletions(-) diff --git a/src/arch/x86_64/interrupts/exceptions.rs b/src/arch/x86_64/interrupts/exceptions.rs index acd3f87..1a7d9a2 100644 --- a/src/arch/x86_64/interrupts/exceptions.rs +++ b/src/arch/x86_64/interrupts/exceptions.rs @@ -1,7 +1,10 @@ use core::arch::asm; use super::idt::{self, InterruptFrame, InterruptStackFrame, stub_err, stub_no_err}; -use crate::{hcf, println}; +use crate::{ + hcf, println, + task::tcb::{ExitReason, Fault}, +}; stub_no_err!(stub_divide_error, 0); stub_no_err!(stub_debug, 1); @@ -21,46 +24,71 @@ stub_no_err!(stub_machine_check, 18); stub_no_err!(stub_simd_floating_point, 19); stub_no_err!(stub_user_test_exit, 0x80); +const EXCEPTION_NAMES: [&str; 32] = [ + "DIVIDE ERROR", + "DEBUG", + "NON-MASKABLE INTERRUPT", + "BREAKPOINT", + "OVERFLOW", + "BOUND RANGE EXCEEDED", + "INVALID OPCODE", + "DEVICE NOT AVAILABLE", + "DOUBLE FAULT", + "COPROCESSOR SEGMENT OVERRUN", + "INVALID TSS", + "SEGMENT NOT PRESENT", + "STACK-SEGMENT FAULT", + "GENERAL PROTECTION FAULT", + "PAGE FAULT", + "RESERVED", + "x87 FLOATING-POINT EXCEPTION", + "ALIGNMENT CHECK", + "MACHINE CHECK", + "SIMD FLOATING-POINT EXCEPTION", + "VIRTUALIZATION EXCEPTION", + "CONTROL PROTECTION EXCEPTION", + "RESERVED", + "RESERVED", + "RESERVED", + "RESERVED", + "RESERVED", + "RESERVED", + "HYPERVISOR INJECTION EXCEPTION", + "VMM COMMUNICATION EXCEPTION", + "SECURITY EXCEPTION", + "RESERVED", +]; + pub(super) fn handle(frame: &mut InterruptFrame) { - match frame.vector as u8 { - 0 => fatal_exception("DIVIDE ERROR", &frame.stack_frame, None), - 1 => fatal_exception("DEBUG EXCEPTION", &frame.stack_frame, None), - 2 => fatal_exception("NON-MASKABLE INTERRUPT", &frame.stack_frame, None), - 3 => report_exception("BREAKPOINT", &frame.stack_frame, None), - 6 => fatal_exception("INVALID OPCODE", &frame.stack_frame, None), - 7 => fatal_exception("DEVICE NOT AVAILABLE", &frame.stack_frame, None), - 8 => fatal_exception("DOUBLE FAULT", &frame.stack_frame, Some(frame.error_code)), - 10 => fatal_exception("INVALID TSS", &frame.stack_frame, Some(frame.error_code)), - 11 => fatal_exception("SEGMENT NOT PRESENT", &frame.stack_frame, Some(frame.error_code)), - 12 => fatal_exception("STACK-SEGMENT FAULT", &frame.stack_frame, Some(frame.error_code)), - 13 => fatal_exception( - "GENERAL PROTECTION FAULT", - &frame.stack_frame, - Some(frame.error_code), - ), - 14 => { - report_exception("PAGE FAULT", &frame.stack_frame, Some(frame.error_code)); + let is_user = frame.stack_frame.code_segment & 0b11 == 3; + let vector = frame.vector as u8; + let name = EXCEPTION_NAMES + .get(vector as usize) + .copied() + .unwrap_or("UNKNOWN EXCEPTION"); + + if !is_user { + if vector == 14 { + report_exception(name, &frame.stack_frame, Some(frame.error_code)); println!("Faulting address: {:#X}", read_cr2()); print_page_fault_error(frame.error_code); hcf(); } - 16 => fatal_exception("X87 FLOATING-POINT EXCEPTION", &frame.stack_frame, None), - 17 => fatal_exception("ALIGNMENT CHECK", &frame.stack_frame, Some(frame.error_code)), - 18 => fatal_exception("MACHINE CHECK", &frame.stack_frame, None), - 19 => fatal_exception("SIMD FLOATING-POINT EXCEPTION", &frame.stack_frame, None), - 0x80 => { - if frame.stack_frame.code_segment & 0b11 != 3 { - panic!("user_test_exit_handler called from kernel"); - } - println!("User test exit"); - hcf(); - } - _ => { - println!("Unhandled exception vector: {:#X}", frame.vector); - hcf(); - } + fatal_exception(name, &frame.stack_frame, Some(frame.error_code)); } + + let fault = match vector { + // Page fault, GPF, Stack/Segment faults -> SegmentationFault + 11 | 12 | 13 | 14 => Fault::SegmentationFault, + // Invalid Opcode -> IllegalInstruction + 6 => Fault::IllegalInstruction, + // Divide by zero, Alignment check, SIMD/x87 -> Abort + 0 | 16 | 17 | 19 => Fault::Abort, + _ => Fault::Abort, + }; + + crate::task::scheduler::exit_current(ExitReason::Fault(fault)); } pub(super) fn install(idt: &mut idt::Idt) { diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index f1c68f4..1c189d6 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -2,6 +2,8 @@ mod table; use table::*; +use crate::task::tcb::{ExitReason, Fault}; + #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u64)] pub enum Status { @@ -31,7 +33,7 @@ pub enum SyscallNumber { } impl TryFrom for SyscallNumber { - type Error = Status; + type Error = (); fn try_from(val: u64) -> Result { match val { 1 => Ok(Self::Yield), @@ -45,14 +47,17 @@ impl TryFrom for SyscallNumber { 9 => Ok(Self::Map), 10 => Ok(Self::Unmap), 11 => Ok(Self::TaskCreate), - _ => Err(Status::InvalidArgument), + _ => Err(()), } } } pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, _arg5: u64) -> u64 { let result = (|| -> Result<(), Status> { - let syscall = SyscallNumber::try_from(num)?; + let syscall = SyscallNumber::try_from(num).unwrap_or_else(|_| { + crate::task::scheduler::exit_current(ExitReason::Fault(Fault::BadSystemCall)) + }); + match syscall { SyscallNumber::Yield => sys_yield(), SyscallNumber::Exit => sys_exit(arg0 as usize), diff --git a/src/syscall/table.rs b/src/syscall/table.rs index 0d268b1..a97f2f2 100644 --- a/src/syscall/table.rs +++ b/src/syscall/table.rs @@ -6,7 +6,7 @@ use crate::{ println, task::{ scheduler::TaskId, - tcb::{BlockReason, Handle, KernelObject, MAX_MSG_SIZE, Message, Rights}, + tcb::{BlockReason, ExitReason, Handle, KernelObject, MAX_MSG_SIZE, Message, Rights}, }, }; @@ -18,7 +18,7 @@ pub fn sys_yield() -> Result<(), Status> { } pub fn sys_exit(exit_code: usize) -> ! { - crate::task::scheduler::exit_current(exit_code); + crate::task::scheduler::exit_current(ExitReason::Exited(exit_code)); } pub fn sys_write(fd: usize, buf_ptr: usize, len: usize, out_ptr: usize) -> Result<(), Status> { diff --git a/src/task/scheduler.rs b/src/task/scheduler.rs index 57b7af1..b43d75d 100644 --- a/src/task/scheduler.rs +++ b/src/task/scheduler.rs @@ -344,7 +344,7 @@ pub fn yield_current() { crate::arch::restore_interrupts(interrupt_state); } -pub fn exit_current(exit_code: usize) -> ! { +pub fn exit_current(reason: ExitReason) -> ! { crate::arch::disable_interrupts(); let switch = { @@ -356,7 +356,7 @@ pub fn exit_current(exit_code: usize) -> ! { }; let current = scheduler.tasks[current_id.0].as_mut().unwrap(); - current.state = ThreadState::Dead(ExitReason::Exited(exit_code)); + current.state = ThreadState::Dead(reason); scheduler.tasks[next_id.0].as_mut().unwrap().state = ThreadState::Running; scheduler.current = Some(next_id); diff --git a/userspace/omega3/src/main.rs b/userspace/omega3/src/main.rs index 3c32d76..3e5c5a2 100644 --- a/userspace/omega3/src/main.rs +++ b/userspace/omega3/src/main.rs @@ -40,9 +40,9 @@ pub extern "C" fn _start() -> ! { map_stack(client_as, STACK_TOP, STACK_PAGES); let _ = sys_task_create(client_as, client_entry, STACK_TOP).unwrap(); - let ptr = 0xDEAD_BEEF as *mut u32; + // call a bogus system call unsafe { - core::ptr::write_volatile(&mut *ptr, 0xDEAD_BEEF); + core::arch::asm!("syscall", in("rax") 134); } sys_exit(0);