diff --git a/third_party/lang-items/Cargo.toml b/third_party/lang-items/Cargo.toml index bfa9570..0a0b6cc 100644 --- a/third_party/lang-items/Cargo.toml +++ b/third_party/lang-items/Cargo.toml @@ -1,15 +1,19 @@ [package] -name = "libtock_core" +name = "lang_items" version = "0.1.0" -authors = ["Tock Project Developers "] +authors = [ + "Tock Project Developers " + "Guillaume Endignoux ", +] +license = "MIT/Apache-2.0" edition = "2018" -[features] -alloc = ["alloc_init", "linked_list_allocator"] -alloc_init = [] -custom_panic_handler = [] -custom_alloc_error_handler = [] - [dependencies] -linked_list_allocator = { optional = true, version = "=0.8.1", default-features = false } -libtock_codegen = { path = "../codegen" } +libtock_core = { path = "../../third_party/libtock-rs/core", default-features = false, features = ["alloc_init", "custom_panic_handler", "custom_alloc_error_handler"] } +libtock_drivers = { path = "../libtock-drivers" } +linked_list_allocator = { version = "0.8.1", default-features = false } + +[features] +debug_allocations = [] +panic_console = [] +std = [] diff --git a/third_party/lang-items/src/allocator.rs b/third_party/lang-items/src/allocator.rs index f971849..d0a02ec 100644 --- a/third_party/lang-items/src/allocator.rs +++ b/third_party/lang-items/src/allocator.rs @@ -1,7 +1,16 @@ +use crate::util; use core::alloc::GlobalAlloc; use core::alloc::Layout; +#[cfg(any(feature = "debug_allocations", feature = "panic_console"))] +use core::fmt::Write; use core::ptr; use core::ptr::NonNull; +#[cfg(feature = "debug_allocations")] +use core::sync::atomic; +#[cfg(feature = "debug_allocations")] +use core::sync::atomic::AtomicUsize; +#[cfg(any(feature = "debug_allocations", feature = "panic_console"))] +use libtock_drivers::console::Console; use linked_list_allocator::Heap; static mut HEAP: Heap = Heap::empty(); @@ -11,35 +20,90 @@ unsafe fn libtock_alloc_init(app_heap_start: usize, app_heap_size: usize) { HEAP.init(app_heap_start, app_heap_size); } -struct TockAllocator; +// With the "debug_allocations" feature, we use `AtomicUsize` to store the +// statistics because: +// - it is `Sync`, so we can use it in a static object (the allocator), +// - it implements interior mutability, so we can use it in the allocator +// methods (that take an immutable `&self` reference). +struct TockAllocator { + #[cfg(feature = "debug_allocations")] + count: AtomicUsize, + #[cfg(feature = "debug_allocations")] + size: AtomicUsize, +} + +impl TockAllocator { + const fn new() -> TockAllocator { + TockAllocator { + #[cfg(feature = "debug_allocations")] + count: AtomicUsize::new(0), + #[cfg(feature = "debug_allocations")] + size: AtomicUsize::new(0), + } + } +} unsafe impl GlobalAlloc for TockAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - HEAP.allocate_first_fit(layout) + let ptr = HEAP + .allocate_first_fit(layout) .ok() - .map_or(ptr::null_mut(), NonNull::as_ptr) + .map_or(ptr::null_mut(), NonNull::as_ptr); + #[cfg(feature = "debug_allocations")] + { + self.count.fetch_add(1, atomic::Ordering::SeqCst); + self.size.fetch_add(layout.size(), atomic::Ordering::SeqCst); + writeln!( + Console::new(), + "alloc[{}, {}] = {:?} ({} ptrs, {} bytes)", + layout.size(), + layout.align(), + ptr, + self.count.load(atomic::Ordering::SeqCst), + self.size.load(atomic::Ordering::SeqCst) + ) + .unwrap(); + } + ptr } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + #[cfg(feature = "debug_allocations")] + { + self.count.fetch_sub(1, atomic::Ordering::SeqCst); + self.size.fetch_sub(layout.size(), atomic::Ordering::SeqCst); + writeln!( + Console::new(), + "dealloc[{}, {}] = {:?} ({} ptrs, {} bytes)", + layout.size(), + layout.align(), + ptr, + self.count.load(atomic::Ordering::SeqCst), + self.size.load(atomic::Ordering::SeqCst) + ) + .unwrap(); + } HEAP.deallocate(NonNull::new_unchecked(ptr), layout) } } +#[cfg(any(target_arch = "arm", target_arch = "riscv32"))] #[global_allocator] -static ALLOCATOR: TockAllocator = TockAllocator; +static ALLOCATOR: TockAllocator = TockAllocator::new(); -#[cfg(not(feature = "custom_alloc_error_handler"))] #[alloc_error_handler] -unsafe fn alloc_error_handler(_: Layout) -> ! { - use crate::syscalls; +unsafe fn alloc_error_handler(_layout: Layout) -> ! { + util::signal_oom(); + util::signal_panic(); - // Print 0x01 using the LowLevelDebug capsule (if available). - let _ = syscalls::command1_insecure(8, 2, 0x01); - - // Signal a panic using the LowLevelDebug capsule (if available). - let _ = syscalls::command1_insecure(8, 1, 0x01); - - loop { - syscalls::raw::yieldk(); + #[cfg(feature = "panic_console")] + { + writeln!(Console::new(), "Couldn't allocate: {:?}", _layout).ok(); + // Force the kernel to report the panic cause, by reading an invalid address. + // The memory protection unit should be setup by the Tock kernel to prevent apps from accessing + // address zero. + core::ptr::read_volatile(0 as *const usize); } + + util::cycle_leds() } diff --git a/third_party/lang-items/src/lib.rs b/third_party/lang-items/src/lib.rs new file mode 100644 index 0000000..c4fdcf8 --- /dev/null +++ b/third_party/lang-items/src/lib.rs @@ -0,0 +1,9 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![feature(alloc_error_handler)] + +#[cfg(not(feature = "std"))] +mod allocator; +#[cfg(not(feature = "std"))] +mod panic_handler; +#[cfg(not(feature = "std"))] +mod util; diff --git a/third_party/lang-items/src/panic_handler.rs b/third_party/lang-items/src/panic_handler.rs index c63a9c1..abf0a6e 100644 --- a/third_party/lang-items/src/panic_handler.rs +++ b/third_party/lang-items/src/panic_handler.rs @@ -1,72 +1,26 @@ -//! Lang item required to make the normal `main` work in applications -//! -//! This is how the `start` lang item works: -//! When `rustc` compiles a binary crate, it creates a `main` function that looks -//! like this: -//! -//! ``` -//! #[export_name = "main"] -//! pub extern "C" fn rustc_main(argc: isize, argv: *const *const u8) -> isize { -//! start(main, argc, argv) -//! } -//! ``` -//! -//! Where `start` is this function and `main` is the binary crate's `main` -//! function. -//! -//! The final piece is that the entry point of our program, _start, has to call -//! `rustc_main`. That's covered by the `_start` function in the root of this -//! crate. - -use crate::led; -use crate::timer; -use crate::timer::Duration; -use core::alloc::Layout; +use crate::util; +#[cfg(feature = "panic_console")] +use core::fmt::Write; use core::panic::PanicInfo; - -#[lang = "start"] -extern "C" fn start(main: fn() -> T, _argc: isize, _argv: *const *const u8) -> i32 -where - T: Termination, -{ - main().report() -} - -pub trait Termination { - fn report(self) -> i32; -} - -impl Termination for () { - fn report(self) -> i32 { - 0 - } -} +#[cfg(feature = "panic_console")] +use libtock_drivers::console::Console; #[panic_handler] fn panic_handler(_info: &PanicInfo) -> ! { - // Signal a panic using the LowLevelDebug capsule (if available). - super::debug::low_level_status_code(1); + util::signal_panic(); - // Flash all LEDs (if available). - loop { - for led in led::all() { - led.on(); - } - timer::sleep(Duration::from_ms(100)); - for led in led::all() { - led.off(); - } - timer::sleep(Duration::from_ms(100)); - } -} - -#[alloc_error_handler] -fn cycle_leds(_: Layout) -> ! { - loop { - for led in led::all() { - led.on(); - timer::sleep(Duration::from_ms(100)); - led.off(); + #[cfg(feature = "panic_console")] + { + let mut console = Console::new(); + writeln!(console, "{}", _info).ok(); + console.flush(); + // Force the kernel to report the panic cause, by reading an invalid address. + // The memory protection unit should be setup by the Tock kernel to prevent apps from accessing + // address zero. + unsafe { + core::ptr::read_volatile(0 as *const usize); } } + + util::flash_all_leds(); } diff --git a/third_party/lang-items/src/util.rs b/third_party/lang-items/src/util.rs new file mode 100644 index 0000000..c1a70fd --- /dev/null +++ b/third_party/lang-items/src/util.rs @@ -0,0 +1,46 @@ +use libtock_drivers::led; +use libtock_drivers::timer::{self, Duration}; + +// Signal a panic using the LowLevelDebug capsule (if available). +pub fn signal_panic() { + let _ = libtock_core::syscalls::command1_insecure(8, 1, 1); +} + +// Signal an out-of-memory error using the LowLevelDebug capsule (if available). +pub fn signal_oom() { + let _ = libtock_core::syscalls::command1_insecure(8, 2, 1); +} + +pub fn flash_all_leds() -> ! { + // Flash all LEDs (if available). All errors from syscalls are ignored: we are already inside a + // panic handler so there is nothing much to do if simple drivers (timer, LEDs) don't work. + loop { + if let Ok(leds) = led::all() { + for led in leds { + let _ = led.on(); + } + } + let _ = timer::sleep(Duration::from_ms(100)); + if let Ok(leds) = led::all() { + for led in leds { + let _ = led.off(); + } + } + let _ = timer::sleep(Duration::from_ms(100)); + } +} + +pub fn cycle_leds() -> ! { + // Cycle though all LEDs (if available). All errors from syscalls are ignored: we are already + // inside an error handler so there is nothing much to do if simple drivers (timer, LEDs) don't + // work. + loop { + if let Ok(leds) = led::all() { + for led in leds { + let _ = led.on(); + let _ = timer::sleep(Duration::from_ms(100)); + let _ = led.off(); + } + } + } +}