    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
    124
    125
    126
    127
    128

    mod context;

    use crate::config::{TRAMPOLINE, TRAP_CONTEXT};
    use crate::syscall::syscall;
    use crate::task::{
        check_signals_error_of_current, current_add_signal, current_trap_cx, current_user_token,
        exit_current_and_run_next, suspend_current_and_run_next, SignalFlags, handle_signals,
    };
    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"));

    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);
        }
    }

    pub fn enable_timer_interrupt() {
        unsafe {
            sie::set_stimer();
        }
    }

    #[no_mangle]
    pub fn trap_handler() -> ! {
        set_kernel_trap_entry();
        let scause = scause::read();
        let stval = stval::read();
        match scause.cause() {
            Trap::Exception(Exception::UserEnvCall) => {
                // jump to next instruction anyway
                let mut cx = current_trap_cx();
                cx.sepc += 4;
                // get system call return value
                let result = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]);
                // cx is changed during sys_exec, so we have to call it again
                cx = current_trap_cx();
                cx.x[10] = result as usize;
            }
            Trap::Exception(Exception::StoreFault)
            | Trap::Exception(Exception::StorePageFault)
            | Trap::Exception(Exception::InstructionFault)
            | Trap::Exception(Exception::InstructionPageFault)
            | Trap::Exception(Exception::LoadFault)
            | Trap::Exception(Exception::LoadPageFault) => {
                /*
                println!(
                    "[kernel] {:?} in application, bad addr = {:#x}, bad instruction = {:#x}, kernel killed it.",
                    scause.cause(),
                    stval,
                    current_trap_cx().sepc,
                );
                */
                current_add_signal(SignalFlags::SIGSEGV);
            }
            Trap::Exception(Exception::IllegalInstruction) => {
                current_add_signal(SignalFlags::SIGILL);
            }
            Trap::Interrupt(Interrupt::SupervisorTimer) => {
                set_next_trigger();
                suspend_current_and_run_next();
            }
            _ => {
                panic!(
                    "Unsupported trap {:?}, stval = {:#x}!",
                    scause.cause(),
                    stval
                );
            }
        }
        // handle signals (handle the sent signal)
        handle_signals();

        // check error signals (if error then exit)
        if let Some((errno, msg)) = check_signals_error_of_current() {
            println!("[kernel] {}", msg);
            exit_current_and_run_next(errno);
        }
        trap_return();
    }

    #[no_mangle]
    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}",
                restore_va = in(reg) restore_va,
                in("a0") trap_cx_ptr,
                in("a1") user_satp,
                options(noreturn)
            );
        }
    }

    #[no_mangle]
    pub fn trap_from_kernel() -> ! {
        use riscv::register::sepc;
        println!("stval = {:#x}, sepc = {:#x}", stval::read(), sepc::read());
        panic!("a trap {:?} from kernel!", scause::read().cause());
    }

    pub use context::TrapContext;