diff --git a/artiq/firmware/Cargo.lock b/artiq/firmware/Cargo.lock index d56092f0e..f625986b4 100644 --- a/artiq/firmware/Cargo.lock +++ b/artiq/firmware/Cargo.lock @@ -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)" = "" "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)" = "" +"checksum fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=b8a6d8f)" = "" "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)" = "" -"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)" = "" +"checksum smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=2139686)" = "" "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" diff --git a/artiq/firmware/Cargo.toml b/artiq/firmware/Cargo.toml index 705210ae1..4810510a2 100644 --- a/artiq/firmware/Cargo.toml +++ b/artiq/firmware/Cargo.toml @@ -3,4 +3,5 @@ members = ["runtime", "ksupport", "satman"] [profile.dev] incremental = false # incompatible with LTO +lto = true debug = 2 diff --git a/artiq/firmware/ksupport/Cargo.toml b/artiq/firmware/ksupport/Cargo.toml index c785d0aa3..633a5eea5 100644 --- a/artiq/firmware/ksupport/Cargo.toml +++ b/artiq/firmware/ksupport/Cargo.toml @@ -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" } diff --git a/artiq/firmware/ksupport/Makefile b/artiq/firmware/ksupport/Makefile index ff93ec31f..00effa3c4 100644 --- a/artiq/firmware/ksupport/Makefile +++ b/artiq/firmware/ksupport/Makefile @@ -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) diff --git a/artiq/firmware/ksupport/api.rs b/artiq/firmware/ksupport/api.rs index 1cf3ce884..ddcb2459b 100644 --- a/artiq/firmware/ksupport/api.rs +++ b/artiq/firmware/ksupport/api.rs @@ -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), diff --git a/artiq/firmware/ksupport/eh.rs b/artiq/firmware/ksupport/eh.rs deleted file mode 100644 index 27727408f..000000000 --- a/artiq/firmware/ksupport/eh.rs +++ /dev/null @@ -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 or the MIT license -// , 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); - -// This contortion is required due to https://github.com/rust-lang/rust/issues/27060. -impl Unaligned { - 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(&mut self) -> T { - let result = *(self.ptr as *const Unaligned); - self.ptr = self.ptr.offset(mem::size_of::() 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::(); - 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::(); - result |= ((byte & 0x7F) as u64) << shift; - shift += 7; - if byte & 0x80 == 0 { - break; - } - } - // sign-extend - if shift < 8 * mem::size_of::() && (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::()) as *const u8; - return self.read::() - } - - let value_ptr = self.ptr; - let mut result = match encoding & 0x0F { - DW_EH_PE_absptr => self.read::(), - DW_EH_PE_uleb128 => self.read_uleb128() as usize, - DW_EH_PE_udata2 => self.read::() as usize, - DW_EH_PE_udata4 => self.read::() as usize, - DW_EH_PE_udata8 => self.read::() as usize, - DW_EH_PE_sleb128 => self.read_sleb128() as usize, - DW_EH_PE_sdata2 => self.read::() as usize, - DW_EH_PE_sdata4 => self.read::() as usize, - DW_EH_PE_sdata8 => self.read::() 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::(), - 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) -> EHAction { - if lsda.is_null() { - return EHAction::None - } - - let mut reader = DwarfReader::new(lsda); - - let start_encoding = reader.read::(); - // 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::(); - 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::(); - 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); - - 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>, - 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)); - 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!() - } -} diff --git a/artiq/firmware/ksupport/eh_artiq.rs b/artiq/firmware/ksupport/eh_artiq.rs new file mode 100644 index 000000000..8bfc171a0 --- /dev/null +++ b/artiq/firmware/ksupport/eh_artiq.rs @@ -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 or the MIT license +// , 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>, + 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)); + 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) + } +} diff --git a/artiq/firmware/ksupport/glue.c b/artiq/firmware/ksupport/glue.c index 475e22a84..4e4ce318d 100644 --- a/artiq/firmware/ksupport/glue.c +++ b/artiq/firmware/ksupport/glue.c @@ -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, ...) { diff --git a/artiq/firmware/ksupport/lib.rs b/artiq/firmware/ksupport/lib.rs index 40ff62221..2639e95d3 100644 --- a/artiq/firmware/ksupport/lib.rs +++ b/artiq/firmware/ksupport/lib.rs @@ -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) { rtio::log(timestamp, text.as_ref()) } +#[unwind(aborts)] extern fn rpc_send(service: u32, tag: CSlice, data: *const *const ()) { while !rpc_queue::empty() {} send(&RpcSend { @@ -124,6 +143,7 @@ extern fn rpc_send(service: u32, tag: CSlice, data: *const *const ()) { }) } +#[unwind(aborts)] extern fn rpc_send_async(service: u32, tag: CSlice, 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, 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) -> CSlice<'static, i32> { send(&CacheGetRequest { key: str::from_utf8(key.as_ref()).unwrap() @@ -213,6 +237,7 @@ extern fn cache_get(key: CSlice) -> CSlice<'static, i32> { recv!(&CacheGetReply { value } => value.as_c_slice()) } +#[unwind(allowed)] extern fn cache_put(key: CSlice, list: CSlice) { 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) { let name = str::from_utf8(name.as_ref()).unwrap(); @@ -265,6 +291,7 @@ extern fn dma_record_start(name: CSlice) { } } +#[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) { 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) { 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) -> DmaTrace { let name = str::from_utf8(name.as_ref()).unwrap(); @@ -369,6 +400,7 @@ extern fn dma_retrieve(name: CSlice) -> 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") } diff --git a/artiq/firmware/liballoc_list/lib.rs b/artiq/firmware/liballoc_list/lib.rs index 4854575bb..f01262007 100644 --- a/artiq/firmware/liballoc_list/lib.rs +++ b/artiq/firmware/liballoc_list/lib.rs @@ -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::
(); @@ -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 { diff --git a/artiq/firmware/liballoc_stub/lib.rs b/artiq/firmware/liballoc_stub/lib.rs index 941b5e16f..ca2376400 100644 --- a/artiq/firmware/liballoc_stub/lib.rs +++ b/artiq/firmware/liballoc_stub/lib.rs @@ -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!() } } diff --git a/artiq/firmware/libeh/Cargo.toml b/artiq/firmware/libeh/Cargo.toml new file mode 100644 index 000000000..c1bea6f4f --- /dev/null +++ b/artiq/firmware/libeh/Cargo.toml @@ -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" } diff --git a/artiq/firmware/libeh/dwarf.rs b/artiq/firmware/libeh/dwarf.rs new file mode 100644 index 000000000..0956dc267 --- /dev/null +++ b/artiq/firmware/libeh/dwarf.rs @@ -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(&mut self) -> T { + let result = ptr::read_unaligned(self.ptr as *const T); + self.ptr = self.ptr.offset(mem::size_of::() 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::(); + 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::(); + result |= ((byte & 0x7F) as u64) << shift; + shift += 7; + if byte & 0x80 == 0 { + break; + } + } + // sign-extend + if shift < 8 * mem::size_of::() && (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::()) as *const u8; + return self.read::() + } + + let value_ptr = self.ptr; + let mut result = match encoding & 0x0F { + DW_EH_PE_absptr => self.read::(), + DW_EH_PE_uleb128 => self.read_uleb128() as usize, + DW_EH_PE_udata2 => self.read::() as usize, + DW_EH_PE_udata4 => self.read::() as usize, + DW_EH_PE_udata8 => self.read::() as usize, + DW_EH_PE_sleb128 => self.read_sleb128() as usize, + DW_EH_PE_sdata2 => self.read::() as usize, + DW_EH_PE_sdata4 => self.read::() as usize, + DW_EH_PE_sdata8 => self.read::() 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::(), + 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) -> EHAction { + if lsda.is_null() { + return EHAction::None + } + + let mut reader = DwarfReader::new(lsda); + + let start_encoding = reader.read::(); + // 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::(); + 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::(); + 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); + + 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 +} diff --git a/artiq/firmware/libeh/eh_rust.rs b/artiq/firmware/libeh/eh_rust.rs new file mode 100644 index 000000000..7fb14193d --- /dev/null +++ b/artiq/firmware/libeh/eh_rust.rs @@ -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 or the MIT license +// , 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 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 +{ + 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())) +} diff --git a/artiq/firmware/libeh/lib.rs b/artiq/firmware/libeh/lib.rs new file mode 100644 index 000000000..e8279d7e0 --- /dev/null +++ b/artiq/firmware/libeh/lib.rs @@ -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; diff --git a/artiq/firmware/liblogger_artiq/Cargo.toml b/artiq/firmware/liblogger_artiq/Cargo.toml index 0b3ec0a13..e5e145bb6 100644 --- a/artiq/firmware/liblogger_artiq/Cargo.toml +++ b/artiq/firmware/liblogger_artiq/Cargo.toml @@ -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" } diff --git a/artiq/firmware/liblogger_artiq/lib.rs b/artiq/firmware/liblogger_artiq/lib.rs index 3964b050f..fd6228978 100644 --- a/artiq/firmware/liblogger_artiq/lib.rs +++ b/artiq/firmware/liblogger_artiq/lib.rs @@ -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>, - filter: RefCell>, - uart_filter: Cell + uart_filter: Cell } 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(&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: F) -> R { @@ -85,34 +72,17 @@ impl BufferLogger { } pub fn buffer<'a>(&'a self) -> Result, ()> { - 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) { + } } diff --git a/artiq/firmware/libproto/Cargo.toml b/artiq/firmware/libproto/Cargo.toml index 487ae483b..eb805f1eb 100644 --- a/artiq/firmware/libproto/Cargo.toml +++ b/artiq/firmware/libproto/Cargo.toml @@ -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" } diff --git a/artiq/firmware/libproto/mgmt_proto.rs b/artiq/firmware/libproto/mgmt_proto.rs index 123af8915..cb5e08856 100644 --- a/artiq/firmware/libproto/mgmt_proto.rs +++ b/artiq/firmware/libproto/mgmt_proto.rs @@ -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), Reboot, @@ -29,14 +29,14 @@ pub enum Reply<'a> { impl Request { pub fn read_from(reader: &mut Read) -> io::Result { #[cfg(feature = "log")] - fn read_log_level_filter(reader: &mut Read) -> io::Result { + fn read_log_level_filter(reader: &mut Read) -> io::Result { 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")) }) diff --git a/artiq/firmware/libstd_artiq/lib.rs b/artiq/firmware/libstd_artiq/lib.rs index f0c7b8c5a..d4569a993 100644 --- a/artiq/firmware/libstd_artiq/lib.rs +++ b/artiq/firmware/libstd_artiq/lib.rs @@ -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 { diff --git a/artiq/firmware/runtime/Cargo.toml b/artiq/firmware/runtime/Cargo.toml index 05d692fd9..6afc9e6a1 100644 --- a/artiq/firmware/runtime/Cargo.toml +++ b/artiq/firmware/runtime/Cargo.toml @@ -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"] diff --git a/artiq/firmware/runtime/ethmac.rs b/artiq/firmware/runtime/ethmac.rs index a19a13933..c24c07eb7 100644 --- a/artiq/firmware/runtime/ethmac.rs +++ b/artiq/firmware/runtime/ethmac.rs @@ -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(self, _timestamp: u64, f: F) -> Result + fn consume(self, _timestamp: Instant, f: F) -> Result where F: FnOnce(&[u8]) -> Result { unsafe { @@ -92,7 +93,7 @@ impl phy::RxToken for EthernetRxSlot { pub struct EthernetTxSlot(usize); impl phy::TxToken for EthernetTxSlot { - fn consume(self, _timestamp: u64, length: usize, f: F) -> Result + fn consume(self, _timestamp: Instant, length: usize, f: F) -> Result where F: FnOnce(&mut [u8]) -> Result { debug_assert!(length < SLOT_SIZE); diff --git a/artiq/firmware/runtime/lib.rs b/artiq/firmware/runtime/lib.rs index 2c950785c..869116c67 100644 --- a/artiq/firmware/runtime/lib.rs +++ b/artiq/firmware/runtime/lib.rs @@ -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>) { - 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>) { + print!("\x1b[37m[{:6}.{:03}s]\n{}\x1b[0m\n", + timestamp.secs(), timestamp.millis(), printer) } - fn net_trace_silent(_timestamp: u64, _printer: PrettyPrinter>) {} + fn net_trace_silent(_timestamp: Instant, _printer: PrettyPrinter>) {} - let net_trace_fn: fn(u64, PrettyPrinter>); + let net_trace_fn: fn(Instant, PrettyPrinter>); 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 {} } } diff --git a/artiq/firmware/runtime/mgmt.rs b/artiq/firmware/runtime/mgmt.rs index e22c77abe..ab7fb16a4 100644 --- a/artiq/firmware/runtime/mgmt.rs +++ b/artiq/firmware/runtime/mgmt.rs @@ -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)?; diff --git a/artiq/firmware/runtime/sched.rs b/artiq/firmware/runtime/sched.rs index eeb31ceca..e2fae4477 100644 --- a/artiq/firmware/runtime/sched.rs +++ b/artiq/firmware/runtime/sched.rs @@ -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 { - self.with_lower(|s| s.timeout()) + self.with_lower(|s| s.timeout().as_ref().map(Duration::millis)) } pub fn set_timeout(&self, value: Option) { - 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 { - 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) { - 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<()> { diff --git a/artiq/firmware/satman/lib.rs b/artiq/firmware/satman/lib.rs index 5ffb0d6a2..692348493 100644 --- a/artiq/firmware/satman/lib.rs +++ b/artiq/firmware/satman/lib.rs @@ -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 {} } diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs new file mode 100644 index 000000000..b109ef96f --- /dev/null +++ b/artiq/firmware/satman/main.rs @@ -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 {} +} diff --git a/artiq/test/coredevice/test_performance.py b/artiq/test/coredevice/test_performance.py index 71101d508..25902b624 100644 --- a/artiq/test/coredevice/test_performance.py +++ b/artiq/test/coredevice/test_performance.py @@ -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) diff --git a/artiq/test/libartiq_support/lib.rs b/artiq/test/libartiq_support/lib.rs index b3188e17a..51344b402 100644 --- a/artiq/test/libartiq_support/lib.rs +++ b/artiq/test/libartiq_support/lib.rs @@ -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(), diff --git a/artiq/test/lit/escape/error_mutable_aug_asgn.py b/artiq/test/lit/escape/error_mutable_aug_asgn.py index ca4811768..1cdbd8838 100644 --- a/artiq/test/lit/escape/error_mutable_aug_asgn.py +++ b/artiq/test/lit/escape/error_mutable_aug_asgn.py @@ -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] diff --git a/artiq/test/lit/lit.cfg b/artiq/test/lit/lit.cfg index c31490091..daa4c1723 100644 --- a/artiq/test/lit/lit.cfg +++ b/artiq/test/lit/lit.cfg @@ -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") diff --git a/conda/artiq-dev/meta.yaml b/conda/artiq-dev/meta.yaml index 982d7aa27..0676a9e8c 100644 --- a/conda/artiq-dev/meta.yaml +++ b/conda/artiq-dev/meta.yaml @@ -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