refactor: 📦 Prepare exception handling code
This commit is contained in:
parent
0f30bf00aa
commit
decdd0c56d
|
@ -1,34 +0,0 @@
|
|||
use {
|
||||
crate::{exception::PrivilegeLevel, info},
|
||||
aarch64_cpu::registers::*,
|
||||
core::cell::UnsafeCell,
|
||||
tock_registers::interfaces::Readable,
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// The processor'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::EL3) => (PrivilegeLevel::Unknown, "EL3"),
|
||||
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 fn handling_init() {
|
||||
extern "Rust" {
|
||||
static __EXCEPTION_VECTORS_START: UnsafeCell<()>;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
super::traps::set_vbar_el1_checked(__EXCEPTION_VECTORS_START.get() as u64)
|
||||
.expect("Vector table properly aligned!");
|
||||
}
|
||||
info!("[!] Exception traps set up");
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Architectural asynchronous exception handling.
|
||||
|
||||
use {
|
||||
aarch64_cpu::registers::*,
|
||||
core::arch::asm,
|
||||
tock_registers::interfaces::{Readable, Writeable},
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Private Definitions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
mod daif_bits {
|
||||
pub const IRQ: u8 = 0b0010;
|
||||
}
|
||||
|
||||
trait DaifField {
|
||||
fn daif_field() -> tock_registers::fields::Field<u64, DAIF::Register>;
|
||||
}
|
||||
|
||||
struct Debug;
|
||||
struct SError;
|
||||
struct IRQ;
|
||||
struct FIQ;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Private Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
impl DaifField for Debug {
|
||||
fn daif_field() -> tock_registers::fields::Field<u64, DAIF::Register> {
|
||||
DAIF::D
|
||||
}
|
||||
}
|
||||
|
||||
impl DaifField for SError {
|
||||
fn daif_field() -> tock_registers::fields::Field<u64, DAIF::Register> {
|
||||
DAIF::A
|
||||
}
|
||||
}
|
||||
|
||||
impl DaifField for IRQ {
|
||||
fn daif_field() -> tock_registers::fields::Field<u64, DAIF::Register> {
|
||||
DAIF::I
|
||||
}
|
||||
}
|
||||
|
||||
impl DaifField for FIQ {
|
||||
fn daif_field() -> tock_registers::fields::Field<u64, DAIF::Register> {
|
||||
DAIF::F
|
||||
}
|
||||
}
|
||||
|
||||
fn is_masked<T>() -> bool
|
||||
where
|
||||
T: DaifField,
|
||||
{
|
||||
DAIF.is_set(T::daif_field())
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Returns whether IRQs are masked on the executing core.
|
||||
pub fn is_local_irq_masked() -> bool {
|
||||
!is_masked::<IRQ>()
|
||||
}
|
||||
|
||||
/// Unmask IRQs on the executing core.
|
||||
///
|
||||
/// It is not needed to place an explicit instruction synchronization barrier after the `msr`.
|
||||
/// Quoting the Architecture Reference Manual for ARMv8-A, section C5.1.3:
|
||||
///
|
||||
/// "Writes to PSTATE.{PAN, D, A, I, F} occur in program order without the need for additional
|
||||
/// synchronization."
|
||||
#[inline(always)]
|
||||
pub fn local_irq_unmask() {
|
||||
unsafe {
|
||||
asm!(
|
||||
"msr DAIFClr, {arg}",
|
||||
arg = const daif_bits::IRQ,
|
||||
options(nomem, nostack, preserves_flags)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Mask IRQs on the executing core.
|
||||
#[inline(always)]
|
||||
pub fn local_irq_mask() {
|
||||
unsafe {
|
||||
asm!(
|
||||
"msr DAIFSet, {arg}",
|
||||
arg = const daif_bits::IRQ,
|
||||
options(nomem, nostack, preserves_flags)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Mask IRQs on the executing core and return the previously saved interrupt mask bits (DAIF).
|
||||
#[inline(always)]
|
||||
pub fn local_irq_mask_save() -> u64 {
|
||||
let saved = DAIF.get();
|
||||
local_irq_mask();
|
||||
|
||||
saved
|
||||
}
|
||||
|
||||
/// Restore the interrupt mask bits (DAIF) using the callee's argument.
|
||||
///
|
||||
/// # Invariant
|
||||
///
|
||||
/// - No sanity checks on the input.
|
||||
#[inline(always)]
|
||||
pub fn local_irq_restore(saved: u64) {
|
||||
DAIF.set(saved);
|
||||
}
|
||||
|
||||
/// Print the AArch64 exceptions status.
|
||||
#[rustfmt::skip]
|
||||
pub fn print_state() {
|
||||
use crate::info;
|
||||
|
||||
let to_mask_str = |x| -> _ {
|
||||
if x { "Masked" } else { "Unmasked" }
|
||||
};
|
||||
|
||||
info!(" Debug: {}", to_mask_str(is_masked::<Debug>()));
|
||||
info!(" SError: {}", to_mask_str(is_masked::<SError>()));
|
||||
info!(" IRQ: {}", to_mask_str(is_masked::<IRQ>()));
|
||||
info!(" FIQ: {}", to_mask_str(is_masked::<FIQ>()));
|
||||
}
|
|
@ -0,0 +1,394 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||
*/
|
||||
|
||||
//! Interrupt handling
|
||||
//!
|
||||
//! The base address is given by VBAR_ELn and each entry has a defined offset from this
|
||||
//! base address. Each table has 16 entries, with each entry being 128 bytes (32 instructions)
|
||||
//! in size. The table effectively consists of 4 sets of 4 entries.
|
||||
//!
|
||||
//! Minimal implementation to help catch MMU traps.
|
||||
//! Reads ESR_ELx to understand why trap was taken.
|
||||
//!
|
||||
//! VBAR_EL1, VBAR_EL2, VBAR_EL3
|
||||
//!
|
||||
//! CurrentEL with SP0: +0x0
|
||||
//!
|
||||
//! * Synchronous
|
||||
//! * IRQ/vIRQ
|
||||
//! * FIQ
|
||||
//! * SError/vSError
|
||||
//!
|
||||
//! CurrentEL with SPx: +0x200
|
||||
//!
|
||||
//! * Synchronous
|
||||
//! * IRQ/vIRQ
|
||||
//! * FIQ
|
||||
//! * SError/vSError
|
||||
//!
|
||||
//! Lower EL using AArch64: +0x400
|
||||
//!
|
||||
//! * Synchronous
|
||||
//! * IRQ/vIRQ
|
||||
//! * FIQ
|
||||
//! * SError/vSError
|
||||
//!
|
||||
//! Lower EL using AArch32: +0x600
|
||||
//!
|
||||
//! * Synchronous
|
||||
//! * IRQ/vIRQ
|
||||
//! * FIQ
|
||||
//! * SError/vSError
|
||||
//!
|
||||
//! When the processor takes an exception to AArch64 execution state,
|
||||
//! all of the PSTATE interrupt masks is set automatically. This means
|
||||
//! that further exceptions are disabled. If software is to support
|
||||
//! nested exceptions, for example, to allow a higher priority interrupt
|
||||
//! to interrupt the handling of a lower priority source, then software needs
|
||||
//! to explicitly re-enable interrupts
|
||||
|
||||
use {
|
||||
crate::{
|
||||
exception::{self, PrivilegeLevel},
|
||||
info,
|
||||
},
|
||||
aarch64_cpu::{asm::barrier, registers::*},
|
||||
core::{cell::UnsafeCell, fmt},
|
||||
snafu::Snafu,
|
||||
tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
registers::InMemoryRegister,
|
||||
},
|
||||
};
|
||||
|
||||
pub mod asynchronous;
|
||||
|
||||
core::arch::global_asm!(include_str!("vectors.S"));
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Private Definitions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Wrapper structs for memory copies of registers.
|
||||
#[repr(transparent)]
|
||||
struct SpsrEL1(InMemoryRegister<u64, SPSR_EL1::Register>);
|
||||
struct EsrEL1(InMemoryRegister<u64, ESR_EL1::Register>);
|
||||
|
||||
/// The exception context as it is stored on the stack on exception entry.
|
||||
#[repr(C)]
|
||||
struct ExceptionContext {
|
||||
/// General Purpose Registers, x0-x29
|
||||
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,
|
||||
|
||||
/// Exception syndrome register.
|
||||
esr_el1: EsrEL1,
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Private Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// The default exception, invoked for every exception type unless the handler
|
||||
/// is overridden.
|
||||
/// Prints verbose information about the exception and then panics.
|
||||
///
|
||||
/// Default pointer is configured in the linker script.
|
||||
fn default_exception_handler(exc: &ExceptionContext) {
|
||||
panic!(
|
||||
"Unexpected CPU Exception!\n\n\
|
||||
{}",
|
||||
exc
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Current, EL0
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#[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.")
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Current, ELx
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Lower, AArch64
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Lower, AArch32
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Misc
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// 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))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Human readable print of the exception context.
|
||||
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:")?;
|
||||
|
||||
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))?;
|
||||
}
|
||||
write!(f, " lr : {:#018x}", self.lr)
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// The processor'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::EL3) => (PrivilegeLevel::Unknown, "EL3"),
|
||||
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_VECTORS_START` from the linker script must
|
||||
/// adhere to the alignment and size constraints demanded by the ARMv8-A Architecture Reference
|
||||
/// Manual.
|
||||
pub fn handling_init() {
|
||||
// Provided by vectors.S.
|
||||
extern "Rust" {
|
||||
static __EXCEPTION_VECTORS_START: UnsafeCell<()>;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
set_vbar_el1_checked(__EXCEPTION_VECTORS_START.get() as u64)
|
||||
.expect("Vector table properly aligned!");
|
||||
}
|
||||
info!("[!] Exception traps set up");
|
||||
}
|
||||
|
||||
/// Errors possibly returned from the traps module.
|
||||
/// @todo a big over-engineered here.
|
||||
#[derive(Debug, Snafu)]
|
||||
enum Error {
|
||||
/// IVT address is unaligned.
|
||||
#[snafu(display("Unaligned base address for interrupt vector table"))]
|
||||
Unaligned,
|
||||
}
|
||||
|
||||
/// Configure base address of interrupt vectors table.
|
||||
/// Checks that address is properly 2KiB aligned.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Totally unsafe in the land of the hardware.
|
||||
unsafe fn set_vbar_el1_checked(vec_base_addr: u64) -> Result<(), Error> {
|
||||
if vec_base_addr.trailing_zeros() < 11 {
|
||||
return Err(Error::Unaligned);
|
||||
}
|
||||
|
||||
VBAR_EL1.set(vec_base_addr);
|
||||
|
||||
// Force VBAR update to complete before next instruction.
|
||||
barrier::isb(barrier::SY);
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -9,4 +9,3 @@ pub mod cpu;
|
|||
pub mod exception;
|
||||
pub mod memory;
|
||||
pub mod time;
|
||||
pub mod traps;
|
||||
|
|
|
@ -1,166 +1,5 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||
*/
|
||||
|
||||
//! Interrupt handling
|
||||
//!
|
||||
//! The base address is given by VBAR_ELn and each entry has a defined offset from this
|
||||
//! base address. Each table has 16 entries, with each entry being 128 bytes (32 instructions)
|
||||
//! in size. The table effectively consists of 4 sets of 4 entries.
|
||||
//!
|
||||
//! Minimal implementation to help catch MMU traps.
|
||||
//! Reads ESR_ELx to understand why trap was taken.
|
||||
//!
|
||||
//! VBAR_EL1, VBAR_EL2, VBAR_EL3
|
||||
//!
|
||||
//! CurrentEL with SP0: +0x0
|
||||
//!
|
||||
//! * Synchronous
|
||||
//! * IRQ/vIRQ
|
||||
//! * FIQ
|
||||
//! * SError/vSError
|
||||
//!
|
||||
//! CurrentEL with SPx: +0x200
|
||||
//!
|
||||
//! * Synchronous
|
||||
//! * IRQ/vIRQ
|
||||
//! * FIQ
|
||||
//! * SError/vSError
|
||||
//!
|
||||
//! Lower EL using AArch64: +0x400
|
||||
//!
|
||||
//! * Synchronous
|
||||
//! * IRQ/vIRQ
|
||||
//! * FIQ
|
||||
//! * SError/vSError
|
||||
//!
|
||||
//! Lower EL using AArch32: +0x600
|
||||
//!
|
||||
//! * Synchronous
|
||||
//! * IRQ/vIRQ
|
||||
//! * FIQ
|
||||
//! * SError/vSError
|
||||
//!
|
||||
//! When the processor takes an exception to AArch64 execution state,
|
||||
//! all of the PSTATE interrupt masks is set automatically. This means
|
||||
//! that further exceptions are disabled. If software is to support
|
||||
//! nested exceptions, for example, to allow a higher priority interrupt
|
||||
//! to interrupt the handling of a lower priority source, then software needs
|
||||
//! to explicitly re-enable interrupts
|
||||
|
||||
use {
|
||||
crate::{cpu::endless_sleep, println},
|
||||
aarch64_cpu::{
|
||||
asm::barrier,
|
||||
registers::{ESR_EL1, FAR_EL1, SPSR_EL1, VBAR_EL1},
|
||||
},
|
||||
snafu::Snafu,
|
||||
tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields, LocalRegisterCopy,
|
||||
},
|
||||
};
|
||||
|
||||
core::arch::global_asm!(include_str!("vectors.S"));
|
||||
|
||||
/// Errors possibly returned from the traps module.
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum Error {
|
||||
/// IVT address is unaligned.
|
||||
#[snafu(display("Unaligned base address for interrupt vector table"))]
|
||||
Unaligned,
|
||||
}
|
||||
|
||||
/// Configure base address of interrupt vectors table.
|
||||
/// Checks that address is properly 2KiB aligned.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Totally unsafe in the land of the hardware.
|
||||
pub unsafe fn set_vbar_el1_checked(vec_base_addr: u64) -> Result<(), Error> {
|
||||
if vec_base_addr.trailing_zeros() < 11 {
|
||||
return Err(Error::Unaligned);
|
||||
}
|
||||
|
||||
VBAR_EL1.set(vec_base_addr);
|
||||
|
||||
// Force VBAR update to complete before next instruction.
|
||||
barrier::isb(barrier::SY);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A blob of general-purpose registers.
|
||||
#[repr(C)]
|
||||
pub struct GPR {
|
||||
x: [u64; 31],
|
||||
}
|
||||
|
||||
/// Saved exception context.
|
||||
#[repr(C)]
|
||||
pub struct ExceptionContext {
|
||||
// General Purpose Registers
|
||||
gpr: GPR,
|
||||
spsr_el1: u64,
|
||||
elr_el1: u64,
|
||||
}
|
||||
|
||||
/// The default exception, invoked for every exception type unless the handler
|
||||
/// is overridden.
|
||||
/// Default pointer is configured in the linker script.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Totally unsafe in the land of the hardware.
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn default_exception_handler() -> ! {
|
||||
println!("Unexpected exception. Halting CPU.");
|
||||
|
||||
#[cfg(not(qemu))]
|
||||
super::cpu::endless_sleep();
|
||||
#[cfg(qemu)]
|
||||
qemu::semihosting::exit_failure()
|
||||
}
|
||||
|
||||
// To implement an exception handler, override it by defining the respective
|
||||
// function below.
|
||||
// Don't forget the #[no_mangle] attribute.
|
||||
//
|
||||
/// # Safety
|
||||
///
|
||||
/// Totally unsafe in the land of the hardware.
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn current_el0_synchronous(e: &mut ExceptionContext) {
|
||||
println!("[!] USER synchronous exception happened.");
|
||||
synchronous_common(e)
|
||||
}
|
||||
// unsafe extern "C" fn current_el0_irq(e: &mut ExceptionContext);
|
||||
// unsafe extern "C" fn current_el0_serror(e: &mut ExceptionContext);
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Totally unsafe in the land of the hardware.
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) {
|
||||
println!("[!] KERNEL synchronous exception happened.");
|
||||
synchronous_common(e)
|
||||
}
|
||||
|
||||
// unsafe extern "C" fn current_elx_irq(e: &mut ExceptionContext);
|
||||
/// # Safety
|
||||
///
|
||||
/// Totally unsafe in the land of the hardware.
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn current_elx_serror(e: &mut ExceptionContext) {
|
||||
println!("[!] KERNEL serror exception happened.");
|
||||
synchronous_common(e);
|
||||
|
||||
#[cfg(not(qemu))]
|
||||
endless_sleep();
|
||||
#[cfg(qemu)]
|
||||
qemu::semihosting::exit_failure()
|
||||
}
|
||||
// @todo this file must be moved to exception/mod.rs
|
||||
// @todo finish porting the exception printing part...
|
||||
|
||||
fn cause_to_string(cause: u64) -> &'static str {
|
||||
if cause == ESR_EL1::EC::DataAbortCurrentEL.read(ESR_EL1::EC) {
|
||||
|
@ -308,14 +147,6 @@ fn iss_dfsc_to_string(iss: IssForDataAbort) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
// unsafe extern "C" fn lower_aarch64_synchronous(e: &mut ExceptionContext);
|
||||
// unsafe extern "C" fn lower_aarch64_irq(e: &mut ExceptionContext);
|
||||
// unsafe extern "C" fn lower_aarch64_serror(e: &mut ExceptionContext);
|
||||
|
||||
// unsafe extern "C" fn lower_aarch32_synchronous(e: &mut ExceptionContext);
|
||||
// unsafe extern "C" fn lower_aarch32_irq(e: &mut ExceptionContext);
|
||||
// unsafe extern "C" fn lower_aarch32_serror(e: &mut ExceptionContext);
|
||||
|
||||
type SpsrCopy = LocalRegisterCopy<u64, SPSR_EL1::Register>;
|
||||
|
||||
/// Helper function to 1) display current exception, 2) skip the offending asm instruction.
|
||||
|
@ -379,17 +210,7 @@ fn synchronous_common(e: &mut ExceptionContext) {
|
|||
}
|
||||
println!(" ELR_EL1: {:#010x} (return to)", e.elr_el1);
|
||||
|
||||
println!(" x00: 0000000000000000 x01: {:016x}", e.gpr.x[0]);
|
||||
|
||||
for index in 0..15 {
|
||||
println!(
|
||||
" x{:02}: {:016x} x{:02}: {:016x}",
|
||||
index * 2 + 2,
|
||||
e.gpr.x[index * 2 + 1],
|
||||
index * 2 + 3,
|
||||
e.gpr.x[index * 2 + 2]
|
||||
);
|
||||
}
|
||||
// GPRs
|
||||
|
||||
println!(
|
||||
" Incrementing ELR_EL1 by 4 to continue with the first \
|
||||
|
|
|
@ -1,5 +1,16 @@
|
|||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::arch::aarch64::exception::asynchronous as arch_asynchronous;
|
||||
|
||||
mod null_irq_manager;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Architectural Public Reexports
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
pub use arch_asynchronous::{
|
||||
is_local_irq_masked, local_irq_mask, local_irq_mask_save, local_irq_restore, local_irq_unmask,
|
||||
print_state,
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Definitions
|
||||
//--------------------------------------------------------------------------------------------------
|
|
@ -1,6 +1,7 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
#![allow(stable_features)]
|
||||
#![feature(asm_const)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(ptr_internals)]
|
||||
#![feature(allocator_api)]
|
||||
|
|
|
@ -32,14 +32,14 @@
|
|||
#[cfg(not(test))]
|
||||
use core::panic::PanicInfo;
|
||||
#[allow(unused_imports)]
|
||||
use machine::devices::SerialOps;
|
||||
use machine::devices::serial::SerialOps;
|
||||
use {
|
||||
cfg_if::cfg_if,
|
||||
core::{cell::UnsafeCell, time::Duration},
|
||||
machine::{
|
||||
arch,
|
||||
console::console,
|
||||
entry, info, memory,
|
||||
entry, exception, info, memory,
|
||||
platform::raspberrypi::{
|
||||
display::{Color, DrawError},
|
||||
mailbox::{channel, Mailbox, MailboxOps},
|
||||
|
@ -70,6 +70,8 @@ pub unsafe fn kernel_init() -> ! {
|
|||
// init_mmu(); // @todo
|
||||
exception::handling_init();
|
||||
|
||||
use machine::memory::mmu::interface::MMU;
|
||||
|
||||
if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() {
|
||||
panic!("MMU: {}", string);
|
||||
}
|
||||
|
@ -82,10 +84,10 @@ pub unsafe fn kernel_init() -> ! {
|
|||
machine::drivers::driver_manager().init_drivers_and_irqs();
|
||||
|
||||
// Unmask interrupts on the boot CPU core.
|
||||
exception::asynchronous::local_irq_unmask();
|
||||
machine::exception::asynchronous::local_irq_unmask();
|
||||
|
||||
// Announce conclusion of the kernel_init() phase.
|
||||
state::state_manager().transition_to_single_core_main();
|
||||
machine::state::state_manager().transition_to_single_core_main();
|
||||
|
||||
// Transition from unsafe to safe.
|
||||
kernel_main()
|
||||
|
@ -108,7 +110,7 @@ pub fn kernel_main() -> ! {
|
|||
info!("Booting on: {}", machine::platform::BcmHost::board_name());
|
||||
|
||||
info!("MMU online. Special regions:");
|
||||
bsp::memory::mmu::virt_mem_layout().print_layout();
|
||||
machine::platform::memory::mmu::virt_mem_layout().print_layout();
|
||||
|
||||
let (_, privilege_level) = exception::current_privilege_level();
|
||||
info!("Current privilege level: {}", privilege_level);
|
||||
|
@ -150,17 +152,6 @@ fn print_mmu_state_and_features() {
|
|||
memory::mmu::mmu().print_features();
|
||||
}
|
||||
|
||||
fn init_mmu() {
|
||||
unsafe {
|
||||
use machine::memory::mmu::interface::MMU;
|
||||
if let Err(e) = memory::mmu::mmu().enable_mmu_and_caching() {
|
||||
panic!("MMU: {}", e);
|
||||
}
|
||||
}
|
||||
info!("[!] MMU initialised");
|
||||
print_mmu_state_and_features();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
// Start a command prompt
|
||||
//------------------------------------------------------------
|
||||
|
@ -169,7 +160,7 @@ fn command_prompt() {
|
|||
let mut buf = [0u8; 64];
|
||||
|
||||
match machine::console::command_prompt(&mut buf) {
|
||||
b"mmu" => init_mmu(),
|
||||
// b"mmu" => init_mmu(),
|
||||
b"feats" => print_mmu_state_and_features(),
|
||||
b"disp" => check_display_init(),
|
||||
b"trap" => check_data_abort_trap(),
|
||||
|
|
Loading…
Reference in New Issue