firmware: migrate to Rust 1.28.0.

See 2648b1b7 and bdd18de2.
release-3
whitequark 2018-09-20 09:43:01 +00:00
parent fb1dfcf372
commit b91822ffe6
32 changed files with 1179 additions and 671 deletions

View File

@ -17,6 +17,11 @@ dependencies = [
name = "backtrace_artiq"
version = "0.0.0"
[[package]]
name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "board"
version = "0.0.0"
@ -37,6 +42,11 @@ name = "byteorder"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "compiler_builtins"
version = "0.1.0"
@ -65,10 +75,17 @@ dependencies = [
name = "dyld"
version = "0.0.0"
[[package]]
name = "eh"
version = "0.0.0"
dependencies = [
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fringe"
version = "1.1.0"
source = "git+https://github.com/m-labs/libfringe?rev=bd23494#bd2349467157969324ca7da5d2ae033c7ffac0c0"
source = "git+https://github.com/m-labs/libfringe?rev=b8a6d8f#b8a6d8f68df0edaa3d67d9f3b7b62af9d3bb64a5"
dependencies = [
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -98,6 +115,7 @@ dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dyld 0.0.0",
"eh 0.0.0",
"proto 0.0.0",
"std_artiq 0.0.0",
]
@ -112,23 +130,31 @@ name = "log"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log_buffer"
version = "1.2.0"
source = "git+https://github.com/whitequark/rust-log_buffer?rev=rust-1.25#ff84e565d9954d5864d60aec12b9e1381505d72a"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "logger_artiq"
version = "0.0.0"
dependencies = [
"board 0.0.0",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log_buffer 1.2.0 (git+https://github.com/whitequark/rust-log_buffer?rev=rust-1.25)",
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log_buffer 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "managed"
version = "0.5.0"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -138,7 +164,7 @@ dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dyld 0.0.0",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"std_artiq 0.0.0",
]
@ -152,14 +178,14 @@ dependencies = [
"board 0.0.0",
"build_artiq 0.0.0",
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568)",
"cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"drtioaux 0.0.0",
"fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=bd23494)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=b8a6d8f)",
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"logger_artiq 0.0.0",
"managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"proto 0.0.0",
"smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=181083f)",
"smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=2139686)",
"std_artiq 0.0.0",
]
@ -194,11 +220,12 @@ dependencies = [
[[package]]
name = "smoltcp"
version = "0.4.0"
source = "git+https://github.com/m-labs/smoltcp?rev=181083f#181083f18c977b8a0463a67e360e4db20594fa21"
source = "git+https://github.com/m-labs/smoltcp?rev=2139686#21396867114d267da06f19cc54cc4a1883b900a5"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"managed 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -226,19 +253,22 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3"
"checksum compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins?rev=631b568)" = "<none>"
"checksum cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f8cb7306107e4b10e64994de6d3274bd08996a7c1322a27b86482392f96be0a"
"checksum fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=bd23494)" = "<none>"
"checksum fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=b8a6d8f)" = "<none>"
"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "36fbc8a8929c632868295d0178dd8f63fc423fd7537ad0738372bd010b3ac9b0"
"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
"checksum log_buffer 1.2.0 (git+https://github.com/whitequark/rust-log_buffer?rev=rust-1.25)" = "<none>"
"checksum managed 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "786bd3519bdfb0e1a57146a74b7584555dd6c4f1b6e1137c70e177d60dde8186"
"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f"
"checksum log_buffer 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f033173c9486b7fe97a79c895c0a3483ae395ab6744c985d10078950e2492419"
"checksum managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba6713e624266d7600e9feae51b1926c6a6a6bebb18ec5a8e11a5f1d5661baba"
"checksum rustc-cfg 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56a596b5718bf5e059d59a30af12f7f462a152de147aa462b70892849ee18704"
"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
"checksum smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=181083f)" = "<none>"
"checksum smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=2139686)" = "<none>"
"checksum walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bb08f9e670fab86099470b97cd2b252d6527f0b3cc1401acdb595ffc9dd288ff"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"

View File

@ -3,4 +3,5 @@ members = ["runtime", "ksupport", "satman"]
[profile.dev]
incremental = false # incompatible with LTO
lto = true
debug = 2

View File

@ -17,6 +17,7 @@ byteorder = { version = "1.0", default-features = false }
cslice = { version = "0.3" }
alloc_stub = { path = "../liballoc_stub" }
std_artiq = { path = "../libstd_artiq" }
eh = { path = "../libeh" }
dyld = { path = "../libdyld" }
board = { path = "../libboard" }
proto = { path = "../libproto" }

View File

@ -9,6 +9,7 @@ CFLAGS += \
LDFLAGS += --eh-frame-hdr \
-L../libcompiler-rt \
-L../libbase \
-L../libprintf \
-L../libm \
-L../libunwind
@ -22,7 +23,7 @@ $(RUSTOUT)/libksupport.a:
ksupport.elf: $(RUSTOUT)/libksupport.a glue.o
$(link) -T $(KSUPPORT_DIRECTORY)/ksupport.ld \
-lunwind-elf -lcompiler-rt -lbase -lm
-lunwind-elf -lcompiler-rt -lprintf-float -lm
%.o: $(KSUPPORT_DIRECTORY)/%.c
$(compile)

View File

@ -75,9 +75,9 @@ static mut API: &'static [(&'static str, *const ())] = &[
/* exceptions */
api!(_Unwind_Resume = ::unwind::_Unwind_Resume),
api!(__artiq_personality = ::eh::personality),
api!(__artiq_raise = ::eh::raise),
api!(__artiq_reraise = ::eh::reraise),
api!(__artiq_personality = ::eh_artiq::personality),
api!(__artiq_raise = ::eh_artiq::raise),
api!(__artiq_reraise = ::eh_artiq::reraise),
/* proxified syscalls */
api!(core_log),

View File

@ -1,474 +0,0 @@
// Portions of the code in this file are derived from code by:
//
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(non_upper_case_globals, non_camel_case_types, dead_code)]
use core::{ptr, mem};
use cslice::CSlice;
use unwind as uw;
use libc::{c_int, c_void};
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code;
extern {
fn _Unwind_ForcedUnwind(exception: *mut uw::_Unwind_Exception,
stop_fn: _Unwind_Stop_Fn,
stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code;
}
const DW_EH_PE_omit: u8 = 0xFF;
const DW_EH_PE_absptr: u8 = 0x00;
const DW_EH_PE_uleb128: u8 = 0x01;
const DW_EH_PE_udata2: u8 = 0x02;
const DW_EH_PE_udata4: u8 = 0x03;
const DW_EH_PE_udata8: u8 = 0x04;
const DW_EH_PE_sleb128: u8 = 0x09;
const DW_EH_PE_sdata2: u8 = 0x0A;
const DW_EH_PE_sdata4: u8 = 0x0B;
const DW_EH_PE_sdata8: u8 = 0x0C;
const DW_EH_PE_pcrel: u8 = 0x10;
const DW_EH_PE_textrel: u8 = 0x20;
const DW_EH_PE_datarel: u8 = 0x30;
const DW_EH_PE_funcrel: u8 = 0x40;
const DW_EH_PE_aligned: u8 = 0x50;
const DW_EH_PE_indirect: u8 = 0x80;
#[derive(Clone)]
struct DwarfReader {
pub ptr: *const u8,
}
#[repr(C,packed)]
#[derive(Clone, Copy)]
struct Unaligned<T>(T);
// This contortion is required due to https://github.com/rust-lang/rust/issues/27060.
impl<T> Unaligned<T> {
fn get(self) -> T { self.0 }
}
impl DwarfReader {
fn new(ptr: *const u8) -> DwarfReader {
DwarfReader { ptr: ptr }
}
// DWARF streams are packed, so e.g. a u32 would not necessarily be aligned
// on a 4-byte boundary. This may cause problems on platforms with strict
// alignment requirements. By wrapping data in a "packed" struct, we are
// telling the backend to generate "misalignment-safe" code.
unsafe fn read<T: Copy>(&mut self) -> T {
let result = *(self.ptr as *const Unaligned<T>);
self.ptr = self.ptr.offset(mem::size_of::<T>() as isize);
result.get()
}
// ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
// Length Data".
unsafe fn read_uleb128(&mut self) -> u64 {
let mut shift: usize = 0;
let mut result: u64 = 0;
let mut byte: u8;
loop {
byte = self.read::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
result
}
unsafe fn read_sleb128(&mut self) -> i64 {
let mut shift: usize = 0;
let mut result: u64 = 0;
let mut byte: u8;
loop {
byte = self.read::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
// sign-extend
if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 {
result |= (!0 as u64) << shift;
}
result as i64
}
unsafe fn read_encoded_pointer(&mut self, encoding: u8) -> usize {
fn round_up(unrounded: usize, align: usize) -> usize {
debug_assert!(align.is_power_of_two());
(unrounded + align - 1) & !(align - 1)
}
debug_assert!(encoding != DW_EH_PE_omit);
// DW_EH_PE_aligned implies it's an absolute pointer value
if encoding == DW_EH_PE_aligned {
self.ptr = round_up(self.ptr as usize, mem::size_of::<usize>()) as *const u8;
return self.read::<usize>()
}
let value_ptr = self.ptr;
let mut result = match encoding & 0x0F {
DW_EH_PE_absptr => self.read::<usize>(),
DW_EH_PE_uleb128 => self.read_uleb128() as usize,
DW_EH_PE_udata2 => self.read::<u16>() as usize,
DW_EH_PE_udata4 => self.read::<u32>() as usize,
DW_EH_PE_udata8 => self.read::<u64>() as usize,
DW_EH_PE_sleb128 => self.read_sleb128() as usize,
DW_EH_PE_sdata2 => self.read::<i16>() as usize,
DW_EH_PE_sdata4 => self.read::<i32>() as usize,
DW_EH_PE_sdata8 => self.read::<i64>() as usize,
_ => panic!(),
};
result += match encoding & 0x70 {
DW_EH_PE_absptr => 0,
// relative to address of the encoded value, despite the name
DW_EH_PE_pcrel => value_ptr as usize,
_ => panic!(),
};
if encoding & DW_EH_PE_indirect != 0 {
result = *(result as *const usize);
}
result
}
}
fn encoding_size(encoding: u8) -> usize {
if encoding == DW_EH_PE_omit {
return 0
}
match encoding & 0x0F {
DW_EH_PE_absptr => mem::size_of::<usize>(),
DW_EH_PE_udata2 => 2,
DW_EH_PE_udata4 => 4,
DW_EH_PE_udata8 => 8,
DW_EH_PE_sdata2 => 2,
DW_EH_PE_sdata4 => 4,
DW_EH_PE_sdata8 => 8,
_ => panic!()
}
}
pub enum EHAction {
None,
Cleanup(usize),
Catch(usize),
Terminate,
}
unsafe fn find_eh_action(lsda: *const u8, func_start: usize, ip: usize,
exn_name: CSlice<u8>) -> EHAction {
if lsda.is_null() {
return EHAction::None
}
let mut reader = DwarfReader::new(lsda);
let start_encoding = reader.read::<u8>();
// base address for landing pad offsets
let lpad_base = if start_encoding != DW_EH_PE_omit {
reader.read_encoded_pointer(start_encoding)
} else {
func_start
};
let ttype_encoding = reader.read::<u8>();
let ttype_encoding_size = encoding_size(ttype_encoding) as isize;
let class_info;
if ttype_encoding != DW_EH_PE_omit {
let class_info_offset = reader.read_uleb128();
class_info = reader.ptr.offset(class_info_offset as isize);
} else {
class_info = ptr::null();
}
assert!(!class_info.is_null());
let call_site_encoding = reader.read::<u8>();
let call_site_table_length = reader.read_uleb128();
let action_table = reader.ptr.offset(call_site_table_length as isize);
while reader.ptr < action_table {
let cs_start = reader.read_encoded_pointer(call_site_encoding);
let cs_len = reader.read_encoded_pointer(call_site_encoding);
let cs_lpad = reader.read_encoded_pointer(call_site_encoding);
let cs_action = reader.read_uleb128();
if ip < func_start + cs_start {
// Callsite table is sorted by cs_start, so if we've passed the ip, we
// may stop searching.
break
}
if ip > func_start + cs_start + cs_len {
continue
}
if cs_lpad == 0 {
return EHAction::None
}
let lpad = lpad_base + cs_lpad;
if cs_action == 0 {
return EHAction::Cleanup(lpad)
}
let action_entry = action_table.offset((cs_action - 1) as isize);
let mut action_reader = DwarfReader::new(action_entry);
loop {
let type_info_offset = action_reader.read_sleb128() as isize;
let action_offset = action_reader.clone().read_sleb128() as isize;
assert!(type_info_offset >= 0);
if type_info_offset > 0 {
let type_info_ptr_ptr = class_info.offset(-type_info_offset * ttype_encoding_size);
let type_info_ptr = DwarfReader::new(type_info_ptr_ptr)
.read_encoded_pointer(ttype_encoding);
let type_info = *(type_info_ptr as *const CSlice<u8>);
if type_info.as_ref() == exn_name.as_ref() {
return EHAction::Catch(lpad)
}
if type_info.len() == 0 {
// This is a catch-all clause. We don't compare type_info_ptr with null here
// because, in PIC mode, the OR1K LLVM backend emits a literal zero
// encoded with DW_EH_PE_pcrel, which of course doesn't result in
// a proper null pointer.
return EHAction::Catch(lpad)
}
}
if action_offset == 0 {
break
} else {
action_reader.ptr = action_reader.ptr.offset(action_offset)
}
}
return EHAction::None
}
// the function has a personality but no landing pads; this is fine
EHAction::None
}
#[repr(C)]
#[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 MAX_BACKTRACE_SIZE: usize = 128;
#[repr(C)]
struct ExceptionInfo {
uw_exception: uw::_Unwind_Exception,
exception: Option<Exception<'static>>,
handled: bool,
backtrace: [usize; MAX_BACKTRACE_SIZE],
backtrace_size: usize
}
#[cfg(target_arch = "x86_64")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
#[cfg(any(target_arch = "or1k"))]
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4
#[export_name="__artiq_personality"]
pub extern fn personality(version: c_int,
actions: uw::_Unwind_Action,
uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
unsafe {
if version != 1 || uw_exception_class != EXCEPTION_CLASS {
return uw::_URC_FATAL_PHASE1_ERROR
}
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let ip = uw::_Unwind_GetIP(context) - 1;
let func_start = uw::_Unwind_GetRegionStart(context);
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
let exception = &exception_info.exception.unwrap();
let eh_action = find_eh_action(lsda, func_start, ip, exception.name);
if actions as u32 & uw::_UA_SEARCH_PHASE as u32 != 0 {
match eh_action {
EHAction::None |
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
}
} else {
match eh_action {
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
EHAction::Cleanup(lpad) |
EHAction::Catch(lpad) => {
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,
}
}
}
}
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
uw_exception: *mut uw::_Unwind_Exception) {
unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
exception_info.exception = None;
}
}
extern fn uncaught_exception(_version: c_int,
actions: uw::_Unwind_Action,
_uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
_stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code {
unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
if exception_info.backtrace_size < exception_info.backtrace.len() {
let ip = uw::_Unwind_GetIP(context);
exception_info.backtrace[exception_info.backtrace_size] = ip;
exception_info.backtrace_size += 1;
}
if actions as u32 & uw::_UA_END_OF_STACK as u32 != 0 {
::terminate(&exception_info.exception.unwrap(),
exception_info.backtrace[..exception_info.backtrace_size].as_mut())
} else {
uw::_URC_NO_REASON
}
}
}
// We can unfortunately not use mem::zeroed in a static, so Option<> is used as a workaround.
// See https://github.com/rust-lang/rust/issues/39498.
static mut INFLIGHT: ExceptionInfo = ExceptionInfo {
uw_exception: uw::_Unwind_Exception {
exception_class: EXCEPTION_CLASS,
exception_cleanup: cleanup,
private: [0; uw::unwinder_private_data_size],
},
exception: None,
handled: true,
backtrace: [0; MAX_BACKTRACE_SIZE],
backtrace_size: 0
};
#[export_name="__artiq_raise"]
#[unwind]
pub unsafe extern fn raise(exception: *const Exception) -> ! {
// Zing! The Exception<'a> as Exception<'static> cast is not really sound in case
// 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]
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)
}
}
// Stub implementations for the functions the panic_unwind crate expects to be provided.
// These all do nothing in libunwind, but aren't built for OR1K.
pub mod stubs {
#![allow(bad_style, unused_variables)]
use super::{uw, c_int};
#[export_name="_Unwind_GetIPInfo"]
pub unsafe extern fn _Unwind_GetIPInfo(ctx: *mut uw::_Unwind_Context,
ip_before_insn: *mut c_int) -> uw::_Unwind_Word {
*ip_before_insn = 0;
uw::_Unwind_GetIP(ctx)
}
#[export_name="_Unwind_GetTextRelBase"]
pub unsafe extern fn _Unwind_GetTextRelBase(ctx: *mut uw::_Unwind_Context) -> uw::_Unwind_Ptr {
unimplemented!()
}
#[export_name="_Unwind_GetDataRelBase"]
pub unsafe extern fn _Unwind_GetDataRelBase(ctx: *mut uw::_Unwind_Context) -> uw::_Unwind_Ptr {
unimplemented!()
}
}

View File

@ -0,0 +1,203 @@
// Portions of the code in this file are derived from code by:
//
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(private_no_mangle_fns, non_camel_case_types)]
use core::{ptr, mem};
use cslice::CSlice;
use unwind as uw;
use libc::{c_int, c_void};
use eh::dwarf::{self, EHAction};
type _Unwind_Stop_Fn = extern "C" fn(version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code;
extern {
fn _Unwind_ForcedUnwind(exception: *mut uw::_Unwind_Exception,
stop_fn: _Unwind_Stop_Fn,
stop_parameter: *mut c_void) -> uw::_Unwind_Reason_Code;
}
#[repr(C)]
#[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 MAX_BACKTRACE_SIZE: usize = 128;
#[repr(C)]
struct ExceptionInfo {
uw_exception: uw::_Unwind_Exception,
exception: Option<Exception<'static>>,
handled: bool,
backtrace: [usize; MAX_BACKTRACE_SIZE],
backtrace_size: usize
}
#[cfg(target_arch = "x86_64")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
#[cfg(any(target_arch = "or1k"))]
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4
#[export_name="__artiq_personality"]
pub extern fn personality(version: c_int,
actions: uw::_Unwind_Action,
uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
unsafe {
if version != 1 || uw_exception_class != EXCEPTION_CLASS {
return uw::_URC_FATAL_PHASE1_ERROR
}
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let ip = uw::_Unwind_GetIP(context) - 1;
let func_start = uw::_Unwind_GetRegionStart(context);
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
let exception = &exception_info.exception.unwrap();
let eh_action = dwarf::find_eh_action(lsda, func_start, ip, exception.name);
if actions as u32 & uw::_UA_SEARCH_PHASE as u32 != 0 {
match eh_action {
EHAction::None |
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
}
} else {
match eh_action {
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
EHAction::Cleanup(lpad) |
EHAction::Catch(lpad) => {
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,
}
}
}
}
extern fn cleanup(_unwind_code: uw::_Unwind_Reason_Code,
uw_exception: *mut uw::_Unwind_Exception) {
unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
exception_info.exception = None;
}
}
extern fn uncaught_exception(_version: c_int,
actions: uw::_Unwind_Action,
_uw_exception_class: uw::_Unwind_Exception_Class,
uw_exception: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
_stop_parameter: *mut c_void)
-> uw::_Unwind_Reason_Code {
unsafe {
let exception_info = &mut *(uw_exception as *mut ExceptionInfo);
if exception_info.backtrace_size < exception_info.backtrace.len() {
let ip = uw::_Unwind_GetIP(context);
exception_info.backtrace[exception_info.backtrace_size] = ip;
exception_info.backtrace_size += 1;
}
if actions as u32 & uw::_UA_END_OF_STACK as u32 != 0 {
::terminate(&exception_info.exception.unwrap(),
exception_info.backtrace[..exception_info.backtrace_size].as_mut())
} else {
uw::_URC_NO_REASON
}
}
}
// We can unfortunately not use mem::zeroed in a static, so Option<> is used as a workaround.
// See https://github.com/rust-lang/rust/issues/39498.
static mut INFLIGHT: ExceptionInfo = ExceptionInfo {
uw_exception: uw::_Unwind_Exception {
exception_class: EXCEPTION_CLASS,
exception_cleanup: cleanup,
private: [0; uw::unwinder_private_data_size],
},
exception: None,
handled: true,
backtrace: [0; MAX_BACKTRACE_SIZE],
backtrace_size: 0
};
#[export_name="__artiq_raise"]
#[unwind(allowed)]
pub unsafe extern fn raise(exception: *const Exception) -> ! {
// Zing! The Exception<'a> to Exception<'static> transmute is not really sound in case
// 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)
}
}

View File

@ -19,6 +19,8 @@ void send_to_rtio_log(long long int timestamp, struct slice data);
#define KERNELCPU_LAST_ADDRESS 0x4fffffff
#define KSUPPORT_HEADER_SIZE 0x80
FILE *stderr;
/* called by libunwind */
int fprintf(FILE *stream, const char *fmt, ...)
{

View File

@ -1,4 +1,5 @@
#![feature(lang_items, asm, libc, panic_unwind, unwind_attributes, global_allocator)]
#![feature(lang_items, asm, panic_unwind, libc, unwind_attributes,
panic_implementation, panic_info_message)]
#![no_std]
extern crate unwind;
@ -10,6 +11,7 @@ extern crate alloc_stub;
extern crate std_artiq as std;
extern crate board;
extern crate eh;
extern crate dyld;
extern crate proto;
extern crate amp;
@ -52,10 +54,26 @@ macro_rules! recv {
}
}
#[no_mangle]
#[lang = "panic_fmt"]
pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, line: u32) -> ! {
send(&Log(format_args!("panic at {}:{}: {}\n", file, line, args)));
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[lang = "oom"] // https://github.com/rust-lang/rust/issues/51540
pub fn oom(_layout: core::alloc::Layout) -> ! {
unreachable!()
}
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[panic_implementation]
pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
if let Some(location) = info.location() {
send(&Log(format_args!("panic at {}:{}:{}",
location.file(), location.line(), location.column())));
} else {
send(&Log(format_args!("panic at unknown location")));
}
if let Some(message) = info.message() {
send(&Log(format_args!("{}\n", message)));
} else {
send(&Log(format_args!("\n")));
}
send(&RunAborted);
loop {}
}
@ -72,7 +90,7 @@ macro_rules! println {
macro_rules! raise {
($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({
use cslice::AsCSlice;
let exn = $crate::eh::Exception {
let exn = $crate::eh_artiq::Exception {
name: concat!("0:artiq.coredevice.exceptions.", $name).as_c_slice(),
file: file!().as_c_slice(),
line: line!(),
@ -83,14 +101,14 @@ macro_rules! raise {
param: [$param0, $param1, $param2]
};
#[allow(unused_unsafe)]
unsafe { $crate::eh::raise(&exn) }
unsafe { $crate::eh_artiq::raise(&exn) }
});
($name:expr, $message:expr) => ({
raise!($name, $message, 0, 0, 0)
});
}
pub mod eh;
mod eh_artiq;
mod api;
mod rtio;
mod nrt_bus;
@ -114,6 +132,7 @@ pub extern fn send_to_rtio_log(timestamp: i64, text: CSlice<u8>) {
rtio::log(timestamp, text.as_ref())
}
#[unwind(aborts)]
extern fn rpc_send(service: u32, tag: CSlice<u8>, data: *const *const ()) {
while !rpc_queue::empty() {}
send(&RpcSend {
@ -124,6 +143,7 @@ extern fn rpc_send(service: u32, tag: CSlice<u8>, data: *const *const ()) {
})
}
#[unwind(aborts)]
extern fn rpc_send_async(service: u32, tag: CSlice<u8>, data: *const *const ()) {
while rpc_queue::full() {}
rpc_queue::enqueue(|mut slice| {
@ -146,6 +166,7 @@ extern fn rpc_send_async(service: u32, tag: CSlice<u8>, data: *const *const ())
})
}
#[unwind(allowed)]
extern fn rpc_recv(slot: *mut ()) -> usize {
send(&RpcRecvRequest(slot));
recv!(&RpcRecvReply(ref result) => {
@ -153,7 +174,7 @@ extern fn rpc_recv(slot: *mut ()) -> usize {
&Ok(alloc_size) => alloc_size,
&Err(ref exception) =>
unsafe {
eh::raise(&eh::Exception {
eh_artiq::raise(&eh_artiq::Exception {
name: exception.name.as_bytes().as_c_slice(),
file: exception.file.as_bytes().as_c_slice(),
line: exception.line,
@ -167,7 +188,7 @@ extern fn rpc_recv(slot: *mut ()) -> usize {
})
}
fn terminate(exception: &eh::Exception, backtrace: &mut [usize]) -> ! {
fn terminate(exception: &eh_artiq::Exception, backtrace: &mut [usize]) -> ! {
let mut cursor = 0;
for index in 0..backtrace.len() {
if backtrace[index] > kernel_proto::KERNELCPU_PAYLOAD_ADDRESS {
@ -193,6 +214,7 @@ fn terminate(exception: &eh::Exception, backtrace: &mut [usize]) -> ! {
loop {}
}
#[unwind(allowed)]
extern fn watchdog_set(ms: i64) -> i32 {
if ms < 0 {
raise!("ValueError", "cannot set a watchdog with a negative timeout")
@ -202,10 +224,12 @@ extern fn watchdog_set(ms: i64) -> i32 {
recv!(&WatchdogSetReply { id } => id) as i32
}
#[unwind(aborts)]
extern fn watchdog_clear(id: i32) {
send(&WatchdogClear { id: id as usize })
}
#[unwind(aborts)]
extern fn cache_get(key: CSlice<u8>) -> CSlice<'static, i32> {
send(&CacheGetRequest {
key: str::from_utf8(key.as_ref()).unwrap()
@ -213,6 +237,7 @@ extern fn cache_get(key: CSlice<u8>) -> CSlice<'static, i32> {
recv!(&CacheGetReply { value } => value.as_c_slice())
}
#[unwind(allowed)]
extern fn cache_put(key: CSlice<u8>, list: CSlice<i32>) {
send(&CachePutRequest {
key: str::from_utf8(key.as_ref()).unwrap(),
@ -246,6 +271,7 @@ fn dma_record_flush() {
}
}
#[unwind(allowed)]
extern fn dma_record_start(name: CSlice<u8>) {
let name = str::from_utf8(name.as_ref()).unwrap();
@ -265,6 +291,7 @@ extern fn dma_record_start(name: CSlice<u8>) {
}
}
#[unwind(allowed)]
extern fn dma_record_stop(duration: i64) {
unsafe {
dma_record_flush();
@ -286,10 +313,12 @@ extern fn dma_record_stop(duration: i64) {
}
}
#[unwind(aborts)]
extern fn dma_record_output(timestamp: i64, channel: i32, address: i32, word: i32) {
dma_record_output_wide(timestamp, channel, address, [word].as_c_slice())
}
#[unwind(aborts)]
extern fn dma_record_output_wide(timestamp: i64, channel: i32, address: i32, words: CSlice<i32>) {
assert!(words.len() <= 16); // enforce the hardware limit
@ -338,6 +367,7 @@ extern fn dma_record_output_wide(timestamp: i64, channel: i32, address: i32, wor
}
}
#[unwind(aborts)]
extern fn dma_erase(name: CSlice<u8>) {
let name = str::from_utf8(name.as_ref()).unwrap();
@ -350,6 +380,7 @@ struct DmaTrace {
address: i32,
}
#[unwind(allowed)]
extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
let name = str::from_utf8(name.as_ref()).unwrap();
@ -369,6 +400,7 @@ extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
})
}
#[unwind(allowed)]
extern fn dma_playback(timestamp: i64, ptr: i32) {
assert!(ptr % 64 == 0);
@ -483,12 +515,13 @@ pub unsafe fn main() {
}
#[no_mangle]
#[unwind(allowed)]
pub extern fn exception_handler(vect: u32, _regs: *const u32, pc: u32, ea: u32) {
panic!("exception {:?} at PC 0x{:x}, EA 0x{:x}", vect, pc, ea)
}
// We don't export this because libbase does.
// #[no_mangle]
#[no_mangle]
#[unwind(allowed)]
pub extern fn abort() {
panic!("aborted")
}

View File

@ -1,10 +1,7 @@
#![feature(alloc, allocator_api)]
#![no_std]
extern crate alloc;
use core::{mem, fmt};
use alloc::allocator::{Layout, AllocErr, Alloc};
use core::{ptr, mem, fmt};
use core::alloc::{GlobalAlloc, Layout};
// The minimum alignment guaranteed by the architecture.
const MIN_ALIGN: usize = 4;
@ -42,10 +39,10 @@ impl ListAlloc {
}
}
unsafe impl<'a> Alloc for &'a ListAlloc {
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
unsafe impl GlobalAlloc for ListAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if layout.align() > MIN_ALIGN {
return Err(AllocErr::Unsupported { details: "alignment too large" })
panic!("cannot allocate with alignment {}", layout.align())
}
let header_size = mem::size_of::<Header>();
@ -83,7 +80,7 @@ unsafe impl<'a> Alloc for &'a ListAlloc {
if (*curr).size >= size {
(*curr).magic = MAGIC_BUSY;
return Ok(curr.offset(1) as *mut u8)
return curr.offset(1) as *mut u8
}
},
_ => panic!("heap corruption detected at {:p}", curr)
@ -92,20 +89,16 @@ unsafe impl<'a> Alloc for &'a ListAlloc {
curr = (*curr).next;
}
Err(AllocErr::Exhausted { request: layout })
ptr::null_mut()
}
unsafe fn dealloc(&mut self, ptr: *mut u8, _layout: Layout) {
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
let curr = (ptr as *mut Header).offset(-1);
if (*curr).magic != MAGIC_BUSY {
panic!("heap corruption detected at {:p}", curr)
}
(*curr).magic = MAGIC_FREE;
}
fn oom(&mut self, err: AllocErr) -> ! {
panic!("heap view: {}\ncannot allocate: {:?}", self, err)
}
}
impl fmt::Display for ListAlloc {

View File

@ -3,16 +3,16 @@
extern crate alloc;
use alloc::allocator::{Layout, AllocErr, Alloc};
use core::alloc::{Layout, GlobalAlloc};
pub struct StubAlloc;
unsafe impl<'a> Alloc for &'a StubAlloc {
unsafe fn alloc(&mut self, _layout: Layout) -> Result<*mut u8, AllocErr> {
unsafe impl GlobalAlloc for StubAlloc {
unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
unimplemented!()
}
unsafe fn dealloc(&mut self, _ptr: *mut u8, _layout: Layout) {
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
unimplemented!()
}
}

View File

@ -0,0 +1,11 @@
[package]
authors = ["M-Labs"]
name = "eh"
version = "0.0.0"
[lib]
name = "eh"
path = "lib.rs"
[dependencies]
cslice = { version = "0.3" }

View File

@ -0,0 +1,243 @@
#![allow(non_upper_case_globals, dead_code)]
use core::{ptr, mem};
use cslice::CSlice;
const DW_EH_PE_omit: u8 = 0xFF;
const DW_EH_PE_absptr: u8 = 0x00;
const DW_EH_PE_uleb128: u8 = 0x01;
const DW_EH_PE_udata2: u8 = 0x02;
const DW_EH_PE_udata4: u8 = 0x03;
const DW_EH_PE_udata8: u8 = 0x04;
const DW_EH_PE_sleb128: u8 = 0x09;
const DW_EH_PE_sdata2: u8 = 0x0A;
const DW_EH_PE_sdata4: u8 = 0x0B;
const DW_EH_PE_sdata8: u8 = 0x0C;
const DW_EH_PE_pcrel: u8 = 0x10;
const DW_EH_PE_textrel: u8 = 0x20;
const DW_EH_PE_datarel: u8 = 0x30;
const DW_EH_PE_funcrel: u8 = 0x40;
const DW_EH_PE_aligned: u8 = 0x50;
const DW_EH_PE_indirect: u8 = 0x80;
#[derive(Clone)]
struct DwarfReader {
pub ptr: *const u8,
}
impl DwarfReader {
fn new(ptr: *const u8) -> DwarfReader {
DwarfReader { ptr: ptr }
}
// DWARF streams are packed, so e.g. a u32 would not necessarily be aligned
// on a 4-byte boundary. This may cause problems on platforms with strict
// alignment requirements. By wrapping data in a "packed" struct, we are
// telling the backend to generate "misalignment-safe" code.
unsafe fn read<T: Copy>(&mut self) -> T {
let result = ptr::read_unaligned(self.ptr as *const T);
self.ptr = self.ptr.offset(mem::size_of::<T>() as isize);
result
}
// ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
// Length Data".
unsafe fn read_uleb128(&mut self) -> u64 {
let mut shift: usize = 0;
let mut result: u64 = 0;
let mut byte: u8;
loop {
byte = self.read::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
result
}
unsafe fn read_sleb128(&mut self) -> i64 {
let mut shift: usize = 0;
let mut result: u64 = 0;
let mut byte: u8;
loop {
byte = self.read::<u8>();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
// sign-extend
if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 {
result |= (!0 as u64) << shift;
}
result as i64
}
unsafe fn read_encoded_pointer(&mut self, encoding: u8) -> usize {
fn round_up(unrounded: usize, align: usize) -> usize {
debug_assert!(align.is_power_of_two());
(unrounded + align - 1) & !(align - 1)
}
debug_assert!(encoding != DW_EH_PE_omit);
// DW_EH_PE_aligned implies it's an absolute pointer value
if encoding == DW_EH_PE_aligned {
self.ptr = round_up(self.ptr as usize, mem::size_of::<usize>()) as *const u8;
return self.read::<usize>()
}
let value_ptr = self.ptr;
let mut result = match encoding & 0x0F {
DW_EH_PE_absptr => self.read::<usize>(),
DW_EH_PE_uleb128 => self.read_uleb128() as usize,
DW_EH_PE_udata2 => self.read::<u16>() as usize,
DW_EH_PE_udata4 => self.read::<u32>() as usize,
DW_EH_PE_udata8 => self.read::<u64>() as usize,
DW_EH_PE_sleb128 => self.read_sleb128() as usize,
DW_EH_PE_sdata2 => self.read::<i16>() as usize,
DW_EH_PE_sdata4 => self.read::<i32>() as usize,
DW_EH_PE_sdata8 => self.read::<i64>() as usize,
_ => panic!(),
};
result += match encoding & 0x70 {
DW_EH_PE_absptr => 0,
// relative to address of the encoded value, despite the name
DW_EH_PE_pcrel => value_ptr as usize,
_ => panic!(),
};
if encoding & DW_EH_PE_indirect != 0 {
result = *(result as *const usize);
}
result
}
}
fn encoding_size(encoding: u8) -> usize {
if encoding == DW_EH_PE_omit {
return 0
}
match encoding & 0x0F {
DW_EH_PE_absptr => mem::size_of::<usize>(),
DW_EH_PE_udata2 => 2,
DW_EH_PE_udata4 => 4,
DW_EH_PE_udata8 => 8,
DW_EH_PE_sdata2 => 2,
DW_EH_PE_sdata4 => 4,
DW_EH_PE_sdata8 => 8,
_ => panic!()
}
}
pub enum EHAction {
None,
Cleanup(usize),
Catch(usize),
Terminate,
}
pub unsafe fn find_eh_action(lsda: *const u8, func_start: usize, ip: usize,
exn_name: CSlice<u8>) -> EHAction {
if lsda.is_null() {
return EHAction::None
}
let mut reader = DwarfReader::new(lsda);
let start_encoding = reader.read::<u8>();
// base address for landing pad offsets
let lpad_base = if start_encoding != DW_EH_PE_omit {
reader.read_encoded_pointer(start_encoding)
} else {
func_start
};
let ttype_encoding = reader.read::<u8>();
let ttype_encoding_size = encoding_size(ttype_encoding) as isize;
let class_info;
if ttype_encoding != DW_EH_PE_omit {
let class_info_offset = reader.read_uleb128();
class_info = reader.ptr.offset(class_info_offset as isize);
} else {
class_info = ptr::null();
}
assert!(!class_info.is_null());
let call_site_encoding = reader.read::<u8>();
let call_site_table_length = reader.read_uleb128();
let action_table = reader.ptr.offset(call_site_table_length as isize);
while reader.ptr < action_table {
let cs_start = reader.read_encoded_pointer(call_site_encoding);
let cs_len = reader.read_encoded_pointer(call_site_encoding);
let cs_lpad = reader.read_encoded_pointer(call_site_encoding);
let cs_action = reader.read_uleb128();
if ip < func_start + cs_start {
// Callsite table is sorted by cs_start, so if we've passed the ip, we
// may stop searching.
break
}
if ip > func_start + cs_start + cs_len {
continue
}
if cs_lpad == 0 {
return EHAction::None
}
let lpad = lpad_base + cs_lpad;
if cs_action == 0 {
return EHAction::Cleanup(lpad)
}
let action_entry = action_table.offset((cs_action - 1) as isize);
let mut action_reader = DwarfReader::new(action_entry);
loop {
let type_info_offset = action_reader.read_sleb128() as isize;
let action_offset = action_reader.clone().read_sleb128() as isize;
assert!(type_info_offset >= 0);
if type_info_offset > 0 {
let type_info_ptr_ptr = class_info.offset(-type_info_offset * ttype_encoding_size);
let type_info_ptr = DwarfReader::new(type_info_ptr_ptr)
.read_encoded_pointer(ttype_encoding);
let type_info = *(type_info_ptr as *const CSlice<u8>);
if type_info.as_ref() == exn_name.as_ref() {
return EHAction::Catch(lpad)
}
if type_info.len() == 0 {
// This is a catch-all clause. We don't compare type_info_ptr with null here
// because, in PIC mode, the OR1K LLVM backend emits a literal zero
// encoded with DW_EH_PE_pcrel, which of course doesn't result in
// a proper null pointer.
return EHAction::Catch(lpad)
}
}
if action_offset == 0 {
break
} else {
action_reader.ptr = action_reader.ptr.offset(action_offset)
}
}
return EHAction::None
}
// the function has a personality but no landing pads; this is fine
EHAction::None
}

View File

@ -0,0 +1,88 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// This is the Rust personality function, adapted for use in ARTIQ. We never actually panic
// from Rust or recover from Rust exceptions (there's nothing to catch the panics), but we
// need a personality function to step back through Rust frames in order to make a backtrace.
//
// By design, this personality function is only ever called in the search phase, although
// to keep things simple and close to upstream, it is not modified
#![allow(private_no_mangle_fns)]
use unwind as uw;
use libc::{c_int, uintptr_t};
use cslice::AsCSlice;
use dwarf::{self, EHAction};
// Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister()
// and TargetLowering::getExceptionSelectorRegister() for each architecture,
// then mapped to DWARF register numbers via register definition tables
// (typically <arch>RegisterInfo.td, search for "DwarfRegNum").
// See also http://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register.
#[cfg(target_arch = "x86")]
const UNWIND_DATA_REG: (i32, i32) = (0, 2); // EAX, EDX
#[cfg(target_arch = "x86_64")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
#[cfg(any(target_arch = "or1k"))]
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4
// The following code is based on GCC's C and C++ personality routines. For reference, see:
// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc
// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
#[lang = "eh_personality"]
#[no_mangle]
#[allow(unused)]
unsafe extern "C" fn rust_eh_personality(version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code {
if version != 1 {
return uw::_URC_FATAL_PHASE1_ERROR;
}
let eh_action = match find_eh_action(context) {
Ok(action) => action,
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
};
if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 {
match eh_action {
EHAction::None |
EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
}
} else {
match eh_action {
EHAction::None => return uw::_URC_CONTINUE_UNWIND,
EHAction::Cleanup(lpad) |
EHAction::Catch(lpad) => {
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, exception_object as uintptr_t);
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
uw::_Unwind_SetIP(context, lpad);
return uw::_URC_INSTALL_CONTEXT;
}
EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
}
}
}
unsafe fn find_eh_action(context: *mut uw::_Unwind_Context)
-> Result<EHAction, ()>
{
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let func = uw::_Unwind_GetRegionStart(context);
let ip = uw::_Unwind_GetIP(context);
Ok(dwarf::find_eh_action(lsda, func, ip, [].as_c_slice()))
}

View File

@ -0,0 +1,9 @@
#![feature(lang_items, panic_unwind, libc, unwind_attributes)]
#![no_std]
extern crate cslice;
extern crate unwind;
extern crate libc;
pub mod dwarf;
// pub mod eh_rust;

View File

@ -8,6 +8,6 @@ name = "logger_artiq"
path = "lib.rs"
[dependencies]
log = { version = "0.3", default-features = false, features = [] }
log_buffer = { version = "1.2", git = "https://github.com/whitequark/rust-log_buffer", rev = "rust-1.25" }
log = { version = "0.4", default-features = false }
log_buffer = { version = "1.2" }
board = { path = "../libboard" }

View File

@ -2,31 +2,29 @@
extern crate log;
extern crate log_buffer;
#[macro_use]
extern crate board;
use core::ptr;
use core::cell::{Cell, RefCell, Ref, RefMut};
use core::cell::{Cell, RefCell, RefMut};
use core::fmt::Write;
use log::{Log, LogMetadata, LogRecord, LogLevelFilter, MaxLogLevelFilter};
use log::{Log, LevelFilter};
use log_buffer::LogBuffer;
use board::{Console, clock};
use board::clock;
pub struct LogBufferRef<'a> {
buffer: RefMut<'a, LogBuffer<&'static mut [u8]>>,
filter: Ref<'a, MaxLogLevelFilter>,
old_log_level: LogLevelFilter
old_log_level: LevelFilter
}
impl<'a> LogBufferRef<'a> {
fn new(buffer: RefMut<'a, LogBuffer<&'static mut [u8]>>,
filter: Ref<'a, MaxLogLevelFilter>) -> LogBufferRef<'a> {
let old_log_level = filter.get();
filter.set(LogLevelFilter::Off);
LogBufferRef { buffer, filter, old_log_level }
fn new(buffer: RefMut<'a, LogBuffer<&'static mut [u8]>>) -> LogBufferRef<'a> {
let old_log_level = log::max_level();
log::set_max_level(LevelFilter::Off);
LogBufferRef { buffer, old_log_level }
}
pub fn is_empty(&mut self) -> bool {
self.buffer.extract().len() == 0
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
pub fn clear(&mut self) {
@ -40,14 +38,13 @@ impl<'a> LogBufferRef<'a> {
impl<'a> Drop for LogBufferRef<'a> {
fn drop(&mut self) {
self.filter.set(self.old_log_level)
log::set_max_level(self.old_log_level)
}
}
pub struct BufferLogger {
buffer: RefCell<LogBuffer<&'static mut [u8]>>,
filter: RefCell<Option<MaxLogLevelFilter>>,
uart_filter: Cell<LogLevelFilter>
uart_filter: Cell<LevelFilter>
}
static mut LOGGER: *const BufferLogger = 0 as *const _;
@ -56,28 +53,18 @@ impl BufferLogger {
pub fn new(buffer: &'static mut [u8]) -> BufferLogger {
BufferLogger {
buffer: RefCell::new(LogBuffer::new(buffer)),
filter: RefCell::new(None),
uart_filter: Cell::new(LogLevelFilter::Info),
uart_filter: Cell::new(LevelFilter::Info),
}
}
pub fn register<F: FnOnce()>(&self, f: F) {
// log::set_logger_raw captures a pointer to ourselves, so we must prevent
// ourselves from being moved or dropped after that function is called (and
// before log::shutdown_logger_raw is called).
unsafe {
log::set_logger_raw(|max_log_level| {
max_log_level.set(LogLevelFilter::Info);
*self.filter.borrow_mut() = Some(max_log_level);
self as *const Log
}).expect("global logger can only be initialized once");
LOGGER = self;
log::set_logger(&*LOGGER)
.expect("global logger can only be initialized once");
}
log::set_max_level(LevelFilter::Info);
f();
log::shutdown_logger_raw().unwrap();
unsafe {
LOGGER = ptr::null();
}
}
pub fn with<R, F: FnOnce(&BufferLogger) -> R>(f: F) -> R {
@ -85,34 +72,17 @@ impl BufferLogger {
}
pub fn buffer<'a>(&'a self) -> Result<LogBufferRef<'a>, ()> {
let filter = Ref::map(self.filter.borrow(), |f| f.as_ref().unwrap());
self.buffer
.try_borrow_mut()
.map(|buffer| LogBufferRef::new(buffer, filter))
.map(LogBufferRef::new)
.map_err(|_| ())
}
pub fn max_log_level(&self) -> LogLevelFilter {
self.filter
.borrow()
.as_ref()
.expect("register the logger before touching maximum log level")
.get()
}
pub fn set_max_log_level(&self, max_level: LogLevelFilter) {
self.filter
.borrow()
.as_ref()
.expect("register the logger before touching maximum log level")
.set(max_level)
}
pub fn uart_log_level(&self) -> LogLevelFilter {
pub fn uart_log_level(&self) -> LevelFilter {
self.uart_filter.get()
}
pub fn set_uart_log_level(&self, max_level: LogLevelFilter) {
pub fn set_uart_log_level(&self, max_level: LevelFilter) {
self.uart_filter.set(max_level)
}
}
@ -121,11 +91,11 @@ impl BufferLogger {
unsafe impl Sync for BufferLogger {}
impl Log for BufferLogger {
fn enabled(&self, _metadata: &LogMetadata) -> bool {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}
fn log(&self, record: &LogRecord) {
fn log(&self, record: &log::Record) {
if self.enabled(record.metadata()) {
let timestamp = clock::get_us();
let seconds = timestamp / 1_000_000;
@ -137,10 +107,12 @@ impl Log for BufferLogger {
}
if record.level() <= self.uart_filter.get() {
writeln!(Console,
"[{:6}.{:06}s] {:>5}({}): {}", seconds, micros,
record.level(), record.target(), record.args()).unwrap();
println!("[{:6}.{:06}s] {:>5}({}): {}", seconds, micros,
record.level(), record.target(), record.args());
}
}
}
fn flush(&self) {
}
}

View File

@ -10,6 +10,6 @@ path = "lib.rs"
[dependencies]
byteorder = { version = "1.0", default-features = false }
cslice = { version = "0.3" }
log = { version = "0.3", default-features = false, optional = true }
log = { version = "0.4", default-features = false, optional = true }
std_artiq = { path = "../libstd_artiq", features = ["alloc"] }
dyld = { path = "../libdyld" }

View File

@ -2,7 +2,7 @@ use std::vec::Vec;
use std::io::{self, Read, Write};
use {ReadExt, WriteExt};
#[cfg(feature = "log")]
use log::LogLevelFilter;
use log;
#[derive(Debug)]
pub enum Request {
@ -10,9 +10,9 @@ pub enum Request {
ClearLog,
PullLog,
#[cfg(feature = "log")]
SetLogFilter(LogLevelFilter),
SetLogFilter(log::LevelFilter),
#[cfg(feature = "log")]
SetUartLogFilter(LogLevelFilter),
SetUartLogFilter(log::LevelFilter),
Hotswap(Vec<u8>),
Reboot,
@ -29,14 +29,14 @@ pub enum Reply<'a> {
impl Request {
pub fn read_from(reader: &mut Read) -> io::Result<Request> {
#[cfg(feature = "log")]
fn read_log_level_filter(reader: &mut Read) -> io::Result<LogLevelFilter> {
fn read_log_level_filter(reader: &mut Read) -> io::Result<log::LevelFilter> {
Ok(match reader.read_u8()? {
0 => LogLevelFilter::Off,
1 => LogLevelFilter::Error,
2 => LogLevelFilter::Warn,
3 => LogLevelFilter::Info,
4 => LogLevelFilter::Debug,
5 => LogLevelFilter::Trace,
0 => log::LevelFilter::Off,
1 => log::LevelFilter::Error,
2 => log::LevelFilter::Warn,
3 => log::LevelFilter::Info,
4 => log::LevelFilter::Debug,
5 => log::LevelFilter::Trace,
_ => return Err(io::Error::new(io::ErrorKind::InvalidData,
"invalid log level"))
})

View File

@ -1,19 +1,17 @@
#![feature(lang_items, asm, alloc, needs_panic_runtime,
unicode, raw, int_error_internals, try_from, macro_reexport,
#![feature(lang_items, asm, alloc, needs_panic_runtime, use_extern_macros,
unicode, raw, int_error_internals, try_from,
allow_internal_unstable, stmt_expr_attributes, str_internals)]
#![no_std]
#![needs_panic_runtime]
extern crate std_unicode;
#[macro_use]
#[macro_reexport(vec, format)]
extern crate alloc;
pub use core::{any, cell, clone, cmp, convert, default, hash, iter, marker, mem, num,
ops, option, ptr, result, sync,
char, i16, i32, i64, i8, isize, u16, u32, u64, u8, usize, f32, f64};
pub use alloc::{arc, rc, raw_vec};
pub use alloc::{binary_heap, borrow, boxed, btree_map, btree_set, fmt, linked_list, slice,
pub use alloc::{binary_heap, borrow, boxed, btree_map, btree_set, fmt, format, linked_list, slice,
str, string, vec, vec_deque};
pub mod prelude {

View File

@ -15,7 +15,8 @@ build_artiq = { path = "../libbuild_artiq" }
[dependencies]
byteorder = { version = "1.0", default-features = false }
cslice = { version = "0.3" }
log = { version = "0.3", default-features = false }
log = { version = "0.4", default-features = false }
managed = { version = "= 0.7.0", default-features = false, features = ["alloc", "map"] }
alloc_list = { path = "../liballoc_list" }
std_artiq = { path = "../libstd_artiq", features = ["alloc", "io_error_alloc"] }
logger_artiq = { path = "../liblogger_artiq" }
@ -25,19 +26,14 @@ proto = { path = "../libproto", features = ["log"] }
amp = { path = "../libamp" }
drtioaux = { path = "../libdrtioaux" }
[dependencies.compiler_builtins]
git = "https://github.com/rust-lang-nursery/compiler-builtins"
rev = "631b568"
features = ["mem"]
[dependencies.fringe]
git = "https://github.com/m-labs/libfringe"
rev = "bd23494"
rev = "b8a6d8f"
default-features = false
features = ["alloc"]
[dependencies.smoltcp]
git = "https://github.com/m-labs/smoltcp"
rev = "181083f"
rev = "2139686" # NB: also change in libboard_misoc/Cargo.toml
default-features = false
features = ["alloc", "log", "proto-ipv4", "socket-tcp"]
features = ["rust-1.28", "alloc", "log", "proto-ipv4", "socket-tcp"]

View File

@ -1,5 +1,6 @@
use core::{slice, fmt};
use smoltcp::Result;
use smoltcp::time::Instant;
use smoltcp::phy::{self, DeviceCapabilities, Device};
use board::{csr, mem};
@ -77,7 +78,7 @@ impl<'a> Device<'a> for EthernetDevice {
pub struct EthernetRxSlot(usize);
impl phy::RxToken for EthernetRxSlot {
fn consume<R, F>(self, _timestamp: u64, f: F) -> Result<R>
fn consume<R, F>(self, _timestamp: Instant, f: F) -> Result<R>
where F: FnOnce(&[u8]) -> Result<R>
{
unsafe {
@ -92,7 +93,7 @@ impl phy::RxToken for EthernetRxSlot {
pub struct EthernetTxSlot(usize);
impl phy::TxToken for EthernetTxSlot {
fn consume<R, F>(self, _timestamp: u64, length: usize, f: F) -> Result<R>
fn consume<R, F>(self, _timestamp: Instant, length: usize, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
{
debug_assert!(length < SLOT_SIZE);

View File

@ -1,7 +1,6 @@
#![no_std]
#![feature(compiler_builtins_lib, lang_items, alloc, global_allocator)]
#![feature(lang_items, alloc, panic_implementation, panic_info_message)]
extern crate compiler_builtins;
extern crate alloc;
extern crate cslice;
#[macro_use]
@ -24,7 +23,7 @@ extern crate drtioaux;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use board::config;
use board::{config, irq, boot, clock};
use proto::{mgmt_proto, analyzer_proto, moninj_proto, rpc_proto, session_proto, kernel_proto};
use amp::{mailbox, rpc_queue};
@ -97,18 +96,18 @@ fn startup() {
let net_device = unsafe { ethmac::EthernetDevice::new() };
let net_device = {
use smoltcp::time::Instant;
use smoltcp::wire::PrettyPrinter;
use smoltcp::wire::EthernetFrame;
fn net_trace_writer(timestamp: u64, printer: PrettyPrinter<EthernetFrame<&[u8]>>) {
let seconds = timestamp / 1000;
let micros = timestamp % 1000 * 1000;
print!("\x1b[37m[{:6}.{:06}s]\n{}\x1b[0m\n", seconds, micros, printer)
fn net_trace_writer(timestamp: Instant, printer: PrettyPrinter<EthernetFrame<&[u8]>>) {
print!("\x1b[37m[{:6}.{:03}s]\n{}\x1b[0m\n",
timestamp.secs(), timestamp.millis(), printer)
}
fn net_trace_silent(_timestamp: u64, _printer: PrettyPrinter<EthernetFrame<&[u8]>>) {}
fn net_trace_silent(_timestamp: Instant, _printer: PrettyPrinter<EthernetFrame<&[u8]>>) {}
let net_trace_fn: fn(u64, PrettyPrinter<EthernetFrame<&[u8]>>);
let net_trace_fn: fn(Instant, PrettyPrinter<EthernetFrame<&[u8]>>);
match config::read_str("net_trace", |r| r.map(|s| s == "1")) {
Ok(true) => net_trace_fn = net_trace_writer,
_ => net_trace_fn = net_trace_silent
@ -135,26 +134,22 @@ fn startup() {
#[cfg(has_rtio_analyzer)]
io.spawn(4096, analyzer::thread);
match config::read_str("log_level", |r| r?.parse()) {
Err(()) => (),
Ok(log_level_filter) => {
match config::read_str("log_level", |r| r.map(|s| s.parse())) {
Ok(Ok(log_level_filter)) => {
info!("log level set to {} by `log_level` config key",
log_level_filter);
logger_artiq::BufferLogger::with(|logger|
logger.set_max_log_level(log_level_filter));
log::set_max_level(log_level_filter);
}
_ => info!("log level set to INFO by default")
}
match config::read_str("uart_log_level", |r| r?.parse()) {
Err(()) => {
info!("UART log level set to INFO by default");
},
Ok(uart_log_level_filter) => {
match config::read_str("uart_log_level", |r| r.map(|s| s.parse())) {
Ok(Ok(uart_log_level_filter)) => {
info!("UART log level set to {} by `uart_log_level` config key",
uart_log_level_filter);
logger_artiq::BufferLogger::with(|logger|
logger.set_uart_log_level(uart_log_level_filter));
}
_ => info!("UART log level set to INFO by default")
}
loop {
@ -163,7 +158,8 @@ fn startup() {
{
let sockets = &mut *scheduler.sockets().borrow_mut();
loop {
match interface.poll(sockets, board::clock::get_ms()) {
let timestamp = smoltcp::time::Instant::from_millis(clock::get_ms() as i64);
match interface.poll(sockets, timestamp) {
Ok(true) => (),
Ok(false) => break,
Err(smoltcp::Error::Unrecognized) => (),
@ -205,23 +201,42 @@ pub extern fn abort() {
loop {}
}
#[no_mangle]
#[lang = "panic_fmt"]
pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, line: u32) -> ! {
println!("panic at {}:{}: {}", file, line, args);
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[lang = "oom"] // https://github.com/rust-lang/rust/issues/51540
pub fn oom(layout: core::alloc::Layout) -> ! {
panic!("heap view: {}\ncannot allocate layout: {:?}", unsafe { &ALLOC }, layout)
}
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[panic_implementation]
pub fn panic_impl(info: &core::panic::PanicInfo) -> ! {
irq::set_ie(false);
if let Some(location) = info.location() {
print!("panic at {}:{}:{}", location.file(), location.line(), location.column());
} else {
print!("panic at unknown location");
}
if let Some(message) = info.message() {
println!("{}", message);
} else {
println!("");
}
println!("backtrace for software version {}:",
include_str!(concat!(env!("OUT_DIR"), "/git-describe")));
let _ = backtrace_artiq::backtrace(|ip| {
println!("{:#08x}", ip);
// Backtrace gives us the return address, i.e. the address after the delay slot,
// but we're interested in the call instruction.
println!("{:#08x}", ip - 2 * 4);
});
if config::read_str("panic_reboot", |r| r == Ok("1")) {
println!("rebooting...");
unsafe { board::boot::reboot() }
if config::read_str("panic_reset", |r| r == Ok("1")) {
println!("restarting...");
unsafe { boot::reboot() }
} else {
println!("halting.");
println!("use `artiq_coreconfig write -s panic_reboot 1` to reboot instead");
println!("use `artiq_coreconfig write -s panic_reset 1` to restart instead");
loop {}
}
}

View File

@ -1,5 +1,6 @@
use log::{self, LevelFilter};
use std::io::{self, Read, Write};
use log::LogLevelFilter;
use logger_artiq::BufferLogger;
use sched::Io;
use sched::{TcpListener, TcpStream};
@ -30,8 +31,7 @@ fn worker(io: &Io, stream: &mut TcpStream) -> io::Result<()> {
let mut buffer = io.until_ok(|| logger.buffer())?;
Reply::LogContent(buffer.extract()).write_to(stream)
})?;
},
}
Request::ClearLog => {
BufferLogger::with(|logger| -> io::Result<()> {
let mut buffer = io.until_ok(|| logger.buffer())?;
@ -39,21 +39,20 @@ fn worker(io: &Io, stream: &mut TcpStream) -> io::Result<()> {
})?;
Reply::Success.write_to(stream)?;
},
}
Request::PullLog => {
BufferLogger::with(|logger| -> io::Result<()> {
loop {
// Do this *before* acquiring the buffer, since that sets the log level
// to OFF.
let log_level = logger.max_log_level();
let log_level = log::max_level();
let mut buffer = io.until_ok(|| logger.buffer())?;
if buffer.is_empty() { continue }
stream.write_string(buffer.extract())?;
if log_level == LogLevelFilter::Trace {
if log_level == LevelFilter::Trace {
// Hold exclusive access over the logger until we get positive
// acknowledgement; otherwise we get an infinite loop of network
// trace messages being transmitted and causing more network
@ -69,21 +68,18 @@ fn worker(io: &Io, stream: &mut TcpStream) -> io::Result<()> {
buffer.clear();
}
})?;
},
}
Request::SetLogFilter(level) => {
info!("changing log level to {}", level);
BufferLogger::with(|logger|
logger.set_max_log_level(level));
log::set_max_level(level);
Reply::Success.write_to(stream)?;
},
}
Request::SetUartLogFilter(level) => {
info!("changing UART log level to {}", level);
BufferLogger::with(|logger|
logger.set_uart_log_level(level));
Reply::Success.write_to(stream)?;
},
}
Request::Hotswap(firmware) => {
Reply::RebootImminent.write_to(stream)?;

View File

@ -8,6 +8,8 @@ use std::io::{Read, Write, Result, Error, ErrorKind};
use fringe::OwnedStack;
use fringe::generator::{Generator, Yielder, State as GeneratorState};
use smoltcp::time::Duration;
use smoltcp::Error as NetworkError;
use smoltcp::wire::IpEndpoint;
use smoltcp::socket::{SocketHandle, SocketRef};
@ -421,19 +423,19 @@ impl<'a> TcpStream<'a> {
}
pub fn timeout(&self) -> Option<u64> {
self.with_lower(|s| s.timeout())
self.with_lower(|s| s.timeout().as_ref().map(Duration::millis))
}
pub fn set_timeout(&self, value: Option<u64>) {
self.with_lower(|mut s| s.set_timeout(value))
self.with_lower(|mut s| s.set_timeout(value.map(Duration::from_millis)))
}
pub fn keep_alive(&self) -> Option<u64> {
self.with_lower(|s| s.keep_alive())
self.with_lower(|s| s.keep_alive().as_ref().map(Duration::millis))
}
pub fn set_keep_alive(&self, value: Option<u64>) {
self.with_lower(|mut s| s.set_keep_alive(value))
self.with_lower(|mut s| s.set_keep_alive(value.map(Duration::from_millis)))
}
pub fn close(&self) -> Result<()> {

View File

@ -1,4 +1,4 @@
#![feature(compiler_builtins_lib, lang_items)]
#![feature(never_type, panic_implementation, panic_info_message)]
#![no_std]
extern crate compiler_builtins;
@ -241,10 +241,19 @@ pub extern fn abort() {
panic!("aborted")
}
#[no_mangle]
#[lang = "panic_fmt"]
pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, line: u32) -> ! {
println!("panic at {}:{}: {}", file, line, args);
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[panic_implementation]
pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
if let Some(location) = info.location() {
print!("panic at {}:{}:{}", location.file(), location.line(), location.column());
} else {
print!("panic at unknown location");
}
if let Some(message) = info.message() {
println!(": {}", message);
} else {
println!("");
}
loop {}
}

View File

@ -0,0 +1,345 @@
#![feature(never_type, panic_implementation, panic_info_message)]
#![no_std]
#[macro_use]
extern crate log;
#[macro_use]
extern crate board_misoc;
extern crate board_artiq;
use board_misoc::{csr, ident, clock, uart_logger};
use board_artiq::{i2c, spi, si5324, drtioaux};
#[cfg(has_serwb_phy_amc)]
use board_artiq::serwb;
#[cfg(has_hmc830_7043)]
use board_artiq::hmc830_7043;
fn drtio_reset(reset: bool) {
unsafe {
(csr::DRTIO[0].reset_write)(if reset { 1 } else { 0 });
}
}
fn drtio_reset_phy(reset: bool) {
unsafe {
(csr::DRTIO[0].reset_phy_write)(if reset { 1 } else { 0 });
}
}
fn drtio_tsc_loaded() -> bool {
unsafe {
let tsc_loaded = (csr::DRTIO[0].tsc_loaded_read)() == 1;
if tsc_loaded {
(csr::DRTIO[0].tsc_loaded_write)(1);
}
tsc_loaded
}
}
fn process_aux_packet(packet: drtioaux::Packet) -> Result<(), drtioaux::Error<!>> {
// In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels,
// and u16 otherwise; hence the `as _` conversion.
match packet {
drtioaux::Packet::EchoRequest =>
drtioaux::send_link(0, &drtioaux::Packet::EchoReply),
drtioaux::Packet::ResetRequest { phy } => {
if phy {
drtio_reset_phy(true);
drtio_reset_phy(false);
} else {
drtio_reset(true);
drtio_reset(false);
}
drtioaux::send_link(0, &drtioaux::Packet::ResetAck)
},
drtioaux::Packet::RtioErrorRequest => {
let errors;
unsafe {
errors = (csr::DRTIO[0].rtio_error_read)();
}
if errors & 1 != 0 {
let channel;
unsafe {
channel = (csr::DRTIO[0].sequence_error_channel_read)();
(csr::DRTIO[0].rtio_error_write)(1);
}
drtioaux::send_link(0,
&drtioaux::Packet::RtioErrorSequenceErrorReply { channel })
} else if errors & 2 != 0 {
let channel;
unsafe {
channel = (csr::DRTIO[0].collision_channel_read)();
(csr::DRTIO[0].rtio_error_write)(2);
}
drtioaux::send_link(0,
&drtioaux::Packet::RtioErrorCollisionReply { channel })
} else if errors & 4 != 0 {
let channel;
unsafe {
channel = (csr::DRTIO[0].busy_channel_read)();
(csr::DRTIO[0].rtio_error_write)(4);
}
drtioaux::send_link(0,
&drtioaux::Packet::RtioErrorBusyReply { channel })
}
else {
drtioaux::send_link(0, &drtioaux::Packet::RtioNoErrorReply)
}
}
drtioaux::Packet::MonitorRequest { channel, probe } => {
let value;
#[cfg(has_rtio_moninj)]
unsafe {
csr::rtio_moninj::mon_chan_sel_write(channel as _);
csr::rtio_moninj::mon_probe_sel_write(probe);
csr::rtio_moninj::mon_value_update_write(1);
value = csr::rtio_moninj::mon_value_read();
}
#[cfg(not(has_rtio_moninj))]
{
value = 0;
}
let reply = drtioaux::Packet::MonitorReply { value: value as u32 };
drtioaux::send_link(0, &reply)
},
drtioaux::Packet::InjectionRequest { channel, overrd, value } => {
#[cfg(has_rtio_moninj)]
unsafe {
csr::rtio_moninj::inj_chan_sel_write(channel as _);
csr::rtio_moninj::inj_override_sel_write(overrd);
csr::rtio_moninj::inj_value_write(value);
}
Ok(())
},
drtioaux::Packet::InjectionStatusRequest { channel, overrd } => {
let value;
#[cfg(has_rtio_moninj)]
unsafe {
csr::rtio_moninj::inj_chan_sel_write(channel as _);
csr::rtio_moninj::inj_override_sel_write(overrd);
value = csr::rtio_moninj::inj_value_read();
}
#[cfg(not(has_rtio_moninj))]
{
value = 0;
}
drtioaux::send_link(0, &drtioaux::Packet::InjectionStatusReply { value: value })
},
drtioaux::Packet::I2cStartRequest { busno } => {
let succeeded = i2c::start(busno).is_ok();
drtioaux::send_link(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cRestartRequest { busno } => {
let succeeded = i2c::restart(busno).is_ok();
drtioaux::send_link(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cStopRequest { busno } => {
let succeeded = i2c::stop(busno).is_ok();
drtioaux::send_link(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cWriteRequest { busno, data } => {
match i2c::write(busno, data) {
Ok(ack) => drtioaux::send_link(0,
&drtioaux::Packet::I2cWriteReply { succeeded: true, ack: ack }),
Err(_) => drtioaux::send_link(0,
&drtioaux::Packet::I2cWriteReply { succeeded: false, ack: false })
}
}
drtioaux::Packet::I2cReadRequest { busno, ack } => {
match i2c::read(busno, ack) {
Ok(data) => drtioaux::send_link(0,
&drtioaux::Packet::I2cReadReply { succeeded: true, data: data }),
Err(_) => drtioaux::send_link(0,
&drtioaux::Packet::I2cReadReply { succeeded: false, data: 0xff })
}
}
drtioaux::Packet::SpiSetConfigRequest { busno, flags, length, div, cs } => {
let succeeded = spi::set_config(busno, flags, length, div, cs).is_ok();
drtioaux::send_link(0,
&drtioaux::Packet::SpiBasicReply { succeeded: succeeded })
},
drtioaux::Packet::SpiWriteRequest { busno, data } => {
let succeeded = spi::write(busno, data).is_ok();
drtioaux::send_link(0,
&drtioaux::Packet::SpiBasicReply { succeeded: succeeded })
}
drtioaux::Packet::SpiReadRequest { busno } => {
match spi::read(busno) {
Ok(data) => drtioaux::send_link(0,
&drtioaux::Packet::SpiReadReply { succeeded: true, data: data }),
Err(_) => drtioaux::send_link(0,
&drtioaux::Packet::SpiReadReply { succeeded: false, data: 0 })
}
}
_ => {
warn!("received unexpected aux packet");
Ok(())
}
}
}
fn process_aux_packets() {
let result =
drtioaux::recv_link(0).and_then(|packet| {
if let Some(packet) = packet {
process_aux_packet(packet)
} else {
Ok(())
}
});
match result {
Ok(()) => (),
Err(e) => warn!("aux packet error ({})", e)
}
}
fn process_errors() {
let errors;
unsafe {
errors = (csr::DRTIO[0].protocol_error_read)();
}
if errors & 1 != 0 {
error!("received packet of an unknown type");
}
if errors & 2 != 0 {
error!("received truncated packet");
}
if errors & 4 != 0 {
let channel;
let timestamp_event;
let timestamp_counter;
unsafe {
channel = (csr::DRTIO[0].underflow_channel_read)();
timestamp_event = (csr::DRTIO[0].underflow_timestamp_event_read)() as i64;
timestamp_counter = (csr::DRTIO[0].underflow_timestamp_counter_read)() as i64;
}
error!("write underflow, channel={}, timestamp={}, counter={}, slack={}",
channel, timestamp_event, timestamp_counter, timestamp_event-timestamp_counter);
}
if errors & 8 != 0 {
error!("write overflow");
}
unsafe {
(csr::DRTIO[0].protocol_error_write)(errors);
}
}
#[cfg(rtio_frequency = "150.0")]
const SI5324_SETTINGS: si5324::FrequencySettings
= si5324::FrequencySettings {
n1_hs : 6,
nc1_ls : 6,
n2_hs : 10,
n2_ls : 270,
n31 : 75,
n32 : 75,
bwsel : 4,
crystal_ref: true
};
fn drtio_link_rx_up() -> bool {
unsafe {
(csr::DRTIO[0].rx_up_read)() == 1
}
}
const SIPHASER_PHASE: u16 = 32;
#[no_mangle]
pub extern fn main() -> i32 {
clock::init();
uart_logger::ConsoleLogger::register();
info!("ARTIQ satellite manager starting...");
info!("software ident {}", csr::CONFIG_IDENTIFIER_STR);
info!("gateware ident {}", ident::read(&mut [0; 64]));
#[cfg(has_slave_fpga_cfg)]
board_artiq::slave_fpga::load().expect("cannot load RTM FPGA gateware");
#[cfg(has_serwb_phy_amc)]
serwb::wait_init();
i2c::init();
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
#[cfg(has_hmc830_7043)]
/* must be the first SPI init because of HMC830 SPI mode selection */
hmc830_7043::init().expect("cannot initialize HMC830/7043");
unsafe {
csr::drtio_transceiver::stable_clkin_write(1);
}
#[cfg(has_ad9154)]
{
board_artiq::ad9154::jesd_unreset();
board_artiq::ad9154::init();
}
#[cfg(has_allaki_atts)]
board_artiq::hmc542::program_all(8/*=4dB*/);
loop {
while !drtio_link_rx_up() {
process_errors();
}
info!("link is up, switching to recovered clock");
si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks");
si5324::siphaser::calibrate_skew(SIPHASER_PHASE).expect("failed to calibrate skew");
drtioaux::reset(0);
drtio_reset(false);
drtio_reset_phy(false);
while drtio_link_rx_up() {
process_errors();
process_aux_packets();
if drtio_tsc_loaded() {
#[cfg(has_ad9154)]
{
if let Err(e) = board_artiq::jesd204sync::sysref_auto_rtio_align() {
error!("failed to align SYSREF at FPGA: {}", e);
}
if let Err(e) = board_artiq::jesd204sync::sysref_auto_dac_align() {
error!("failed to align SYSREF at DAC: {}", e);
}
}
if let Err(e) = drtioaux::send_link(0, &drtioaux::Packet::TSCAck) {
error!("aux packet error: {}", e);
}
}
}
drtio_reset_phy(true);
drtio_reset(true);
drtio_tsc_loaded();
info!("link is down, switching to local crystal clock");
si5324::siphaser::select_recovered_clock(false).expect("failed to switch clocks");
}
}
#[no_mangle]
pub extern fn exception(vect: u32, _regs: *const u32, pc: u32, ea: u32) {
panic!("exception {:?} at PC 0x{:x}, EA 0x{:x}", vect, pc, ea)
}
#[no_mangle]
pub extern fn abort() {
println!("aborted");
loop {}
}
#[no_mangle] // https://github.com/rust-lang/rust/issues/{38281,51647}
#[panic_implementation]
pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
if let Some(location) = info.location() {
print!("panic at {}:{}:{}", location.file(), location.line(), location.column());
} else {
print!("panic at unknown location");
}
if let Some(message) = info.message() {
println!(": {}", message);
} else {
println!("");
}
loop {}
}

View File

@ -43,7 +43,7 @@ class TransferTest(ExperimentCase):
exp = self.create(_Transfer)
host_to_device_rate = exp.host_to_device()
print(host_to_device_rate, "B/s")
self.assertGreater(host_to_device_rate, 2e6)
self.assertGreater(host_to_device_rate, 1.8e6)
@unittest.skipUnless(artiq_low_latency,
"timings are dependent on CPU load and network conditions")
@ -51,4 +51,32 @@ class TransferTest(ExperimentCase):
exp = self.create(_Transfer)
device_to_host_rate = exp.device_to_host()
print(device_to_host_rate, "B/s")
self.assertGreater(device_to_host_rate, 2e6)
self.assertGreater(device_to_host_rate, 1.8e6)
class _KernelOverhead(EnvExperiment):
def build(self):
self.setattr_device("core")
def kernel_overhead(self):
n = 100
t0 = time.monotonic()
for _ in range(n):
self.dummy_kernel()
t1 = time.monotonic()
return (t1-t0)/n
@kernel
def dummy_kernel(self):
pass
class KernelOverheadTest(ExperimentCase):
@unittest.skipUnless(artiq_low_latency,
"timings are dependent on CPU load and network conditions")
def test_kernel_overhead(self):
exp = self.create(_KernelOverhead)
kernel_overhead = exp.kernel_overhead()
print(kernel_overhead, "s")
self.assertGreater(kernel_overhead, 0.001)
self.assertLess(kernel_overhead, 0.5)

View File

@ -50,12 +50,17 @@ mod cslice {
}
}
#[path = "../../firmware/ksupport/eh.rs"]
pub mod eh;
#[path = "."]
pub mod eh {
#[path = "../../firmware/libeh/dwarf.rs"]
pub mod dwarf;
}
#[path = "../../firmware/ksupport/eh_artiq.rs"]
pub mod eh_artiq;
use std::{str, process};
fn terminate(exception: &eh::Exception, mut _backtrace: &mut [usize]) -> ! {
fn terminate(exception: &eh_artiq::Exception, mut _backtrace: &mut [usize]) -> ! {
println!("Uncaught {}: {} ({}, {}, {})",
str::from_utf8(exception.name.as_ref()).unwrap(),
str::from_utf8(exception.message.as_ref()).unwrap(),

View File

@ -6,7 +6,7 @@ from artiq.experiment import *
@kernel
def entrypoint():
a = [1,2]
# CHECK-L: ${LINE:+2}: error: values cannot be mutated in-place
# CHECK-L: ${LINE:+2}: error: lists cannot be mutated in-place
# CHECK-L: ${LINE:+1}: note: try using `a = a + [3,4]`
a += [3,4]

View File

@ -30,7 +30,7 @@ if os.name == "posix":
support_build = os.path.join(root, "libartiq_support")
if subprocess.call(["rustc", os.path.join(support_build, "lib.rs"),
"--out-dir", support_build,
"-Cpanic=abort", "-g"]) != 0:
"-Cpanic=unwind", "-g"]) != 0:
lit_config.fatal("Unable to build JIT support library")
support_lib = os.path.join(support_build, "libartiq_support.so")

View File

@ -19,8 +19,8 @@ requirements:
- jesd204b 0.3
- binutils-or1k-linux >=2.27
- llvm-or1k 6.0.0
- llvmlite-artiq 0.23.0.dev py35_2
- rust-core-or1k 1.25.0 20
- llvmlite-artiq 0.23.0.dev py35_4
- rust-core-or1k 1.28.0 21
- openocd 0.10.0 1
- lit
- outputcheck