os/trap/
mod.rs

1mod context;
2
3use crate::config::TRAMPOLINE;
4use crate::syscall::syscall;
5use crate::task::{
6    SignalFlags, check_signals_of_current, current_add_signal, current_trap_cx,
7    current_trap_cx_user_va, current_user_token, exit_current_and_run_next,
8    suspend_current_and_run_next,
9};
10use crate::timer::{check_timer, set_next_trigger};
11use core::arch::{asm, global_asm};
12use riscv::register::{
13    mtvec::TrapMode,
14    scause::{self, Exception, Interrupt, Trap},
15    sie, stval, stvec,
16};
17
18global_asm!(include_str!("trap.S"));
19
20pub fn init() {
21    set_kernel_trap_entry();
22}
23
24fn set_kernel_trap_entry() {
25    unsafe {
26        stvec::write(trap_from_kernel as usize, TrapMode::Direct);
27    }
28}
29
30fn set_user_trap_entry() {
31    unsafe {
32        stvec::write(TRAMPOLINE as usize, TrapMode::Direct);
33    }
34}
35
36pub fn enable_timer_interrupt() {
37    unsafe {
38        sie::set_stimer();
39    }
40}
41
42#[unsafe(no_mangle)]
43/// handle an interrupt, exception, or system call from user space
44pub fn trap_handler() -> ! {
45    set_kernel_trap_entry();
46    let scause = scause::read();
47    let stval = stval::read();
48    match scause.cause() {
49        Trap::Exception(Exception::UserEnvCall) => {
50            // jump to next instruction anyway
51            let mut cx = current_trap_cx();
52            cx.sepc += 4;
53            // get system call return value
54            let result = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]);
55            // cx is changed during sys_exec, so we have to call it again
56            cx = current_trap_cx();
57            cx.x[10] = result as usize;
58        }
59        Trap::Exception(Exception::StoreFault)
60        | Trap::Exception(Exception::StorePageFault)
61        | Trap::Exception(Exception::InstructionFault)
62        | Trap::Exception(Exception::InstructionPageFault)
63        | Trap::Exception(Exception::LoadFault)
64        | Trap::Exception(Exception::LoadPageFault) => {
65            /*
66            println!(
67                "[kernel] {:?} in application, bad addr = {:#x}, bad instruction = {:#x}, kernel killed it.",
68                scause.cause(),
69                stval,
70                current_trap_cx().sepc,
71            );
72            */
73            current_add_signal(SignalFlags::SIGSEGV);
74        }
75        Trap::Exception(Exception::IllegalInstruction) => {
76            current_add_signal(SignalFlags::SIGILL);
77        }
78        Trap::Interrupt(Interrupt::SupervisorTimer) => {
79            set_next_trigger();
80            check_timer();
81            suspend_current_and_run_next();
82        }
83        _ => {
84            panic!(
85                "Unsupported trap {:?}, stval = {:#x}!",
86                scause.cause(),
87                stval
88            );
89        }
90    }
91    // check signals
92    if let Some((errno, msg)) = check_signals_of_current() {
93        println!("[kernel] {}", msg);
94        exit_current_and_run_next(errno);
95    }
96    trap_return();
97}
98
99#[unsafe(no_mangle)]
100/// set the new addr of __restore asm function in TRAMPOLINE page,
101/// set the reg a0 = trap_cx_ptr, reg a1 = phy addr of usr page table,
102/// finally, jump to new addr of __restore asm function
103pub fn trap_return() -> ! {
104    set_user_trap_entry();
105    let trap_cx_user_va = current_trap_cx_user_va();
106    let user_satp = current_user_token();
107    unsafe extern "C" {
108        unsafe fn __alltraps();
109        unsafe fn __restore();
110    }
111    let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE;
112    unsafe {
113        asm!(
114            "fence.i",
115            "jr {restore_va}",
116            restore_va = in(reg) restore_va,
117            in("a0") trap_cx_user_va,
118            in("a1") user_satp,
119            options(noreturn)
120        );
121    }
122}
123
124#[unsafe(no_mangle)]
125/// Unimplement: traps/interrupts/exceptions from kernel mode
126/// Todo: Chapter 9: I/O device
127pub fn trap_from_kernel() -> ! {
128    use riscv::register::sepc;
129    println!("stval = {:#x}, sepc = {:#x}", stval::read(), sepc::read());
130    panic!("a trap {:?} from kernel!", scause::read().cause());
131}
132
133pub use context::TrapContext;