You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

102 lines
2.7 KiB

//! Trap handler
use super::context::TrapFrame;
use super::syndrome::{Fault, Syndrome};
use crate::arch::board::irq::handle_irq;
use aarch64::regs::*;
use log::*;
global_asm!(include_str!("trap.S"));
global_asm!(include_str!("vector.S"));
#[repr(u16)]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Kind {
Synchronous = 0,
Irq = 1,
Fiq = 2,
SError = 3,
}
#[repr(u16)]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Source {
CurrentSpEl0 = 0,
CurrentSpElx = 1,
LowerAArch64 = 2,
LowerAArch32 = 3,
}
#[repr(C)]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct Info {
source: Source,
kind: Kind,
}
/// This function is called when an exception occurs. The `info` parameter
/// specifies the source and kind of exception that has occurred. The `esr` is
/// the value of the exception syndrome register. Finally, `tf` is a pointer to
/// the trap frame for the exception.
#[no_mangle]
pub extern "C" fn rust_trap(info: Info, esr: u32, tf: &mut TrapFrame) {
trace!("Interrupt: {:?}, ELR: {:#x?}", info, tf.elr);
match info.kind {
Kind::Synchronous => {
let syndrome = Syndrome::from(esr);
trace!("ESR: {:#x?}, Syndrome: {:?}", esr, syndrome);
// syndrome is only valid with sync
match syndrome {
Syndrome::Brk(brk) => handle_break(brk, tf),
Syndrome::Svc(svc) => handle_syscall(svc, tf),
Syndrome::DataAbort { kind, level: _ }
| Syndrome::InstructionAbort { kind, level: _ } => match kind {
Fault::Translation | Fault::AccessFlag | Fault::Permission => {
handle_page_fault(tf)
}
_ => crate::trap::error(tf),
},
_ => crate::trap::error(tf),
}
}
Kind::Irq => handle_irq(tf),
_ => crate::trap::error(tf),
}
trace!("Interrupt end");
}
fn handle_break(_num: u16, tf: &mut TrapFrame) {
// Skip the current brk instruction (ref: J1.1.2, page 6147)
tf.elr += 4;
}
fn handle_syscall(num: u16, tf: &mut TrapFrame) {
if num != 0 {
crate::trap::error(tf);
}
// svc instruction has been skipped in syscall (ref: J1.1.2, page 6152)
let ret = crate::syscall::syscall(
tf.x1to29[7] as usize,
[
tf.x0,
tf.x1to29[0],
tf.x1to29[1],
tf.x1to29[2],
tf.x1to29[3],
tf.x1to29[4],
],
tf,
);
tf.x0 = ret as usize;
}
fn handle_page_fault(tf: &mut TrapFrame) {
let addr = FAR_EL1.get() as usize;
if !crate::memory::handle_page_fault(addr) {
error!("\nEXCEPTION: Page Fault @ {:#x}", addr);
crate::trap::error(tf);
}
}