Enter a trap: saves state, updates CSRs, jumps to the appropriate handler. Delegation: if the current privilege is U or S and the corresponding bit in medeleg (exceptions) or mideleg (interrupts) is set, the trap is delivered to S-mode using sepc/scause/stval/sstatus/stvec. Otherwise it goes to M-mode. Returns the new PC (trap handler entry address).
(regs: &mut Registers, csrs: &mut CsrFile, cause: u64, tval: u64, pc: u64)
| 53 | csrs.sepc = pc & !0x3u64; |
| 54 | csrs.scause = cause; |
| 55 | csrs.stval = tval; |
| 56 | |
| 57 | // sstatus bits live inside mstatus: |
| 58 | // SPIE = old SIE; SIE = 0; SPP = current_priv (0=U, 1=S) |
| 59 | let sie = (csrs.mstatus >> 1) & 1; |
| 60 | csrs.mstatus = (csrs.mstatus & !(1u64 << 5)) | (sie << 5); // SPIE = old SIE |
| 61 | csrs.mstatus &= !(1u64 << 1); // SIE = 0 |
| 62 | let spp: u64 = u64::from(current_priv != PrivilegeMode::User); |
| 63 | csrs.mstatus = (csrs.mstatus & !(1u64 << 8)) | (spp << 8); // SPP = current_priv |
| 64 | |
| 65 | regs.priv_mode = PrivilegeMode::Supervisor; |
| 66 | |
| 67 | let stvec = csrs.stvec; |
| 68 | let base = stvec & !0x3u64; |
| 69 | if (stvec & 0x3) == 1 && is_interrupt { |
| 70 | base + 4 * cause_idx |
| 71 | } else { |
| 72 | base |
| 73 | } |
| 74 | } else { |
| 75 | csrs.mepc = pc & !0x3u64; |
| 76 | csrs.mcause = cause; |
| 77 | csrs.mtval = tval; |
| 78 | |
| 79 | // mstatus: MPIE = old MIE; MIE = 0; MPP = current_priv |
| 80 | let mie = (csrs.mstatus >> 3) & 1; |
| 81 | csrs.mstatus = (csrs.mstatus & !(1u64 << 7)) | (mie << 7); // MPIE = old MIE |
| 82 | csrs.mstatus &= !(1u64 << 3); // MIE = 0 |
| 83 | csrs.mstatus = (csrs.mstatus & !(0x3u64 << 11)) | ((current_priv as u64) << 11); // MPP |
| 84 | |
| 85 | regs.priv_mode = PrivilegeMode::Machine; |
| 86 | |
| 87 | let mtvec = csrs.mtvec; |
| 88 | let base = mtvec & !0x3u64; |
| 89 | if (mtvec & 0x3) == 1 && is_interrupt { |
| 90 | base + 4 * cause_idx |
| 91 | } else { |
| 92 | base |
| 93 | } |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | // --- Trap dispatch (error to trap mapping) --- |
| 98 | |
| 99 | /// `Some((cause, tval))` when the error should trap, `None` when it is fatal and |
| 100 | /// cannot be trapped. |
| 101 | pub fn error_to_trap_cause(e: &VmError) -> Option<(u64, u64)> { |
| 102 | match e { |
| 103 | VmError::InstructionAccessFault(addr) => Some((CAUSE_INSN_ACCESS_FAULT, *addr)), |
| 104 | VmError::IllegalInstruction(insn) => Some((CAUSE_ILLEGAL_INSN, *insn as u64)), |
| 105 | VmError::LoadAccessFault(addr) | VmError::BusError(addr) => { |
| 106 | Some((CAUSE_LOAD_ACCESS_FAULT, *addr)) |
| 107 | } |
| 108 | VmError::StoreAccessFault(addr) => Some((CAUSE_STORE_ACCESS_FAULT, *addr)), |
| 109 | VmError::PageFault(addr) => Some((CAUSE_PAGE_FAULT_LOAD, *addr)), |
| 110 | _ => None, |
| 111 | } |
| 112 | } |
no outgoing calls