2023-07-18 18:03:45 +03:00
|
|
|
//! Exception and interrupt management functions
|
|
|
|
use core::{arch::global_asm, fmt};
|
|
|
|
|
2023-08-14 14:31:44 +03:00
|
|
|
use aarch64_cpu::{
|
|
|
|
asm::barrier,
|
|
|
|
registers::{
|
|
|
|
ELR_EL1, ESR_EL1, FAR_EL1, SCTLR_EL1, TCR_EL1, TPIDR_EL0, TPIDR_EL1, TTBR0_EL1, TTBR1_EL1,
|
|
|
|
VBAR_EL1,
|
|
|
|
},
|
|
|
|
};
|
2023-07-27 16:24:52 +03:00
|
|
|
use abi::{
|
2023-08-02 20:43:21 +03:00
|
|
|
arch::SavedFrame,
|
|
|
|
process::{Signal, SignalEntryData},
|
2023-07-27 16:24:52 +03:00
|
|
|
syscall::SyscallFunction,
|
|
|
|
};
|
2023-07-18 18:03:45 +03:00
|
|
|
use tock_registers::interfaces::{Readable, Writeable};
|
|
|
|
|
|
|
|
use crate::{
|
2023-12-05 14:55:12 +02:00
|
|
|
arch::{Architecture, ArchitectureImpl},
|
2023-07-18 18:03:45 +03:00
|
|
|
debug::LogLevel,
|
|
|
|
syscall::raw_syscall_handler,
|
2023-11-21 14:16:18 +02:00
|
|
|
task::{context::TaskFrame, thread::Thread},
|
2023-07-18 18:03:45 +03:00
|
|
|
};
|
|
|
|
|
2023-08-13 21:23:58 +03:00
|
|
|
use super::ARCHITECTURE;
|
|
|
|
|
2023-07-18 18:03:45 +03:00
|
|
|
/// Struct for register values saved when taking an exception
|
|
|
|
#[repr(C)]
|
|
|
|
pub struct ExceptionFrame {
|
2023-07-27 16:24:52 +03:00
|
|
|
/// General-purpose registers
|
|
|
|
pub r: [u64; 32],
|
|
|
|
/// SPSR_EL1, userspace flags register
|
|
|
|
pub spsr_el1: u64,
|
|
|
|
/// ELR_EL1, userspace program counter
|
|
|
|
pub elr_el1: u64,
|
|
|
|
/// SP_EL0, userspace stack pointer
|
|
|
|
pub sp_el0: u64,
|
|
|
|
_x: u64,
|
2023-07-18 18:03:45 +03:00
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
2023-08-02 20:43:21 +03:00
|
|
|
impl TaskFrame for ExceptionFrame {
|
|
|
|
fn store(&self) -> SavedFrame {
|
2023-07-27 16:24:52 +03:00
|
|
|
SavedFrame {
|
|
|
|
gp_regs: self.r,
|
|
|
|
spsr_el1: self.spsr_el1,
|
|
|
|
elr_el1: self.elr_el1,
|
|
|
|
sp_el0: self.sp_el0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-02 20:43:21 +03:00
|
|
|
fn restore(&mut self, saved: &SavedFrame) {
|
|
|
|
self.r = saved.gp_regs;
|
|
|
|
self.spsr_el1 = saved.spsr_el1;
|
|
|
|
self.elr_el1 = saved.elr_el1;
|
|
|
|
self.sp_el0 = saved.sp_el0;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn argument(&self) -> u64 {
|
|
|
|
self.r[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn user_ip(&self) -> usize {
|
|
|
|
self.elr_el1 as _
|
|
|
|
}
|
|
|
|
|
|
|
|
fn user_sp(&self) -> usize {
|
|
|
|
self.sp_el0 as _
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_argument(&mut self, value: u64) {
|
|
|
|
self.r[0] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_return_value(&mut self, value: u64) {
|
|
|
|
self.r[0] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_user_ip(&mut self, value: usize) {
|
|
|
|
self.elr_el1 = value as _;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_user_sp(&mut self, value: usize) {
|
|
|
|
self.sp_el0 = value as _;
|
2023-07-27 16:24:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-18 18:03:45 +03:00
|
|
|
impl fmt::Debug for ExceptionFrame {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
for i in (0..32).step_by(2) {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"x{:<2} = {:#020x}\tx{:<2} = {:#020x}",
|
|
|
|
i,
|
|
|
|
self.r[i],
|
|
|
|
i + 1,
|
|
|
|
self.r[i + 1]
|
|
|
|
)?;
|
|
|
|
if i != 30 {
|
|
|
|
f.write_str("\n")?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Initializes the exception/interrupt vectors. May be called repeatedly (though that makes no
|
|
|
|
/// sense).
|
|
|
|
pub fn init_exceptions() {
|
|
|
|
extern "C" {
|
|
|
|
static __aarch64_el1_vectors: u8;
|
|
|
|
}
|
|
|
|
let vbar = unsafe { &__aarch64_el1_vectors as *const _ };
|
|
|
|
VBAR_EL1.set(vbar as u64);
|
2023-08-14 14:31:44 +03:00
|
|
|
barrier::isb(barrier::SY);
|
2023-07-18 18:03:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn dump_irrecoverable_exception(frame: &ExceptionFrame, ec: u64, iss: u64) {
|
2023-12-05 14:55:12 +02:00
|
|
|
// let cpu = Cpu::get_local();
|
2023-07-18 18:03:45 +03:00
|
|
|
|
|
|
|
log_print_raw!(LogLevel::Fatal, "SYNC exception:\n");
|
|
|
|
log_print_raw!(LogLevel::Fatal, "FAR: {:#x}\n", FAR_EL1.get());
|
|
|
|
log_print_raw!(LogLevel::Fatal, "ELR: {:#x}\n", ELR_EL1.get());
|
|
|
|
log_print_raw!(LogLevel::Fatal, "ESR: {:#x}\n", ESR_EL1.get());
|
|
|
|
log_print_raw!(LogLevel::Fatal, "TTBR0_EL1: {:#x}\n", TTBR0_EL1.get());
|
|
|
|
log_print_raw!(LogLevel::Fatal, "TTBR1_EL1: {:#x}\n", TTBR1_EL1.get());
|
|
|
|
log_print_raw!(LogLevel::Fatal, "Register dump:\n");
|
|
|
|
log_print_raw!(LogLevel::Fatal, "{:?}\n", frame);
|
|
|
|
|
2023-11-21 14:16:18 +02:00
|
|
|
// XXX
|
|
|
|
// if let Some(cpu) = cpu {
|
|
|
|
// let current = cpu.queue().current_process();
|
2023-07-18 18:03:45 +03:00
|
|
|
|
2023-11-21 14:16:18 +02:00
|
|
|
// if let Some(current) = current {
|
|
|
|
// log_print_raw!(LogLevel::Fatal, "In process {}\n", current.id());
|
|
|
|
// }
|
|
|
|
// }
|
2023-07-18 18:03:45 +03:00
|
|
|
|
|
|
|
match ec {
|
|
|
|
// Data abort from lower level
|
|
|
|
0b100100 => {
|
|
|
|
log_print_raw!(LogLevel::Fatal, "Exception kind: Data Abort from EL0\n");
|
|
|
|
let dfsc = iss & 0x3F;
|
|
|
|
|
|
|
|
if iss & (1 << 24) != 0 {
|
|
|
|
let access_size_str = match (iss >> 22) & 0x3 {
|
|
|
|
0 => "i8",
|
|
|
|
1 => "i16",
|
|
|
|
2 => "i32",
|
|
|
|
3 => "i64",
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
let access_type_str = if iss & (1 << 6) != 0 { "write" } else { "read" };
|
|
|
|
|
|
|
|
log_print_raw!(
|
|
|
|
LogLevel::Fatal,
|
|
|
|
"Invalid {} of a {} to/from {:#x}\n",
|
|
|
|
access_type_str,
|
|
|
|
access_size_str,
|
|
|
|
FAR_EL1.get()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
log_print_raw!(LogLevel::Fatal, "DFSC = {:#x}\n", dfsc);
|
|
|
|
}
|
|
|
|
// Instruction abort from lower level
|
|
|
|
0b100000 => {
|
|
|
|
log_print_raw!(
|
|
|
|
LogLevel::Fatal,
|
|
|
|
"Exception kind: Instruction Abort from EL0\n"
|
|
|
|
);
|
|
|
|
let ifsc = iss & 0x3F;
|
|
|
|
log_print_raw!(LogLevel::Fatal, "IFSC = {:#x}\n", ifsc);
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => (),
|
|
|
|
}
|
2023-08-14 14:31:44 +03:00
|
|
|
|
2023-11-16 00:16:38 +02:00
|
|
|
// unsafe {
|
|
|
|
// let space = AddressSpace::from_phys_raw(TTBR0_EL1.get_baddr() as _);
|
|
|
|
// let far = FAR_EL1.get() as usize;
|
|
|
|
// space.walk(far, |level, raw| {
|
|
|
|
// log_print_raw!(LogLevel::Fatal, "Level {}: entry={:#x}\n", level, raw);
|
|
|
|
// true
|
|
|
|
// });
|
|
|
|
// }
|
2023-08-14 14:31:44 +03:00
|
|
|
|
|
|
|
log_print_raw!(LogLevel::Fatal, "System register dump:\n");
|
|
|
|
log_print_raw!(LogLevel::Fatal, "SCTLR_EL1 = {:#x}\n", SCTLR_EL1.get());
|
|
|
|
log_print_raw!(LogLevel::Fatal, "TCR_EL1 = {:#x}\n", TCR_EL1.get());
|
|
|
|
log_print_raw!(LogLevel::Fatal, "TPIDR_EL1 = {:#x}\n", TPIDR_EL1.get());
|
|
|
|
log_print_raw!(LogLevel::Fatal, "TPIDR_EL0 = {:#x}\n", TPIDR_EL0.get());
|
2023-07-18 18:03:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
2023-07-27 16:24:52 +03:00
|
|
|
extern "C" fn __aa64_el0_sync_handler(frame: *mut ExceptionFrame) {
|
2023-08-15 13:32:48 +03:00
|
|
|
assert!(ArchitectureImpl::interrupt_mask());
|
2023-07-27 16:24:52 +03:00
|
|
|
let frame = unsafe { &mut *frame };
|
2023-08-15 13:32:48 +03:00
|
|
|
|
|
|
|
// let current_id = Process::get_current().map(|p| p.id());
|
|
|
|
// log_print_raw!(
|
|
|
|
// LogLevel::Fatal,
|
|
|
|
// "sync_entry {:?}: x30 = {:#x} @ {:p}\n",
|
|
|
|
// current_id,
|
|
|
|
// frame.r[30],
|
|
|
|
// &frame.r[30]
|
|
|
|
// );
|
|
|
|
|
2023-07-27 16:24:52 +03:00
|
|
|
el0_sync_inner(frame);
|
2023-08-15 13:32:48 +03:00
|
|
|
|
|
|
|
// let current_id = Process::get_current().map(|p| p.id());
|
|
|
|
// log_print_raw!(
|
|
|
|
// LogLevel::Fatal,
|
|
|
|
// "sync_exit {:?}: x30 = {:#x} @ {:p}\n",
|
|
|
|
// current_id,
|
|
|
|
// frame.r[30],
|
|
|
|
// &frame.r[30]
|
|
|
|
// );
|
|
|
|
|
2023-07-27 16:24:52 +03:00
|
|
|
unsafe {
|
2023-11-21 14:16:18 +02:00
|
|
|
let thread = Thread::current();
|
|
|
|
thread.handle_pending_signals(frame);
|
2023-07-27 16:24:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn __aa64_el0_irq_handler(frame: *mut ExceptionFrame) {
|
2023-08-15 13:32:48 +03:00
|
|
|
assert!(ArchitectureImpl::interrupt_mask());
|
2023-07-18 18:03:45 +03:00
|
|
|
let frame = unsafe { &mut *frame };
|
2023-08-15 13:32:48 +03:00
|
|
|
|
|
|
|
// let current_id = Process::get_current().map(|p| p.id());
|
|
|
|
// log_print_raw!(LogLevel::Fatal, "+++ irq_handler ENTRY +++\n");
|
|
|
|
// log_print_raw!(
|
|
|
|
// LogLevel::Fatal,
|
|
|
|
// "{:?}: x30 = {:#x} @ {:p}\n",
|
|
|
|
// current_id,
|
|
|
|
// frame.r[30],
|
|
|
|
// &frame.r[30]
|
|
|
|
// );
|
|
|
|
// log_print_raw!(LogLevel::Fatal, "sp = {:#x}\n", frame.sp_el0);
|
|
|
|
|
2023-07-27 16:24:52 +03:00
|
|
|
irq_common();
|
2023-08-15 13:32:48 +03:00
|
|
|
|
|
|
|
// let current_id = Process::get_current().map(|p| p.id());
|
|
|
|
// log_print_raw!(LogLevel::Fatal, "+++ irq_handler EXIT +++\n");
|
|
|
|
// log_print_raw!(
|
|
|
|
// LogLevel::Fatal,
|
|
|
|
// "{:?}: x30 = {:#x} @ {:p}\n",
|
|
|
|
// current_id,
|
|
|
|
// frame.r[30],
|
|
|
|
// &frame.r[30]
|
|
|
|
// );
|
|
|
|
// log_print_raw!(LogLevel::Fatal, "sp = {:#x}\n", frame.sp_el0);
|
|
|
|
|
2023-07-27 16:24:52 +03:00
|
|
|
unsafe {
|
2023-11-21 14:16:18 +02:00
|
|
|
let thread = Thread::current();
|
|
|
|
thread.handle_pending_signals(frame);
|
2023-07-27 16:24:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn __aa64_el0_fiq_handler() {
|
|
|
|
todo!();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn __aa64_el0_serror_handler() {
|
|
|
|
todo!();
|
|
|
|
}
|
2023-07-18 18:03:45 +03:00
|
|
|
|
2023-07-27 16:24:52 +03:00
|
|
|
// EL1
|
|
|
|
#[no_mangle]
|
2023-08-13 21:23:58 +03:00
|
|
|
extern "C" fn __aa64_el1_sync_handler(frame: *mut ExceptionFrame) {
|
|
|
|
let frame = unsafe { &*frame };
|
|
|
|
let esr_el1 = ESR_EL1.get();
|
|
|
|
let ec = (esr_el1 >> 26) & 0x3F;
|
|
|
|
let iss = esr_el1 & 0x1FFFFFF;
|
|
|
|
|
|
|
|
unsafe {
|
2023-11-30 11:00:51 +02:00
|
|
|
kernel_util::sync::hack_locks();
|
2023-08-13 21:23:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
dump_irrecoverable_exception(frame, ec, iss);
|
|
|
|
|
|
|
|
panic!("Irrecoverable exception in kernel mode");
|
2023-07-27 16:24:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn __aa64_el1_irq_handler(_frame: *mut ExceptionFrame) {
|
|
|
|
irq_common();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn __aa64_el1_fiq_handler() {
|
|
|
|
todo!();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
extern "C" fn __aa64_el1_serror_handler() {
|
|
|
|
todo!();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn el0_sync_inner(frame: &mut ExceptionFrame) {
|
2023-07-18 18:03:45 +03:00
|
|
|
let esr_el1 = ESR_EL1.get();
|
|
|
|
let ec = (esr_el1 >> 26) & 0x3F;
|
|
|
|
|
|
|
|
match ec {
|
|
|
|
// SVC in AArch64
|
|
|
|
0b010101 => {
|
|
|
|
let func = frame.r[8];
|
2023-07-27 16:24:52 +03:00
|
|
|
if func == usize::from(SyscallFunction::ExitSignal) as u64 {
|
|
|
|
unsafe {
|
|
|
|
handle_signal_exit(frame);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-18 18:03:45 +03:00
|
|
|
let args = &frame.r[0..6];
|
|
|
|
let result = raw_syscall_handler(func, args);
|
|
|
|
frame.r[0] = result;
|
|
|
|
}
|
|
|
|
// BRK in AArch64
|
|
|
|
0b111100 => {
|
2023-11-21 14:16:18 +02:00
|
|
|
let thread = Thread::current();
|
2024-01-04 23:04:34 +02:00
|
|
|
warnln!("Thread {} {:?} hit a breakpoint", thread.id, thread.name);
|
2023-11-21 14:16:18 +02:00
|
|
|
thread.raise_signal(Signal::Aborted);
|
2023-07-18 18:03:45 +03:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
let iss = esr_el1 & 0x1FFFFFF;
|
2023-07-25 16:47:00 +03:00
|
|
|
if ec == 0b100100 {
|
|
|
|
// Data abort from lower level
|
2023-11-21 14:16:18 +02:00
|
|
|
let thread = Thread::current();
|
2023-07-25 16:47:00 +03:00
|
|
|
warnln!(
|
2023-11-21 14:16:18 +02:00
|
|
|
"Data abort in {} {:?} at {:#x} with address {:#x}",
|
2024-01-04 23:04:34 +02:00
|
|
|
thread.id,
|
|
|
|
thread.name,
|
2023-07-25 16:47:00 +03:00
|
|
|
ELR_EL1.get(),
|
|
|
|
FAR_EL1.get()
|
|
|
|
);
|
|
|
|
|
2023-11-21 14:16:18 +02:00
|
|
|
thread.raise_signal(Signal::MemoryAccessViolation);
|
2023-07-25 16:47:00 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-18 18:03:45 +03:00
|
|
|
dump_irrecoverable_exception(frame, ec, iss);
|
|
|
|
|
|
|
|
panic!("Irrecoverable exception");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-27 16:24:52 +03:00
|
|
|
fn irq_common() {
|
2023-08-13 21:23:58 +03:00
|
|
|
ARCHITECTURE
|
|
|
|
.external_interrupt_controller()
|
|
|
|
.handle_pending_irqs();
|
2023-07-18 18:03:45 +03:00
|
|
|
}
|
|
|
|
|
2023-07-27 16:24:52 +03:00
|
|
|
unsafe fn handle_signal_exit(frame: &mut ExceptionFrame) {
|
|
|
|
// TODO validate the argument
|
|
|
|
let saved_data = &*(frame.r[0] as *const SignalEntryData);
|
|
|
|
debugln!(
|
|
|
|
"Handling signal exit to pc={:#x}, sp={:#x}",
|
|
|
|
saved_data.frame.elr_el1,
|
|
|
|
saved_data.frame.sp_el0
|
|
|
|
);
|
2023-07-18 18:03:45 +03:00
|
|
|
|
2023-08-02 20:43:21 +03:00
|
|
|
frame.restore(&saved_data.frame);
|
2023-07-18 18:03:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
global_asm!(include_str!("vectors.S"));
|