aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jorge Aparicio <jorge@japaric.io> 2018-05-11 22:24:37 +0200
committerGravatar Jorge Aparicio <jorge@japaric.io> 2018-05-11 22:32:31 +0200
commitc3dbc7225d666043a57ca0cb36b32e938a06c7e6 (patch)
tree42ed30b926061a9edb88b19286649ca81e8d9554
parent7719662f287a8fc184b59822fb90d2297a72ea15 (diff)
downloadcortex-m-c3dbc7225d666043a57ca0cb36b32e938a06c7e6.tar.gz
cortex-m-c3dbc7225d666043a57ca0cb36b32e938a06c7e6.tar.zst
cortex-m-c3dbc7225d666043a57ca0cb36b32e938a06c7e6.zip
add CI, add device specific check of the vector table size, ..
- document the `main` symbol as an alternative to `entry!` - document `ResetTrampoline` - fix: `PendSV` is available on ARMv6-M - document that `entry!` and `exception!` must be called from accessible modules. - add a deny lint to `entry!` and `exception!` to prevent them from being invoked from inaccessible modules.
-rw-r--r--cortex-m-rt/.travis.yml85
-rw-r--r--cortex-m-rt/Cargo.toml9
-rw-r--r--cortex-m-rt/bors.toml3
-rw-r--r--cortex-m-rt/build.rs35
-rw-r--r--cortex-m-rt/ci/install.sh9
-rw-r--r--cortex-m-rt/ci/script.sh34
-rw-r--r--cortex-m-rt/device.x3
-rw-r--r--cortex-m-rt/examples/device.rs50
-rw-r--r--cortex-m-rt/examples/main.rs28
-rw-r--r--cortex-m-rt/examples/minimal.rs31
-rw-r--r--cortex-m-rt/examples/state.rs38
-rw-r--r--cortex-m-rt/link.x.in (renamed from cortex-m-rt/link.x)20
-rw-r--r--cortex-m-rt/memory.x2
-rw-r--r--cortex-m-rt/src/lib.rs78
14 files changed, 382 insertions, 43 deletions
diff --git a/cortex-m-rt/.travis.yml b/cortex-m-rt/.travis.yml
new file mode 100644
index 0000000..518037d
--- /dev/null
+++ b/cortex-m-rt/.travis.yml
@@ -0,0 +1,85 @@
+language: rust
+
+matrix:
+ include:
+ - env: TARGET=x86_64-unknown-linux-gnu
+
+ - env: TARGET=thumbv6m-none-eabi
+ rust: beta
+ addons:
+ apt:
+ packages:
+ - gcc-arm-none-eabi
+
+ - env: TARGET=thumbv7m-none-eabi
+ rust: beta
+ addons:
+ apt:
+ packages:
+ - gcc-arm-none-eabi
+
+ - env: TARGET=thumbv7em-none-eabi
+ rust: beta
+ addons:
+ apt:
+ packages:
+ - gcc-arm-none-eabi
+
+ - env: TARGET=thumbv7em-none-eabihf
+ rust: beta
+ addons:
+ apt:
+ packages:
+ - gcc-arm-none-eabi
+
+ - env: TARGET=thumbv6m-none-eabi
+ rust: nightly
+ addons:
+ apt:
+ packages:
+ - gcc-arm-none-eabi
+
+ - env: TARGET=thumbv7m-none-eabi
+ rust: nightly
+ addons:
+ apt:
+ packages:
+ - gcc-arm-none-eabi
+
+ - env: TARGET=thumbv7em-none-eabi
+ rust: nightly
+ addons:
+ apt:
+ packages:
+ - gcc-arm-none-eabi
+
+ - env: TARGET=thumbv7em-none-eabihf
+ rust: nightly
+ addons:
+ apt:
+ packages:
+ - gcc-arm-none-eabi
+
+before_install: set -e
+
+install:
+ - bash ci/install.sh
+
+script:
+ - bash ci/script.sh
+
+after_script: set +e
+
+cache: cache
+
+before_cache:
+ - chmod -R a+r $HOME/.cargo;
+
+branches:
+ only:
+ - staging
+ - trying
+
+notifications:
+ email:
+ on_success: never
diff --git a/cortex-m-rt/Cargo.toml b/cortex-m-rt/Cargo.toml
index 51269af..460917b 100644
--- a/cortex-m-rt/Cargo.toml
+++ b/cortex-m-rt/Cargo.toml
@@ -9,11 +9,14 @@ name = "cortex-m-rt"
repository = "https://github.com/japaric/cortex-m-rt"
version = "0.5.0"
+[build-dependencies]
+cc = "1.0.10"
+
[dependencies]
r0 = "0.2.1"
-[build-dependencies]
-cc = "1.0.10"
+[dev-dependencies]
+panic-semihosting = "0.2.0"
[features]
-device = [] \ No newline at end of file
+device = []
diff --git a/cortex-m-rt/bors.toml b/cortex-m-rt/bors.toml
new file mode 100644
index 0000000..5ccee21
--- /dev/null
+++ b/cortex-m-rt/bors.toml
@@ -0,0 +1,3 @@
+status = [
+ "continuous-integration/travis-ci/push",
+] \ No newline at end of file
diff --git a/cortex-m-rt/build.rs b/cortex-m-rt/build.rs
index 1098dca..1b5c3d1 100644
--- a/cortex-m-rt/build.rs
+++ b/cortex-m-rt/build.rs
@@ -9,7 +9,7 @@ fn main() {
let target = env::var("TARGET").unwrap();
has_fpu(&target);
- is_armv6m(&target);
+ let is_armv6m = is_armv6m(&target);
if target.starts_with("thumbv") {
cc::Build::new().file("asm.s").compile("asm");
@@ -17,8 +17,8 @@ fn main() {
// Put the linker script somewhere the linker can find it
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
- let link_x = include_bytes!("link.x");
- if env::var_os("CARGO_FEATURE_DEVICE").is_some() {
+ let link_x = include_bytes!("link.x.in");
+ let mut f = if env::var_os("CARGO_FEATURE_DEVICE").is_some() {
let mut f = File::create(out.join("link.x")).unwrap();
writeln!(
@@ -29,12 +29,28 @@ fn main() {
INCLUDE device.x"#
).unwrap();
f.write_all(link_x).unwrap();
+ f
} else {
- File::create(out.join("link.x"))
- .unwrap()
- .write_all(link_x)
- .unwrap();
+ let mut f = File::create(out.join("link.x")).unwrap();
+ f.write_all(link_x).unwrap();
+ f
};
+
+ let max_int_handlers = if is_armv6m { 32 } else { 240 };
+
+ // checking the size of the interrupts portion of the vector table is sub-architecture dependent
+ writeln!(
+ f,
+ r#"
+ASSERT(__einterrupts - __eexceptions <= 0x{:x}, "
+There can't be more than {} interrupt handlers. This may be a bug in
+your device crate, or you may have registered more than 240 interrupt
+handlers.");
+"#,
+ max_int_handlers * 4,
+ max_int_handlers
+ ).unwrap();
+
println!("cargo:rustc-link-search={}", out.display());
println!("cargo:rerun-if-changed=build.rs");
@@ -47,8 +63,11 @@ fn has_fpu(target: &str) {
}
}
-fn is_armv6m(target: &str) {
+fn is_armv6m(target: &str) -> bool {
if target.starts_with("thumbv6m-") {
println!("cargo:rustc-cfg=armv6m");
+ true
+ } else {
+ false
}
}
diff --git a/cortex-m-rt/ci/install.sh b/cortex-m-rt/ci/install.sh
new file mode 100644
index 0000000..3c41921
--- /dev/null
+++ b/cortex-m-rt/ci/install.sh
@@ -0,0 +1,9 @@
+set -euxo pipefail
+
+main() {
+ if [ $TARGET != x86_64-unknown-linux-gnu ]; then
+ rustup target add $TARGET
+ fi
+}
+
+main
diff --git a/cortex-m-rt/ci/script.sh b/cortex-m-rt/ci/script.sh
new file mode 100644
index 0000000..221386d
--- /dev/null
+++ b/cortex-m-rt/ci/script.sh
@@ -0,0 +1,34 @@
+set -euxo pipefail
+
+main() {
+ cargo check --target $TARGET
+
+ cargo check --target $TARGET --features device
+
+ local examples=(
+ minimal
+ main
+ state
+ )
+ if [ $TRAVIS_RUST_VERSION = nightly ]; then
+ for ex in "${examples[@]}"; do
+ cargo rustc --target $TARGET --example $ex -- \
+ -C link-arg=-nostartfiles \
+ -C link-arg=-Wl,-Tlink.x
+
+ cargo rustc --target $TARGET --example $ex --release -- \
+ -C link-arg=-nostartfiles \
+ -C link-arg=-Wl,-Tlink.x
+ done
+
+ cargo rustc --target $TARGET --example device --features device -- \
+ -C link-arg=-nostartfiles \
+ -C link-arg=-Wl,-Tlink.x
+
+ cargo rustc --target $TARGET --example device --features device --release -- \
+ -C link-arg=-nostartfiles \
+ -C link-arg=-Wl,-Tlink.x
+ fi
+}
+
+main
diff --git a/cortex-m-rt/device.x b/cortex-m-rt/device.x
new file mode 100644
index 0000000..28f975e
--- /dev/null
+++ b/cortex-m-rt/device.x
@@ -0,0 +1,3 @@
+/* Sample device.x file */
+PROVIDE(WWDG = DefaultHandler);
+PROVIDE(PVD = DefaultHandler);
diff --git a/cortex-m-rt/examples/device.rs b/cortex-m-rt/examples/device.rs
new file mode 100644
index 0000000..cf91f21
--- /dev/null
+++ b/cortex-m-rt/examples/device.rs
@@ -0,0 +1,50 @@
+//! Manually create the interrupts portion of the vector table
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+#[macro_use(entry, exception)]
+extern crate cortex_m_rt as rt;
+extern crate panic_semihosting;
+
+use rt::ExceptionFrame;
+
+// the program entry point
+entry!(main);
+
+fn main() -> ! {
+ loop {}
+}
+
+// the hard fault handler
+exception!(HardFault, hard_fault);
+
+fn hard_fault(_ef: &ExceptionFrame) -> ! {
+ loop {}
+}
+
+// the default exception handler
+exception!(*, default_handler);
+
+fn default_handler(_irqn: i16) {}
+
+// interrupts portion of the vector table
+pub union Vector {
+ handler: unsafe extern "C" fn(),
+ reserved: usize,
+}
+
+extern "C" {
+ fn WWDG();
+ fn PVD();
+}
+
+#[link_section = ".vector_table.interrupts"]
+#[no_mangle]
+pub static __INTERRUPTS: [Vector; 3] = [
+ Vector { handler: WWDG },
+ Vector { reserved: 0 },
+ Vector { handler: PVD },
+];
diff --git a/cortex-m-rt/examples/main.rs b/cortex-m-rt/examples/main.rs
new file mode 100644
index 0000000..d319249
--- /dev/null
+++ b/cortex-m-rt/examples/main.rs
@@ -0,0 +1,28 @@
+//! Directly plug a `main` symbol instead of using `entry!`
+
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+#[macro_use(exception)]
+extern crate cortex_m_rt as rt;
+extern crate panic_semihosting;
+
+use rt::ExceptionFrame;
+
+#[no_mangle]
+pub unsafe extern "C" fn main() -> ! {
+ loop {}
+}
+
+// the hard fault handler
+exception!(HardFault, hard_fault);
+
+fn hard_fault(_ef: &ExceptionFrame) -> ! {
+ loop {}
+}
+
+// the default exception handler
+exception!(*, default_handler);
+
+fn default_handler(_irqn: i16) {}
diff --git a/cortex-m-rt/examples/minimal.rs b/cortex-m-rt/examples/minimal.rs
new file mode 100644
index 0000000..c12d12d
--- /dev/null
+++ b/cortex-m-rt/examples/minimal.rs
@@ -0,0 +1,31 @@
+//! Minimal `cortex-m-rt` based program
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+#[macro_use(entry, exception)]
+extern crate cortex_m_rt as rt;
+extern crate panic_semihosting;
+
+use rt::ExceptionFrame;
+
+// the program entry point
+entry!(main);
+
+fn main() -> ! {
+ loop {}
+}
+
+// the hard fault handler
+exception!(HardFault, hard_fault);
+
+fn hard_fault(_ef: &ExceptionFrame) -> ! {
+ loop {}
+}
+
+// the default exception handler
+exception!(*, default_handler);
+
+fn default_handler(_irqn: i16) {}
diff --git a/cortex-m-rt/examples/state.rs b/cortex-m-rt/examples/state.rs
new file mode 100644
index 0000000..0b5eeeb
--- /dev/null
+++ b/cortex-m-rt/examples/state.rs
@@ -0,0 +1,38 @@
+//! Preserving state across executions of an exception handler
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+#[macro_use(entry, exception)]
+extern crate cortex_m_rt as rt;
+extern crate panic_semihosting;
+
+use rt::ExceptionFrame;
+
+// the program entry point
+entry!(main);
+
+fn main() -> ! {
+ loop {}
+}
+
+// exception handler with state
+exception!(SysTick, sys_tick, state: u32 = 0);
+
+fn sys_tick(state: &mut u32) {
+ *state += 1;
+}
+
+// the hard fault handler
+exception!(HardFault, hard_fault);
+
+fn hard_fault(_ef: &ExceptionFrame) -> ! {
+ loop {}
+}
+
+// the default exception handler
+exception!(*, default_handler);
+
+fn default_handler(_irqn: i16) {}
diff --git a/cortex-m-rt/link.x b/cortex-m-rt/link.x.in
index 44c427d..0e68199 100644
--- a/cortex-m-rt/link.x
+++ b/cortex-m-rt/link.x.in
@@ -155,7 +155,7 @@ __sheap = __edata;
/* Do not exceed this mark in the error messages below | */
ASSERT(__reset_vector == ORIGIN(FLASH) + 0x8,
-"Reset vector is missing. This is a cortex-m-rt bug.
+"The 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");
@@ -169,14 +169,9 @@ 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, "
-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, "
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' (cf. `nm`)");
ASSERT(_stext < ORIGIN(FLASH) + LENGTH(FLASH), "
The '.text' section must be placed inside the FLASH memory
@@ -193,8 +188,9 @@ Set '_stext' to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)");
/* __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 `cc` crate
-then modify your build script to compile the C code _without_ the
--fPIC flag. See the documentation of the `cc::Build.pic` method for
-details.");
+.got section detected in the input object files. Dynamic relocations
+are not supported. If you are linking to C code compiled using the
+`cc` crate then modify your build script to compile the C code
+_without_ the -fPIC flag. See the documentation of the
+`cc::Build.pic` method for details.");
+/* Do not exceed this mark in the error messages above | */
diff --git a/cortex-m-rt/memory.x b/cortex-m-rt/memory.x
index 8027efc..3d97414 100644
--- a/cortex-m-rt/memory.x
+++ b/cortex-m-rt/memory.x
@@ -16,4 +16,4 @@ MEMORY
/* The location of the .text section can be overridden using the `_stext` symbol.
By default it will place after .vector_table */
-/* _stext = ORIGIN(FLASH) + 0x100; */
+/* _stext = ORIGIN(FLASH) + 0x40c; */
diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs
index 4bb5fe1..e9aacf7 100644
--- a/cortex-m-rt/src/lib.rs
+++ b/cortex-m-rt/src/lib.rs
@@ -21,7 +21,7 @@
//! # `memory.x`
//!
//! This crate expects the user, or some other crate, to provide the memory layout of the target
-//! device via a linker named `memory.x`. This section covers the contents of `memory.x`
+//! device via a linker script named `memory.x`. This section covers the contents of `memory.x`
//!
//! ## `MEMORY`
//!
@@ -65,8 +65,8 @@
//! ## `_stext`
//!
//! This optional symbol can be used to control where the `.text` section is placed. If omitted the
-//! `.text` section will be placed right after the vector table (which is placed at the beginning of
-//! `FLASH`). Some devices store settings like Flash configuration right after the vector table;
+//! `.text` section will be placed right after the vector table, which is placed at the beginning of
+//! `FLASH`. Some devices store settings like Flash configuration right after the vector table;
//! for these devices one must place the `.text` section after this configuration section --
//! `_stext` can be used for this purpose.
//!
@@ -218,7 +218,42 @@
//! If you override any exception handler you'll find it as an unmangled symbol, e.g. `SysTick` or
//! `SVCall`, in the output of `objdump`,
//!
-//! # Incorporating device specific interrupts
+//! If you are targeting the `thumbv7em-none-eabihf` target you'll also see a `ResetTrampoline`
+//! symbol in the output. To avoid the compiler placing FPU instructions before the FPU has been
+//! enabled (cf. `vpush`) `Reset` calls the function `ResetTrampoline` which is marked as
+//! `#[inline(never)]` and `ResetTrampoline` calls `main`. The compiler is free to inline `main`
+//! into `ResetTrampoline` but it can't inline `ResetTrampoline` into `Reset` -- the FPU is enabled
+//! in `Reset`.
+//!
+//! # Advanced usage
+//!
+//! ## Setting the program entry point
+//!
+//! This section describes how `entry!` is implemented. This information is useful to developers who
+//! want to provide an alternative to `entry!` that provides extra guarantees.
+//!
+//! The `Reset` handler will call a symbol named `main` (unmangled) *after* initializing `.bss` and
+//! `.data`, and enabling the FPU (if the target is `thumbv7em-none-eabihf`). `entry!` provides this
+//! symbol in its expansion:
+//!
+//! ``` ignore
+//! entry!(path::to::main);
+//!
+//! // expands into
+//!
+//! #[export_name = "main"]
+//! pub extern "C" fn __impl_main() -> ! {
+//! // validate the signature of the program entry point
+//! let f: fn() -> ! = path::to::main;
+//!
+//! f()
+//! }
+//! ```
+//!
+//! The unmangled `main` symbol must have signature `extern "C" fn() -> !` or its invocation from
+//! `Reset` will result in undefined behavior.
+//!
+//! ## Incorporating device specific interrupts
//!
//! This section covers how an external crate can insert device specific interrupt handlers into the
//! vector table. Most users don't need to concern themselves with these details, but if you are
@@ -226,7 +261,7 @@
//!
//! The information in this section applies when the `"device"` feature has been enabled.
//!
-//! ## `__INTERRUPTS`
+//! ### `__INTERRUPTS`
//!
//! The external crate must provide the interrupts portion of the vector table via a `static`
//! variable named`__INTERRUPTS` (unmangled) that must be placed in the `.vector_table.interrupts`
@@ -273,7 +308,7 @@
//! ];
//! ```
//!
-//! ## `device.x`
+//! ### `device.x`
//!
//! Linking in `__INTERRUPTS` creates a bunch of undefined references. If the user doesn't set a
//! handler for *all* the device specific interrupts then linking will fail with `"undefined
@@ -425,9 +460,12 @@ pub unsafe extern "C" fn Reset() -> ! {
}
}
-/// Macro to define the user entry point of a program
+/// Macro to define the entry point of the program
///
-/// Usage: `entry!($path::to::user::entry)`
+/// **NOTE** This macro must be invoked once and must be invoked from an accessible module, ideally
+/// from the root of the crate.
+///
+/// Usage: `entry!(path::to::entry::point)`
///
/// The specified 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 function
@@ -439,7 +477,7 @@ macro_rules! entry {
($path:path) => {
#[export_name = "main"]
pub extern "C" fn __impl_main() -> ! {
- // validate the signature of the user provide `main`
+ // validate the signature of the program entry point
let f: fn() -> ! = $path;
f()
@@ -561,10 +599,7 @@ pub static __EXCEPTIONS: [Vector; 14] = [
// 13: Reserved
Vector { reserved: 0 },
// Exception 14: Pend SV Interrupt [not on Cortex-M0 variants].
- #[cfg(not(armv6m))]
Vector { handler: PendSV },
- #[cfg(armv6m)]
- Vector { reserved: 0 },
// Exception 15: System Tick Interrupt.
Vector { handler: SysTick },
];
@@ -598,6 +633,9 @@ pub static __INTERRUPTS: [unsafe extern "C" fn(); 32] = [{
/// Macro to set or override a processor core exception handler
///
+/// **NOTE** This macro must be invoked from an accessible module, ideally from the root of the
+/// crate.
+///
/// # Syntax
///
/// ``` ignore
@@ -613,7 +651,7 @@ pub static __INTERRUPTS: [unsafe extern "C" fn(); 32] = [{
/// );
/// ```
///
-/// `$Name` can be one of:
+/// where `$Name` can be one of:
///
/// - `*`
/// - `NonMaskableInt`
@@ -701,9 +739,9 @@ pub static __INTERRUPTS: [unsafe extern "C" fn(); 32] = [{
macro_rules! exception {
(* , $handler:path) => {
#[allow(unsafe_code)]
- #[allow(non_snake_case)]
- #[export_name = "DefaultHandler"]
- pub unsafe extern "C" fn __impl_DefaultHandler() {
+ #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible
+ #[no_mangle]
+ pub unsafe extern "C" fn DefaultHandler() {
extern crate core;
// validate the signature of the user provided handler
@@ -719,9 +757,9 @@ macro_rules! exception {
(HardFault, $handler:path) => {
#[allow(unsafe_code)]
- #[allow(non_snake_case)]
- #[export_name = "UserHardFault"]
- pub unsafe extern "C" fn __impl_UserHardFault(ef: &$crate::ExceptionFrame) {
+ #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible
+ #[no_mangle]
+ pub unsafe extern "C" fn UserHardFault(ef: &$crate::ExceptionFrame) {
// validate the signature of the user provided handler
let f: fn(&$crate::ExceptionFrame) -> ! = $handler;
@@ -731,6 +769,7 @@ macro_rules! exception {
($Name:ident, $handler:path,state: $State:ty = $initial_state:expr) => {
#[allow(unsafe_code)]
+ #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible
#[no_mangle]
pub unsafe extern "C" fn $Name() {
static mut STATE: $State = $initial_state;
@@ -747,6 +786,7 @@ macro_rules! exception {
($Name:ident, $handler:path) => {
#[allow(unsafe_code)]
+ #[deny(private_no_mangle_fns)] // raise an error if this item is not accessible
#[no_mangle]
pub unsafe extern "C" fn $Name() {
// check that this exception exists