aarch64: exception management and clone fixups

This commit is contained in:
Robin Randhawa
2021-01-26 18:17:09 +00:00
parent 78d1cd1798
commit 4a215c7c2c
7 changed files with 407 additions and 206 deletions

View File

@@ -7,95 +7,117 @@
.align 11
exception_vector_base:
// Synchronous
.align 7
__vec_00:
mov x18, #0xb0b0
b do_exception_synchronous
b synchronous_exception_at_el1_with_sp0
b __vec_00
// IRQ
.align 7
__vec_01:
mov x18, #0xb0b1
b do_exception_irq
b __vec_01
// FIQ
.align 7
__vec_02:
mov x18, #0xb0b2
b do_exception_unhandled
b __vec_02
// SError
.align 7
__vec_03:
mov x18, #0xb0b3
b do_exception_unhandled
b __vec_03
// Synchronous
.align 7
__vec_04:
mov x18, #0xb0b4
b do_exception_synchronous
b synchronous_exception_at_el1_with_spx
b __vec_04
// IRQ
.align 7
__vec_05:
mov x18, #0xb0b5
b do_exception_irq
b __vec_05
// FIQ
.align 7
__vec_06:
mov x18, #0xb0b6
b do_exception_unhandled
b __vec_06
// SError
.align 7
__vec_07:
mov x18, #0xb0b7
b do_exception_unhandled
b __vec_07
// Synchronous
.align 7
__vec_08:
mov x18, #0xb0b8
b do_exception_synchronous
b synchronous_exception_at_el0
b __vec_08
// IRQ
.align 7
__vec_09:
mov x18, #0xb0b9
b do_exception_irq
b __vec_09
// FIQ
.align 7
__vec_10:
mov x18, #0xb0ba
b do_exception_unhandled
b __vec_10
// SError
.align 7
__vec_11:
mov x18, #0xb0bb
b do_exception_unhandled
b __vec_11
// Synchronous
.align 7
__vec_12:
mov x18, #0xb0bc
b do_exception_unhandled
b __vec_12
// IRQ
.align 7
__vec_13:
mov x18, #0xb0bd
b do_exception_unhandled
b __vec_13
// FIQ
.align 7
__vec_14:
mov x18, #0xb0be
b do_exception_unhandled
b __vec_14
// SError
.align 7
__vec_15:
mov x18, #0xb0bf
b do_exception_unhandled
b __vec_15
.align 7
exception_vector_end:

View File

@@ -1,25 +1,28 @@
use crate::syscall::IntRegisters;
#[derive(Default)]
#[repr(packed)]
pub struct ScratchRegisters {
pub x18: usize,
pub x17: usize,
pub x16: usize,
pub x15: usize,
pub x14: usize,
pub x13: usize,
pub x12: usize,
pub x11: usize,
pub x10: usize,
pub x9: usize,
pub x8: usize,
pub x7: usize,
pub x6: usize,
pub x5: usize,
pub x4: usize,
pub x3: usize,
pub x2: usize,
pub x1: usize,
pub x0: usize,
pub x1: usize,
pub x2: usize,
pub x3: usize,
pub x4: usize,
pub x5: usize,
pub x6: usize,
pub x7: usize,
pub x8: usize,
pub x9: usize,
pub x10: usize,
pub x11: usize,
pub x12: usize,
pub x13: usize,
pub x14: usize,
pub x15: usize,
pub x16: usize,
pub x17: usize,
pub x18: usize,
pub padding: usize,
}
impl ScratchRegisters {
@@ -50,18 +53,18 @@ impl ScratchRegisters {
#[repr(packed)]
pub struct PreservedRegisters {
//TODO: is X30 a preserved register?
pub x30: usize,
pub x29: usize,
pub x28: usize,
pub x27: usize,
pub x26: usize,
pub x25: usize,
pub x24: usize,
pub x23: usize,
pub x22: usize,
pub x21: usize,
pub x20: usize,
pub x19: usize,
pub x20: usize,
pub x21: usize,
pub x22: usize,
pub x23: usize,
pub x24: usize,
pub x25: usize,
pub x26: usize,
pub x27: usize,
pub x28: usize,
pub x29: usize,
pub x30: usize,
}
impl PreservedRegisters {
@@ -83,34 +86,245 @@ impl PreservedRegisters {
#[derive(Default)]
#[repr(packed)]
pub struct InterruptStack {
pub elr_el1: usize,
//TODO: should this push be removed?
pub unknown: usize,
pub tpidr_el0: usize,
pub tpidrro_el0: usize,
pub spsr_el1: usize,
pub struct IretRegisters {
// occurred
// The exception vector disambiguates at which EL the interrupt
pub sp_el0: usize, // Shouldn't be used if interrupt occurred at EL1
pub esr_el1: usize,
pub sp_el0: usize,
pub preserved: PreservedRegisters,
pub spsr_el1: usize,
pub tpidrro_el0: usize,
pub tpidr_el0: usize,
pub elr_el1: usize,
}
impl IretRegisters {
pub fn dump(&self) {
println!("ELR_EL1: {:>016X}", { self.elr_el1 });
println!("TPIDR_EL0: {:>016X}", { self.tpidr_el0 });
println!("TPIDRRO_EL0: {:>016X}", { self.tpidrro_el0 });
println!("SPSR_EL1: {:>016X}", { self.spsr_el1 });
println!("ESR_EL1: {:>016X}", { self.esr_el1 });
println!("SP_EL0: {:>016X}", { self.sp_el0 });
}
}
#[derive(Default)]
#[repr(packed)]
pub struct InterruptStack {
pub iret: IretRegisters,
pub scratch: ScratchRegisters,
//TODO: eret registers
pub preserved: PreservedRegisters,
}
impl InterruptStack {
pub fn dump(&self) {
self.iret.dump();
self.scratch.dump();
self.preserved.dump();
println!("SP_EL0: {:>016X}", { self.sp_el0 });
println!("ESR_EL1: {:>016X}", { self.esr_el1 });
println!("SPSR_EL1: {:>016X}", { self.spsr_el1 });
println!("TPIDRRO_EL0: {:>016X}", { self.tpidrro_el0 });
println!("TPIDR_EL0: {:>016X}", { self.tpidr_el0 });
println!("UNKNOWN: {:>016X}", { self.unknown });
println!("ELR_EL1: {:>016X}", { self.elr_el1 });
}
/// Saves all registers to a struct used by the proc:
/// scheme to read/write registers.
pub fn save(&self, all: &mut IntRegisters) {
all.elr_el1 = self.iret.elr_el1;
all.tpidr_el0 = self.iret.tpidr_el0;
all.tpidrro_el0 = self.iret.tpidrro_el0;
all.spsr_el1 = self.iret.spsr_el1;
all.esr_el1 = self.iret.esr_el1;
all.sp_el0 = self.iret.sp_el0;
all.padding = 0;
all.x30 = self.preserved.x30;
all.x29 = self.preserved.x29;
all.x28 = self.preserved.x28;
all.x27 = self.preserved.x27;
all.x26 = self.preserved.x26;
all.x25 = self.preserved.x25;
all.x24 = self.preserved.x24;
all.x23 = self.preserved.x23;
all.x22 = self.preserved.x22;
all.x21 = self.preserved.x21;
all.x20 = self.preserved.x20;
all.x19 = self.preserved.x19;
all.x18 = self.scratch.x18;
all.x17 = self.scratch.x17;
all.x16 = self.scratch.x16;
all.x15 = self.scratch.x15;
all.x14 = self.scratch.x14;
all.x13 = self.scratch.x13;
all.x12 = self.scratch.x12;
all.x11 = self.scratch.x11;
all.x10 = self.scratch.x10;
all.x9 = self.scratch.x9;
all.x8 = self.scratch.x8;
all.x7 = self.scratch.x7;
all.x6 = self.scratch.x6;
all.x5 = self.scratch.x5;
all.x4 = self.scratch.x4;
all.x3 = self.scratch.x3;
all.x2 = self.scratch.x2;
all.x1 = self.scratch.x1;
all.x0 = self.scratch.x0;
}
//TODO
pub fn is_singlestep(&self) -> bool { false }
pub fn set_singlestep(&mut self, singlestep: bool) {}
}
#[macro_export]
macro_rules! aarch64_asm {
($($strings:expr,)+) => {
global_asm!(concat!(
$($strings),+,
));
};
}
#[macro_export]
macro_rules! function {
($name:ident => { $($body:expr,)+ }) => {
aarch64_asm!(
".global ", stringify!($name), "\n",
".type ", stringify!($name), ", @function\n",
".section .text.", stringify!($name), ", \"ax\", @progbits\n",
stringify!($name), ":\n",
$($body),+,
".size ", stringify!($name), ", . - ", stringify!($name), "\n",
".text\n",
);
extern "C" {
pub fn $name();
}
};
}
#[macro_export]
macro_rules! push_scratch {
() => { "
// Push scratch registers
stp x18, x18, [sp, #-16]!
stp x16, x17, [sp, #-16]!
stp x14, x15, [sp, #-16]!
stp x12, x13, [sp, #-16]!
stp x10, x11, [sp, #-16]!
stp x8, x9, [sp, #-16]!
stp x6, x7, [sp, #-16]!
stp x4, x5, [sp, #-16]!
stp x2, x3, [sp, #-16]!
stp x0, x1, [sp, #-16]!
" };
}
#[macro_export]
macro_rules! pop_scratch {
() => { "
// Pop scratch registers
ldp x0, x1, [sp], #16
ldp x2, x3, [sp], #16
ldp x4, x5, [sp], #16
ldp x6, x7, [sp], #16
ldp x8, x9, [sp], #16
ldp x10, x11, [sp], #16
ldp x12, x13, [sp], #16
ldp x14, x15, [sp], #16
ldp x16, x17, [sp], #16
ldp x18, x18, [sp], #16
" };
}
#[macro_export]
macro_rules! push_preserved {
() => { "
// Push preserved registers
stp x29, x30, [sp, #-16]!
stp x27, x28, [sp, #-16]!
stp x25, x26, [sp, #-16]!
stp x23, x24, [sp, #-16]!
stp x21, x22, [sp, #-16]!
stp x19, x20, [sp, #-16]!
" };
}
#[macro_export]
macro_rules! pop_preserved {
() => { "
// Pop preserved registers
ldp x19, x20, [sp], #16
ldp x21, x22, [sp], #16
ldp x23, x24, [sp], #16
ldp x25, x26, [sp], #16
ldp x27, x28, [sp], #16
ldp x29, x30, [sp], #16
" };
}
#[macro_export]
macro_rules! push_special {
() => { "
mrs x14, tpidr_el0
mrs x15, elr_el1
stp x14, x15, [sp, #-16]!
mrs x14, spsr_el1
mrs x15, tpidrro_el0
stp x14, x15, [sp, #-16]!
mrs x14, sp_el0
mrs x15, esr_el1
stp x14, x15, [sp, #-16]!
" };
}
#[macro_export]
macro_rules! pop_special {
() => { "
ldp x14, x15, [sp], 16
msr esr_el1, x15
msr sp_el0, x14
ldp x14, x15, [sp], 16
msr tpidrro_el0, x15
msr spsr_el1, x14
ldp x14, x15, [sp], 16
msr elr_el1, x15
msr tpidr_el0, x14
" };
}
#[macro_export]
macro_rules! exception_stack {
($name:ident, |$stack:ident| $code:block) => {
paste::item! {
#[no_mangle]
unsafe extern "C" fn [<__exception_ $name>](stack: *mut $crate::arch::aarch64::interrupt::InterruptStack) {
// This inner function is needed because macros are buggy:
// https://github.com/dtolnay/paste/issues/7
#[inline(always)]
unsafe fn inner($stack: &mut $crate::arch::aarch64::interrupt::InterruptStack) {
$code
}
inner(&mut *stack);
}
function!($name => {
// Backup all userspace registers to stack
push_preserved!(),
push_scratch!(),
push_special!(),
// Call inner function with pointer to stack
"mov x29, sp\n",
"mov x0, sp\n",
"bl __exception_", stringify!($name), "\n",
// Restore all userspace registers
pop_special!(),
pop_scratch!(),
pop_preserved!(),
"eret\n",
});
}
};
}

View File

@@ -1,10 +1,12 @@
//! Interrupt instructions
#[macro_use]
pub mod handler;
pub mod exception;
pub mod irq;
pub mod syscall;
pub mod trace;
pub mod unhandled_exceptions;
pub use self::handler::InterruptStack;
pub use self::trace::stack_trace;

View File

@@ -1,6 +1,14 @@
use crate::interrupt::InterruptStack;
use crate::syscall;
use crate::{
arch::{interrupt::InterruptStack},
context,
syscall,
syscall::flag::{PTRACE_FLAG_IGNORE, PTRACE_STOP_PRE_SYSCALL, PTRACE_STOP_POST_SYSCALL},
};
#[no_mangle]
pub unsafe extern fn do_exception_unhandled() {}
/*
#[naked]
#[no_mangle]
pub unsafe extern fn do_exception_unhandled() {
@@ -69,7 +77,12 @@ pub unsafe extern fn do_exception_unhandled() {
let a = inner(&mut *(sp as *mut InterruptStack));
}
*/
#[no_mangle]
pub unsafe extern fn do_exception_synchronous() {}
/*
#[naked]
#[no_mangle]
pub unsafe extern fn do_exception_synchronous() {
@@ -219,6 +232,7 @@ pub unsafe extern fn do_exception_synchronous() {
llvm_asm!("eret" :::: "volatile");
}
*/
#[allow(dead_code)]
#[repr(packed)]
@@ -263,8 +277,55 @@ pub struct SyscallStack {
pub x0: usize,
}
#[naked]
pub unsafe extern fn clone_ret() {
llvm_asm!("ldp x29, x30, [sp], #0x60");
llvm_asm!("mov x0, 0");
#[macro_export]
macro_rules! with_exception_stack {
(|$stack:ident| $code:block) => {{
let $stack = &mut *$stack;
(*$stack).scratch.x0 = $code;
}}
}
#[no_mangle]
pub unsafe extern "C" fn __inner_syscall_instruction(stack: *mut InterruptStack) {
with_exception_stack!(|stack| {
// Set a restore point for clone
let fp;
asm!("mov {}, fp", out(reg) fp);
let scratch = &stack.scratch;
syscall::syscall(scratch.x8, scratch.x0, scratch.x1, scratch.x2, scratch.x3, scratch.x4, fp, stack)
});
}
function!(syscall_instruction => {
"
nop
",
// Push context registers
push_preserved!(),
push_scratch!(),
push_special!(),
// TODO: Map PTI
// Call inner function
"mov x0, sp\n",
"bl __inner_syscall_instruction\n",
// TODO: Unmap PTI
// Pop context registers
pop_special!(),
pop_scratch!(),
pop_preserved!(),
// Return
"eret\n",
});
function!(clone_ret => {
"ldp x29, x30, [sp], #16\n",
"mov sp, x29\n",
"ret\n",
});

View File

@@ -1,145 +0,0 @@
use crate::{
context,
cpu_id,
interrupt::{self, InterruptStack, stack_trace},
};
bitflags! {
pub struct ExceptionClasses: u32 {
const SVC_INSN_IN_AARCH64_STATE = 0b10101 << 26;
const DATA_ABORT_FROM_LOWER_EL = 0b100100 << 26;
const BKPT_INSN_IN_AARCH64_STATE = 0b111100 << 26;
}
}
#[inline(never)]
pub unsafe extern fn report_exception(stack: &InterruptStack) {
println!("Unhandled exception");
stack.dump();
stack_trace();
println!("CPU {}, PID {:?}", cpu_id(), context::context_id());
//WARNING: name cannot be grabed, it may deadlock
println!("HALT");
loop {
interrupt::halt();
}
}
#[naked]
#[no_mangle]
pub unsafe extern fn do_report_exception() {
llvm_asm!("str x0, [sp, #-8]!
str x1, [sp, #-8]!
str x2, [sp, #-8]!
str x3, [sp, #-8]!
str x4, [sp, #-8]!
str x5, [sp, #-8]!
str x6, [sp, #-8]!
str x7, [sp, #-8]!
str x8, [sp, #-8]!
str x9, [sp, #-8]!
str x10, [sp, #-8]!
str x11, [sp, #-8]!
str x12, [sp, #-8]!
str x13, [sp, #-8]!
str x14, [sp, #-8]!
str x15, [sp, #-8]!
str x16, [sp, #-8]!
str x17, [sp, #-8]!
str x18, [sp, #-8]!
str x19, [sp, #-8]!
str x20, [sp, #-8]!
str x21, [sp, #-8]!
str x22, [sp, #-8]!
str x23, [sp, #-8]!
str x24, [sp, #-8]!
str x25, [sp, #-8]!
str x26, [sp, #-8]!
str x27, [sp, #-8]!
str x28, [sp, #-8]!
str x29, [sp, #-8]!
str x30, [sp, #-8]!
mrs x18, sp_el0
str x18, [sp, #-8]!
mrs x18, esr_el1
str x18, [sp, #-8]!
mrs x18, spsr_el1
str x18, [sp, #-8]!
mrs x18, tpidrro_el0
str x18, [sp, #-8]!
mrs x18, tpidr_el0
str x18, [sp, #-8]!
str x18, [sp, #-8]!
mrs x18, elr_el1
str x18, [sp, #-8]!"
: : : : "volatile");
let sp: usize;
llvm_asm!("" : "={sp}"(sp) : : : "volatile");
report_exception(&*(sp as *const InterruptStack));
llvm_asm!("ldr x18, [sp], #8
msr elr_el1, x18
ldr x18, [sp], #8
ldr x18, [sp], #8
msr tpidr_el0, x18
ldr x18, [sp], #8
msr tpidrro_el0, x18
ldr x18, [sp], #8
msr spsr_el1, x18
ldr x18, [sp], #8
msr esr_el1, x18
ldr x18, [sp], #8
msr sp_el0, x18
ldr x30, [sp], #8
ldr x29, [sp], #8
ldr x28, [sp], #8
ldr x27, [sp], #8
ldr x26, [sp], #8
ldr x25, [sp], #8
ldr x24, [sp], #8
ldr x23, [sp], #8
ldr x22, [sp], #8
ldr x21, [sp], #8
ldr x20, [sp], #8
ldr x19, [sp], #8
ldr x18, [sp], #8
ldr x17, [sp], #8
ldr x16, [sp], #8
ldr x15, [sp], #8
ldr x14, [sp], #8
ldr x13, [sp], #8
ldr x12, [sp], #8
ldr x11, [sp], #8
ldr x10, [sp], #8
ldr x9, [sp], #8
ldr x8, [sp], #8
ldr x7, [sp], #8
ldr x6, [sp], #8
ldr x5, [sp], #8
ldr x4, [sp], #8
ldr x3, [sp], #8
ldr x2, [sp], #8
ldr x1, [sp], #8
ldr x0, [sp], #8"
: : : : "volatile");
llvm_asm!("eret" :::: "volatile");
}

View File

@@ -150,6 +150,42 @@ impl Context {
value
}
pub fn dump(&self) {
println!("elr_el1: 0x{:016x}", self.elr_el1);
println!("sp_el0: 0x{:016x}", self.sp_el0);
println!("ttbr0_el1: 0x{:016x}", self.ttbr0_el1);
println!("ttbr1_el1: 0x{:016x}", self.ttbr1_el1);
println!("tpidr_el0: 0x{:016x}", self.tpidr_el0);
println!("tpidrro_el0: 0x{:016x}", self.tpidrro_el0);
println!("rflags: 0x{:016x}", self.rflags);
println!("esr_el1: 0x{:016x}", self.esr_el1);
println!("padding: 0x{:016x}", self.padding);
println!("sp: 0x{:016x}", self.sp);
println!("lr: 0x{:016x}", self.lr);
println!("fp: 0x{:016x}", self.fp);
println!("x28: 0x{:016x}", self.x28);
println!("x27: 0x{:016x}", self.x27);
println!("x26: 0x{:016x}", self.x26);
println!("x25: 0x{:016x}", self.x25);
println!("x24: 0x{:016x}", self.x24);
println!("x23: 0x{:016x}", self.x23);
println!("x22: 0x{:016x}", self.x22);
println!("x21: 0x{:016x}", self.x21);
println!("x20: 0x{:016x}", self.x20);
println!("x19: 0x{:016x}", self.x19);
println!("x18: 0x{:016x}", self.x18);
println!("x17: 0x{:016x}", self.x17);
println!("x16: 0x{:016x}", self.x16);
println!("x15: 0x{:016x}", self.x15);
println!("x14: 0x{:016x}", self.x14);
println!("x13: 0x{:016x}", self.x13);
println!("x12: 0x{:016x}", self.x12);
println!("x11: 0x{:016x}", self.x11);
println!("x10: 0x{:016x}", self.x10);
println!("x9: 0x{:016x}", self.x9);
println!("x8: 0x{:016x}", self.x8);
}
#[cold]
#[inline(never)]
#[naked]

View File

@@ -510,14 +510,25 @@ pub fn clone(flags: CloneFlags, stack_base: usize) -> Result<ContextId> {
{
if let Some(stack) = &mut context.kstack {
unsafe {
let interrupt_stack_offset_from_stack_base = *(stack_base as *const u64) - stack_base as u64;
let mut interrupt_stack = &mut *(stack.as_mut_ptr().add(offset + interrupt_stack_offset_from_stack_base as usize) as *mut crate::arch::interrupt::InterruptStack);
interrupt_stack.tpidr_el0 = tcb_addr;
// stack_base contains a pointer to InterruptStack. Get its offset from
// stack_base itself
let istack_offset = *(stack_base as *const u64) - stack_base as u64;
// Get the top of the new process' stack
let new_sp = stack.as_mut_ptr().add(offset);
// Update the pointer to the InterruptStack to reflect the new process'
// stack. (Without this the pointer would be InterruptStack on the parent
// process' stack.
*(new_sp as *mut u64) = new_sp as u64 + istack_offset;
// Update tpidr_el0 in the new process' InterruptStack
let mut interrupt_stack = &mut *(stack.as_mut_ptr().add(offset + istack_offset as usize) as *mut crate::arch::interrupt::InterruptStack);
interrupt_stack.iret.tpidr_el0 = tcb_addr;
}
}
}
// Setup user TLS
if let Some(mut tls) = tls_opt {
// Copy TLS mapping