use aarch32_cpu::register::{
cpsr::{Cpsr, ProcessorMode},
dfsr::DfsrStatus,
ifsr::FsrStatus,
};
use super::TrapFrame;
use crate::trap::PageFaultFlags;
core::arch::global_asm!(include_str!("trap.S"));
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub enum TrapKind {
Reset = 0,
Undefined = 1,
Svc = 2,
PrefetchAbort = 3,
DataAbort = 4,
Reserved = 5,
Irq = 6,
Fiq = 7,
}
#[unsafe(no_mangle)]
fn invalid_exception(tf: &TrapFrame, kind: u32) {
let kind = match kind {
0 => TrapKind::Reset,
1 => TrapKind::Undefined,
2 => TrapKind::Svc,
3 => TrapKind::PrefetchAbort,
4 => TrapKind::DataAbort,
5 => TrapKind::Reserved,
6 => TrapKind::Irq,
7 => TrapKind::Fiq,
_ => TrapKind::Reserved,
};
panic!("Invalid exception {:?}:\n{:#x?}", kind, tf);
}
#[unsafe(no_mangle)]
fn handle_irq_exception(_tf: &TrapFrame) {
trace!("IRQ received");
handle_trap!(IRQ, 0);
}
#[unsafe(no_mangle)]
fn handle_sync_exception(tf: &mut TrapFrame) {
let svc_num = tf.r[7];
trace!("SVC #{} at {:#x}", svc_num, tf.pc);
#[cfg(feature = "uspace")]
{
tf.r[0] = crate::trap::handle_syscall(tf, svc_num as usize) as u32;
}
#[cfg(not(feature = "uspace"))]
{
panic!(
"SVC #{} at {:#x} but uspace feature not enabled:\n{:#x?}",
svc_num, tf.pc, tf
);
}
}
fn handle_page_fault(tf: &TrapFrame, vaddr: usize, base_flags: PageFaultFlags) {
let is_user = Cpsr::new_with_raw_value(tf.cpsr).mode() == Ok(ProcessorMode::Usr);
let mut access_flags = base_flags;
if is_user {
access_flags |= PageFaultFlags::USER;
}
handle_trap!(PAGE_FAULT, vaddr.into(), access_flags, is_user);
}
#[unsafe(no_mangle)]
fn handle_prefetch_abort_exception(tf: &mut TrapFrame) {
let (fsr, far) = (super::asm::read_ifsr(), super::asm::read_ifar());
let fsr_status = match fsr.status() {
Ok(status) => status,
Err(raw) => panic!(
"Unknown IFSR status {:#x} in Prefetch Abort at {:#x}:\n{:#x?}",
raw, tf.pc, tf
),
};
match fsr_status {
FsrStatus::TranslationFaultFirstLevel | FsrStatus::TranslationFaultSecondLevel => {
handle_page_fault(tf, far.0 as usize, PageFaultFlags::EXECUTE);
}
FsrStatus::DebugEvent => {
let is_thumb = (tf.cpsr & (1 << 5)) != 0;
let instr_len = if is_thumb { 2 } else { 4 };
tf.pc = tf.pc.wrapping_add(instr_len);
}
_ => {
panic!(
"Unhandled IFSR status {:?} in Prefetch Abort at {:#x} (IFAR={:#x}):\n{:#x?}",
fsr_status, tf.pc, far.0, tf
);
}
}
}
#[unsafe(no_mangle)]
fn handle_data_abort_exception(tf: &mut TrapFrame) {
let (fsr, far) = (super::asm::read_dfsr(), super::asm::read_dfar());
let base_flags = if fsr.wnr() {
PageFaultFlags::WRITE
} else {
PageFaultFlags::READ
};
let fsr_status = match fsr.status() {
Ok(status) => status,
Err(raw) => panic!(
"Unknown DFSR status {:#x} in Data Abort at {:#x}:\n{:#x?}",
raw, tf.pc, tf
),
};
match fsr_status {
DfsrStatus::CommonFsr(FsrStatus::TranslationFaultFirstLevel)
| DfsrStatus::CommonFsr(FsrStatus::TranslationFaultSecondLevel)
| DfsrStatus::CommonFsr(FsrStatus::PermissionFaultFirstLevel)
| DfsrStatus::CommonFsr(FsrStatus::PermissionFaultSecondLevel) => {
handle_page_fault(tf, far.0 as usize, base_flags);
}
_ => {
panic!(
"Unhandled DFSR status {:?} in Data Abort at {:#x}, FAR={:#x}:\n{:#x?}",
fsr_status, tf.pc, far.0, tf
);
}
}
}