Add trap handling and test
This commit is contained in:
parent
504dcd1f65
commit
0692c324af
|
@ -6,6 +6,7 @@
|
|||
|
||||
mod boot;
|
||||
pub mod memory;
|
||||
pub mod traps;
|
||||
|
||||
/// Loop forever in sleep mode.
|
||||
#[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_main]
|
||||
#![feature(asm)]
|
||||
#![feature(global_asm)]
|
||||
#![feature(ptr_internals)]
|
||||
#![feature(format_args_nl)]
|
||||
#![feature(custom_test_frameworks)]
|
||||
|
@ -47,10 +48,25 @@ fn init_mmu() {
|
|||
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.
|
||||
/// `arch` crate is responsible for calling it.
|
||||
#[inline]
|
||||
pub fn kmain() -> ! {
|
||||
init_exception_traps();
|
||||
init_mmu();
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -63,3 +79,21 @@ pub fn kmain() -> ! {
|
|||
fn panicked(_info: &core::panic::PanicInfo) -> ! {
|
||||
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