diff options
Diffstat (limited to 'cortex-m-rt')
-rw-r--r-- | cortex-m-rt/Cargo.toml | 3 | ||||
-rw-r--r-- | cortex-m-rt/link.x | 192 | ||||
-rw-r--r-- | cortex-m-rt/src/lang_items.rs | 39 | ||||
-rw-r--r-- | cortex-m-rt/src/lib.rs | 697 |
4 files changed, 277 insertions, 654 deletions
diff --git a/cortex-m-rt/Cargo.toml b/cortex-m-rt/Cargo.toml index cd31b2d..9648b76 100644 --- a/cortex-m-rt/Cargo.toml +++ b/cortex-m-rt/Cargo.toml @@ -7,8 +7,7 @@ keywords = ["arm", "cortex-m", "runtime", "startup"] license = "MIT OR Apache-2.0" name = "cortex-m-rt" repository = "https://github.com/japaric/cortex-m-rt" -version = "0.4.0" +version = "0.5.0" [dependencies] -cortex-m = "0.3.0" r0 = "0.2.1"
\ No newline at end of file diff --git a/cortex-m-rt/link.x b/cortex-m-rt/link.x index 7e398de..1f5fd6f 100644 --- a/cortex-m-rt/link.x +++ b/cortex-m-rt/link.x @@ -1,123 +1,191 @@ -INCLUDE memory.x - -/* With multiple codegen units the rlib produced for this crate has several object files in it. */ -/* Because the linker is Smart it may not look into all the object files and not pick up the */ -/* .vector_table.exceptions section. But we want it to! To workaround the problem we create an */ -/* undefined reference to the EXCEPTIONS symbol (located in .vector_table.exceptions); this way the */ -/* linker will look at all the object of the rlib and pick up our EXCEPTIONS symbol */ -EXTERN(EXCEPTIONS); - -/* Create an undefined reference to the INTERRUPTS symbol. This is required to - force the linker to *not* drop the INTERRUPTS symbol if it comes from an - object file that's passed to the linker *before* this crate */ -EXTERN(INTERRUPTS); +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut __sbss }`). +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol if not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) +- `PROVIDE` is used to provide default values that can be overridden by a user linker script +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization + routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see + "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ +INCLUDE memory.x +INCLUDE interrupts.x + +/* # Entry point */ +ENTRY(__reset); +EXTERN(__RESET_VECTOR); /* depends on the `__reset` symbol */ + +EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */ + +/* # Exception vectors */ +/* This is effectively weak aliasing at the linker level */ +/* The user can override any of these aliases by defining the corresponding symbol themselves (cf. + the `exception!` macro) */ +EXTERN(DefaultHandler); +PROVIDE(NMI = DefaultHandler); +PROVIDE(HardFault = DefaultHandler); +PROVIDE(MemManage = DefaultHandler); +PROVIDE(BusFault = DefaultHandler); +PROVIDE(UsageFault = DefaultHandler); +PROVIDE(SVC = DefaultHandler); +PROVIDE(DebugMon = DefaultHandler); +PROVIDE(PendSV = DefaultHandler); +PROVIDE(SysTick = DefaultHandler); + +/* # Interrupt vectors */ +EXTERN(__INTERRUPTS); /* to be provided by the device crate or equivalent */ + +/* # User overridable symbols I */ +/* Lets the user place the stack in a different RAM region */ PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); +/* # Sections */ SECTIONS { + /* ## Sections in FLASH */ + /* ### Vector table */ .vector_table ORIGIN(FLASH) : ALIGN(4) { - /* Vector table */ - _svector_table = .; + /* Initial Stack Pointer (SP) value */ + __STACK_START = .; /* Just to get a nicer name in the disassembly */ LONG(_stack_start); + /* Reset vector */ KEEP(*(.vector_table.reset_vector)); + __reset_vector = ABSOLUTE(.); + /* Exceptions */ KEEP(*(.vector_table.exceptions)); - _eexceptions = .; + __eexceptions = ABSOLUTE(.); + /* Device specific interrupts */ KEEP(*(.vector_table.interrupts)); - _einterrupts = .; + __einterrupts = ABSOLUTE(.); } > FLASH - PROVIDE(_stext = _einterrupts); - - .text _stext : ALIGN(4) + /* ### .text */ + .text _stext : { - /* Put reset handler first in .text section so it ends up as the entry */ - /* point of the program. */ - KEEP(*(.reset_handler)); - *(.text .text.*); + __etext = ABSOLUTE(.); } > FLASH - .rodata : ALIGN(4) + /* ### .rodata */ + .rodata : { + . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ + /* __srodata = ABSOLUTE(.); */ + *(.rodata .rodata.*); - . = ALIGN(4); + + . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ + __erodata = ABSOLUTE(.); } > FLASH - PROVIDE(_sbss = ORIGIN(RAM)); - .bss _sbss : ALIGN(4) - { - *(.bss .bss.*); - . = ALIGN(4); - _ebss = .; - } > RAM AT > FLASH - /* NOTE(AT > FLASH) without this LLD v6 produces a binary that crashes OpenOCD whereas LLD v7 - emits a ".rodata and .bss sections overlap" error ... This hacky workaround doesn't increase - the binary size AFAICT */ - - .data : ALIGN(4) + /* ## Sections in RAM */ + /* ### .data */ + .data : AT(__erodata) /* LMA */ { - _sidata = LOADADDR(.data); - _sdata = .; + . = ALIGN(4); /* 4-byte align the start (VMA) of this section */ + __sdata = ABSOLUTE(.); + *(.data .data.*); - . = ALIGN(4); - _edata = .; - } > RAM AT > FLASH - /* The heap starts right after the .bss + .data section ends */ - _sheap = _edata; + . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ + __edata = ABSOLUTE(.); + } > RAM + + /* ### .bss */ + .bss : + { + . = ALIGN(4); /* 4-byte align the start (VMA) of this section */ + __sbss = ABSOLUTE(.); + + *(.bss .bss.*); - /* fake output .got section */ - /* Dynamic relocations are unsupported. This section is only used to detect - relocatable code in the input files and raise an error if relocatable code - is found */ + . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ + __ebss = ABSOLUTE(.); + } > RAM + + /* ## Fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in + the input files and raise an error if relocatable code is found */ .got : { - _sgot = .; + __sgot = ABSOLUTE(.); KEEP(*(.got .got.*)); - _egot = .; - } > RAM AT > FLASH + __egot = ABSOLUTE(.); + } > FLASH + /* ## Discarded sections */ /DISCARD/ : { + /* Unused exception related info that only wastes space */ *(.ARM.exidx.*); } } +/* # User overridable symbols II */ +/* (The user overridable symbols are split in two parts because LLD demands that the RHS of PROVIDE + to be defined before the PROVIDE invocation) */ +/* Lets the user override this to place .text a bit further than the vector table. Required by +microcontrollers that store their configuration right after the vector table. */ +PROVIDE(_stext = __einterrupts); + +/* # Hardcoded symbols */ +/* Place `.bss` at the start of the RAM region */ +__sidata = LOADADDR(.data); +/* Place the heap right after `.bss` and `.data` */ +__sheap = __edata; + +/* # Sanity checks */ + /* Do not exceed this mark in the error messages below | */ -ASSERT(_eexceptions - ORIGIN(FLASH) > 8, " -The exception handlers are missing. This is likely a cortex-m-rt bug. +ASSERT(__reset_vector == ORIGIN(FLASH) + 0x8, +"Reset vector is missing. This is a cortex-m-rt bug. Please file a bug report at: https://github.com/japaric/cortex-m-rt/issues"); -ASSERT(_eexceptions - ORIGIN(FLASH) == 0x40, " -Invalid '.vector_table.exceptions' section. This is likely a -cortex-m-rt bug. Please file a bug report at: +ASSERT(__eexceptions - ORIGIN(FLASH) == 0x40, " +The exception handlers are missing. This is a cortex-m-rt bug. +Please file a bug report at: https://github.com/japaric/cortex-m-rt/issues"); -ASSERT(_einterrupts - _eexceptions > 0, " +ASSERT(__einterrupts - __eexceptions > 0, " The interrupt handlers are missing. If you are not linking to a device crate then you supply the interrupt handlers yourself. Check the documentation."); -ASSERT(_einterrupts - _eexceptions <= 0x3c0, " +ASSERT(__einterrupts - __eexceptions <= 0x3c0, " There can't be more than 240 interrupt handlers. This may be a bug in your device crate, or you may have registered more than 240 interrupt handlers."); -ASSERT(_einterrupts <= _stext, " +ASSERT(__einterrupts <= _stext, " The '.text' section can't be placed inside '.vector_table' section. -Set '_stext' to an address greater than '_einterrupts'"); +Set '_stext' to an address greater than '__einterrupts'"); ASSERT(_stext < ORIGIN(FLASH) + LENGTH(FLASH), " The '.text' section must be placed inside the FLASH memory Set '_stext' to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)"); -ASSERT(_sgot == _egot, " +/* This has been temporarily omitted because it's not supported by LLD */ +/* ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, " */ +/* .bss is not 4-byte aligned at its boundaries. This is a cortex-m-rt bug."); */ + +/* ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, " */ +/* .data is not 4-byte aligned at its boundaries. This is a cortex-m-rt bug."); */ + +/* ASSERT(__sidata % 4 == 0, " */ +/* __sidata is not 4-byte aligned. This is a cortex-m-rt bug."); */ + +ASSERT(__sgot == __egot, " .got section detected in the input files. Dynamic relocations are not supported. If you are linking to C code compiled using the `gcc` crate then modify your build script to compile the C code _without_ the diff --git a/cortex-m-rt/src/lang_items.rs b/cortex-m-rt/src/lang_items.rs deleted file mode 100644 index 571a8a5..0000000 --- a/cortex-m-rt/src/lang_items.rs +++ /dev/null @@ -1,39 +0,0 @@ -// 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, the reset handler, -// has to call `rustc_main`. That's covered by the `reset_handler` function in -// root of this crate. -#[lang = "start"] -extern "C" fn start<T>(main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize -where - T: Termination, -{ - main(); - - 0 -} - -#[lang = "termination"] -pub trait Termination { - fn report(self) -> i32; -} - -impl Termination for () { - fn report(self) -> i32 { - 0 - } -} diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs index 7028cce..b689387 100644 --- a/cortex-m-rt/src/lib.rs +++ b/cortex-m-rt/src/lib.rs @@ -1,615 +1,210 @@ //! Minimal startup / runtime for Cortex-M microcontrollers -//! -//! # Features -//! -//! This crate provides -//! -//! - Before main initialization of the `.bss` and `.data` sections. -//! -//! - Before main initialization of the FPU (for targets that have a FPU). -//! -//! - A minimal `start` lang item to support the standard `fn main()` -//! interface. (The processor goes to sleep (`loop { asm!("wfi") }`) after -//! returning from `main`) -//! -//! - A linker script that encodes the memory layout of a generic Cortex-M -//! microcontroller. This linker script is missing some information that must -//! be supplied through a `memory.x` file (see example below). -//! -//! - A default exception handler tailored for debugging that lets you inspect -//! what was the state of the processor at the time of the exception. By -//! default, all exceptions are serviced by this handler but each exception -//! can be individually overridden using the -//! [`exception!`](macro.exception.html) macro. The default exception handler -//! itself can also be overridden using the -//! [`default_handler!`](macro.default_handler.html) macro. -//! -//! - A `_sheap` symbol at whose address you can locate a heap. -//! -//! # Example -//! -//! Creating a new bare metal project. (I recommend you use the -//! [`cortex-m-quickstart`](https://docs.rs/cortex-m-quickstart/0.2.0/cortex_m_quickstart/) template -//! as it takes of all the boilerplate shown here) -//! -//! ``` text -//! $ cargo new --bin app && cd $_ -//! -//! $ # add this crate as a dependency -//! $ cargo add cortex-m-rt --vers 0.4.0 -//! -//! $ # select a panicking behavior (look for the panic-impl keyword on crates.io) -//! $ cargo add panic-abort -//! -//! $ # memory layout of the device -//! $ $EDITOR memory.x && cat $_ -//! MEMORY -//! { -//! /* NOTE K = KiBi = 1024 bytes */ -//! FLASH : ORIGIN = 0x08000000, LENGTH = 128K -//! RAM : ORIGIN = 0x20000000, LENGTH = 8K -//! } -//! -//! $ $EDITOR src/main.rs && cat $_ -//! ``` -//! -//! ``` ignore,no_run -//! #![feature(used)] -//! #![no_std] -//! -//! extern crate cortex_m_rt; -//! extern crate panic_abort; // panicking behavior -//! -//! fn main() { -//! // do something here -//! } -//! -//! // As we are not using interrupts, we just register a dummy catch all -//! // handler -//! #[link_section = ".vector_table.interrupts"] -//! #[used] -//! static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; -//! -//! extern "C" fn default_handler() { -//! loop {} -//! } -//! ``` -//! -//! ``` text -//! $ rustup target add thumbv7m-none-eabi -//! -//! $ cargo rustc --target thumbv7m-none-eabi -- \ -//! -C link-arg=-Tlink.x -C linker=arm-none-eabi-ld -Z linker-flavor=ld -//! -//! $ arm-none-eabi-objdump -Cd $(find target -name app) | head -//! -//! Disassembly of section .text: -//! -//! 08000400 <cortex_m_rt::reset_handler>: -//! 8000400: b580 push {r7, lr} -//! 8000402: 466f mov r7, sp -//! 8000404: b084 sub sp, #8 -//! -//! -//! $ arm-none-eabi-size -Ax $(find target -name app) | head -//! target/thumbv7m-none-eabi/debug/app : -//! section size addr -//! .vector_table 0x400 0x8000000 -//! .text 0x24a 0x8000400 -//! .rodata 0x0 0x800064c -//! .bss 0x0 0x20000000 -//! .data 0x0 0x20000000 -//! ``` -//! -//! # Symbol interfaces -//! -//! This crate makes heavy use of symbols, linker sections and linker scripts to -//! provide most of its functionality. Below are described the main symbol -//! interfaces. -//! -//! ## `DEFAULT_HANDLER` -//! -//! This weak symbol can be overridden to override the default exception handler -//! that this crate provides. It's recommended that you use the -//! `default_handler!` to do the override, but below is shown how to manually -//! override the symbol: -//! -//! ``` ignore,no_run -//! #[no_mangle] -//! pub extern "C" fn DEFAULT_HANDLER() { -//! // do something here -//! } -//! ``` -//! -//! ## `.vector_table.interrupts` -//! -//! This linker section is used to register interrupt handlers in the vector -//! table. The recommended way to use this section is to populate it, once, with -//! an array of *weak* functions that just call the `DEFAULT_HANDLER` symbol. -//! Then the user can override them by name. -//! -//! ### Example -//! -//! Populating the vector table -//! -//! ``` ignore,no_run -//! // Number of interrupts the device has -//! const N: usize = 60; -//! -//! // Default interrupt handler that just calls the `DEFAULT_HANDLER` -//! #[linkage = "weak"] -//! #[naked] -//! #[no_mangle] -//! extern "C" fn WWDG() { -//! unsafe { -//! asm!("b DEFAULT_HANDLER" :::: "volatile"); -//! core::intrinsics::unreachable(); -//! } -//! } -//! -//! // You need one function per interrupt handler -//! #[linkage = "weak"] -//! #[naked] -//! #[no_mangle] -//! extern "C" fn PVD() { -//! unsafe { -//! asm!("b DEFAULT_HANDLER" :::: "volatile"); -//! core::intrinsics::unreachable(); -//! } -//! } -//! -//! // .. -//! -//! // Use `None` for reserved spots in the vector table -//! #[link_section = ".vector_table.interrupts"] -//! #[no_mangle] -//! #[used] -//! static INTERRUPTS: [Option<extern "C" fn()>; N] = [ -//! Some(WWDG), -//! Some(PVD), -//! // .. -//! ]; -//! ``` -//! -//! Overriding an interrupt (this can be in a different crate) -//! -//! ``` ignore,no_run -//! // the name must match the name of one of the weak functions used to -//! // populate the vector table. -//! #[no_mangle] -//! pub extern "C" fn WWDG() { -//! // do something here -//! } -//! ``` -//! -//! ## `memory.x` -//! -//! This file supplies the information about the device to the linker. -//! -//! ### `MEMORY` -//! -//! The main information that this file must provide is the memory layout of -//! the device in the form of the `MEMORY` command. The command is documented -//! [here](https://sourceware.org/binutils/docs/ld/MEMORY.html), but at a minimum you'll want to -//! create two memory regions: one for Flash memory and another for RAM. -//! -//! The program instructions (the `.text` section) will be stored in the memory -//! region named FLASH, and the program `static` variables (the sections `.bss` -//! and `.data`) will be allocated in the memory region named RAM. -//! -//! ### `_stack_start` -//! -//! This symbol provides the address at which the call stack will be allocated. -//! The call stack grows downwards so this address is usually set to the highest -//! valid RAM address plus one (this *is* an invalid address but the processor -//! will decrement the stack pointer *before* using its value as an address). -//! -//! If omitted this symbol value will default to `ORIGIN(RAM) + LENGTH(RAM)`. -//! -//! #### Example -//! -//! Allocating the call stack on a different RAM region. -//! -//! ``` ignore -//! MEMORY -//! { -//! /* call stack will go here */ -//! CCRAM : ORIGIN = 0x10000000, LENGTH = 8K -//! FLASH : ORIGIN = 0x08000000, LENGTH = 256K -//! /* static variables will go here */ -//! RAM : ORIGIN = 0x20000000, LENGTH = 40K -//! } -//! -//! _stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM); -//! ``` -//! -//! ### `_stext` -//! -//! This symbol indicates where the `.text` section will be located. If not -//! specified in the `memory.x` file it will default to right after the vector -//! table -- the vector table is always located at the start of the FLASH -//! region. -//! -//! The main use of this symbol is leaving some space between the vector table -//! and the `.text` section unused. This is required on some microcontrollers -//! that store some configuration information right after the vector table. -//! -//! #### Example -//! -//! Locate the `.text` section 1024 bytes after the start of the FLASH region. -//! -//! ``` ignore -//! _stext = ORIGIN(FLASH) + 0x400; -//! ``` -//! -//! ### `_sheap` -//! -//! This symbol is located in RAM right after the `.bss` and `.data` sections. -//! You can use the address of this symbol as the start address of a heap -//! region. This symbol is 4 byte aligned so that address will be a multiple of 4. -//! -//! #### Example -//! -//! ``` ignore -//! extern crate some_allocator; -//! -//! // Size of the heap in bytes -//! const SIZE: usize = 1024; -//! -//! extern "C" { -//! static mut _sheap: u8; -//! } -//! -//! fn main() { -//! unsafe { -//! let start_address = &mut _sheap as *mut u8; -//! some_allocator::initialize(start_address, SIZE); -//! } -//! } -//! ``` -//! -//! [1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html -//! [qs]: https://docs.rs/cortex-m-quickstart/0.2.0/cortex_m_quickstart/ -//! [2]: https://sourceware.org/binutils/docs/ld/MEMORY.html + +// # Developer notes +// +// - `link_section` is used to place symbols in specific places if the final binary. The names used +// here will appear in the linker script (`link.x`) in conjunction with the `KEEP` command. #![deny(missing_docs)] #![deny(warnings)] -#![feature(asm)] -#![feature(core_intrinsics)] -#![feature(global_asm)] -#![feature(lang_items)] -#![feature(linkage)] -#![feature(naked_functions)] -#![feature(used)] #![no_std] -#[cfg(target_arch = "arm")] -extern crate cortex_m; -#[cfg(target_arch = "arm")] extern crate r0; -#[cfg(not(test))] -mod lang_items; - -#[cfg(target_arch = "arm")] -use core::intrinsics; - -#[cfg(target_arch = "arm")] -use cortex_m::asm; -#[cfg(target_arch = "arm")] -use cortex_m::exception::ExceptionFrame; - -extern "C" { - // NOTE `rustc` forces this signature on us. See `src/lang_items.rs` - #[cfg(target_arch = "arm")] - fn main(argc: isize, argv: *const *const u8) -> isize; - - // Boundaries of the .bss section - static mut _ebss: u32; - static mut _sbss: u32; - - // Boundaries of the .data section - static mut _edata: u32; - static mut _sdata: u32; +/// Returns a pointer into which the heap can be placed +#[inline] +pub fn heap_start() -> *mut u32 { + extern "C" { + static mut __sheap: u32; + } - // Initial values of the .data section (stored in Flash) - static _sidata: u32; + unsafe { &mut __sheap } } -#[cfg(target_arch = "arm")] +/* Entry point */ +#[doc(hidden)] #[link_section = ".vector_table.reset_vector"] -#[used] -static RESET_VECTOR: unsafe extern "C" fn() -> ! = reset_handler; +#[no_mangle] +pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = __reset; -/// The reset handler -/// -/// This is the entry point of all programs -#[cfg(target_arch = "arm")] -#[link_section = ".reset_handler"] -unsafe extern "C" fn reset_handler() -> ! { - r0::zero_bss(&mut _sbss, &mut _ebss); - r0::init_data(&mut _sdata, &mut _edata, &_sidata); +#[doc(hidden)] +#[no_mangle] +pub unsafe extern "C" fn __reset() -> ! { + extern "C" { + // This symbol will be provided by the user via the `main!` macro + fn main() -> !; + + // These symbols come from `link.x` + static mut __sbss: u32; + static mut __ebss: u32; + + static mut __sdata: u32; + static mut __edata: u32; + static __sidata: u32; + } + + // Initialize RAM + r0::zero_bss(&mut __sbss, &mut __ebss); + r0::init_data(&mut __sdata, &mut __edata, &__sidata); match () { #[cfg(not(has_fpu))] - () => { - // Neither `argc` or `argv` make sense in bare metal context so we - // just stub them - main(0, ::core::ptr::null()); - } + () => main(), #[cfg(has_fpu)] () => { - // NOTE(safe) no exception / interrupt that also accesses the FPU - // can occur here - let scb = &*cortex_m::peripheral::SCB.get(); - scb.enable_fpu(); - - // Make sure the user main function never gets inlined into this - // function as that may cause FPU related instructions like vpush to - // be executed *before* enabling the FPU and that would generate an - // exception + // We redefine these here to avoid pulling the `cortex-m` crate as a dependency + const SCB_CPACR: *mut u32 = 0xE000_ED88 as *mut u32; + const SCB_CPACR_FPU_ENABLE: u32 = 0b01_01 << 20; + const SCB_CPACR_FPU_USER: u32 = 0b10_10 << 20; + + // enable the FPU + core::ptr::write_volatile( + SCB_CPACR, + *SCB_CPACR | SCB_CPACR_FPU_ENABLE | SCB_CPACR_FPU_USER, + ); + + // this is used to prevent the compiler from inlining the user `main` into the reset + // handler. Inlining can cause the FPU instructions in the user `main` to be executed + // before enabling the FPU, and that would produce a hard to diagnose hard fault at + // runtime. #[inline(never)] - fn main() { - unsafe { - ::main(0, ::core::ptr::null()); - } + #[export_name = "__reset_trampoline"] + fn trampoline() -> ! { + unsafe { main() } } - main() + trampoline() } } - - // If `main` returns, then we go into "reactive" mode and simply attend - // interrupts as they occur. - loop { - asm!("wfi" :::: "volatile"); - } } -#[cfg(target_arch = "arm")] -global_asm!( - r#" -.weak NMI -NMI = DEFAULT_HANDLER - -.weak HARD_FAULT -HARD_FAULT = DEFAULT_HANDLER - -.weak MEM_MANAGE -MEM_MANAGE = DEFAULT_HANDLER - -.weak BUS_FAULT -BUS_FAULT = DEFAULT_HANDLER - -.weak USAGE_FAULT -USAGE_FAULT = DEFAULT_HANDLER - -.weak SVCALL -SVCALL = DEFAULT_HANDLER - -.weak PENDSV -PENDSV = DEFAULT_HANDLER +/// Macro to define the user entry point of a program +/// +/// Usage: `main!(path::to::user::main)` +/// +/// This function will be called by the reset handler *after* RAM has been initialized. In the case +/// of the `thumbv7em-none-eabihf` target the FPU will also be enabled before the user `main` is +/// called. +#[macro_export] +macro_rules! main { + ($path:path) => { + #[export_name = "main"] + pub extern "C" fn __impl_main() -> ! { + // validate the signature of the user provide `main` + let f: fn() -> ! = $path; -.weak SYS_TICK -SYS_TICK = DEFAULT_HANDLER -"# -); + f() + } + }; +} -#[cfg(not(armv6m))] -global_asm!( - r#" -.weak DEBUG_MONITOR -DEBUG_MONITOR = DEFAULT_HANDLER -"# -); +/* Exceptions */ +// NOTE we purposefully go against Rust style here and use PascalCase for the handlers. The +// rationale is in the definition of the `exception!` macro. +#[doc(hidden)] +pub enum Exception { + NMI, + MenManage, + BusFault, + UsageFault, + SVC, + DebugMon, + PendSV, + SysTick, +} -#[cfg(target_arch = "arm")] extern "C" { fn NMI(); - fn HARD_FAULT(); - fn MEM_MANAGE(); - fn BUS_FAULT(); - fn USAGE_FAULT(); - fn SVCALL(); + fn HardFault(); + fn MemManage(); + fn BusFault(); + fn UsageFault(); + fn SVC(); #[cfg(not(armv6m))] - fn DEBUG_MONITOR(); - fn PENDSV(); - fn SYS_TICK(); + fn DebugMon(); + fn PendSV(); + fn SysTick(); } -#[allow(private_no_mangle_statics)] -#[cfg(target_arch = "arm")] #[doc(hidden)] #[link_section = ".vector_table.exceptions"] #[no_mangle] -#[used] -pub static EXCEPTIONS: [Option<unsafe extern "C" fn()>; 14] = [ +pub static __EXCEPTIONS: [Option<unsafe extern "C" fn()>; 14] = [ Some(NMI), - Some(HARD_FAULT), - Some(MEM_MANAGE), - Some(BUS_FAULT), - Some(USAGE_FAULT), + Some(HardFault), + Some(MemManage), + Some(BusFault), + Some(UsageFault), None, None, None, None, - Some(SVCALL), + Some(SVC), #[cfg(armv6m)] None, #[cfg(not(armv6m))] - Some(DEBUG_MONITOR), + Some(DebugMon), None, - Some(PENDSV), - Some(SYS_TICK), + Some(PendSV), + Some(SysTick), ]; -/// `ef` points to the exception frame +/// Macro to override an exception handler /// -/// That exception frame is a snapshot of the program state right before the -/// exception occurred. -#[allow(unused_variables)] -#[cfg(target_arch = "arm")] -extern "C" fn default_handler(ef: &ExceptionFrame) -> ! { - asm::bkpt(); - - loop {} - - #[export_name = "DEFAULT_HANDLER"] - #[linkage = "weak"] - #[naked] - extern "C" fn trampoline() -> ! { - unsafe { - asm!("mrs r0, MSP - b $0" - : - : "i"(default_handler as extern "C" fn(&ExceptionFrame) -> !) - : - : "volatile"); - - intrinsics::unreachable() - } - } - - #[used] - static KEEP: extern "C" fn() -> ! = trampoline; -} - -// make sure the compiler emits the DEFAULT_HANDLER symbol so the linker can -// find it! -#[cfg(target_arch = "arm")] -#[used] -static KEEP: extern "C" fn(&ExceptionFrame) -> ! = default_handler; - -/// This macro lets you override the default exception handler -/// -/// The first and only argument to this macro is the path to the function that -/// will be used as the default handler. That function must have signature -/// `fn()` +/// Usage: `exception!(ExceptionName, path::to::handler)` /// -/// # Examples +/// All exceptions are serviced by the `DefaultHandler` exception handler unless overridden using +/// this macro. `ExceptionName` can be one of are: /// -/// ``` ignore -/// default_handler!(foo::bar); +/// - `DefaultHandler` (\*) +/// - `NMI` +/// - `HardFault` +/// - `MemManage` +/// - `BusFault` +/// - `UsageFault` +/// - `SVC` +/// - `DebugMon` (not available on ARMv6-M) +/// - `PendSV` +/// - `SysTick` /// -/// mod foo { -/// pub fn bar() { -/// ::cortex_m::asm::bkpt(); -/// loop {} -/// } -/// } -/// ``` -#[macro_export] -macro_rules! default_handler { - ($path:path) => { - #[allow(non_snake_case)] - #[doc(hidden)] - #[no_mangle] - pub unsafe extern "C" fn DEFAULT_HANDLER() { - // type checking - let f: fn() = $path; - f(); - } - } -} - -/// Fault and system exceptions -#[allow(non_camel_case_types)] -#[doc(hidden)] -pub enum Exception { - /// Non-maskable interrupt - NMI, - /// All class of fault. - HARD_FAULT, - /// Memory management. - MEN_MANAGE, - /// Pre-fetch fault, memory access fault. - BUS_FAULT, - /// Undefined instruction or illegal state. - USAGE_FAULT, - /// System service call via SWI instruction - SVCALL, - /// Debug monitor - #[cfg(not(armv6m))] - DEBUG_MONITOR, - /// Pendable request for system service - PENDSV, - /// System tick timer - SYS_TICK, -} - -/// Assigns a handler to an exception -/// -/// This macro takes two arguments: the name of an exception and the path to the -/// function that will be used as the handler of that exception. That function -/// must have signature `fn()`. -/// -/// Optionally, a third argument may be used to declare exception local data. -/// The handler will have exclusive access to these *local* variables on each -/// invocation. If the third argument is used then the signature of the handler -/// function must be `fn(&mut $NAME::Locals)` where `$NAME` is the first -/// argument passed to the macro. -/// -/// # Example -/// -/// ``` ignore -/// exception!(MEM_MANAGE, mpu_fault); -/// -/// fn mpu_fault() { -/// panic!("Oh no! Something went wrong"); -/// } -/// -/// exception!(SYS_TICK, periodic, locals: { -/// counter: u32 = 0; -/// }); -/// -/// fn periodic(locals: &mut SYS_TICK::Locals) { -/// locals.counter += 1; -/// println!("This function has been called {} times", locals.counter); -/// } -/// ``` +/// (\*) Note that `DefaultHandler` is left undefined and *must* be defined by the user somewhere +/// using this macro. #[macro_export] macro_rules! exception { - ($NAME:ident, $path:path, locals: { - $($lvar:ident:$lty:ident = $lval:expr;)+ - }) => { + (DefaultHandler, $path:path) => { #[allow(non_snake_case)] - mod $NAME { - pub struct Locals { - $( - pub $lvar: $lty, - )+ - } - } + #[export_name = "DefaultHandler"] + pub unsafe extern "C" fn __impl_DefaultHandler() { + // XXX should we really prevent this handler from returning? + // validate the signature of the user provided handler + let f: fn() -> ! = $path; + f() + } + }; + (HardFault, $path:path) => { #[allow(non_snake_case)] - #[doc(hidden)] - #[no_mangle] - pub unsafe extern "C" fn $NAME() { - // check that the handler exists - let _ = $crate::Exception::$NAME; - - static mut LOCALS: self::$NAME::Locals = self::$NAME::Locals { - $( - $lvar: $lval, - )* - }; + #[export_name = "HardFaultr"] + pub unsafe extern "C" fn __impl_HardFault() { + // XXX should we really prevent this handler from returning? + // validate the signature of the user provided handler + let f: fn() -> ! = $path; - // type checking - let f: fn(&mut self::$NAME::Locals) = $path; - f(&mut LOCALS); + f() } }; - ($NAME:ident, $path:path) => { - #[allow(non_snake_case)] - #[doc(hidden)] + // NOTE Unfortunately, this will end up leaking `$exception` into the function call namespace. + // But the damage is somewhat reduced by having `$exception` not being a `snake_case` function. + ($ExceptionName:ident, $path:path) => { #[no_mangle] - pub unsafe extern "C" fn $NAME() { - // check that the handler exists - let _ = $crate::Exception::$NAME; + pub unsafe extern "C" fn $ExceptionName() { + // check that this exception exists + let _ = $crate::Exception::$ExceptionName; - // type checking + // validate the signature of the user provided handler let f: fn() = $path; - f(); + + f() } - } + }; } |