Add trap handling and test
This commit is contained in:
parent
504dcd1f65
commit
0692c324af
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
mod boot;
|
mod boot;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
|
pub mod traps;
|
||||||
|
|
||||||
/// Loop forever in sleep mode.
|
/// Loop forever in sleep mode.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: BlueOak-1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
//! 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::{arch::endless_sleep, println},
|
||||||
|
cortex_a::{
|
||||||
|
barrier,
|
||||||
|
regs::{RegisterReadOnly, RegisterReadWrite, ESR_EL1, FAR_EL1, VBAR_EL1},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
global_asm!(include_str!("vectors.S"));
|
||||||
|
|
||||||
|
/// 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<(), ()> {
|
||||||
|
if vec_base_addr.trailing_zeros() < 11 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
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.");
|
||||||
|
|
||||||
|
endless_sleep()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
endless_sleep()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
/// Helper function to 1) display current exception, 2) skip the offending asm instruction.
|
||||||
|
/// Not for production use!
|
||||||
|
fn synchronous_common(e: &mut ExceptionContext) {
|
||||||
|
println!(" ESR_EL1: {:#010x} (syndrome)", ESR_EL1.get());
|
||||||
|
println!(" EC: {:#06b} (cause)", ESR_EL1.read(ESR_EL1::EC));
|
||||||
|
println!(" FAR_EL1: {:#016x} (location)", FAR_EL1.get());
|
||||||
|
println!(" ELR_EL1: {:#010x}", e.elr_el1);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
" Incrementing ELR_EL1 by 4 now to continue with the first \
|
||||||
|
instruction after the exception!"
|
||||||
|
);
|
||||||
|
|
||||||
|
e.elr_el1 += 4;
|
||||||
|
|
||||||
|
println!(" ELR_EL1 modified: {:#010x}", e.elr_el1);
|
||||||
|
println!(" Returning from exception...\n");
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT OR BlueOak-1.0.0
|
||||||
|
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
|
||||||
|
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
|
||||||
|
* Original code distributed under MIT, additional changes are under BlueOak-1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
.macro SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE handler
|
||||||
|
.balign 0x80
|
||||||
|
|
||||||
|
sub sp, sp, #16 * 17
|
||||||
|
|
||||||
|
stp x0, x1, [sp, #16 * 0]
|
||||||
|
stp x2, x3, [sp, #16 * 1]
|
||||||
|
stp x4, x5, [sp, #16 * 2]
|
||||||
|
stp x6, x7, [sp, #16 * 3]
|
||||||
|
stp x8, x9, [sp, #16 * 4]
|
||||||
|
stp x10, x11, [sp, #16 * 5]
|
||||||
|
stp x12, x13, [sp, #16 * 6]
|
||||||
|
stp x14, x15, [sp, #16 * 7]
|
||||||
|
stp x16, x17, [sp, #16 * 8]
|
||||||
|
stp x18, x19, [sp, #16 * 9]
|
||||||
|
stp x20, x21, [sp, #16 * 10]
|
||||||
|
stp x22, x23, [sp, #16 * 11]
|
||||||
|
stp x24, x25, [sp, #16 * 12]
|
||||||
|
stp x26, x27, [sp, #16 * 13]
|
||||||
|
stp x28, x29, [sp, #16 * 14]
|
||||||
|
|
||||||
|
mrs x1, SPSR_EL1
|
||||||
|
mrs x2, ELR_EL1
|
||||||
|
|
||||||
|
stp x30, x1, [sp, #16 * 15]
|
||||||
|
str x2, [sp, #16 * 16]
|
||||||
|
|
||||||
|
mov x0, sp
|
||||||
|
bl \handler
|
||||||
|
b __restore_context
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro FIQ_DUMMY
|
||||||
|
.balign 0x80
|
||||||
|
1: wfe
|
||||||
|
b 1b
|
||||||
|
.endm
|
||||||
|
|
||||||
|
// The vector definitions
|
||||||
|
.section .vectors, "ax"
|
||||||
|
.global __exception_vectors_start
|
||||||
|
__exception_vectors_start:
|
||||||
|
SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_el0_synchronous // 0x000
|
||||||
|
SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_el0_irq // 0x080
|
||||||
|
FIQ_DUMMY // 0x100
|
||||||
|
SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_el0_serror // 0x180
|
||||||
|
|
||||||
|
SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_elx_synchronous // 0x200
|
||||||
|
SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_elx_irq // 0x280
|
||||||
|
FIQ_DUMMY // 0x300
|
||||||
|
SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE current_elx_serror // 0x380
|
||||||
|
|
||||||
|
SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch64_synchronous // 0x400
|
||||||
|
SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch64_irq // 0x480
|
||||||
|
FIQ_DUMMY // 0x500
|
||||||
|
SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch64_serror // 0x580
|
||||||
|
|
||||||
|
SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch32_synchronous // 0x600
|
||||||
|
SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch32_irq // 0x680
|
||||||
|
FIQ_DUMMY // 0x700
|
||||||
|
SAVE_CONTEXT_CALL_HANDLER_AND_RESTORE lower_aarch32_serror // 0x780
|
||||||
|
|
||||||
|
.balign 0x80
|
||||||
|
.global __restore_context
|
||||||
|
__restore_context:
|
||||||
|
ldr x19, [sp, #16 * 16]
|
||||||
|
ldp x30, x20, [sp, #16 * 15]
|
||||||
|
|
||||||
|
msr ELR_EL1, x19
|
||||||
|
msr SPSR_EL1, x20
|
||||||
|
|
||||||
|
ldp x0, x1, [sp, #16 * 0]
|
||||||
|
ldp x2, x3, [sp, #16 * 1]
|
||||||
|
ldp x4, x5, [sp, #16 * 2]
|
||||||
|
ldp x6, x7, [sp, #16 * 3]
|
||||||
|
ldp x8, x9, [sp, #16 * 4]
|
||||||
|
ldp x10, x11, [sp, #16 * 5]
|
||||||
|
ldp x12, x13, [sp, #16 * 6]
|
||||||
|
ldp x14, x15, [sp, #16 * 7]
|
||||||
|
ldp x16, x17, [sp, #16 * 8]
|
||||||
|
ldp x18, x19, [sp, #16 * 9]
|
||||||
|
ldp x20, x21, [sp, #16 * 10]
|
||||||
|
ldp x22, x23, [sp, #16 * 11]
|
||||||
|
ldp x24, x25, [sp, #16 * 12]
|
||||||
|
ldp x26, x27, [sp, #16 * 13]
|
||||||
|
ldp x28, x29, [sp, #16 * 14]
|
||||||
|
|
||||||
|
add sp, sp, #16 * 17
|
||||||
|
|
||||||
|
eret
|
|
@ -9,6 +9,7 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(asm)]
|
#![feature(asm)]
|
||||||
|
#![feature(global_asm)]
|
||||||
#![feature(ptr_internals)]
|
#![feature(ptr_internals)]
|
||||||
#![feature(format_args_nl)]
|
#![feature(format_args_nl)]
|
||||||
#![feature(custom_test_frameworks)]
|
#![feature(custom_test_frameworks)]
|
||||||
|
@ -47,10 +48,25 @@ fn init_mmu() {
|
||||||
println!("MMU initialised");
|
println!("MMU initialised");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn init_exception_traps() {
|
||||||
|
extern "C" {
|
||||||
|
static __exception_vectors_start: u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let exception_vectors_start: u64 = &__exception_vectors_start as *const _ as u64;
|
||||||
|
|
||||||
|
arch::traps::set_vbar_el1_checked(exception_vectors_start)
|
||||||
|
.expect("Vector table properly aligned!");
|
||||||
|
}
|
||||||
|
println!("Exception traps set up");
|
||||||
|
}
|
||||||
|
|
||||||
/// Kernel entry point.
|
/// Kernel entry point.
|
||||||
/// `arch` crate is responsible for calling it.
|
/// `arch` crate is responsible for calling it.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn kmain() -> ! {
|
pub fn kmain() -> ! {
|
||||||
|
init_exception_traps();
|
||||||
init_mmu();
|
init_mmu();
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -63,3 +79,21 @@ pub fn kmain() -> ! {
|
||||||
fn panicked(_info: &core::panic::PanicInfo) -> ! {
|
fn panicked(_info: &core::panic::PanicInfo) -> ! {
|
||||||
endless_sleep()
|
endless_sleep()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod main_tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn check_data_abort_trap() {
|
||||||
|
// Cause an exception by accessing a virtual address for which no
|
||||||
|
// address translations have been set up.
|
||||||
|
//
|
||||||
|
// This line of code accesses the address 3 GiB, but page tables are
|
||||||
|
// only set up for the range [0..1) GiB.
|
||||||
|
let big_addr: u64 = 3 * 1024 * 1024 * 1024;
|
||||||
|
unsafe { core::ptr::read_volatile(big_addr as *mut u64) };
|
||||||
|
|
||||||
|
println!("[i] Whoa! We recovered from an exception.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue