    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123

    //! Trap handling functionality
    //!
    //! For rCore, we have a single trap entry point, namely `__alltraps`. At
    //! initialization in [`init()`], we set the `stvec` CSR to point to it.
    //!
    //! All traps go through `__alltraps`, which is defined in `trap.S`. The
    //! assembly language code does just enough work restore the kernel space
    //! context, ensuring that Rust code safely runs, and transfers control to
    //! [`trap_handler()`].
    //!
    //! It then calls different functionality based on what exactly the exception
    //! was. For example, timer interrupts trigger task preemption, and syscalls go
    //! to [`syscall()`].
    mod context;

    use crate::config::{TRAMPOLINE, TRAP_CONTEXT};
    use crate::syscall::syscall;
    use crate::task::{
        current_trap_cx, current_user_token, exit_current_and_run_next, suspend_current_and_run_next,
    };
    use crate::timer::set_next_trigger;
    use core::arch::{asm, global_asm};
    use riscv::register::{
        mtvec::TrapMode,
        scause::{self, Exception, Interrupt, Trap},
        sie, stval, stvec,
    };

    global_asm!(include_str!("trap.S"));

    /// initialize CSR `stvec` as the entry of `__alltraps`
    pub fn init() {
        set_kernel_trap_entry();
    }

    fn set_kernel_trap_entry() {
        unsafe {
            stvec::write(trap_from_kernel as usize, TrapMode::Direct);
        }
    }

    fn set_user_trap_entry() {
        unsafe {
            stvec::write(TRAMPOLINE as usize, TrapMode::Direct);
        }
    }

    /// enable timer interrupt in sie CSR
    pub fn enable_timer_interrupt() {
        unsafe {
            sie::set_stimer();
        }
    }

    #[no_mangle]
    /// handle an interrupt, exception, or system call from user space
    pub fn trap_handler() -> ! {
        set_kernel_trap_entry();
        let cx = current_trap_cx();
        let scause = scause::read();
        let stval = stval::read();
        match scause.cause() {
            Trap::Exception(Exception::UserEnvCall) => {
                cx.sepc += 4;
                cx.x[10] = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]) as usize;
            }
            Trap::Exception(Exception::StoreFault) | Trap::Exception(Exception::StorePageFault) |
            Trap::Exception(Exception::LoadFault)  | Trap::Exception(Exception::LoadPageFault) => {
                println!("[kernel] PageFault in application, bad addr = {:#x}, bad instruction = {:#x}, kernel killed it.", stval, cx.sepc);
                exit_current_and_run_next();
            }
            Trap::Exception(Exception::IllegalInstruction) => {
                println!("[kernel] IllegalInstruction in application, kernel killed it.");
                exit_current_and_run_next();
            }
            Trap::Interrupt(Interrupt::SupervisorTimer) => {
                set_next_trigger();
                suspend_current_and_run_next();
            }
            _ => {
                panic!(
                    "Unsupported trap {:?}, stval = {:#x}!",
                    scause.cause(),
                    stval
                );
            }
        }
        trap_return();
    }

    #[no_mangle]
    /// set the new addr of __restore asm function in TRAMPOLINE page,
    /// set the reg a0 = trap_cx_ptr, reg a1 = phy addr of usr page table,
    /// finally, jump to new addr of __restore asm function
    pub fn trap_return() -> ! {
        set_user_trap_entry();
        let trap_cx_ptr = TRAP_CONTEXT;
        let user_satp = current_user_token();
        extern "C" {
            fn __alltraps();
            fn __restore();
        }
        let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE;
        unsafe {
            asm!(
                "fence.i",
                "jr {restore_va}",             // jump to new addr of __restore asm function
                restore_va = in(reg) restore_va,
                in("a0") trap_cx_ptr,      // a0 = virt addr of Trap Context
                in("a1") user_satp,        // a1 = phy addr of usr page table
                options(noreturn)
            );
        }
    }

    #[no_mangle]
    /// Unimplement: traps/interrupts/exceptions from kernel mode
    /// Todo: Chapter 9: I/O device
    pub fn trap_from_kernel() -> ! {
        panic!("a trap from kernel!");
    }

    pub use context::TrapContext;