You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
10 KiB
Rust
316 lines
10 KiB
Rust
5 years ago
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||
|
//
|
||
3 years ago
|
// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
|
||
5 years ago
|
|
||
|
//! Architectural synchronous and asynchronous exception handling.
|
||
4 years ago
|
//!
|
||
|
//! # Orientation
|
||
|
//!
|
||
|
//! Since arch modules are imported into generic modules using the path attribute, the path of this
|
||
|
//! file is:
|
||
|
//!
|
||
|
//! crate::exception::arch_exception
|
||
5 years ago
|
|
||
|
use crate::{bsp, exception};
|
||
3 years ago
|
use core::{arch::global_asm, cell::UnsafeCell, fmt};
|
||
3 years ago
|
use cortex_a::{asm::barrier, registers::*};
|
||
|
use tock_registers::{
|
||
|
interfaces::{Readable, Writeable},
|
||
|
registers::InMemoryRegister,
|
||
|
};
|
||
5 years ago
|
|
||
|
// Assembly counterpart to this file.
|
||
4 years ago
|
global_asm!(include_str!("exception.s"));
|
||
5 years ago
|
|
||
|
//--------------------------------------------------------------------------------------------------
|
||
|
// Private Definitions
|
||
|
//--------------------------------------------------------------------------------------------------
|
||
|
|
||
3 years ago
|
/// Wrapper structs for memory copies of registers.
|
||
5 years ago
|
#[repr(transparent)]
|
||
4 years ago
|
struct SpsrEL1(InMemoryRegister<u64, SPSR_EL1::Register>);
|
||
3 years ago
|
struct EsrEL1(InMemoryRegister<u64, ESR_EL1::Register>);
|
||
5 years ago
|
|
||
|
/// The exception context as it is stored on the stack on exception entry.
|
||
|
#[repr(C)]
|
||
|
struct ExceptionContext {
|
||
|
/// General Purpose Registers.
|
||
|
gpr: [u64; 30],
|
||
|
|
||
|
/// The link register, aka x30.
|
||
|
lr: u64,
|
||
|
|
||
|
/// Exception link register. The program counter at the time the exception happened.
|
||
|
elr_el1: u64,
|
||
|
|
||
|
/// Saved program status.
|
||
|
spsr_el1: SpsrEL1,
|
||
|
|
||
3 years ago
|
// Exception syndrome register.
|
||
|
esr_el1: EsrEL1,
|
||
|
}
|
||
5 years ago
|
|
||
|
//--------------------------------------------------------------------------------------------------
|
||
|
// Private Code
|
||
|
//--------------------------------------------------------------------------------------------------
|
||
|
|
||
4 years ago
|
/// Prints verbose information about the exception and then panics.
|
||
3 years ago
|
fn default_exception_handler(exc: &ExceptionContext) {
|
||
5 years ago
|
panic!(
|
||
2 years ago
|
"CPU Exception!\n\n\
|
||
3 years ago
|
{}",
|
||
|
exc
|
||
5 years ago
|
);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Current, EL0
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
#[no_mangle]
|
||
4 years ago
|
unsafe extern "C" fn current_el0_synchronous(_e: &mut ExceptionContext) {
|
||
|
panic!("Should not be here. Use of SP_EL0 in EL1 is not supported.")
|
||
5 years ago
|
}
|
||
|
|
||
|
#[no_mangle]
|
||
4 years ago
|
unsafe extern "C" fn current_el0_irq(_e: &mut ExceptionContext) {
|
||
|
panic!("Should not be here. Use of SP_EL0 in EL1 is not supported.")
|
||
5 years ago
|
}
|
||
|
|
||
|
#[no_mangle]
|
||
4 years ago
|
unsafe extern "C" fn current_el0_serror(_e: &mut ExceptionContext) {
|
||
|
panic!("Should not be here. Use of SP_EL0 in EL1 is not supported.")
|
||
5 years ago
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Current, ELx
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
#[no_mangle]
|
||
|
unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) {
|
||
3 years ago
|
#[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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
5 years ago
|
default_exception_handler(e);
|
||
|
}
|
||
|
|
||
|
#[no_mangle]
|
||
|
unsafe extern "C" fn current_elx_irq(_e: &mut ExceptionContext) {
|
||
|
use exception::asynchronous::interface::IRQManager;
|
||
|
|
||
|
let token = &exception::asynchronous::IRQContext::new();
|
||
|
bsp::exception::asynchronous::irq_manager().handle_pending_irqs(token);
|
||
|
}
|
||
|
|
||
|
#[no_mangle]
|
||
|
unsafe extern "C" fn current_elx_serror(e: &mut ExceptionContext) {
|
||
|
default_exception_handler(e);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Lower, AArch64
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
#[no_mangle]
|
||
|
unsafe extern "C" fn lower_aarch64_synchronous(e: &mut ExceptionContext) {
|
||
|
default_exception_handler(e);
|
||
|
}
|
||
|
|
||
|
#[no_mangle]
|
||
|
unsafe extern "C" fn lower_aarch64_irq(e: &mut ExceptionContext) {
|
||
|
default_exception_handler(e);
|
||
|
}
|
||
|
|
||
|
#[no_mangle]
|
||
|
unsafe extern "C" fn lower_aarch64_serror(e: &mut ExceptionContext) {
|
||
|
default_exception_handler(e);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
|
// Lower, AArch32
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
#[no_mangle]
|
||
|
unsafe extern "C" fn lower_aarch32_synchronous(e: &mut ExceptionContext) {
|
||
|
default_exception_handler(e);
|
||
|
}
|
||
|
|
||
|
#[no_mangle]
|
||
|
unsafe extern "C" fn lower_aarch32_irq(e: &mut ExceptionContext) {
|
||
|
default_exception_handler(e);
|
||
|
}
|
||
|
|
||
|
#[no_mangle]
|
||
|
unsafe extern "C" fn lower_aarch32_serror(e: &mut ExceptionContext) {
|
||
|
default_exception_handler(e);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------
|
||
3 years ago
|
// Misc
|
||
5 years ago
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
/// Human readable SPSR_EL1.
|
||
|
#[rustfmt::skip]
|
||
|
impl fmt::Display for SpsrEL1 {
|
||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||
|
// Raw value.
|
||
|
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))
|
||
4 years ago
|
)
|
||
5 years ago
|
}
|
||
|
}
|
||
|
|
||
3 years ago
|
impl EsrEL1 {
|
||
|
#[inline(always)]
|
||
|
fn exception_class(&self) -> Option<ESR_EL1::EC::Value> {
|
||
|
self.0.read_as_enum(ESR_EL1::EC)
|
||
|
}
|
||
3 years ago
|
|
||
|
#[cfg(feature = "test_build")]
|
||
|
#[inline(always)]
|
||
|
fn iss(&self) -> u64 {
|
||
|
self.0.read(ESR_EL1::ISS)
|
||
|
}
|
||
3 years ago
|
}
|
||
|
|
||
|
/// Human readable ESR_EL1.
|
||
|
#[rustfmt::skip]
|
||
|
impl fmt::Display for EsrEL1 {
|
||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||
|
// Raw print of whole register.
|
||
|
writeln!(f, "ESR_EL1: {:#010x}", self.0.get())?;
|
||
|
|
||
|
// Raw print of exception class.
|
||
|
write!(f, " Exception Class (EC) : {:#x}", self.0.read(ESR_EL1::EC))?;
|
||
|
|
||
|
// Exception class.
|
||
|
let ec_translation = match self.exception_class() {
|
||
|
Some(ESR_EL1::EC::Value::DataAbortCurrentEL) => "Data Abort, current EL",
|
||
|
_ => "N/A",
|
||
|
};
|
||
|
writeln!(f, " - {}", ec_translation)?;
|
||
|
|
||
|
// Raw print of instruction specific syndrome.
|
||
|
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
|
||
|
),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
5 years ago
|
/// Human readable print of the exception context.
|
||
|
impl fmt::Display for ExceptionContext {
|
||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||
3 years ago
|
writeln!(f, "{}", self.esr_el1)?;
|
||
|
|
||
|
if self.fault_address_valid() {
|
||
|
writeln!(f, "FAR_EL1: {:#018x}", FAR_EL1.get() as usize)?;
|
||
|
}
|
||
|
|
||
5 years ago
|
writeln!(f, "{}", self.spsr_el1)?;
|
||
3 years ago
|
writeln!(f, "ELR_EL1: {:#018x}", self.elr_el1)?;
|
||
5 years ago
|
writeln!(f)?;
|
||
|
writeln!(f, "General purpose register:")?;
|
||
|
|
||
|
#[rustfmt::skip]
|
||
|
let alternating = |x| -> _ {
|
||
|
if x % 2 == 0 { " " } else { "\n" }
|
||
|
};
|
||
|
|
||
|
// Print two registers per line.
|
||
|
for (i, reg) in self.gpr.iter().enumerate() {
|
||
|
write!(f, " x{: <2}: {: >#018x}{}", i, reg, alternating(i))?;
|
||
|
}
|
||
4 years ago
|
write!(f, " lr : {:#018x}", self.lr)
|
||
5 years ago
|
}
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------
|
||
|
// Public Code
|
||
|
//--------------------------------------------------------------------------------------------------
|
||
|
use crate::exception::PrivilegeLevel;
|
||
|
|
||
|
/// The processing element's current privilege level.
|
||
|
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"),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Init exception handling by setting the exception vector base address register.
|
||
|
///
|
||
|
/// # Safety
|
||
|
///
|
||
|
/// - Changes the HW state of the executing core.
|
||
|
/// - The vector table and the symbol `__exception_vector_table_start` from the linker script must
|
||
|
/// adhere to the alignment and size constraints demanded by the ARMv8-A Architecture Reference
|
||
|
/// Manual.
|
||
|
pub unsafe fn handling_init() {
|
||
|
// Provided by exception.S.
|
||
4 years ago
|
extern "Rust" {
|
||
|
static __exception_vector_start: UnsafeCell<()>;
|
||
5 years ago
|
}
|
||
|
|
||
4 years ago
|
VBAR_EL1.set(__exception_vector_start.get() as u64);
|
||
5 years ago
|
|
||
|
// Force VBAR update to complete before next instruction.
|
||
|
barrier::isb(barrier::SY);
|
||
|
}
|