# mod.rs - source
Source of the Rust file `src/trap/mod.rs`.
Keywords: rust, rustlang, rust-lang

      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

    mod context;

    use crate::config::{TRAMPOLINE, TRAP_CONTEXT};
    use crate::syscall::syscall;
    use crate::task::{
        check_signals_of_current, current_add_signal, current_trap_cx, current_user_token,
        exit_current_and_run_next, suspend_current_and_run_next, SignalFlags,
    };
    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
                );
            }
        }
        // check signals
        if let Some((errno, msg)) = check_signals_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;