use crate::systems::rpi::{exception, memory};
use aarch64_cpu::{asm::barrier, registers::*};
use core::{arch::global_asm, cell::UnsafeCell, fmt};
use tock_registers::{
interfaces::{Readable, Writeable},
registers::InMemoryRegister,
};
global_asm!(include_str!("exception.s"),);
#[repr(transparent)]
struct SpsrEL1(InMemoryRegister<u64, SPSR_EL1::Register>);
struct EsrEL1(InMemoryRegister<u64, ESR_EL1::Register>);
#[repr(C)]
struct ExceptionContext
{
gpr: [u64; 30],
lr: u64,
elr_el1: u64,
spsr_el1: SpsrEL1,
esr_el1: EsrEL1,
}
fn default_exception_handler(exc: &ExceptionContext)
{
panic!(
"CPU Exception!\n\n\
{}",
exc
);
}
#[no_mangle]
extern "C" fn current_el0_synchronous(_e: &mut ExceptionContext)
{
panic!("Should not be here. Use of SP_EL0 in EL1 is not supported.")
}
#[no_mangle]
extern "C" fn current_el0_irq(_e: &mut ExceptionContext)
{
panic!("Should not be here. Use of SP_EL0 in EL1 is not supported.")
}
#[no_mangle]
extern "C" fn current_el0_serror(_e: &mut ExceptionContext)
{
panic!("Should not be here. Use of SP_EL0 in EL1 is not supported.")
}
#[no_mangle]
extern "C" fn current_elx_synchronous(e: &mut ExceptionContext)
{
#[cfg(feature = "test_build")]
{
const TEST_SVC_ID: u64 = 0x1337;
if let Some(ESR_EL1::EC::Value::SVC64) = e.esr_el1.exception_class()
{
if e.esr_el1.iss() == TEST_SVC_ID
{
return;
}
}
}
default_exception_handler(e);
}
#[no_mangle]
extern "C" fn current_elx_irq(_e: &mut ExceptionContext)
{
let token = unsafe { &exception::asynchronous::IRQContext::new() };
exception::asynchronous::irq_manager().handle_pending_irqs(token);
}
#[no_mangle]
extern "C" fn current_elx_serror(e: &mut ExceptionContext)
{
default_exception_handler(e);
}
#[no_mangle]
extern "C" fn lower_aarch64_synchronous(e: &mut ExceptionContext)
{
default_exception_handler(e);
}
#[no_mangle]
extern "C" fn lower_aarch64_irq(e: &mut ExceptionContext)
{
default_exception_handler(e);
}
#[no_mangle]
extern "C" fn lower_aarch64_serror(e: &mut ExceptionContext)
{
default_exception_handler(e);
}
#[no_mangle]
extern "C" fn lower_aarch32_synchronous(e: &mut ExceptionContext)
{
default_exception_handler(e);
}
#[no_mangle]
extern "C" fn lower_aarch32_irq(e: &mut ExceptionContext)
{
default_exception_handler(e);
}
#[no_mangle]
extern "C" fn lower_aarch32_serror(e: &mut ExceptionContext)
{
default_exception_handler(e);
}
#[rustfmt::skip]
impl fmt::Display for SpsrEL1 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "SPSR_EL1: {:#010x}", self.0.get())?;
let to_flag_str = |x| -> _ {
if x { "Set" } else { "Not set" }
};
writeln!(f, " Flags:")?;
writeln!(f, " Negative (N): {}", to_flag_str(self.0.is_set(SPSR_EL1::N)))?;
writeln!(f, " Zero (Z): {}", to_flag_str(self.0.is_set(SPSR_EL1::Z)))?;
writeln!(f, " Carry (C): {}", to_flag_str(self.0.is_set(SPSR_EL1::C)))?;
writeln!(f, " Overflow (V): {}", to_flag_str(self.0.is_set(SPSR_EL1::V)))?;
let to_mask_str = |x| -> _ {
if x { "Masked" } else { "Unmasked" }
};
writeln!(f, " Exception handling state:")?;
writeln!(f, " Debug (D): {}", to_mask_str(self.0.is_set(SPSR_EL1::D)))?;
writeln!(f, " SError (A): {}", to_mask_str(self.0.is_set(SPSR_EL1::A)))?;
writeln!(f, " IRQ (I): {}", to_mask_str(self.0.is_set(SPSR_EL1::I)))?;
writeln!(f, " FIQ (F): {}", to_mask_str(self.0.is_set(SPSR_EL1::F)))?;
write!(f, " Illegal Execution State (IL): {}",
to_flag_str(self.0.is_set(SPSR_EL1::IL))
)
}
}
impl EsrEL1
{
#[inline(always)]
fn exception_class(&self) -> Option<ESR_EL1::EC::Value>
{
self.0.read_as_enum(ESR_EL1::EC)
}
#[cfg(feature = "test_build")]
#[inline(always)]
fn iss(&self) -> u64
{
self.0.read(ESR_EL1::ISS)
}
}
#[rustfmt::skip]
impl fmt::Display for EsrEL1 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "ESR_EL1: {:#010x}", self.0.get())?;
write!(f, " Exception Class (EC) : {:#x}", self.0.read(ESR_EL1::EC))?;
let ec_translation = match self.exception_class() {
Some(ESR_EL1::EC::Value::DataAbortCurrentEL) => "Data Abort, current EL",
_ => "N/A",
};
writeln!(f, " - {}", ec_translation)?;
write!(f, " Instr Specific Syndrome (ISS): {:#x}", self.0.read(ESR_EL1::ISS))
}
}
impl ExceptionContext
{
#[inline(always)]
fn exception_class(&self) -> Option<ESR_EL1::EC::Value>
{
self.esr_el1.exception_class()
}
#[inline(always)]
fn fault_address_valid(&self) -> bool
{
use ESR_EL1::EC::Value::*;
match self.exception_class()
{
None => false,
Some(ec) => matches!(
ec,
InstrAbortLowerEL
| InstrAbortCurrentEL
| PCAlignmentFault
| DataAbortLowerEL
| DataAbortCurrentEL
| WatchpointLowerEL
| WatchpointCurrentEL
),
}
}
}
impl fmt::Display for ExceptionContext
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
{
writeln!(f, "{}", self.esr_el1)?;
if self.fault_address_valid()
{
writeln!(f, "FAR_EL1: {:#018x}", FAR_EL1.get() as usize)?;
}
writeln!(f, "{}", self.spsr_el1)?;
writeln!(f, "ELR_EL1: {:#018x}", self.elr_el1)?;
writeln!(f)?;
writeln!(f, "General purpose register:")?;
#[rustfmt::skip]
let alternating = |x| -> _ {
if x % 2 == 0 { " " } else { "\n" }
};
for (i, reg) in self.gpr.iter().enumerate()
{
write!(f, " x{: <2}: {: >#018x}{}", i, reg, alternating(i))?;
}
write!(f, " lr : {:#018x}", self.lr)
}
}
use crate::systems::rpi::exception::PrivilegeLevel;
pub fn current_privilege_level() -> (PrivilegeLevel, &'static str)
{
let el = CurrentEL.read_as_enum(CurrentEL::EL);
match el
{
Some(CurrentEL::EL::Value::EL2) => (PrivilegeLevel::Hypervisor, "EL2"),
Some(CurrentEL::EL::Value::EL1) => (PrivilegeLevel::Kernel, "EL1"),
Some(CurrentEL::EL::Value::EL0) => (PrivilegeLevel::User, "EL0"),
_ => (PrivilegeLevel::Unknown, "Unknown"),
}
}
pub unsafe fn handling_init()
{
extern "Rust" {
static __exception_vector_start: UnsafeCell<()>;
}
VBAR_EL1.set(__exception_vector_start.get() as u64);
barrier::isb(barrier::SY);
}