Skip to main content

os/trap/
mod.rs

1//! Trap handling functionality
2//!
3//! For rCore, we have a single trap entry point, namely `__alltraps`. At
4//! initialization in [`init()`], we set the `stvec` CSR to point to it.
5//!
6//! All traps go through `__alltraps`, which is defined in `trap.S`. The
7//! assembly language code does just enough work restore the kernel space
8//! context, ensuring that Rust code safely runs, and transfers control to
9//! [`trap_handler()`].
10//!
11//! It then calls different functionality based on what exactly the exception
12//! was. For example, timer interrupts trigger task preemption, and syscalls go
13//! to [`syscall()`].
14mod context;
15
16use crate::config::{TRAMPOLINE, TRAP_CONTEXT};
17use crate::syscall::syscall;
18use crate::task::{
19    current_trap_cx, current_user_token, exit_current_and_run_next, suspend_current_and_run_next,
20};
21use crate::timer::set_next_trigger;
22use core::arch::{asm, global_asm};
23use riscv::interrupt::{Exception, Interrupt, Trap};
24use riscv::register::{mtvec::TrapMode, scause, sie, stval, stvec};
25
26global_asm!(include_str!("trap.S"));
27/// initialize CSR `stvec` as the entry of `__alltraps`
28pub fn init() {
29    set_kernel_trap_entry();
30}
31
32fn set_kernel_trap_entry() {
33    unsafe {
34        stvec::write(stvec::Stvec::new(
35            linker_symbol_addr!(trap_from_kernel),
36            TrapMode::Direct,
37        ));
38    }
39}
40
41fn set_user_trap_entry() {
42    unsafe {
43        stvec::write(stvec::Stvec::new(TRAMPOLINE as usize, TrapMode::Direct));
44    }
45}
46/// enable timer interrupt in sie CSR
47pub fn enable_timer_interrupt() {
48    unsafe {
49        sie::set_stimer();
50    }
51}
52
53#[unsafe(no_mangle)]
54/// handle an interrupt, exception, or system call from user space
55pub fn trap_handler() -> ! {
56    set_kernel_trap_entry();
57    let scause = scause::read();
58    let stval = stval::read();
59    let trap: Trap<Interrupt, Exception> = match scause.cause().try_into() {
60        Ok(trap) => trap,
61        Err(_) => panic!(
62            "Unsupported trap {:?}, stval = {:#x}!",
63            scause.cause(),
64            stval
65        ),
66    };
67    match trap {
68        Trap::Exception(Exception::UserEnvCall) => {
69            // jump to next instruction anyway
70            let mut cx = current_trap_cx();
71            cx.sepc += 4;
72            // get system call return value
73            let result = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]);
74            // cx is changed during sys_exec, so we have to call it again
75            cx = current_trap_cx();
76            cx.x[10] = result as usize;
77        }
78        Trap::Exception(Exception::StoreFault)
79        | Trap::Exception(Exception::StorePageFault)
80        | Trap::Exception(Exception::InstructionFault)
81        | Trap::Exception(Exception::InstructionPageFault)
82        | Trap::Exception(Exception::LoadFault)
83        | Trap::Exception(Exception::LoadPageFault) => {
84            println!(
85                "[kernel] {:?} in application, bad addr = {:#x}, bad instruction = {:#x}, kernel killed it.",
86                scause.cause(),
87                stval,
88                current_trap_cx().sepc,
89            );
90            // page fault exit code
91            exit_current_and_run_next(-2);
92        }
93        Trap::Exception(Exception::IllegalInstruction) => {
94            println!("[kernel] IllegalInstruction in application, kernel killed it.");
95            // illegal instruction exit code
96            exit_current_and_run_next(-3);
97        }
98        Trap::Interrupt(Interrupt::SupervisorTimer) => {
99            set_next_trigger();
100            suspend_current_and_run_next();
101        }
102        _ => {
103            panic!(
104                "Unsupported trap {:?}, stval = {:#x}!",
105                scause.cause(),
106                stval
107            );
108        }
109    }
110    //println!("before trap_return");
111    trap_return();
112}
113
114#[unsafe(no_mangle)]
115/// set the new addr of __restore asm function in TRAMPOLINE page,
116/// set the reg a0 = trap_cx_ptr, reg a1 = phy addr of usr page table,
117/// finally, jump to new addr of __restore asm function
118pub fn trap_return() -> ! {
119    set_user_trap_entry();
120    let trap_cx_ptr = TRAP_CONTEXT;
121    let user_satp = current_user_token();
122    unsafe extern "C" {
123        unsafe fn __alltraps();
124        unsafe fn __restore();
125    }
126    let restore_va = linker_symbol_addr!(__restore) - linker_symbol_addr!(__alltraps) + TRAMPOLINE;
127    unsafe {
128        asm!(
129            "fence.i",
130            "jr {restore_va}",
131            restore_va = in(reg) restore_va,
132            in("a0") trap_cx_ptr,
133            in("a1") user_satp,
134            options(noreturn)
135        );
136    }
137}
138
139#[unsafe(no_mangle)]
140/// Unimplement: traps/interrupts/exceptions from kernel mode
141/// Todo: Chapter 9: I/O device
142pub fn trap_from_kernel() -> ! {
143    use riscv::register::sepc;
144    let scause = scause::read();
145    let stval = stval::read();
146    let trap: Trap<Interrupt, Exception> = match scause.cause().try_into() {
147        Ok(trap) => trap,
148        Err(_) => panic!(
149            "Unsupported trap from kernel: {:?}, stval = {:#x}, sepc = {:#x}!",
150            scause.cause(),
151            stval,
152            sepc::read()
153        ),
154    };
155    println!("stval = {:#x}, sepc = {:#x}", stval, sepc::read());
156    panic!("a trap {:?} from kernel!", trap);
157}
158
159pub use context::TrapContext;