use crate::registers::{esr_el1_read, far_el1_read};
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExceptionClass {
Unknown = 0x00,
WfiWfe = 0x01,
Cp15McrMrc = 0x03,
Cp15McrrMrrc = 0x04,
Cp14McrMrc = 0x05,
Cp14LdcStc = 0x06,
SimdFp = 0x07,
Cp10Vmrs = 0x08,
Cp14Mrrc = 0x0C,
IllegalState = 0x0E,
Svc32 = 0x11,
Svc64 = 0x15,
MsrMrs = 0x18,
InsnAbortLower = 0x20,
InsnAbortSame = 0x21,
PcAlignment = 0x22,
DataAbortLower = 0x24,
DataAbortSame = 0x25,
SpAlignment = 0x26,
Fp32 = 0x28,
Fp64 = 0x2C,
SError = 0x2F,
BreakpointLower = 0x30,
BreakpointSame = 0x31,
SoftwareStepLower = 0x32,
SoftwareStepSame = 0x33,
WatchpointLower = 0x34,
WatchpointSame = 0x35,
Brk = 0x3C,
}
impl ExceptionClass {
pub fn from_esr(esr: u64) -> Self {
let ec = ((esr >> 26) & 0x3F) as u8;
match ec {
0x00 => ExceptionClass::Unknown,
0x01 => ExceptionClass::WfiWfe,
0x03 => ExceptionClass::Cp15McrMrc,
0x04 => ExceptionClass::Cp15McrrMrrc,
0x05 => ExceptionClass::Cp14McrMrc,
0x06 => ExceptionClass::Cp14LdcStc,
0x07 => ExceptionClass::SimdFp,
0x08 => ExceptionClass::Cp10Vmrs,
0x0C => ExceptionClass::Cp14Mrrc,
0x0E => ExceptionClass::IllegalState,
0x11 => ExceptionClass::Svc32,
0x15 => ExceptionClass::Svc64,
0x18 => ExceptionClass::MsrMrs,
0x20 => ExceptionClass::InsnAbortLower,
0x21 => ExceptionClass::InsnAbortSame,
0x22 => ExceptionClass::PcAlignment,
0x24 => ExceptionClass::DataAbortLower,
0x25 => ExceptionClass::DataAbortSame,
0x26 => ExceptionClass::SpAlignment,
0x28 => ExceptionClass::Fp32,
0x2C => ExceptionClass::Fp64,
0x2F => ExceptionClass::SError,
0x30 => ExceptionClass::BreakpointLower,
0x31 => ExceptionClass::BreakpointSame,
0x32 => ExceptionClass::SoftwareStepLower,
0x33 => ExceptionClass::SoftwareStepSame,
0x34 => ExceptionClass::WatchpointLower,
0x35 => ExceptionClass::WatchpointSame,
0x3C => ExceptionClass::Brk,
_ => ExceptionClass::Unknown,
}
}
}
#[repr(C)]
pub struct ExceptionContext {
pub gpr: [u64; 31],
pub sp: u64,
pub elr: u64,
pub spsr: u64,
}
#[no_mangle]
pub unsafe extern "C" fn handle_sync_exception(ctx: &mut ExceptionContext) {
let esr = unsafe { esr_el1_read() };
let ec = ExceptionClass::from_esr(esr);
match ec {
ExceptionClass::Svc64 => {
handle_syscall(ctx, esr);
}
ExceptionClass::DataAbortSame | ExceptionClass::DataAbortLower => {
handle_page_fault(ctx, esr);
}
ExceptionClass::InsnAbortSame | ExceptionClass::InsnAbortLower => {
handle_insn_abort(ctx, esr);
}
_ => {
panic_exception(ctx, esr, "Unhandled synchronous exception");
}
}
}
#[no_mangle]
pub unsafe extern "C" fn handle_irq(_ctx: &mut ExceptionContext) {
}
#[no_mangle]
pub unsafe extern "C" fn handle_fiq(_ctx: &mut ExceptionContext) {
}
#[no_mangle]
pub unsafe extern "C" fn handle_serror(ctx: &mut ExceptionContext) {
let esr = unsafe { esr_el1_read() };
panic_exception(ctx, esr, "SError interrupt");
}
fn handle_syscall(ctx: &mut ExceptionContext, esr: u64) {
let _syscall_num = esr & 0xFFFF;
ctx.gpr[0] = u64::MAX; }
fn handle_page_fault(ctx: &mut ExceptionContext, esr: u64) {
let _fault_addr = unsafe { far_el1_read() };
let is_write = (esr & (1 << 6)) != 0;
panic_exception(
ctx,
esr,
if is_write {
"Write page fault"
} else {
"Read page fault"
},
);
}
fn handle_insn_abort(ctx: &mut ExceptionContext, esr: u64) {
let _fault_addr = unsafe { far_el1_read() };
panic_exception(ctx, esr, "Instruction abort");
}
fn panic_exception(ctx: &ExceptionContext, esr: u64, msg: &str) -> ! {
let far = unsafe { far_el1_read() };
let ec = ExceptionClass::from_esr(esr);
panic!(
"{}\nEC: {:?}\nESR: 0x{:016x}\nFAR: 0x{:016x}\nELR: 0x{:016x}\nSPSR: 0x{:016x}",
msg, ec, esr, far, ctx.elr, ctx.spsr
);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_exception_class_from_esr() {
let esr = 0x15 << 26;
assert_eq!(ExceptionClass::from_esr(esr), ExceptionClass::Svc64);
let esr = 0x25 << 26;
assert_eq!(ExceptionClass::from_esr(esr), ExceptionClass::DataAbortSame);
}
}