feat: userspace faults

This commit is contained in:
Zoe
2026-09-07 13:52:07 -05:00
parent bf1a81cf82
commit e5c2889acc
5 changed files with 76 additions and 43 deletions
+62 -34
View File
@@ -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) {
+8 -3
View File
@@ -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<u64> for SyscallNumber {
type Error = Status;
type Error = ();
fn try_from(val: u64) -> Result<Self, Self::Error> {
match val {
1 => Ok(Self::Yield),
@@ -45,14 +47,17 @@ impl TryFrom<u64> 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),
+2 -2
View File
@@ -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> {
+2 -2
View File
@@ -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);
+2 -2
View File
@@ -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);