diff --git a/artiq/firmware/Cargo.lock b/artiq/firmware/Cargo.lock index b9096a13e..885310773 100644 --- a/artiq/firmware/Cargo.lock +++ b/artiq/firmware/Cargo.lock @@ -30,9 +30,8 @@ dependencies = [ "build_misoc 0.0.0", "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "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)", ] [[package]] @@ -43,7 +42,7 @@ dependencies = [ "build_misoc 0.0.0", "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "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)", ] [[package]] @@ -70,11 +69,6 @@ name = "cfg-if" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "compiler_builtins" -version = "0.1.0" -source = "git+https://github.com/rust-lang-nursery/compiler-builtins#28daccd9159d33fac5e36394e4f9618db8870dc0" - [[package]] name = "crc" version = "1.8.1" @@ -92,6 +86,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" 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 = "failure" version = "0.1.1" @@ -110,7 +111,7 @@ dependencies = [ [[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.40 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -133,6 +134,7 @@ dependencies = [ "build_misoc 0.0.0", "cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "dyld 0.0.0", + "eh 0.0.0", "io 0.0.0", "proto_artiq 0.0.0", ] @@ -174,12 +176,7 @@ dependencies = [ [[package]] name = "managed" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "managed" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -210,15 +207,16 @@ dependencies = [ "build_misoc 0.0.0", "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "eh 0.0.0", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=bd23494)", + "fringe 1.1.0 (git+https://github.com/m-labs/libfringe?rev=b8a6d8f)", "io 0.0.0", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "logger_artiq 0.0.0", - "managed 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "proto_artiq 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)", "unwind_backtrace 0.0.0", ] @@ -235,11 +233,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.3 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "managed 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -284,20 +283,18 @@ version = "0.0.0" "checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9" "checksum cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0ebb87d1116151416c0cf66a0e3fb6430cccd120fd6300794b4dfaa050ac40ba" "checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" -"checksum compiler_builtins 0.1.0 (git+https://github.com/rust-lang-nursery/compiler-builtins)" = "" "checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" "checksum cslice 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f8cb7306107e4b10e64994de6d3274bd08996a7c1322a27b86482392f96be0a" "checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" "checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b" -"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 libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" "checksum log_buffer 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f033173c9486b7fe97a79c895c0a3483ae395ab6744c985d10078950e2492419" -"checksum managed 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43e2737ecabe4ae36a68061398bf27d2bfd0763f4c3c837a398478459494c4b7" -"checksum managed 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5a31885241e61ba264d780d2e6686e7e59561c947b4581470364eb3e10102d86" +"checksum managed 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba6713e624266d7600e9feae51b1926c6a6a6bebb18ec5a8e11a5f1d5661baba" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"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 syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" diff --git a/artiq/firmware/bootloader/Cargo.toml b/artiq/firmware/bootloader/Cargo.toml index 519e587ba..5ab62863a 100644 --- a/artiq/firmware/bootloader/Cargo.toml +++ b/artiq/firmware/bootloader/Cargo.toml @@ -19,6 +19,6 @@ board_misoc = { path = "../libboard_misoc", features = ["uart_console", "smoltcp [dependencies.smoltcp] git = "https://github.com/m-labs/smoltcp" -rev = "181083f" +rev = "2139686" default-features = false features = ["proto-ipv4", "socket-tcp"] diff --git a/artiq/firmware/bootloader/bootloader.ld b/artiq/firmware/bootloader/bootloader.ld index 6331f920e..d4976d534 100644 --- a/artiq/firmware/bootloader/bootloader.ld +++ b/artiq/firmware/bootloader/bootloader.ld @@ -15,6 +15,13 @@ SECTIONS *(.text .text.*) } > rom + /* + * The compiler_builtins crate includes some GOTPC relocations, which require a GOT symbol, + * but don't actually need a GOT. This really ought to be fixed on rustc level, but I'm afraid + * it will add further complications to our build system that aren't pulling their weight. + */ + _GLOBAL_OFFSET_TABLE_ = .; + .rodata : { *(.rodata.*) diff --git a/artiq/firmware/bootloader/main.rs b/artiq/firmware/bootloader/main.rs index e5515fb20..c48adf77f 100644 --- a/artiq/firmware/bootloader/main.rs +++ b/artiq/firmware/bootloader/main.rs @@ -1,5 +1,5 @@ #![no_std] -#![feature(lang_items)] +#![feature(panic_implementation, panic_info_message)] extern crate crc; extern crate byteorder; @@ -188,7 +188,8 @@ fn network_boot() { println!("Waiting for connections..."); loop { - match interface.poll(&mut sockets, clock::get_ms()) { + let timestamp = smoltcp::time::Instant::from_millis(clock::get_ms() as i64); + match interface.poll(&mut sockets, timestamp) { Ok(_) => (), Err(smoltcp::Error::Unrecognized) => (), Err(err) => println!("Network error: {}", err) @@ -232,10 +233,18 @@ pub extern fn abort() { loop {} } -#[no_mangle] -#[lang = "panic_fmt"] -pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, - line: u32, column: u32) -> ! { - println!("panic at {}:{}:{}: {}", file, line, column, 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/ksupport/Cargo.toml b/artiq/firmware/ksupport/Cargo.toml index 3318dc44d..ba53a7c9a 100644 --- a/artiq/firmware/ksupport/Cargo.toml +++ b/artiq/firmware/ksupport/Cargo.toml @@ -14,6 +14,7 @@ build_misoc = { path = "../libbuild_misoc" } [dependencies] cslice = { version = "0.3" } +eh = { path = "../libeh" } io = { path = "../libio", features = ["byteorder"] } dyld = { path = "../libdyld" } board_misoc = { path = "../libboard_misoc" } diff --git a/artiq/firmware/ksupport/api.rs b/artiq/firmware/ksupport/api.rs index 20efa1411..77d8b1edc 100644 --- a/artiq/firmware/ksupport/api.rs +++ b/artiq/firmware/ksupport/api.rs @@ -71,9 +71,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 73ba51ae2..000000000 --- a/artiq/firmware/ksupport/eh.rs +++ /dev/null @@ -1,465 +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, -} - -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, -} - -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(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) - } -} - -// 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/lib.rs b/artiq/firmware/ksupport/lib.rs index b3a18bf1a..bd4552dd2 100644 --- a/artiq/firmware/ksupport/lib.rs +++ b/artiq/firmware/ksupport/lib.rs @@ -1,12 +1,12 @@ -#![feature(lang_items, asm, libc, panic_unwind, unwind_attributes, global_allocator, - needs_panic_runtime)] +#![feature(lang_items, asm, panic_unwind, libc, unwind_attributes, + panic_implementation, panic_info_message)] #![no_std] -#![needs_panic_runtime] -extern crate cslice; -extern crate unwind; extern crate libc; +extern crate unwind; +extern crate cslice; +extern crate eh; extern crate io; extern crate dyld; extern crate board_misoc; @@ -46,11 +46,20 @@ macro_rules! recv { } } -#[no_mangle] -#[lang = "panic_fmt"] -pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, - line: u32, column: u32) -> ! { - send(&Log(format_args!("panic at {}:{}:{}: {}\n", file, line, column, 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() { + 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 {} } @@ -67,7 +76,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!(), @@ -78,14 +87,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; @@ -109,6 +118,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 { @@ -119,6 +129,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| { @@ -141,6 +152,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) => { @@ -148,7 +160,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, @@ -162,7 +174,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 { @@ -188,6 +200,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") @@ -197,10 +210,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() @@ -208,6 +223,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(), @@ -241,6 +257,7 @@ fn dma_record_flush() { } } +#[unwind(allowed)] extern fn dma_record_start(name: CSlice) { let name = str::from_utf8(name.as_ref()).unwrap(); @@ -260,6 +277,7 @@ extern fn dma_record_start(name: CSlice) { } } +#[unwind(allowed)] extern fn dma_record_stop(duration: i64) { unsafe { dma_record_flush(); @@ -281,10 +299,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 @@ -333,6 +353,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(); @@ -345,6 +366,7 @@ struct DmaTrace { address: i32, } +#[unwind(allowed)] extern fn dma_retrieve(name: CSlice) -> DmaTrace { let name = str::from_utf8(name.as_ref()).unwrap(); @@ -365,6 +387,7 @@ extern fn dma_retrieve(name: CSlice) -> DmaTrace { } #[cfg(has_rtio_dma)] +#[unwind(allowed)] extern fn dma_playback(timestamp: i64, ptr: i32) { assert!(ptr % 64 == 0); @@ -399,6 +422,7 @@ extern fn dma_playback(timestamp: i64, ptr: i32) { } #[cfg(not(has_rtio_dma))] +#[unwind(allowed)] extern fn dma_playback(_timestamp: i64, _ptr: i32) { unimplemented!("not(has_rtio_dma)") } @@ -485,11 +509,13 @@ pub unsafe fn main() { } #[no_mangle] +#[unwind(allowed)] 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] +#[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/libboard_misoc/Cargo.toml b/artiq/firmware/libboard_misoc/Cargo.toml index 35851cfb5..1efd23a4e 100644 --- a/artiq/firmware/libboard_misoc/Cargo.toml +++ b/artiq/firmware/libboard_misoc/Cargo.toml @@ -13,13 +13,12 @@ cc = "1.0" build_misoc = { path = "../libbuild_misoc" } [dependencies] -compiler_builtins = { git = "https://github.com/rust-lang-nursery/compiler-builtins", features = ["mem"] } byteorder = { version = "1.0", default-features = false } log = { version = "0.4", default-features = false, optional = true } [dependencies.smoltcp] git = "https://github.com/m-labs/smoltcp" -rev = "181083f" +rev = "2139686" # NB: also change in runtime/Cargo.toml default-features = false optional = true diff --git a/artiq/firmware/libboard_misoc/ethmac.rs b/artiq/firmware/libboard_misoc/ethmac.rs index ce8af0e42..263f8122d 100644 --- a/artiq/firmware/libboard_misoc/ethmac.rs +++ b/artiq/firmware/libboard_misoc/ethmac.rs @@ -1,5 +1,6 @@ use core::{slice, fmt}; use smoltcp::Result; +use smoltcp::time::Instant; use smoltcp::phy::{self, DeviceCapabilities, Device}; use csr; @@ -95,7 +96,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 { @@ -110,7 +111,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/libboard_misoc/lib.rs b/artiq/firmware/libboard_misoc/lib.rs index 690f29e99..ad941f00a 100644 --- a/artiq/firmware/libboard_misoc/lib.rs +++ b/artiq/firmware/libboard_misoc/lib.rs @@ -1,7 +1,6 @@ #![no_std] -#![feature(compiler_builtins_lib, asm, try_from)] +#![feature(asm, try_from)] -extern crate compiler_builtins; extern crate byteorder; #[cfg(feature = "log")] extern crate log; 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..da209ea25 --- /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/runtime/Cargo.toml b/artiq/firmware/runtime/Cargo.toml index ec0990aff..8be210b35 100644 --- a/artiq/firmware/runtime/Cargo.toml +++ b/artiq/firmware/runtime/Cargo.toml @@ -18,7 +18,8 @@ failure_derive = { version = "0.1", default-features = false } byteorder = { version = "1.0", default-features = false } cslice = { version = "0.3" } log = { version = "0.4", default-features = false } -managed = { version = "0.6", default-features = false, features = ["alloc", "map"] } +managed = { version = "= 0.7.0", default-features = false, features = ["alloc", "map"] } +eh = { path = "../libeh" } unwind_backtrace = { path = "../libunwind_backtrace" } io = { path = "../libio", features = ["byteorder"] } alloc_list = { path = "../liballoc_list" } @@ -29,12 +30,12 @@ proto_artiq = { path = "../libproto_artiq", features = ["log", "alloc"] } [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/main.rs b/artiq/firmware/runtime/main.rs index 494c3ff87..77e76cc09 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -1,7 +1,8 @@ -#![feature(lang_items, alloc, global_allocator, try_from, nonzero, nll, needs_panic_runtime, asm)] +#![feature(lang_items, alloc, try_from, nonzero, asm, + panic_implementation, panic_info_message)] #![no_std] -#![needs_panic_runtime] +extern crate eh; #[macro_use] extern crate alloc; extern crate failure; @@ -250,18 +251,18 @@ fn startup_ethernet() { net_device.reset_phy_if_any(); 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 @@ -299,7 +300,8 @@ fn startup_ethernet() { { let sockets = &mut *scheduler.sockets().borrow_mut(); loop { - match interface.poll(sockets, 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) => (), @@ -373,13 +375,27 @@ pub extern fn abort() { loop {} } -#[no_mangle] -#[lang = "panic_fmt"] -pub extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, - line: u32, column: u32) -> ! { +#[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); - println!("panic at {}:{}:{}: {}", file, line, column, args); + 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 {}:", csr::CONFIG_IDENTIFIER_STR); let _ = unwind_backtrace::backtrace(|ip| { diff --git a/artiq/firmware/runtime/profiler.rs b/artiq/firmware/runtime/profiler.rs index ac5097dfe..6df9c3f8c 100644 --- a/artiq/firmware/runtime/profiler.rs +++ b/artiq/firmware/runtime/profiler.rs @@ -64,27 +64,23 @@ impl Profile { pub fn record_hit(&mut self, addr: Address) -> Result<(), ()> { let mut hits = self.hits(); - match hits.get_mut(&addr) { - Some(count) => *count = count.saturating_add(1), - None => { - if let Err(_) = hits.insert(addr, 1) { - return Err(()) - } - } + if let Some(count) = hits.get_mut(&addr) { + return Ok(*count = count.saturating_add(1)) } - Ok(()) + if let Err(_) = hits.insert(addr, 1) { + return Err(()) + } + return Ok(()) } #[allow(dead_code)] pub fn record_edge(&mut self, caller: Address, callee: Address) -> Result<(), ()> { let mut edges = self.edges(); - match edges.get_mut(&(caller, callee)) { - Some(count) => *count = count.saturating_add(1), - None => { - if let Err(_) = edges.insert((caller, callee), 1) { - return Err(()) - } - } + if let Some(count) = edges.get_mut(&(caller, callee)) { + return Ok(*count = count.saturating_add(1)) + } + if let Err(_) = edges.insert((caller, callee), 1) { + return Err(()) } Ok(()) } diff --git a/artiq/firmware/runtime/sched.rs b/artiq/firmware/runtime/sched.rs index 5578be444..c28fff956 100644 --- a/artiq/firmware/runtime/sched.rs +++ b/artiq/firmware/runtime/sched.rs @@ -6,6 +6,7 @@ use core::cell::{Cell, RefCell}; use alloc::Vec; 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}; @@ -426,19 +427,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<(), Error> { diff --git a/artiq/test/coredevice/test_performance.py b/artiq/test/coredevice/test_performance.py index 8e91b7859..90653b0c0 100644 --- a/artiq/test/coredevice/test_performance.py +++ b/artiq/test/coredevice/test_performance.py @@ -44,7 +44,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, 1.9e6) + self.assertGreater(host_to_device_rate, 1.8e6) @unittest.skipUnless(artiq_low_latency, "timings are dependent on CPU load and network conditions") 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/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 84da9437a..e9f878df5 100644 --- a/conda/artiq-dev/meta.yaml +++ b/conda/artiq-dev/meta.yaml @@ -21,7 +21,7 @@ requirements: - binutils-or1k-linux >=2.27 - llvm-or1k 6.0.0 - llvmlite-artiq 0.23.0.dev py35_4 - - rust-core-or1k 1.26.0 21 + - rust-core-or1k 1.28.0 21 - openocd 0.10.0 6 - lit - outputcheck