feat: userspace faults
This commit is contained in:
@@ -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
@@ -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),
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user