aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jorge Aparicio <jorge@japaric.io> 2018-04-24 09:38:46 +0200
committerGravatar Jorge Aparicio <jorge@japaric.io> 2018-04-24 10:40:23 +0200
commit7739bc6a36652ac8d38f81b95eb149789a75edce (patch)
tree39340defc0df6035d7db5260c0798523d50b5cb6
parent17b72ec698240ff70e6a4f64158eefdf58df598a (diff)
downloadcortex-m-7739bc6a36652ac8d38f81b95eb149789a75edce.tar.gz
cortex-m-7739bc6a36652ac8d38f81b95eb149789a75edce.tar.zst
cortex-m-7739bc6a36652ac8d38f81b95eb149789a75edce.zip
compile on stable
-rw-r--r--cortex-m-rt/Cargo.toml3
-rw-r--r--cortex-m-rt/link.x192
-rw-r--r--cortex-m-rt/src/lang_items.rs39
-rw-r--r--cortex-m-rt/src/lib.rs697
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()
}
- }
+ };
}