firmware: runtime changes for exception

Ported from:
M-Labs/artiq-zynq#162

This includes new API for exception handling, some refactoring to avoid
code duplication for exception structures, and modified protocols to
send nested exceptions and avoid string allocation.
This commit is contained in:
pca006132 2022-01-25 15:10:09 +08:00 committed by Sébastien Bourdeauducq
parent 536b3e0c26
commit 4132c450a5
15 changed files with 433 additions and 243 deletions

View File

@ -253,6 +253,7 @@ dependencies = [
"byteorder", "byteorder",
"cslice", "cslice",
"dyld", "dyld",
"eh",
"failure", "failure",
"failure_derive", "failure_derive",
"io", "io",

View File

@ -117,7 +117,8 @@ static mut API: &'static [(&'static str, *const ())] = &[
api!(_Unwind_Resume = ::unwind::_Unwind_Resume), api!(_Unwind_Resume = ::unwind::_Unwind_Resume),
api!(__artiq_personality = ::eh_artiq::personality), api!(__artiq_personality = ::eh_artiq::personality),
api!(__artiq_raise = ::eh_artiq::raise), api!(__artiq_raise = ::eh_artiq::raise),
api!(__artiq_reraise = ::eh_artiq::reraise), api!(__artiq_resume = ::eh_artiq::resume),
api!(__artiq_end_catch = ::eh_artiq::end_catch),
/* proxified syscalls */ /* proxified syscalls */
api!(core_log), api!(core_log),

View File

@ -10,12 +10,15 @@
// except according to those terms. // except according to those terms.
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
use core::{ptr, mem}; use core::mem;
use cslice::CSlice; use cslice::AsCSlice;
use unwind as uw; use unwind as uw;
use libc::{c_int, c_void}; use libc::{c_int, c_void};
use eh::dwarf::{self, EHAction, EHContext}; use eh::{self, dwarf::{self, EHAction, EHContext}};
pub type Exception<'a> = eh::eh_artiq::Exception<'a>;
pub type StackPointerBacktrace = eh::eh_artiq::StackPointerBacktrace;
type _Unwind_Stop_Fn = extern "C" fn(version: c_int, type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
actions: uw::_Unwind_Action, actions: uw::_Unwind_Action,
@ -30,40 +33,74 @@ extern {
stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code; stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code;
} }
#[repr(C)] pub static mut PAYLOAD_ADDRESS: usize = 0;
#[derive(Clone, Copy)]
pub struct Exception<'a> {
pub name: CSlice<'a, u8>,
pub file: CSlice<'a, u8>,
pub line: u32,
pub column: u32,
pub function: CSlice<'a, u8>,
pub message: CSlice<'a, u8>,
pub param: [i64; 3]
}
const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51; /* 'MLBSARTQ' */ const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51; /* 'MLBSARTQ' */
const MAX_INFLIGHT_EXCEPTIONS: usize = 10;
const MAX_BACKTRACE_SIZE: usize = 128; const MAX_BACKTRACE_SIZE: usize = 128;
#[repr(C)] struct ExceptionBuffer {
struct ExceptionInfo { // we need n _Unwind_Exception, because each will have their own private data
uw_exception: uw::_Unwind_Exception, uw_exceptions: [uw::_Unwind_Exception; MAX_INFLIGHT_EXCEPTIONS],
exception: Option<Exception<'static>>, exceptions: [Option<Exception<'static>>; MAX_INFLIGHT_EXCEPTIONS + 1],
handled: bool, exception_stack: [isize; MAX_INFLIGHT_EXCEPTIONS + 1],
backtrace: [usize; MAX_BACKTRACE_SIZE], // nested exceptions will share the backtrace buffer, treated as a tree
backtrace_size: usize // backtrace contains a tuple of IP and SP
backtrace: [(usize, usize); MAX_BACKTRACE_SIZE],
backtrace_size: usize,
// stack pointers are stored to reconstruct backtrace for each exception
stack_pointers: [StackPointerBacktrace; MAX_INFLIGHT_EXCEPTIONS + 1],
// current allocated nested exceptions
exception_count: usize,
}
static mut EXCEPTION_BUFFER: ExceptionBuffer = ExceptionBuffer {
uw_exceptions: [uw::_Unwind_Exception {
exception_class: EXCEPTION_CLASS,
exception_cleanup: cleanup,
private: [0; uw::unwinder_private_data_size],
}; MAX_INFLIGHT_EXCEPTIONS],
exceptions: [None; MAX_INFLIGHT_EXCEPTIONS + 1],
exception_stack: [-1; MAX_INFLIGHT_EXCEPTIONS + 1],
backtrace: [(0, 0); MAX_BACKTRACE_SIZE],
backtrace_size: 0,
stack_pointers: [StackPointerBacktrace {
stack_pointer: 0,
initial_backtrace_size: 0,
current_backtrace_size: 0
}; MAX_INFLIGHT_EXCEPTIONS + 1],
exception_count: 0
};
pub unsafe extern fn reset_exception_buffer(payload_addr: usize) {
EXCEPTION_BUFFER.uw_exceptions = [uw::_Unwind_Exception {
exception_class: EXCEPTION_CLASS,
exception_cleanup: cleanup,
private: [0; uw::unwinder_private_data_size],
}; MAX_INFLIGHT_EXCEPTIONS];
EXCEPTION_BUFFER.exceptions = [None; MAX_INFLIGHT_EXCEPTIONS + 1];
EXCEPTION_BUFFER.exception_stack = [-1; MAX_INFLIGHT_EXCEPTIONS + 1];
EXCEPTION_BUFFER.backtrace_size = 0;
EXCEPTION_BUFFER.exception_count = 0;
PAYLOAD_ADDRESS = payload_addr;
} }
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
#[cfg(target_arch = "x86_64")]
// actually this is not the SP, but frame pointer
// but it serves its purpose, and getting SP will somehow cause segfault...
const UNW_FP_REG: c_int = 12;
#[cfg(any(target_arch = "riscv32"))] #[cfg(any(target_arch = "riscv32"))]
const UNWIND_DATA_REG: (i32, i32) = (10, 11); // X10, X11 const UNWIND_DATA_REG: (i32, i32) = (10, 11); // X10, X11
#[cfg(any(target_arch = "riscv32"))]
const UNW_FP_REG: c_int = 2;
#[export_name="__artiq_personality"] #[export_name="__artiq_personality"]
pub extern fn personality(version: c_int, pub extern fn personality(version: c_int,
actions: uw::_Unwind_Action, _actions: uw::_Unwind_Action,
uw_exception_class: uw::_Unwind_Exception_Class, uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception, uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context) context: *mut uw::_Unwind_Context)
@ -85,133 +122,236 @@ pub extern fn personality(version: c_int,
get_data_start: &|| uw::_Unwind_GetDataRelBase(context), get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
}; };
let exception_info = &mut *(uw_exception as *mut ExceptionInfo); let index = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1];
let exception = &exception_info.exception.unwrap(); assert!(index != -1);
let exception = EXCEPTION_BUFFER.exceptions[index as usize].as_ref().unwrap();
let id = exception.id;
let name_ptr = exception.name.as_ptr(); let eh_action = match dwarf::find_eh_action(lsda, &eh_context, id) {
let len = exception.name.len();
let eh_action = match dwarf::find_eh_action(lsda, &eh_context, name_ptr, len) {
Ok(action) => action, Ok(action) => action,
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR, Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
}; };
if actions as u32 & uw::_UA_SEARCH_PHASE as u32 != 0 { match eh_action {
match eh_action { EHAction::None => return uw::_URC_CONTINUE_UNWIND,
EHAction::None | EHAction::Cleanup(lpad) |
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND, EHAction::Catch(lpad) => {
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND, // Pass a pair of the unwinder exception and ARTIQ exception
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR, // (which immediately follows).
} uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
} else { uw_exception as uw::_Unwind_Word);
match eh_action { uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1,
EHAction::None => return uw::_URC_CONTINUE_UNWIND, exception as *const _ as uw::_Unwind_Word);
EHAction::Cleanup(lpad) | uw::_Unwind_SetIP(context, lpad);
EHAction::Catch(lpad) => { return uw::_URC_INSTALL_CONTEXT;
if actions as u32 & uw::_UA_HANDLER_FRAME as u32 != 0 {
exception_info.handled = true
}
// Pass a pair of the unwinder exception and ARTIQ exception
// (which immediately follows).
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
uw_exception as uw::_Unwind_Word);
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1,
exception as *const _ as uw::_Unwind_Word);
uw::_Unwind_SetIP(context, lpad);
return uw::_URC_INSTALL_CONTEXT;
}
EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
} }
EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
} }
} }
} }
#[export_name="__artiq_raise"]
#[unwind(allowed)]
pub unsafe extern fn raise(exception: *const Exception) -> ! {
let count = EXCEPTION_BUFFER.exception_count;
let stack = &mut EXCEPTION_BUFFER.exception_stack;
let diff = exception as isize - EXCEPTION_BUFFER.exceptions.as_ptr() as isize;
if 0 <= diff && diff <= (mem::size_of::<Option<Exception>>() * MAX_INFLIGHT_EXCEPTIONS) as isize {
let index = diff / (mem::size_of::<Option<Exception>>() as isize);
let mut found = false;
for i in 0..=MAX_INFLIGHT_EXCEPTIONS + 1 {
if found {
if stack[i] == -1 {
stack[i - 1] = index;
assert!(i == count);
break;
} else {
stack[i - 1] = stack[i];
}
} else {
if stack[i] == index {
found = true;
}
}
}
assert!(found);
let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[stack[count - 1] as usize],
stop_fn, core::ptr::null_mut());
} else {
if count < MAX_INFLIGHT_EXCEPTIONS {
let exception = &*exception;
for (i, slot) in EXCEPTION_BUFFER.exceptions.iter_mut().enumerate() {
// we should always be able to find a slot
if slot.is_none() {
*slot = Some(
*mem::transmute::<*const Exception, *const Exception<'static>>
(exception));
EXCEPTION_BUFFER.exception_stack[count] = i as isize;
EXCEPTION_BUFFER.uw_exceptions[i].private =
[0; uw::unwinder_private_data_size];
EXCEPTION_BUFFER.stack_pointers[i] = StackPointerBacktrace {
stack_pointer: 0,
initial_backtrace_size: EXCEPTION_BUFFER.backtrace_size,
current_backtrace_size: 0,
};
EXCEPTION_BUFFER.exception_count += 1;
let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[i],
stop_fn, core::ptr::null_mut());
}
}
} else {
// TODO: better reporting?
let exception = Exception {
id: get_exception_id("RuntimeError"),
file: file!().as_c_slice(),
line: line!(),
column: column!(),
// https://github.com/rust-lang/rfcs/pull/1719
function: "__artiq_raise".as_c_slice(),
message: "too many nested exceptions".as_c_slice(),
param: [0, 0, 0]
};
EXCEPTION_BUFFER.exceptions[MAX_INFLIGHT_EXCEPTIONS] = Some(mem::transmute(exception));
EXCEPTION_BUFFER.stack_pointers[MAX_INFLIGHT_EXCEPTIONS] = Default::default();
EXCEPTION_BUFFER.exception_count += 1;
uncaught_exception()
}
}
unreachable!();
}
#[export_name="__artiq_resume"]
#[unwind(allowed)]
pub unsafe extern fn resume() -> ! {
assert!(EXCEPTION_BUFFER.exception_count != 0);
let i = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1];
assert!(i != -1);
let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[i as usize],
stop_fn, core::ptr::null_mut());
unreachable!()
}
#[export_name="__artiq_end_catch"]
#[unwind(allowed)]
pub unsafe extern fn end_catch() {
let mut count = EXCEPTION_BUFFER.exception_count;
assert!(count != 0);
// we remove all exceptions with SP <= current exception SP
// i.e. the outer exception escapes the finally block
let index = EXCEPTION_BUFFER.exception_stack[count - 1] as usize;
EXCEPTION_BUFFER.exception_stack[count - 1] = -1;
EXCEPTION_BUFFER.exceptions[index] = None;
let outer_sp = EXCEPTION_BUFFER.stack_pointers
[index].stack_pointer;
count -= 1;
for i in (0..count).rev() {
let index = EXCEPTION_BUFFER.exception_stack[i];
assert!(index != -1);
let index = index as usize;
let sp = EXCEPTION_BUFFER.stack_pointers[index].stack_pointer;
if sp >= outer_sp {
break;
}
EXCEPTION_BUFFER.exceptions[index] = None;
EXCEPTION_BUFFER.exception_stack[i] = -1;
count -= 1;
}
EXCEPTION_BUFFER.exception_count = count;
EXCEPTION_BUFFER.backtrace_size = if count > 0 {
let index = EXCEPTION_BUFFER.exception_stack[count - 1];
assert!(index != -1);
EXCEPTION_BUFFER.stack_pointers[index as usize].current_backtrace_size
} else {
0
};
}
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code, extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
uw_exception: *mut uw::_Unwind_Exception) { _uw_exception: *mut uw::_Unwind_Exception) {
unsafe { unimplemented!()
let exception_info = &mut *(uw_exception as *mut ExceptionInfo); }
exception_info.exception = None; fn uncaught_exception() -> ! {
unsafe {
// dump way to reorder the stack
for i in 0..EXCEPTION_BUFFER.exception_count {
if EXCEPTION_BUFFER.exception_stack[i] != i as isize {
// find the correct index
let index = EXCEPTION_BUFFER.exception_stack
.iter()
.position(|v| *v == i as isize).unwrap();
let a = EXCEPTION_BUFFER.exception_stack[index];
let b = EXCEPTION_BUFFER.exception_stack[i];
assert!(a != -1 && b != -1);
core::mem::swap(&mut EXCEPTION_BUFFER.exception_stack[index],
&mut EXCEPTION_BUFFER.exception_stack[i]);
core::mem::swap(&mut EXCEPTION_BUFFER.exceptions[a as usize],
&mut EXCEPTION_BUFFER.exceptions[b as usize]);
core::mem::swap(&mut EXCEPTION_BUFFER.stack_pointers[a as usize],
&mut EXCEPTION_BUFFER.stack_pointers[b as usize]);
}
}
}
unsafe {
::terminate(
EXCEPTION_BUFFER.exceptions[..EXCEPTION_BUFFER.exception_count].as_ref(),
EXCEPTION_BUFFER.stack_pointers[..EXCEPTION_BUFFER.exception_count].as_ref(),
EXCEPTION_BUFFER.backtrace[..EXCEPTION_BUFFER.backtrace_size].as_mut())
} }
} }
extern fn uncaught_exception(_version: c_int,
actions: uw::_Unwind_Action, // stop function which would be executed when we unwind each frame
_uw_exception_class: uw::_Unwind_Exception_Class, extern fn stop_fn(_version: c_int,
uw_exception: *mut uw::_Unwind_Exception, actions: uw::_Unwind_Action,
context: *mut uw::_Unwind_Context, _uw_exception_class: uw::_Unwind_Exception_Class,
_stop_parameter: *mut c_void) _uw_exception: *mut uw::_Unwind_Exception,
-> uw::_Unwind_Reason_Code { context: *mut uw::_Unwind_Context,
_stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code {
unsafe { unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo); let backtrace_size = EXCEPTION_BUFFER.backtrace_size;
if backtrace_size < MAX_BACKTRACE_SIZE {
if exception_info.backtrace_size < exception_info.backtrace.len() {
let ip = uw::_Unwind_GetIP(context); let ip = uw::_Unwind_GetIP(context);
exception_info.backtrace[exception_info.backtrace_size] = ip; let fp = uw::_Unwind_GetGR(context, UNW_FP_REG);
exception_info.backtrace_size += 1; if PAYLOAD_ADDRESS == 0 || ip > PAYLOAD_ADDRESS {
let ip = ip - PAYLOAD_ADDRESS;
EXCEPTION_BUFFER.backtrace[backtrace_size] = (ip, fp);
EXCEPTION_BUFFER.backtrace_size += 1;
let last_index = EXCEPTION_BUFFER.exception_stack[EXCEPTION_BUFFER.exception_count - 1];
assert!(last_index != -1);
let sp_info = &mut EXCEPTION_BUFFER.stack_pointers[last_index as usize];
sp_info.stack_pointer = fp;
sp_info.current_backtrace_size = backtrace_size + 1;
}
} }
if actions as u32 & uw::_UA_END_OF_STACK as u32 != 0 { if actions as u32 & uw::_UA_END_OF_STACK as u32 != 0 {
::terminate(&exception_info.exception.unwrap(), uncaught_exception()
exception_info.backtrace[..exception_info.backtrace_size].as_mut())
} else { } else {
uw::_URC_NO_REASON uw::_URC_NO_REASON
} }
} }
} }
// We can unfortunately not use mem::zeroed in a static, so Option<> is used as a workaround. static EXCEPTION_ID_LOOKUP: [(&str, u32); 10] = [
// See https://github.com/rust-lang/rust/issues/39498. ("RuntimeError", 0),
static mut INFLIGHT: ExceptionInfo = ExceptionInfo { ("RTIOUnderflow", 1),
uw_exception: uw::_Unwind_Exception { ("RTIOOverflow", 2),
exception_class: EXCEPTION_CLASS, ("RTIODestinationUnreachable", 3),
exception_cleanup: cleanup, ("DMAError", 4),
private: [0; uw::unwinder_private_data_size], ("I2CError", 5),
}, ("CacheError", 6),
exception: None, ("SPIError", 7),
handled: true, ("ZeroDivisionError", 8),
backtrace: [0; MAX_BACKTRACE_SIZE], ("IndexError", 9)
backtrace_size: 0 ];
};
#[export_name="__artiq_raise"] pub fn get_exception_id(name: &str) -> u32 {
#[unwind(allowed)] for (n, id) in EXCEPTION_ID_LOOKUP.iter() {
pub unsafe extern fn raise(exception: *const Exception) -> ! { if *n == name {
// Zing! The Exception<'a> to Exception<'static> transmute is not really sound in case return *id
// the exception is ever captured. Fortunately, they currently aren't, and we save
// on the hassle of having to allocate exceptions somewhere except on stack.
INFLIGHT.exception = Some(mem::transmute::<Exception, Exception<'static>>(*exception));
INFLIGHT.handled = false;
let result = uw::_Unwind_RaiseException(&mut INFLIGHT.uw_exception);
assert!(result == uw::_URC_END_OF_STACK);
INFLIGHT.backtrace_size = 0;
let _result = _Unwind_ForcedUnwind(&mut INFLIGHT.uw_exception,
uncaught_exception, ptr::null_mut());
unreachable!()
}
#[export_name="__artiq_reraise"]
#[unwind(allowed)]
pub unsafe extern fn reraise() -> ! {
use cslice::AsCSlice;
if INFLIGHT.handled {
match INFLIGHT.exception {
Some(ref exception) => raise(exception),
None => raise(&Exception {
name: "0:artiq.coredevice.exceptions.RuntimeError".as_c_slice(),
file: file!().as_c_slice(),
line: line!(),
column: column!(),
// https://github.com/rust-lang/rfcs/pull/1719
function: "__artiq_reraise".as_c_slice(),
message: "No active exception to reraise".as_c_slice(),
param: [0, 0, 0]
})
} }
} else {
uw::_Unwind_Resume(&mut INFLIGHT.uw_exception)
} }
unimplemented!("unallocated internal exception id")
} }

View File

@ -1,5 +1,5 @@
#![feature(lang_items, llvm_asm, panic_unwind, libc, unwind_attributes, #![feature(lang_items, llvm_asm, panic_unwind, libc, unwind_attributes,
panic_info_message, nll)] panic_info_message, nll, const_in_array_repeat_expressions)]
#![no_std] #![no_std]
extern crate libc; extern crate libc;
@ -80,8 +80,9 @@ macro_rules! println {
macro_rules! raise { macro_rules! raise {
($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({ ($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({
use cslice::AsCSlice; use cslice::AsCSlice;
let name_id = $crate::eh_artiq::get_exception_id($name);
let exn = $crate::eh_artiq::Exception { let exn = $crate::eh_artiq::Exception {
name: concat!("0:artiq.coredevice.exceptions.", $name).as_c_slice(), id: name_id,
file: file!().as_c_slice(), file: file!().as_c_slice(),
line: line!(), line: line!(),
column: column!(), column: column!(),
@ -164,12 +165,12 @@ extern fn rpc_recv(slot: *mut ()) -> usize {
&Err(ref exception) => &Err(ref exception) =>
unsafe { unsafe {
eh_artiq::raise(&eh_artiq::Exception { eh_artiq::raise(&eh_artiq::Exception {
name: exception.name.as_bytes().as_c_slice(), id: exception.id,
file: exception.file.as_bytes().as_c_slice(), file: exception.file,
line: exception.line, line: exception.line,
column: exception.column, column: exception.column,
function: exception.function.as_bytes().as_c_slice(), function: exception.function,
message: exception.message.as_bytes().as_c_slice(), message: exception.message,
param: exception.param param: exception.param
}) })
} }
@ -177,27 +178,13 @@ extern fn rpc_recv(slot: *mut ()) -> usize {
}) })
} }
fn terminate(exception: &eh_artiq::Exception, backtrace: &mut [usize]) -> ! { fn terminate(exceptions: &'static [Option<eh_artiq::Exception<'static>>],
let mut cursor = 0; stack_pointers: &'static [eh_artiq::StackPointerBacktrace],
for index in 0..backtrace.len() { backtrace: &mut [(usize, usize)]) -> ! {
if backtrace[index] > kernel_proto::KERNELCPU_PAYLOAD_ADDRESS {
backtrace[cursor] = backtrace[index] - kernel_proto::KERNELCPU_PAYLOAD_ADDRESS;
cursor += 1;
}
}
let backtrace = &mut backtrace.as_mut()[0..cursor];
send(&RunException { send(&RunException {
exception: kernel_proto::Exception { exceptions,
name: str::from_utf8(exception.name.as_ref()).unwrap(), stack_pointers,
file: str::from_utf8(exception.file.as_ref()).unwrap(), backtrace
line: exception.line,
column: exception.column,
function: str::from_utf8(exception.function.as_ref()).unwrap(),
message: str::from_utf8(exception.message.as_ref()).unwrap(),
param: exception.param,
},
backtrace: backtrace
}); });
loop {} loop {}
} }
@ -472,6 +459,7 @@ unsafe fn attribute_writeback(typeinfo: *const ()) {
#[no_mangle] #[no_mangle]
pub unsafe fn main() { pub unsafe fn main() {
eh_artiq::reset_exception_buffer(KERNELCPU_PAYLOAD_ADDRESS);
let image = slice::from_raw_parts_mut(kernel_proto::KERNELCPU_PAYLOAD_ADDRESS as *mut u8, let image = slice::from_raw_parts_mut(kernel_proto::KERNELCPU_PAYLOAD_ADDRESS as *mut u8,
kernel_proto::KERNELCPU_LAST_ADDRESS - kernel_proto::KERNELCPU_LAST_ADDRESS -
kernel_proto::KERNELCPU_PAYLOAD_ADDRESS); kernel_proto::KERNELCPU_PAYLOAD_ADDRESS);

View File

@ -202,8 +202,7 @@ unsafe fn get_ttype_entry(
pub unsafe fn find_eh_action( pub unsafe fn find_eh_action(
lsda: *const u8, lsda: *const u8,
context: &EHContext<'_>, context: &EHContext<'_>,
name: *const u8, id: u32,
len: usize,
) -> Result<EHAction, ()> { ) -> Result<EHAction, ()> {
if lsda.is_null() { if lsda.is_null() {
return Ok(EHAction::None); return Ok(EHAction::None);
@ -275,19 +274,9 @@ pub unsafe fn find_eh_action(
return Ok(EHAction::Catch(lpad)); return Ok(EHAction::Catch(lpad));
} }
// this seems to be target dependent // this seems to be target dependent
let clause_ptr = *(catch_type as *const CSlice<u8>); let clause_id = *(catch_type as *const u32);
let clause_name_ptr = (clause_ptr).as_ptr(); if clause_id == id {
let clause_name_len = (clause_ptr).len(); return Ok(EHAction::Catch(lpad));
if clause_name_len == len {
if (clause_name_ptr == core::ptr::null() ||
clause_name_ptr == name ||
// somehow their name pointers might differ, but the content is the
// same
core::slice::from_raw_parts(clause_name_ptr, clause_name_len) ==
core::slice::from_raw_parts(name, len))
{
return Ok(EHAction::Catch(lpad));
}
} }
} else if ar_filter < 0 { } else if ar_filter < 0 {
// FIXME: how to handle this? // FIXME: how to handle this?

View File

@ -0,0 +1,47 @@
// ARTIQ Exception struct declaration
use cslice::CSlice;
// Note: CSlice within an exception may not be actual cslice, they may be strings that exist only
// in the host. If the length == usize:MAX, the pointer is actually a string key in the host.
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Exception<'a> {
pub id: u32,
pub file: CSlice<'a, u8>,
pub line: u32,
pub column: u32,
pub function: CSlice<'a, u8>,
pub message: CSlice<'a, u8>,
pub param: [i64; 3]
}
fn str_err(_: core::str::Utf8Error) -> core::fmt::Error {
core::fmt::Error
}
fn exception_str<'a>(s: &'a CSlice<'a, u8>) -> Result<&'a str, core::str::Utf8Error> {
if s.len() == usize::MAX {
Ok("<host string>")
} else {
core::str::from_utf8(s.as_ref())
}
}
impl<'a> core::fmt::Debug for Exception<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Exception {} from {} in {}:{}:{}, message: {}",
self.id,
exception_str(&self.function).map_err(str_err)?,
exception_str(&self.file).map_err(str_err)?,
self.line, self.column,
exception_str(&self.message).map_err(str_err)?)
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct StackPointerBacktrace {
pub stack_pointer: usize,
pub initial_backtrace_size: usize,
pub current_backtrace_size: usize,
}

View File

@ -87,5 +87,5 @@ unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result<EHAction,
get_text_start: &|| uw::_Unwind_GetTextRelBase(context), get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
get_data_start: &|| uw::_Unwind_GetDataRelBase(context), get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
}; };
dwarf::find_eh_action(lsda, &eh_context, core::ptr::null(), 0) dwarf::find_eh_action(lsda, &eh_context, 0)
} }

View File

@ -7,3 +7,4 @@ extern crate libc;
pub mod dwarf; pub mod dwarf;
pub mod eh_rust; pub mod eh_rust;
pub mod eh_artiq;

View File

@ -15,6 +15,7 @@ cslice = { version = "0.3" }
log = { version = "0.4", default-features = false, optional = true } log = { version = "0.4", default-features = false, optional = true }
io = { path = "../libio", features = ["byteorder"] } io = { path = "../libio", features = ["byteorder"] }
dyld = { path = "../libdyld" } dyld = { path = "../libdyld" }
eh = { path = "../libeh" }
[features] [features]
alloc = ["io/alloc"] alloc = ["io/alloc"]

View File

@ -6,17 +6,6 @@ pub const KERNELCPU_PAYLOAD_ADDRESS: usize = 0x45060000;
pub const KERNELCPU_LAST_ADDRESS: usize = 0x4fffffff; pub const KERNELCPU_LAST_ADDRESS: usize = 0x4fffffff;
pub const KSUPPORT_HEADER_SIZE: usize = 0x80; pub const KSUPPORT_HEADER_SIZE: usize = 0x80;
#[derive(Debug, Clone)]
pub struct Exception<'a> {
pub name: &'a str,
pub file: &'a str,
pub line: u32,
pub column: u32,
pub function: &'a str,
pub message: &'a str,
pub param: [i64; 3]
}
#[derive(Debug)] #[derive(Debug)]
pub enum Message<'a> { pub enum Message<'a> {
LoadRequest(&'a [u8]), LoadRequest(&'a [u8]),
@ -47,8 +36,9 @@ pub enum Message<'a> {
RunFinished, RunFinished,
RunException { RunException {
exception: Exception<'a>, exceptions: &'a [Option<eh::eh_artiq::Exception<'a>>],
backtrace: &'a [usize] stack_pointers: &'a [eh::eh_artiq::StackPointerBacktrace],
backtrace: &'a [(usize, usize)]
}, },
RunAborted, RunAborted,
@ -59,7 +49,7 @@ pub enum Message<'a> {
data: *const *const () data: *const *const ()
}, },
RpcRecvRequest(*mut ()), RpcRecvRequest(*mut ()),
RpcRecvReply(Result<usize, Exception<'a>>), RpcRecvReply(Result<usize, eh::eh_artiq::Exception<'a>>),
RpcFlush, RpcFlush,
CacheGetRequest { key: &'a str }, CacheGetRequest { key: &'a str },

View File

@ -13,6 +13,7 @@ extern crate log;
extern crate byteorder; extern crate byteorder;
extern crate io; extern crate io;
extern crate dyld; extern crate dyld;
extern crate eh;
// Internal protocols. // Internal protocols.
pub mod kernel_proto; pub mod kernel_proto;

View File

@ -1,5 +1,7 @@
use core::str::Utf8Error; use core::str::Utf8Error;
use alloc::{vec::Vec, string::String}; use alloc::vec::Vec;
use eh::eh_artiq::{Exception, StackPointerBacktrace};
use cslice::CSlice;
use io::{Read, ProtoRead, Write, ProtoWrite, Error as IoError, ReadStringError}; use io::{Read, ProtoRead, Write, ProtoWrite, Error as IoError, ReadStringError};
@ -70,13 +72,13 @@ pub enum Request {
RpcReply { tag: Vec<u8> }, RpcReply { tag: Vec<u8> },
RpcException { RpcException {
name: String, id: u32,
message: String, message: u32,
param: [i64; 3], param: [i64; 3],
file: String, file: u32,
line: u32, line: u32,
column: u32, column: u32,
function: String, function: u32,
}, },
} }
@ -95,14 +97,9 @@ pub enum Reply<'a> {
}, },
KernelStartupFailed, KernelStartupFailed,
KernelException { KernelException {
name: &'a str, exceptions: &'a [Option<Exception<'a>>],
message: &'a str, stack_pointers: &'a [StackPointerBacktrace],
param: [i64; 3], backtrace: &'a [(usize, usize)],
file: &'a str,
line: u32,
column: u32,
function: &'a str,
backtrace: &'a [usize],
async_errors: u8 async_errors: u8
}, },
@ -126,15 +123,15 @@ impl Request {
tag: reader.read_bytes()? tag: reader.read_bytes()?
}, },
8 => Request::RpcException { 8 => Request::RpcException {
name: reader.read_string()?, id: reader.read_u32()?,
message: reader.read_string()?, message: reader.read_u32()?,
param: [reader.read_u64()? as i64, param: [reader.read_u64()? as i64,
reader.read_u64()? as i64, reader.read_u64()? as i64,
reader.read_u64()? as i64], reader.read_u64()? as i64],
file: reader.read_string()?, file: reader.read_u32()?,
line: reader.read_u32()?, line: reader.read_u32()?,
column: reader.read_u32()?, column: reader.read_u32()?,
function: reader.read_string()? function: reader.read_u32()?
}, },
ty => return Err(Error::UnknownPacket(ty)) ty => return Err(Error::UnknownPacket(ty))
@ -142,6 +139,18 @@ impl Request {
} }
} }
fn write_exception_string<'a, W>(writer: &mut W, s: &CSlice<'a, u8>) -> Result<(), IoError<W::WriteError>>
where W: Write + ?Sized
{
if s.len() == usize::MAX {
writer.write_u32(u32::MAX)?;
writer.write_u32(s.as_ptr() as u32)?;
} else {
writer.write_string(core::str::from_utf8(s.as_ref()).unwrap())?;
}
Ok(())
}
impl<'a> Reply<'a> { impl<'a> Reply<'a> {
pub fn write_to<W>(&self, writer: &mut W) -> Result<(), IoError<W::WriteError>> pub fn write_to<W>(&self, writer: &mut W) -> Result<(), IoError<W::WriteError>>
where W: Write + ?Sized where W: Write + ?Sized
@ -171,22 +180,36 @@ impl<'a> Reply<'a> {
writer.write_u8(8)?; writer.write_u8(8)?;
}, },
Reply::KernelException { Reply::KernelException {
name, message, param, file, line, column, function, backtrace, exceptions,
stack_pointers,
backtrace,
async_errors async_errors
} => { } => {
writer.write_u8(9)?; writer.write_u8(9)?;
writer.write_string(name)?; writer.write_u32(exceptions.len() as u32)?;
writer.write_string(message)?; for exception in exceptions.iter() {
writer.write_u64(param[0] as u64)?; let exception = exception.as_ref().unwrap();
writer.write_u64(param[1] as u64)?; writer.write_u32(exception.id as u32)?;
writer.write_u64(param[2] as u64)?; write_exception_string(writer, &exception.message)?;
writer.write_string(file)?; writer.write_u64(exception.param[0] as u64)?;
writer.write_u32(line)?; writer.write_u64(exception.param[1] as u64)?;
writer.write_u32(column)?; writer.write_u64(exception.param[2] as u64)?;
writer.write_string(function)?; write_exception_string(writer, &exception.file)?;
writer.write_u32(exception.line)?;
writer.write_u32(exception.column)?;
write_exception_string(writer, &exception.function)?;
}
for sp in stack_pointers.iter() {
writer.write_u32(sp.stack_pointer as u32)?;
writer.write_u32(sp.initial_backtrace_size as u32)?;
writer.write_u32(sp.current_backtrace_size as u32)?;
}
writer.write_u32(backtrace.len() as u32)?; writer.write_u32(backtrace.len() as u32)?;
for &addr in backtrace { for &(addr, sp) in backtrace {
writer.write_u32(addr as u32)? writer.write_u32(addr as u32)?;
writer.write_u32(sp as u32)?;
} }
writer.write_u8(async_errors)?; writer.write_u8(async_errors)?;
}, },

View File

@ -1,6 +1,7 @@
use core::{mem, str, cell::{Cell, RefCell}, fmt::Write as FmtWrite}; use core::{mem, str, cell::{Cell, RefCell}, fmt::Write as FmtWrite};
use alloc::{vec::Vec, string::String}; use alloc::{vec::Vec, string::String};
use byteorder::{ByteOrder, NativeEndian}; use byteorder::{ByteOrder, NativeEndian};
use cslice::CSlice;
use io::{Read, Write, Error as IoError}; use io::{Read, Write, Error as IoError};
use board_misoc::{ident, cache, config}; use board_misoc::{ident, cache, config};
@ -291,7 +292,7 @@ fn process_host_message(io: &Io,
} }
host::Request::RpcException { host::Request::RpcException {
name, message, param, file, line, column, function id, message, param, file, line, column, function
} => { } => {
if session.kernel_state != KernelState::RpcWait { if session.kernel_state != KernelState::RpcWait {
unexpected!("unsolicited RPC reply") unexpected!("unsolicited RPC reply")
@ -305,16 +306,18 @@ fn process_host_message(io: &Io,
} }
})?; })?;
let exn = kern::Exception { unsafe {
name: name.as_ref(), let exn = eh::eh_artiq::Exception {
message: message.as_ref(), id: id,
param: param, message: CSlice::new(message as *const u8, usize::MAX),
file: file.as_ref(), param: param,
line: line, file: CSlice::new(file as *const u8, usize::MAX),
column: column, line: line,
function: function.as_ref() column: column,
}; function: CSlice::new(function as *const u8, usize::MAX),
kern_send(io, &kern::RpcRecvReply(Err(exn)))?; };
kern_send(io, &kern::RpcRecvReply(Err(exn)))?;
}
session.kernel_state = KernelState::Running session.kernel_state = KernelState::Running
} }
@ -438,7 +441,8 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
} }
} }
&kern::RunException { &kern::RunException {
exception: kern::Exception { name, message, param, file, line, column, function }, exceptions,
stack_pointers,
backtrace backtrace
} => { } => {
unsafe { kernel::stop() } unsafe { kernel::stop() }
@ -448,19 +452,15 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
match stream { match stream {
None => { None => {
error!("exception in flash kernel"); error!("exception in flash kernel");
error!("{}: {} {:?}", name, message, param); for exception in exceptions {
error!("at {}:{}:{} in {}", file, line, column, function); error!("{:?}", exception.unwrap());
}
return Ok(true) return Ok(true)
}, },
Some(ref mut stream) => { Some(ref mut stream) => {
host_write(stream, host::Reply::KernelException { host_write(stream, host::Reply::KernelException {
name: name, exceptions: exceptions,
message: message, stack_pointers: stack_pointers,
param: param,
file: file,
line: line,
column: column,
function: function,
backtrace: backtrace, backtrace: backtrace,
async_errors: unsafe { get_async_errors() } async_errors: unsafe { get_async_errors() }
}).map_err(|e| e.into()) }).map_err(|e| e.into())

View File

@ -1,4 +1,4 @@
#![feature(libc, panic_unwind, unwind_attributes, rustc_private, int_bits_const)] #![feature(libc, panic_unwind, unwind_attributes, rustc_private, int_bits_const, const_in_array_repeat_expressions)]
#![crate_name = "artiq_support"] #![crate_name = "artiq_support"]
#![crate_type = "cdylib"] #![crate_type = "cdylib"]
@ -58,23 +58,31 @@ mod cslice {
pub mod eh { pub mod eh {
#[path = "../../firmware/libeh/dwarf.rs"] #[path = "../../firmware/libeh/dwarf.rs"]
pub mod dwarf; pub mod dwarf;
#[path = "../../firmware/libeh/eh_artiq.rs"]
pub mod eh_artiq;
} }
#[path = "../../firmware/ksupport/eh_artiq.rs"] #[path = "../../firmware/ksupport/eh_artiq.rs"]
pub mod eh_artiq; pub mod eh_artiq;
use std::{str, process}; use std::{str, process};
fn terminate(exception: &eh_artiq::Exception, mut _backtrace: &mut [usize]) -> ! { fn terminate(exceptions: &'static [Option<eh_artiq::Exception<'static>>],
println!("Uncaught {}: {} ({}, {}, {})", _stack_pointers: &'static [eh_artiq::StackPointerBacktrace],
str::from_utf8(exception.name.as_ref()).unwrap(), _backtrace: &'static mut [(usize, usize)]) -> ! {
str::from_utf8(exception.message.as_ref()).unwrap(), println!("{}", exceptions.len());
exception.param[0], for exception in exceptions.iter() {
exception.param[1], let exception = exception.as_ref().unwrap();
exception.param[2]); println!("Uncaught {}: {} ({}, {}, {})",
println!("at {}:{}:{}", exception.id,
str::from_utf8(exception.file.as_ref()).unwrap(), str::from_utf8(exception.message.as_ref()).unwrap(),
exception.line, exception.param[0],
exception.column); exception.param[1],
exception.param[2]);
println!("at {}:{}:{}",
str::from_utf8(exception.file.as_ref()).unwrap(),
exception.line,
exception.column);
}
process::exit(1); process::exit(1);
} }

View File

@ -257,7 +257,7 @@
cargoDeps = rustPlatform.fetchCargoTarball { cargoDeps = rustPlatform.fetchCargoTarball {
name = "artiq-firmware-cargo-deps"; name = "artiq-firmware-cargo-deps";
src = "${self}/artiq/firmware"; src = "${self}/artiq/firmware";
sha256 = "sha256-Lf6M4M/jdRiO5MsWSoqtOSNfRIhbze+qvg4kaiiBWW4="; sha256 = "sha256-YyycMsDzR+JRcMZJd6A/CRi2J9nKmaWY/KXUnAQaZ00=";
}; };
nativeBuildInputs = [ nativeBuildInputs = [
(pkgs.python3.withPackages(ps: [ migen misoc artiq ])) (pkgs.python3.withPackages(ps: [ migen misoc artiq ]))