feat: userspace faults
This commit is contained in:
@@ -1,7 +1,10 @@
|
|||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
|
||||||
use super::idt::{self, InterruptFrame, InterruptStackFrame, stub_err, stub_no_err};
|
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_divide_error, 0);
|
||||||
stub_no_err!(stub_debug, 1);
|
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_simd_floating_point, 19);
|
||||||
stub_no_err!(stub_user_test_exit, 0x80);
|
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) {
|
pub(super) fn handle(frame: &mut InterruptFrame) {
|
||||||
match frame.vector as u8 {
|
let is_user = frame.stack_frame.code_segment & 0b11 == 3;
|
||||||
0 => fatal_exception("DIVIDE ERROR", &frame.stack_frame, None),
|
let vector = frame.vector as u8;
|
||||||
1 => fatal_exception("DEBUG EXCEPTION", &frame.stack_frame, None),
|
let name = EXCEPTION_NAMES
|
||||||
2 => fatal_exception("NON-MASKABLE INTERRUPT", &frame.stack_frame, None),
|
.get(vector as usize)
|
||||||
3 => report_exception("BREAKPOINT", &frame.stack_frame, None),
|
.copied()
|
||||||
6 => fatal_exception("INVALID OPCODE", &frame.stack_frame, None),
|
.unwrap_or("UNKNOWN EXCEPTION");
|
||||||
7 => fatal_exception("DEVICE NOT AVAILABLE", &frame.stack_frame, None),
|
|
||||||
8 => fatal_exception("DOUBLE FAULT", &frame.stack_frame, Some(frame.error_code)),
|
if !is_user {
|
||||||
10 => fatal_exception("INVALID TSS", &frame.stack_frame, Some(frame.error_code)),
|
if vector == 14 {
|
||||||
11 => fatal_exception("SEGMENT NOT PRESENT", &frame.stack_frame, Some(frame.error_code)),
|
report_exception(name, &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));
|
|
||||||
println!("Faulting address: {:#X}", read_cr2());
|
println!("Faulting address: {:#X}", read_cr2());
|
||||||
print_page_fault_error(frame.error_code);
|
print_page_fault_error(frame.error_code);
|
||||||
hcf();
|
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");
|
fatal_exception(name, &frame.stack_frame, Some(frame.error_code));
|
||||||
hcf();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
println!("Unhandled exception vector: {:#X}", frame.vector);
|
|
||||||
hcf();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
pub(super) fn install(idt: &mut idt::Idt) {
|
||||||
|
|||||||
+8
-3
@@ -2,6 +2,8 @@ mod table;
|
|||||||
|
|
||||||
use table::*;
|
use table::*;
|
||||||
|
|
||||||
|
use crate::task::tcb::{ExitReason, Fault};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
#[repr(u64)]
|
#[repr(u64)]
|
||||||
pub enum Status {
|
pub enum Status {
|
||||||
@@ -31,7 +33,7 @@ pub enum SyscallNumber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u64> for SyscallNumber {
|
impl TryFrom<u64> for SyscallNumber {
|
||||||
type Error = Status;
|
type Error = ();
|
||||||
fn try_from(val: u64) -> Result<Self, Self::Error> {
|
fn try_from(val: u64) -> Result<Self, Self::Error> {
|
||||||
match val {
|
match val {
|
||||||
1 => Ok(Self::Yield),
|
1 => Ok(Self::Yield),
|
||||||
@@ -45,14 +47,17 @@ impl TryFrom<u64> for SyscallNumber {
|
|||||||
9 => Ok(Self::Map),
|
9 => Ok(Self::Map),
|
||||||
10 => Ok(Self::Unmap),
|
10 => Ok(Self::Unmap),
|
||||||
11 => Ok(Self::TaskCreate),
|
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 {
|
pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, _arg5: u64) -> u64 {
|
||||||
let result = (|| -> Result<(), Status> {
|
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 {
|
match syscall {
|
||||||
SyscallNumber::Yield => sys_yield(),
|
SyscallNumber::Yield => sys_yield(),
|
||||||
SyscallNumber::Exit => sys_exit(arg0 as usize),
|
SyscallNumber::Exit => sys_exit(arg0 as usize),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::{
|
|||||||
println,
|
println,
|
||||||
task::{
|
task::{
|
||||||
scheduler::TaskId,
|
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) -> ! {
|
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> {
|
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);
|
crate::arch::restore_interrupts(interrupt_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exit_current(exit_code: usize) -> ! {
|
pub fn exit_current(reason: ExitReason) -> ! {
|
||||||
crate::arch::disable_interrupts();
|
crate::arch::disable_interrupts();
|
||||||
|
|
||||||
let switch = {
|
let switch = {
|
||||||
@@ -356,7 +356,7 @@ pub fn exit_current(exit_code: usize) -> ! {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let current = scheduler.tasks[current_id.0].as_mut().unwrap();
|
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.tasks[next_id.0].as_mut().unwrap().state = ThreadState::Running;
|
||||||
scheduler.current = Some(next_id);
|
scheduler.current = Some(next_id);
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ pub extern "C" fn _start() -> ! {
|
|||||||
map_stack(client_as, STACK_TOP, STACK_PAGES);
|
map_stack(client_as, STACK_TOP, STACK_PAGES);
|
||||||
let _ = sys_task_create(client_as, client_entry, STACK_TOP).unwrap();
|
let _ = sys_task_create(client_as, client_entry, STACK_TOP).unwrap();
|
||||||
|
|
||||||
let ptr = 0xDEAD_BEEF as *mut u32;
|
// call a bogus system call
|
||||||
unsafe {
|
unsafe {
|
||||||
core::ptr::write_volatile(&mut *ptr, 0xDEAD_BEEF);
|
core::arch::asm!("syscall", in("rax") 134);
|
||||||
}
|
}
|
||||||
|
|
||||||
sys_exit(0);
|
sys_exit(0);
|
||||||
|
|||||||
Reference in New Issue
Block a user