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
28/// initialize CSR `stvec` as the entry of `__alltraps`
29pub fn init() {
30    set_kernel_trap_entry();
31}
32
33fn set_kernel_trap_entry() {
34    unsafe {
35        stvec::write(stvec::Stvec::new(
36            linker_symbol_addr!(trap_from_kernel),
37            TrapMode::Direct,
38        ));
39    }
40}
41
42fn set_user_trap_entry() {
43    unsafe {
44        stvec::write(stvec::Stvec::new(TRAMPOLINE, TrapMode::Direct));
45    }
46}
47
48/// enable timer interrupt in sie CSR
49pub fn enable_timer_interrupt() {
50    unsafe {
51        sie::set_stimer();
52    }
53}
54
55#[unsafe(no_mangle)]
56/// handle an interrupt, exception, or system call from user space
57pub fn trap_handler() -> ! {
58    set_kernel_trap_entry();
59    let cx = current_trap_cx();
60    let scause = scause::read();
61    let stval = stval::read();
62    let trap: Trap<Interrupt, Exception> = match scause.cause().try_into() {
63        Ok(trap) => trap,
64        Err(_) => panic!(
65            "Unsupported trap {:?}, stval = {:#x}!",
66            scause.cause(),
67            stval
68        ),
69    };
70    match trap {
71        Trap::Exception(Exception::UserEnvCall) => {
72            cx.sepc += 4;
73            cx.x[10] = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]) as usize;
74        }
75        Trap::Exception(Exception::StoreFault)
76        | Trap::Exception(Exception::StorePageFault)
77        | Trap::Exception(Exception::LoadFault)
78        | Trap::Exception(Exception::LoadPageFault) => {
79            println!(
80                "[kernel] PageFault in application, bad addr = {:#x}, bad instruction = {:#x}, kernel killed it.",
81                stval, cx.sepc
82            );
83            exit_current_and_run_next();
84        }
85        Trap::Exception(Exception::IllegalInstruction) => {
86            println!("[kernel] IllegalInstruction in application, kernel killed it.");
87            exit_current_and_run_next();
88        }
89        Trap::Interrupt(Interrupt::SupervisorTimer) => {
90            set_next_trigger();
91            suspend_current_and_run_next();
92        }
93        _ => {
94            panic!("Unsupported trap {:?}, stval = {:#x}!", trap, stval);
95        }
96    }
97    trap_return();
98}
99
100#[unsafe(no_mangle)]
101/// set the new addr of __restore asm function in TRAMPOLINE page,
102/// set the reg a0 = trap_cx_ptr, reg a1 = phy addr of usr page table,
103/// finally, jump to new addr of __restore asm function
104pub fn trap_return() -> ! {
105    set_user_trap_entry();
106    let trap_cx_ptr = TRAP_CONTEXT;
107    let user_satp = current_user_token();
108    unsafe extern "C" {
109        unsafe fn __alltraps();
110        unsafe fn __restore();
111    }
112    let restore_va = linker_symbol_addr!(__restore) - linker_symbol_addr!(__alltraps) + TRAMPOLINE;
113    unsafe {
114        asm!(
115            "fence.i",
116            "jr {restore_va}",             // jump to new addr of __restore asm function
117            restore_va = in(reg) restore_va,
118            in("a0") trap_cx_ptr,      // a0 = virt addr of Trap Context
119            in("a1") user_satp,        // a1 = phy addr of usr page table
120            options(noreturn)
121        );
122    }
123}
124
125#[unsafe(no_mangle)]
126/// Unimplement: traps/interrupts/exceptions from kernel mode
127/// Todo: Chapter 9: I/O device
128pub fn trap_from_kernel() -> ! {
129    panic!("a trap from kernel!");
130}
131
132pub use context::TrapContext;