aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml2
-rw-r--r--asm/inline.rs7
-rw-r--r--asm/lib.rs5
-rw-r--r--bin/thumbv6m-none-eabi-lto.abin14432 -> 15040 bytes
-rw-r--r--bin/thumbv6m-none-eabi.abin17496 -> 18136 bytes
-rw-r--r--bin/thumbv7em-none-eabi-lto.abin18648 -> 19288 bytes
-rw-r--r--bin/thumbv7em-none-eabi.abin21892 -> 22532 bytes
-rw-r--r--bin/thumbv7em-none-eabihf-lto.abin19624 -> 20276 bytes
-rw-r--r--bin/thumbv7em-none-eabihf.abin22976 -> 23616 bytes
-rw-r--r--bin/thumbv7m-none-eabi-lto.abin17408 -> 18040 bytes
-rw-r--r--bin/thumbv7m-none-eabi.abin20724 -> 21364 bytes
-rw-r--r--bin/thumbv8m.base-none-eabi-lto.abin17688 -> 18300 bytes
-rw-r--r--bin/thumbv8m.base-none-eabi.abin21208 -> 21836 bytes
-rw-r--r--bin/thumbv8m.main-none-eabi-lto.abin22376 -> 22960 bytes
-rw-r--r--bin/thumbv8m.main-none-eabi.abin26560 -> 27188 bytes
-rw-r--r--bin/thumbv8m.main-none-eabihf-lto.abin23360 -> 23948 bytes
-rw-r--r--bin/thumbv8m.main-none-eabihf.abin27612 -> 28236 bytes
-rw-r--r--cortex-m-semihosting/CHANGELOG.md123
-rw-r--r--cortex-m-semihosting/Cargo.toml22
-rw-r--r--cortex-m-semihosting/README.md40
-rw-r--r--cortex-m-semihosting/build.rs23
-rw-r--r--cortex-m-semihosting/src/debug.rs96
-rw-r--r--cortex-m-semihosting/src/export.rs51
-rw-r--r--cortex-m-semihosting/src/hio.rs88
-rw-r--r--cortex-m-semihosting/src/lib.rs232
-rw-r--r--cortex-m-semihosting/src/macros.rs119
-rw-r--r--cortex-m-semihosting/src/nr.rs57
-rw-r--r--panic-semihosting/CHANGELOG.md66
-rw-r--r--panic-semihosting/Cargo.toml22
-rw-r--r--panic-semihosting/README.md37
-rw-r--r--panic-semihosting/src/lib.rs96
-rw-r--r--xtask/tests/ci.rs85
32 files changed, 1146 insertions, 25 deletions
diff --git a/Cargo.toml b/Cargo.toml
index eddc6fa..e3c6a3f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,7 +27,7 @@ inline-asm = []
linker-plugin-lto = []
[workspace]
-members = ["xtask"]
+members = ["xtask", "cortex-m-semihosting", "panic-semihosting"]
[package.metadata.docs.rs]
targets = [
diff --git a/asm/inline.rs b/asm/inline.rs
index 9a0c66e..688604e 100644
--- a/asm/inline.rs
+++ b/asm/inline.rs
@@ -175,6 +175,13 @@ pub unsafe fn __wfi() {
asm!("wfi");
}
+/// Semihosting syscall.
+#[inline(always)]
+pub unsafe fn __syscall(mut nr: u32, arg: u32) -> u32 {
+ asm!("bkpt #0xab", inout("r0") nr, in("r1") arg);
+ nr
+}
+
// v7m *AND* v8m.main, but *NOT* v8m.base
#[cfg(any(armv7m, armv8m_main))]
pub use self::v7m::*;
diff --git a/asm/lib.rs b/asm/lib.rs
index 93d56fb..ec46d5b 100644
--- a/asm/lib.rs
+++ b/asm/lib.rs
@@ -46,7 +46,7 @@ macro_rules! shims {
pub unsafe extern "C" fn $name(
$($arg: $argty),*
) $(-> $ret)? {
- crate::inline::$name($($arg)*)
+ crate::inline::$name($($arg),*)
}
)+
};
@@ -72,9 +72,10 @@ shims! {
fn __udf();
fn __wfe();
fn __wfi();
+ fn __syscall(nr: u32, arg: u32) -> u32;
}
-// v7m *AND* v8m.main, but *NOT* v8m.base
+// v7m *AND* v8m.main, but *NOT* v8m.base
#[cfg(any(armv7m, armv8m_main))]
shims! {
fn __basepri_max(val: u8);
diff --git a/bin/thumbv6m-none-eabi-lto.a b/bin/thumbv6m-none-eabi-lto.a
index 6ee0427..93d2953 100644
--- a/bin/thumbv6m-none-eabi-lto.a
+++ b/bin/thumbv6m-none-eabi-lto.a
Binary files differ
diff --git a/bin/thumbv6m-none-eabi.a b/bin/thumbv6m-none-eabi.a
index 34dba4a..5fbc73c 100644
--- a/bin/thumbv6m-none-eabi.a
+++ b/bin/thumbv6m-none-eabi.a
Binary files differ
diff --git a/bin/thumbv7em-none-eabi-lto.a b/bin/thumbv7em-none-eabi-lto.a
index 781ea4c..608cbf1 100644
--- a/bin/thumbv7em-none-eabi-lto.a
+++ b/bin/thumbv7em-none-eabi-lto.a
Binary files differ
diff --git a/bin/thumbv7em-none-eabi.a b/bin/thumbv7em-none-eabi.a
index 05d98ba..665ff58 100644
--- a/bin/thumbv7em-none-eabi.a
+++ b/bin/thumbv7em-none-eabi.a
Binary files differ
diff --git a/bin/thumbv7em-none-eabihf-lto.a b/bin/thumbv7em-none-eabihf-lto.a
index b0a2204..feecade 100644
--- a/bin/thumbv7em-none-eabihf-lto.a
+++ b/bin/thumbv7em-none-eabihf-lto.a
Binary files differ
diff --git a/bin/thumbv7em-none-eabihf.a b/bin/thumbv7em-none-eabihf.a
index bd416b5..7517720 100644
--- a/bin/thumbv7em-none-eabihf.a
+++ b/bin/thumbv7em-none-eabihf.a
Binary files differ
diff --git a/bin/thumbv7m-none-eabi-lto.a b/bin/thumbv7m-none-eabi-lto.a
index a6ff71a..bf84238 100644
--- a/bin/thumbv7m-none-eabi-lto.a
+++ b/bin/thumbv7m-none-eabi-lto.a
Binary files differ
diff --git a/bin/thumbv7m-none-eabi.a b/bin/thumbv7m-none-eabi.a
index 1f57c90..803a0ba 100644
--- a/bin/thumbv7m-none-eabi.a
+++ b/bin/thumbv7m-none-eabi.a
Binary files differ
diff --git a/bin/thumbv8m.base-none-eabi-lto.a b/bin/thumbv8m.base-none-eabi-lto.a
index d157ab5..559bf13 100644
--- a/bin/thumbv8m.base-none-eabi-lto.a
+++ b/bin/thumbv8m.base-none-eabi-lto.a
Binary files differ
diff --git a/bin/thumbv8m.base-none-eabi.a b/bin/thumbv8m.base-none-eabi.a
index 6142436..abfa14f 100644
--- a/bin/thumbv8m.base-none-eabi.a
+++ b/bin/thumbv8m.base-none-eabi.a
Binary files differ
diff --git a/bin/thumbv8m.main-none-eabi-lto.a b/bin/thumbv8m.main-none-eabi-lto.a
index f5849b1..1a381f8 100644
--- a/bin/thumbv8m.main-none-eabi-lto.a
+++ b/bin/thumbv8m.main-none-eabi-lto.a
Binary files differ
diff --git a/bin/thumbv8m.main-none-eabi.a b/bin/thumbv8m.main-none-eabi.a
index 63a0dd0..4d4d5c6 100644
--- a/bin/thumbv8m.main-none-eabi.a
+++ b/bin/thumbv8m.main-none-eabi.a
Binary files differ
diff --git a/bin/thumbv8m.main-none-eabihf-lto.a b/bin/thumbv8m.main-none-eabihf-lto.a
index 5dae3aa..ab82609 100644
--- a/bin/thumbv8m.main-none-eabihf-lto.a
+++ b/bin/thumbv8m.main-none-eabihf-lto.a
Binary files differ
diff --git a/bin/thumbv8m.main-none-eabihf.a b/bin/thumbv8m.main-none-eabihf.a
index 3989cda..2d4703a 100644
--- a/bin/thumbv8m.main-none-eabihf.a
+++ b/bin/thumbv8m.main-none-eabihf.a
Binary files differ
diff --git a/cortex-m-semihosting/CHANGELOG.md b/cortex-m-semihosting/CHANGELOG.md
new file mode 100644
index 0000000..f813f8b
--- /dev/null
+++ b/cortex-m-semihosting/CHANGELOG.md
@@ -0,0 +1,123 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+This project adheres to [Semantic Versioning](http://semver.org/).
+
+## [Unreleased]
+
+## [v0.3.5] - 2019-08-29
+
+### Added
+
+- Adds a feature to work around JLink quirks
+- Adds a dbg! macro using heprintln
+- Added thumbv8m.main support on stable
+
+### Fixed
+
+- Now Rust 2018 edition
+
+## [v0.3.4] - 2019-08-13
+
+### Fixed
+
+- Support for thumbv8 mainline hf target
+
+## [v0.3.3] - 2019-04-22
+
+### Added
+
+- Adds support for thumbv8 and cortex-m v0.6.0
+
+## [v0.3.2] - 2018-11-04
+
+### Added
+
+- Added a family of `hprint` macros for printing to the host standard output /
+ error via globally shared `HStdout` / `HStderr` handles .
+
+## [v0.3.1] - 2018-08-27
+
+### Changed
+
+- This crate no longer depends on `arm-none-eabi-gcc`.
+
+## [v0.3.0] - 2018-05-10
+
+### Changed
+
+- [breaking-change] `inline-asm` is no longer a default feature (i.e. a feature that's enabled by
+ default). The consequence is that this crate now compiles on 1.27 (beta) by default, and opting
+ into `inline-asm` requires nightly.
+
+## [v0.2.1] - 2018-04-25
+
+### Added
+
+- An opt-out "inline-asm" Cargo feature. When this feature is disabled semihosting is implemented
+ using an external assembly file instead of using the unstable inline assembly (`asm!`) feature
+ meaning that this crate can be compiled on stable.
+
+## [v0.2.0] - 2017-07-07
+
+### Added
+
+- `exit` and `report_exception` syscalls
+
+- `HStdout` and `HStderr` structs that represent handles to the host stdout and
+ stderr stream respectively.
+
+### Changed
+
+- [breaking-change] The `io` module has been renamed to `hio` to reflect that
+ this is I/O *on the host*.
+
+### Removed
+
+- [breaking-change] the family of `write` functions in the `io` module. Instead
+ use `HStdout` / `HStderr` and its `write_all` method and `fmt::Write`
+ implementation.
+
+- [breaking-change] the `hprint!` family of macros. Instead use `HStdout` and
+ the standard `write!` macro.
+
+## [v0.1.3] - 2017-02-27
+
+### Added
+
+- A family of `ewrite` functions and `ehprint!` macros to write to the host's
+ stderr.
+
+### Fixed
+
+- `write_all` logic when a single write doesn't write all the buffer bytes
+
+## [v0.1.2] - 2017-02-15
+
+### Fixed
+
+- the `hprintln!` macro when called without arguments.
+
+## [v0.1.1] - 2017-01-22
+
+### Added
+
+- Expose a family of `write` functions to write to the host's stdout without
+ going through the `hprint!` macros.
+
+## v0.1.0 - 2017-01-22
+
+- Initial release
+
+[Unreleased]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.5...HEAD
+[v0.3.5]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.4...v0.3.5
+[v0.3.4]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.3...v0.3.4
+[v0.3.3]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.2...v0.3.3
+[v0.3.2]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.1...v0.3.2
+[v0.3.1]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.0...v0.3.1
+[v0.3.0]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.2.1...v0.3.0
+[v0.2.1]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.2.0...v0.2.1
+[v0.2.0]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.3...v0.2.0
+[v0.1.3]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.2...v0.1.3
+[v0.1.2]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.1...v0.1.2
+[v0.1.1]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.0...v0.1.1
diff --git a/cortex-m-semihosting/Cargo.toml b/cortex-m-semihosting/Cargo.toml
new file mode 100644
index 0000000..8eb28b0
--- /dev/null
+++ b/cortex-m-semihosting/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+authors = [
+ "The Cortex-M Team <cortex-m@teams.rust-embedded.org>",
+ "Jorge Aparicio <japaricious@gmail.com>",
+]
+description = "Semihosting for ARM Cortex-M processors"
+documentation = "https://docs.rs/cortex-m-semihosting"
+keywords = ["semihosting", "arm", "cortex-m"]
+license = "MIT OR Apache-2.0"
+name = "cortex-m-semihosting"
+readme = "README.md"
+repository = "https://github.com/rust-embedded/cortex-m"
+version = "0.3.5"
+edition = "2018"
+
+[features]
+inline-asm = []
+jlink-quirks = []
+no-semihosting = []
+
+[dependencies]
+cortex-m = { path = "..", version = ">= 0.5.8, < 0.7" }
diff --git a/cortex-m-semihosting/README.md b/cortex-m-semihosting/README.md
new file mode 100644
index 0000000..bfbfb44
--- /dev/null
+++ b/cortex-m-semihosting/README.md
@@ -0,0 +1,40 @@
+[![crates.io](https://img.shields.io/crates/v/cortex-m-semihosting.svg)](https://crates.io/crates/cortex-m-semihosting)
+[![crates.io](https://img.shields.io/crates/d/cortex-m-semihosting.svg)](https://crates.io/crates/cortex-m-semihosting)
+
+# `cortex-m-semihosting`
+
+> Semihosting for ARM Cortex-M processors
+
+This project is developed and maintained by the [Cortex-M team][team].
+
+## [Documentation](https://docs.rs/cortex-m-semihosting)
+
+# Minimum Supported Rust Version (MSRV)
+
+This crate is guaranteed to compile on stable Rust 1.33.0 and up. It *might*
+compile with older versions but that may change in any new patch release.
+
+## License
+
+Licensed under either of
+
+- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or
+ http://www.apache.org/licenses/LICENSE-2.0)
+- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
+
+## Code of Conduct
+
+Contribution to this crate is organized under the terms of the [Rust Code of
+Conduct][CoC], the maintainer of this crate, the [Cortex-M team][team], promises
+to intervene to uphold that code of conduct.
+
+[CoC]: ../CODE_OF_CONDUCT.md
+[team]: https://github.com/rust-embedded/wg#the-cortex-m-team
diff --git a/cortex-m-semihosting/build.rs b/cortex-m-semihosting/build.rs
new file mode 100644
index 0000000..5fc6a04
--- /dev/null
+++ b/cortex-m-semihosting/build.rs
@@ -0,0 +1,23 @@
+use std::path::PathBuf;
+use std::{env, fs};
+
+fn main() {
+ let target = env::var("TARGET").unwrap();
+ let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
+ let name = env::var("CARGO_PKG_NAME").unwrap();
+
+ if target.starts_with("thumbv") {
+ if env::var_os("CARGO_FEATURE_INLINE_ASM").is_none() {
+ fs::copy(
+ format!("../bin/{}.a", target),
+ out_dir.join(format!("lib{}.a", name)),
+ )
+ .unwrap();
+
+ println!("cargo:rustc-link-lib=static={}", name);
+ println!("cargo:rustc-link-search={}", out_dir.display());
+ }
+
+ println!("cargo:rustc-cfg=thumb");
+ }
+}
diff --git a/cortex-m-semihosting/src/debug.rs b/cortex-m-semihosting/src/debug.rs
new file mode 100644
index 0000000..a4fa6d8
--- /dev/null
+++ b/cortex-m-semihosting/src/debug.rs
@@ -0,0 +1,96 @@
+//! Interacting with debugging agent
+//!
+//! # Example
+//!
+//! This example will show how to terminate the QEMU session. The program
+//! should be running under QEMU with semihosting enabled
+//! (use `-semihosting` flag).
+//!
+//! Target program:
+//!
+//! ```no_run
+//! use cortex_m_semihosting::debug::{self, EXIT_SUCCESS, EXIT_FAILURE};
+//!
+//! fn main() {
+//! if 2 == 2 {
+//! // report success
+//! debug::exit(EXIT_SUCCESS);
+//! } else {
+//! // report failure
+//! debug::exit(EXIT_FAILURE);
+//! }
+//! }
+//!
+
+/// This values are taken from section 5.5.2 of
+/// ADS Debug Target Guide (DUI0058).
+// TODO document
+#[allow(missing_docs)]
+pub enum Exception {
+ // Hardware reason codes
+ BranchThroughZero = 0x20000,
+ UndefinedInstr = 0x20001,
+ SoftwareInterrupt = 0x20002,
+ PrefetchAbort = 0x20003,
+ DataAbort = 0x20004,
+ AddressException = 0x20005,
+ IRQ = 0x20006,
+ FIQ = 0x20007,
+ // Software reason codes
+ BreakPoint = 0x20020,
+ WatchPoint = 0x20021,
+ StepComplete = 0x20022,
+ RunTimeErrorUnknown = 0x20023,
+ InternalError = 0x20024,
+ UserInterruption = 0x20025,
+ ApplicationExit = 0x20026,
+ StackOverflow = 0x20027,
+ DivisionByZero = 0x20028,
+ OSSpecific = 0x20029,
+}
+
+/// Status enum for `exit` syscall.
+pub type ExitStatus = Result<(), ()>;
+
+/// Successful execution of a program.
+pub const EXIT_SUCCESS: ExitStatus = Ok(());
+
+/// Unsuccessful execution of a program.
+pub const EXIT_FAILURE: ExitStatus = Err(());
+
+/// Reports to the debugger that the execution has completed.
+///
+/// This call can be used to terminate QEMU session and report back success
+/// or failure. If you need to pass more than one type of error, consider
+/// using `report_exception` syscall instead.
+///
+/// This call should not return. However, it is possible for the debugger
+/// to request that the application continue. In that case this call
+/// returns normally.
+///
+pub fn exit(status: ExitStatus) {
+ match status {
+ EXIT_SUCCESS => report_exception(Exception::ApplicationExit),
+ EXIT_FAILURE => report_exception(Exception::RunTimeErrorUnknown),
+ }
+}
+
+/// Report an exception to the debugger directly.
+///
+/// Exception handlers can use this SWI at the end of handler chains
+/// as the default action, to indicate that the exception has not been handled.
+///
+/// This call should not return. However, it is possible for the debugger
+/// to request that the application continue. In that case this call
+/// returns normally.
+///
+/// # Arguments
+///
+/// * `reason` - A reason code reported back to the debugger.
+///
+pub fn report_exception(reason: Exception) {
+ let code = reason as usize;
+ unsafe {
+ syscall1!(REPORT_EXCEPTION, code);
+ }
+}
diff --git a/cortex-m-semihosting/src/export.rs b/cortex-m-semihosting/src/export.rs
new file mode 100644
index 0000000..c188ab0
--- /dev/null
+++ b/cortex-m-semihosting/src/export.rs
@@ -0,0 +1,51 @@
+//! IMPLEMENTATION DETAILS USED BY MACROS
+
+use core::fmt::{self, Write};
+
+use cortex_m::interrupt;
+
+use crate::hio::{self, HStderr, HStdout};
+
+static mut HSTDOUT: Option<HStdout> = None;
+
+pub fn hstdout_str(s: &str) {
+ let _result = interrupt::free(|_| unsafe {
+ if HSTDOUT.is_none() {
+ HSTDOUT = Some(hio::hstdout()?);
+ }
+
+ HSTDOUT.as_mut().unwrap().write_str(s).map_err(drop)
+ });
+}
+
+pub fn hstdout_fmt(args: fmt::Arguments) {
+ let _result = interrupt::free(|_| unsafe {
+ if HSTDOUT.is_none() {
+ HSTDOUT = Some(hio::hstdout()?);
+ }
+
+ HSTDOUT.as_mut().unwrap().write_fmt(args).map_err(drop)
+ });
+}
+
+static mut HSTDERR: Option<HStderr> = None;
+
+pub fn hstderr_str(s: &str) {
+ let _result = interrupt::free(|_| unsafe {
+ if HSTDERR.is_none() {
+ HSTDERR = Some(hio::hstderr()?);
+ }
+
+ HSTDERR.as_mut().unwrap().write_str(s).map_err(drop)
+ });
+}
+
+pub fn hstderr_fmt(args: fmt::Arguments) {
+ let _result = interrupt::free(|_| unsafe {
+ if HSTDERR.is_none() {
+ HSTDERR = Some(hio::hstderr()?);
+ }
+
+ HSTDERR.as_mut().unwrap().write_fmt(args).map_err(drop)
+ });
+}
diff --git a/cortex-m-semihosting/src/hio.rs b/cortex-m-semihosting/src/hio.rs
new file mode 100644
index 0000000..61ac749
--- /dev/null
+++ b/cortex-m-semihosting/src/hio.rs
@@ -0,0 +1,88 @@
+//! Host I/O
+
+use core::{fmt, slice};
+use crate::nr;
+
+/// Host's standard error
+#[derive(Clone, Copy)]
+pub struct HStderr {
+ fd: usize,
+}
+
+impl HStderr {
+ /// Attempts to write an entire `buffer` into this sink
+ pub fn write_all(&mut self, buffer: &[u8]) -> Result<(), ()> {
+ write_all(self.fd, buffer)
+ }
+}
+
+impl fmt::Write for HStderr {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ self.write_all(s.as_bytes()).map_err(|_| fmt::Error)
+ }
+}
+
+/// Host's standard output
+#[derive(Clone, Copy)]
+pub struct HStdout {
+ fd: usize,
+}
+
+impl HStdout {
+ /// Attempts to write an entire `buffer` into this sink
+ pub fn write_all(&mut self, buffer: &[u8]) -> Result<(), ()> {
+ write_all(self.fd, buffer)
+ }
+}
+
+impl fmt::Write for HStdout {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ self.write_all(s.as_bytes()).map_err(|_| fmt::Error)
+ }
+}
+
+/// Construct a new handle to the host's standard error.
+pub fn hstderr() -> Result<HStderr, ()> {
+ // There is actually no stderr access in ARM Semihosting documentation. Use
+ // convention used in libgloss.
+ // See: libgloss/arm/syscalls.c, line 139.
+ // https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/arm/syscalls.c#l139
+ open(":tt\0", nr::open::W_APPEND).map(|fd| HStderr { fd })
+}
+
+/// Construct a new handle to the host's standard output.
+pub fn hstdout() -> Result<HStdout, ()> {
+ open(":tt\0", nr::open::W_TRUNC).map(|fd| HStdout { fd })
+}
+
+fn open(name: &str, mode: usize) -> Result<usize, ()> {
+ let name = name.as_bytes();
+ match unsafe { syscall!(OPEN, name.as_ptr(), mode, name.len() - 1) } as
+ isize {
+ -1 => Err(()),
+ fd => Ok(fd as usize),
+ }
+}
+
+fn write_all(fd: usize, mut buffer: &[u8]) -> Result<(), ()> {
+ while !buffer.is_empty() {
+ match unsafe { syscall!(WRITE, fd, buffer.as_ptr(), buffer.len()) } {
+ // Done
+ 0 => return Ok(()),
+ // `n` bytes were not written
+ n if n <= buffer.len() => {
+ let offset = (buffer.len() - n) as isize;
+ buffer = unsafe {
+ slice::from_raw_parts(buffer.as_ptr().offset(offset), n)
+ }
+ }
+ #[cfg(feature = "jlink-quirks")]
+ // Error (-1) - should be an error but JLink can return -1, -2, -3,...
+ // For good measure, we allow up to negative 15.
+ n if n > 0xfffffff0 => return Ok(()),
+ // Error
+ _ => return Err(()),
+ }
+ }
+ Ok(())
+}
diff --git a/cortex-m-semihosting/src/lib.rs b/cortex-m-semihosting/src/lib.rs
new file mode 100644
index 0000000..b70dea0
--- /dev/null
+++ b/cortex-m-semihosting/src/lib.rs
@@ -0,0 +1,232 @@
+//! Semihosting for ARM Cortex-M processors
+//!
+//! # What is semihosting?
+//!
+//! "Semihosting is a mechanism that enables code running on an ARM target to communicate and use
+//! the Input/Output facilities on a host computer that is running a debugger." - ARM
+//!
+//! # Interface
+//!
+//! This crate provides implementations of
+//! [`core::fmt::Write`](https://doc.rust-lang.org/core/fmt/trait.Write.html), so you can use it,
+//! in conjunction with
+//! [`core::format_args!`](https://doc.rust-lang.org/core/macro.format_args.html) or the [`write!` macro](https://doc.rust-lang.org/core/macro.write.html), for user-friendly construction and printing of formatted strings.
+//!
+//! Since semihosting operations are modeled as [system calls][sc], this crate exposes an untyped
+//! `syscall!` interface just like the [`sc`] crate does.
+//!
+//! [sc]: https://en.wikipedia.org/wiki/System_call
+//! [`sc`]: https://crates.io/crates/sc
+//!
+//! # Forewarning
+//!
+//! Semihosting operations are *very* slow. Like, each WRITE operation can take hundreds of
+//! milliseconds.
+//!
+//! # Example
+//!
+//! ## Using `hio::HStdout`
+//!
+//! This example will demonstrate how to print formatted strings.
+//!
+//! ```no_run
+//! use cortex_m_semihosting::hio;
+//! use core::fmt::Write;
+//!
+//! // This function will be called by the application
+//! fn print() -> Result<(), core::fmt::Error> {
+//! let mut stdout = match hio::hstdout() {
+//! Ok(fd) => fd,
+//! Err(()) => return Err(core::fmt::Error),
+//! };
+//!
+//! let language = "Rust";
+//! let ranking = 1;
+//!
+//! write!(stdout, "{} on embedded is #{}!", language, ranking)?;
+//!
+//! Ok(())
+//! }
+//! ```
+//!
+//! On the host side:
+//!
+//! ``` text
+//! $ openocd -f $INTERFACE -f $TARGET -l /tmp/openocd.log
+//! Open On-Chip Debugger 0.9.0 (2016-04-27-23:18)
+//! Licensed under GNU GPL v2
+//! For bug reports, read
+//! http://openocd.org/doc/doxygen/bugs.html
+//! # the command will block at this point
+//! ```
+//!
+//! The OpenOCD logs will be redirected to `/tmp/openocd.log`. You can view those logs in "real
+//! time" using `tail`
+//!
+//! ``` text
+//! $ tail -f /tmp/openocd.log
+//! Info : Unable to match requested speed 1000 kHz, using 950 kHz
+//! Info : Unable to match requested speed 1000 kHz, using 950 kHz
+//! Info : clock speed 950 kHz
+//! Info : STLINK v1 JTAG v11 API v2 SWIM v0 VID 0x0483 PID 0x3744
+//! Info : using stlink api v2
+//! Info : nrf51.cpu: hardware has 4 breakpoints, 2 watchpoints
+//! ```
+//!
+//! Alternatively you could omit the `-l` flag from the `openocd` call, and the `tail -f` command
+//! but the OpenOCD output will have intermingled in it logs from its normal operation.
+//!
+//! Then, we run the program:
+//!
+//! ``` text
+//! $ arm-none-eabi-gdb hello-world
+//! (gdb) # Connect to OpenOCD
+//! (gdb) target remote :3333
+//!
+//! (gdb) # Enable OpenOCD's semihosting support
+//! (gdb) monitor arm semihosting enable
+//!
+//! (gdb) # Flash the program
+//! (gdb) load
+//!
+//! (gdb) # Run the program
+//! (gdb) continue
+//! ```
+//!
+//! And you'll see the output under OpenOCD's terminal
+//!
+//! ``` text
+//! # openocd -f $INTERFACE -f $TARGET -l /tmp/openocd.log
+//! (..)
+//! Rust on embedded is #1!
+//! ```
+//! ## Using the syscall interface
+//!
+//! This example will show how to print "Hello, world!" on the host.
+//!
+//! Target program:
+//!
+//! ```no_run
+//! use cortex_m_semihosting::syscall;
+//!
+//! // This function will be called by the application
+//! fn print() {
+//! // File descriptor (on the host)
+//! const STDOUT: usize = 1; // NOTE the host stdout may not always be fd 1
+//! static MSG: &'static [u8] = b"Hello, world!\n";
+//!
+//! // Signature: fn write(fd: usize, ptr: *const u8, len: usize) -> usize
+//! let r = unsafe { syscall!(WRITE, STDOUT, MSG.as_ptr(), MSG.len()) };
+//! }
+//! ```
+//! Output and monitoring proceed as in the above example.
+//!
+//! ## The `dbg!` macro
+//!
+//! Analogous to [`std::dbg`](https://doc.rust-lang.org/std/macro.dbg.html) the macro
+//! `dbg!` returns a given expression and prints it using `heprintln!` including context
+//! for quick and dirty debugging.
+//!
+//! Panics if `heprintln!` returns an error.
+//!
+//! Example:
+//!
+//! ```no_run
+//! const UUID: *mut u32 = 0x0009_FC70 as *mut u32;
+//! dbg!(UUID);
+//! let mut uuid: [u32; 4] = [0; 4];
+//! for i in 0..4 {
+//! dbg!(i);
+//! uuid[i] = unsafe { dbg!(UUID.offset(i as isize).read_volatile()) };
+//! }
+//! ```
+//! outputs
+//! ```text
+//! [examples/semihosting.rs:37] UUID = 0x0009fc70
+//! [examples/semihosting.rs:40] i = 0
+//! [examples/semihosting.rs:41] UUID.offset(i as isize).read_volatile() = 3370045464
+//! [examples/semihosting.rs:40] i = 1
+//! [examples/semihosting.rs:41] UUID.offset(i as isize).read_volatile() = 1426218275
+//! [examples/semihosting.rs:40] i = 2
+//! [examples/semihosting.rs:41] UUID.offset(i as isize).read_volatile() = 2422621116
+//! [examples/semihosting.rs:40] i = 3
+//! [examples/semihosting.rs:41] UUID.offset(i as isize).read_volatile() = 1044138593
+//! ```
+//!
+//! # Optional features
+//!
+//! ## `inline-asm`
+//!
+//! When this feature is enabled semihosting is implemented using inline assembly (`llvm_asm!`) and
+//! compiling this crate requires nightly.
+//!
+//! When this feature is disabled semihosting is implemented using FFI calls into an external
+//! assembly file and compiling this crate works on stable and beta.
+//!
+//! ## `jlink-quirks`
+//!
+//! When this feature is enabled, return values above `0xfffffff0` from semihosting operation
+//! `SYS_WRITE` (0x05) are interpreted as if the entire buffer had been written. The current
+//! latest version 6.48b of J-Link exhibits such behaviour, causing a panic if this feature
+//! is not enabled.
+//!
+//! ## `no-semihosting`
+//!
+//! When this feature is enabled, the underlying system calls to `bkpt` are patched out.
+//!
+//! # Reference
+//!
+//! For documentation about the semihosting operations, check:
+//!
+//! 'Chapter 8 - Semihosting' of the ['ARM Compiler toolchain Version 5.0'][pdf]
+//! manual.
+//!
+//! [pdf]: http://infocenter.arm.com/help/topic/com.arm.doc.dui0471e/DUI0471E_developing_for_arm_processors.pdf
+
+#![cfg_attr(feature = "inline-asm", feature(llvm_asm))]
+#![deny(missing_docs)]
+#![no_std]
+
+#[macro_use]
+mod macros;
+
+pub mod debug;
+#[doc(hidden)]
+pub mod export;
+pub mod hio;
+pub mod nr;
+
+#[cfg(all(thumb, not(feature = "inline-asm")))]
+extern "C" {
+ fn __syscall(nr: usize, arg: usize) -> usize;
+}
+
+/// Performs a semihosting operation, takes a pointer to an argument block
+#[inline(always)]
+pub unsafe fn syscall<T>(nr: usize, arg: &T) -> usize {
+ syscall1(nr, arg as *const T as usize)
+}
+
+/// Performs a semihosting operation, takes one integer as an argument
+#[inline(always)]
+pub unsafe fn syscall1(_nr: usize, _arg: usize) -> usize {
+ match () {
+ #[cfg(all(thumb, not(feature = "inline-asm"), not(feature = "no-semihosting")))]
+ () => __syscall(_nr, _arg),
+
+ #[cfg(all(thumb, feature = "inline-asm", not(feature = "no-semihosting")))]
+ () => {
+ let mut nr = _nr;
+ llvm_asm!("bkpt 0xAB" : "+{r0}"(nr) : "{r1}"(_arg) :: "volatile");
+ nr
+ }
+
+ #[cfg(all(thumb, feature = "no-semihosting"))]
+ () => {
+ 0
+ }
+
+ #[cfg(not(thumb))]
+ () => unimplemented!(),
+ }
+}
diff --git a/cortex-m-semihosting/src/macros.rs b/cortex-m-semihosting/src/macros.rs
new file mode 100644
index 0000000..d10cd3f
--- /dev/null
+++ b/cortex-m-semihosting/src/macros.rs
@@ -0,0 +1,119 @@
+/// Variable argument version of `syscall`
+#[macro_export]
+macro_rules! syscall {
+ ($nr:ident) => {
+ $crate::syscall1($crate::nr::$nr, 0)
+ };
+ ($nr:ident, $a1:expr) => {
+ $crate::syscall($crate::nr::$nr, &[$a1 as usize])
+ };
+ ($nr:ident, $a1:expr, $a2:expr) => {
+ $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize])
+ };
+ ($nr:ident, $a1:expr, $a2:expr, $a3:expr) => {
+ $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize,
+ $a3 as usize])
+ };
+ ($nr:ident, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
+ $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize,
+ $a3 as usize, $a4 as usize])
+ };
+}
+
+/// Macro version of `syscall1`.
+#[macro_export]
+macro_rules! syscall1 {
+ ($nr:ident, $a1:expr) => {
+ $crate::syscall1($crate::nr::$nr, $a1 as usize)
+ };
+}
+
+/// Macro for printing to the HOST standard output.
+///
+/// This is similar to the `print!` macro in the standard library. Both will panic on any failure to
+/// print.
+#[macro_export]
+macro_rules! hprint {
+ ($s:expr) => {
+ $crate::export::hstdout_str($s)
+ };
+ ($($tt:tt)*) => {
+ $crate::export::hstdout_fmt(format_args!($($tt)*))
+ };
+}
+
+/// Macro for printing to the HOST standard output, with a newline.
+///
+/// This is similar to the `println!` macro in the standard library. Both will panic on any failure to
+/// print.
+#[macro_export]
+macro_rules! hprintln {
+ () => {
+ $crate::export::hstdout_str("\n")
+ };
+ ($s:expr) => {
+ $crate::export::hstdout_str(concat!($s, "\n"))
+ };
+ ($s:expr, $($tt:tt)*) => {
+ $crate::export::hstdout_fmt(format_args!(concat!($s, "\n"), $($tt)*))
+ };
+}
+
+/// Macro for printing to the HOST standard error.
+///
+/// This is similar to the `eprint!` macro in the standard library. Both will panic on any failure
+/// to print.
+#[macro_export]
+macro_rules! heprint {
+ ($s:expr) => {
+ $crate::export::hstderr_str($s)
+ };
+ ($($tt:tt)*) => {
+ $crate::export::hstderr_fmt(format_args!($($tt)*))
+ };
+}
+
+/// Macro for printing to the HOST standard error, with a newline.
+///
+/// This is similar to the `eprintln!` macro in the standard library. Both will panic on any failure
+/// to print.
+#[macro_export]
+macro_rules! heprintln {
+ () => {
+ $crate::export::hstderr_str("\n")
+ };
+ ($s:expr) => {
+ $crate::export::hstderr_str(concat!($s, "\n"))
+ };
+ ($s:expr, $($tt:tt)*) => {
+ $crate::export::hstderr_fmt(format_args!(concat!($s, "\n"), $($tt)*))
+ };
+}
+
+/// Macro that prints and returns the value of a given expression for quick and
+/// dirty debugging.
+///
+/// Works exactly like `dbg!` in the standard library, replacing `eprintln!`
+/// with `heprintln!`.
+#[macro_export]
+macro_rules! dbg {
+ () => {
+ $crate::heprintln!("[{}:{}]", file!(), line!());
+ };
+ ($val:expr) => {
+ // Use of `match` here is intentional because it affects the lifetimes
+ // of temporaries - https://stackoverflow.com/a/48732525/1063961
+ match $val {
+ tmp => {
+ $crate::heprintln!("[{}:{}] {} = {:#?}",
+ file!(), line!(), stringify!($val), &tmp);
+ tmp
+ }
+ }
+ };
+ // Trailing comma with single argument is ignored
+ ($val:expr,) => { $crate::dbg!($val) };
+ ($($val:expr),+ $(,)?) => {
+ ($($crate::dbg!($val)),+,)
+ };
+}
diff --git a/cortex-m-semihosting/src/nr.rs b/cortex-m-semihosting/src/nr.rs
new file mode 100644
index 0000000..5d206de
--- /dev/null
+++ b/cortex-m-semihosting/src/nr.rs
@@ -0,0 +1,57 @@
+//! Semihosting operations
+
+// TODO document
+#![allow(missing_docs)]
+
+pub const CLOCK: usize = 0x10;
+pub const CLOSE: usize = 0x02;
+pub const ELAPSED: usize = 0x30;
+pub const ERRNO: usize = 0x13;
+pub const FLEN: usize = 0x0c;
+pub const GET_CMDLINE: usize = 0x15;
+pub const HEAPINFO: usize = 0x16;
+pub const ISERROR: usize = 0x08;
+pub const ISTTY: usize = 0x09;
+pub const OPEN: usize = 0x01;
+pub const READ: usize = 0x06;
+pub const READC: usize = 0x07;
+pub const REMOVE: usize = 0x0e;
+pub const RENAME: usize = 0x0f;
+pub const SEEK: usize = 0x0a;
+pub const SYSTEM: usize = 0x12;
+pub const TICKFREQ: usize = 0x31;
+pub const TIME: usize = 0x11;
+pub const TMPNAM: usize = 0x0d;
+pub const WRITE0: usize = 0x04;
+pub const WRITE: usize = 0x05;
+pub const WRITEC: usize = 0x03;
+pub const ENTER_SVC: usize = 0x17;
+pub const REPORT_EXCEPTION: usize = 0x18;
+
+/// Values for the mode parameter of the OPEN syscall.
+pub mod open {
+ /// Mode corresponding to fopen "r" mode.
+ pub const R: usize = 0;
+ /// Mode corresponding to fopen "rb" mode.
+ pub const R_BINARY: usize = 1;
+ /// Mode corresponding to fopen "r+" mode.
+ pub const RW: usize = 2;
+ /// Mode corresponding to fopen "r+b" mode.
+ pub const RW_BINARY: usize = 3;
+ /// Mode corresponding to fopen "w" mode.
+ pub const W_TRUNC: usize = 4;
+ /// Mode corresponding to fopen "wb" mode.
+ pub const W_TRUNC_BINARY: usize = 5;
+ /// Mode corresponding to fopen "w+" mode.
+ pub const RW_TRUNC: usize = 6;
+ /// Mode corresponding to fopen "w+b" mode.
+ pub const RW_TRUNC_BINARY: usize = 7;
+ /// Mode corresponding to fopen "a" mode.
+ pub const W_APPEND: usize = 8;
+ /// Mode corresponding to fopen "ab" mode.
+ pub const W_APPEND_BINARY: usize = 9;
+ /// Mode corresponding to fopen "a+" mode.
+ pub const RW_APPEND: usize = 10;
+ /// Mode corresponding to fopen "a+b" mode.
+ pub const RW_APPEND_BINARY: usize = 11;
+}
diff --git a/panic-semihosting/CHANGELOG.md b/panic-semihosting/CHANGELOG.md
new file mode 100644
index 0000000..0036754
--- /dev/null
+++ b/panic-semihosting/CHANGELOG.md
@@ -0,0 +1,66 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/)
+and this project adheres to [Semantic Versioning](http://semver.org/).
+
+## [Unreleased]
+
+## [v0.5.3] - 2019-09-01
+
+- Added feature `jlink-quirks` to work with JLink
+
+## [v0.5.2] - 2019-04-28
+
+- Updated `cortex-m` version to not have the issue when linking multiple
+ versions of it.
+
+## [v0.5.1] - 2018-10-27
+
+### Added
+
+- An opt-in "exit" Cargo feature to have the panic handler perform an exit
+ semihosting call after logging the panic message.
+
+## [v0.5.0] - 2018-09-10
+
+- [breaking-change] The `panic_handler` feature gate has been removed. This
+ crate will compile on 1.30-beta and on stable 1.30 when they are released.
+
+## [v0.4.0] - 2018-09-03
+
+### Changed
+
+- This crate no longer depends on `arm-none-eabi-gcc`.
+
+- [breaking-change] Move from the `panic_implementation` attribute to the
+ `panic_handler` attribute, which will be stabilized.
+
+## [v0.3.0] - 2018-06-04
+
+### Changed
+
+- [breaking-change] moved from the, now removed, `panic_fmt` lang item to the
+ `#[panic_implementation]` attribute.
+
+## [v0.2.0] - 2018-05-11
+
+### Changed
+
+- [breaking-change] made inline assembly (`asm!`) opt-in via the `"inline-asm"` feature. This is a
+ breaking change because this crate now requires `arm-none-eabi-gcc` to be installed to build
+ without the `"inline-asm"` feature, which is the default.
+
+## v0.1.0 - 2018-04-09
+
+Initial release
+
+[Unreleased]: https://github.com/rust-embedded/panic-semihosting/compare/v0.5.3...HEAD
+[v0.5.3]: https://github.com/rust-embedded/panic-semihosting/compare/v0.5.2...v0.5.3
+[v0.5.2]: https://github.com/rust-embedded/panic-semihosting/compare/v0.5.1...v0.5.2
+[v0.5.1]: https://github.com/rust-embedded/panic-semihosting/compare/v0.5.0...v0.5.1
+[v0.5.0]: https://github.com/rust-embedded/panic-semihosting/compare/v0.4.0...v0.5.0
+[v0.4.0]: https://github.com/rust-embedded/panic-semihosting/compare/v0.3.0...v0.4.0
+[v0.3.0]: https://github.com/rust-embedded/panic-semihosting/compare/v0.2.0...v0.3.0
+[v0.2.0]: https://github.com/rust-embedded/panic-semihosting/compare/v0.1.0...v0.2.0
diff --git a/panic-semihosting/Cargo.toml b/panic-semihosting/Cargo.toml
new file mode 100644
index 0000000..2e6c3be
--- /dev/null
+++ b/panic-semihosting/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+authors = [
+ "The Cortex-M Team <cortex-m@teams.rust-embedded.org>",
+ "Jorge Aparicio <jorge@japaric.io>",
+]
+categories = ["no-std"]
+description = "Report panic messages to the host stderr using semihosting"
+documentation = "https://docs.rs/panic-semihosting"
+keywords = ["panic-handler", "panic-impl", "panic", "semihosting"]
+license = "MIT OR Apache-2.0"
+name = "panic-semihosting"
+repository = "https://github.com/rust-embedded/cortex-m"
+version = "0.5.3"
+
+[dependencies]
+cortex-m = { path = "..", version = ">= 0.5.6, < 0.7" }
+cortex-m-semihosting = { path = "../cortex-m-semihosting", version = "0.3" }
+
+[features]
+exit = []
+inline-asm = ["cortex-m-semihosting/inline-asm", "cortex-m/inline-asm"]
+jlink-quirks = ["cortex-m-semihosting/jlink-quirks"]
diff --git a/panic-semihosting/README.md b/panic-semihosting/README.md
new file mode 100644
index 0000000..baacf1a
--- /dev/null
+++ b/panic-semihosting/README.md
@@ -0,0 +1,37 @@
+# `panic-semihosting`
+
+> Report panic messages to the host stderr using semihosting
+
+This project is developed and maintained by the [Cortex-M team][team].
+
+## [Documentation](https://docs.rs/panic-semihosting)
+
+## Minimum Supported Rust Version (MSRV)
+
+This crate is guaranteed to compile on stable Rust 1.32.0 and up. It *might*
+compile with older versions but that may change in any new patch release.
+
+## License
+
+Licensed under either of
+
+- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or
+ http://www.apache.org/licenses/LICENSE-2.0)
+- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
+
+## Code of Conduct
+
+Contribution to this crate is organized under the terms of the [Rust Code of
+Conduct][CoC], the maintainer of this crate, the [Cortex-M team][team], promises
+to intervene to uphold that code of conduct.
+
+[CoC]: ../CODE_OF_CONDUCT.md
+[team]: https://github.com/rust-embedded/wg#the-cortex-m-team
diff --git a/panic-semihosting/src/lib.rs b/panic-semihosting/src/lib.rs
new file mode 100644
index 0000000..1db7b72
--- /dev/null
+++ b/panic-semihosting/src/lib.rs
@@ -0,0 +1,96 @@
+//! Report panic messages to the host stderr using semihosting
+//!
+//! This crate contains an implementation of `panic_fmt` that logs panic messages to the host stderr
+//! using [`cortex-m-semihosting`]. Before logging the message the panic handler disables (masks)
+//! the device specific interrupts. After logging the message the panic handler trigger a breakpoint
+//! and then goes into an infinite loop.
+//!
+//! Currently, this crate only supports the ARM Cortex-M architecture.
+//!
+//! [`cortex-m-semihosting`]: https://crates.io/crates/cortex-m-semihosting
+//!
+//! # Usage
+//!
+//! ``` ignore
+//! #![no_std]
+//!
+//! extern crate panic_semihosting;
+//!
+//! fn main() {
+//! panic!("FOO")
+//! }
+//! ```
+//!
+//! ``` text
+//! (gdb) monitor arm semihosting enable
+//! (gdb) continue
+//! Program received signal SIGTRAP, Trace/breakpoint trap.
+//! rust_begin_unwind (args=..., file=..., line=8, col=5)
+//! at $CRATE/src/lib.rs:69
+//! 69 asm::bkpt();
+//! ```
+//!
+//! ``` text
+//! $ openocd -f (..)
+//! (..)
+//! panicked at 'FOO', src/main.rs:6:5
+//! ```
+//!
+//! # Optional features
+//!
+//! ## `exit`
+//!
+//! When this feature is enabled the panic handler performs an exit semihosting call after logging
+//! the panic message. This is useful when emulating the program on QEMU as it causes the QEMU
+//! process to exit with a non-zero exit code; thus it can be used to implement Cortex-M tests that
+//! run on the host.
+//!
+//! We discourage using this feature when the program will run on hardware as the exit call can
+//! leave the hardware debugger in an inconsistent state.
+//!
+//! ## `inline-asm`
+//!
+//! When this feature is enabled semihosting is implemented using inline assembly (`asm!`) and
+//! compiling this crate requires nightly.
+//!
+//! When this feature is disabled semihosting is implemented using FFI calls into an external
+//! assembly file and compiling this crate works on stable and beta.
+
+#![cfg(all(target_arch = "arm", target_os = "none"))]
+#![deny(missing_docs)]
+#![deny(warnings)]
+#![no_std]
+
+extern crate cortex_m;
+extern crate cortex_m_semihosting as sh;
+
+use core::fmt::Write;
+use core::panic::PanicInfo;
+
+#[cfg(not(feature = "exit"))]
+use cortex_m::asm;
+use cortex_m::interrupt;
+#[cfg(feature = "exit")]
+use sh::debug::{self, EXIT_FAILURE};
+use sh::hio;
+
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ interrupt::disable();
+
+ if let Ok(mut hstdout) = hio::hstdout() {
+ writeln!(hstdout, "{}", info).ok();
+ }
+
+ match () {
+ // Exit the QEMU process
+ #[cfg(feature = "exit")]
+ () => debug::exit(EXIT_FAILURE),
+ // OK to fire a breakpoint here because we know the microcontroller is connected to a
+ // debugger
+ #[cfg(not(feature = "exit"))]
+ () => asm::bkpt(),
+ }
+
+ loop {}
+}
diff --git a/xtask/tests/ci.rs b/xtask/tests/ci.rs
index 48356e4..5b449ba 100644
--- a/xtask/tests/ci.rs
+++ b/xtask/tests/ci.rs
@@ -2,6 +2,9 @@ use std::process::Command;
use std::{env, str};
use xtask::{check_blobs, install_targets};
+/// List of all compilation targets we support.
+///
+/// This should generally list all of the bare-metal thumb targets starting at thumbv6.
static TARGETS: &[&str] = &[
"thumbv6m-none-eabi",
"thumbv7m-none-eabi",
@@ -12,16 +15,71 @@ static TARGETS: &[&str] = &[
"thumbv8m.main-none-eabihf",
];
-fn build(target: &str, features: &[&str]) {
- println!("building for {} {:?}", target, features);
+fn build(package: &str, target: &str, features: &[&str]) {
+ println!("building {} for {} {:?}", package, target, features);
let mut cargo = Command::new("cargo");
- cargo.args(&["build", "--target", target]);
+ cargo.args(&["build", "-p", package, "--target", target]);
for feat in features {
cargo.args(&["--features", *feat]);
}
+ // Cargo features don't work right when invoked from the workspace root, so change to the
+ // package's directory when necessary.
+ if package != "cortex-m" {
+ cargo.current_dir(package);
+ }
+
let status = cargo.status().unwrap();
- assert!(status.success());
+ assert!(status.success(), "failed to execute: {:?}", cargo);
+}
+
+#[rustfmt::skip]
+static PACKAGE_FEATURES: &[(&str, &[&str])] = &[
+ ("cortex-m", &["inline-asm", "cm7-r0p1"]), // no `linker-plugin-lto` since it's experimental
+ ("cortex-m-semihosting", &["inline-asm", "no-semihosting", "jlink-quirks"]),
+ ("panic-semihosting", &["inline-asm", "exit", "jlink-quirks"]),
+];
+
+fn check_crates_build(is_nightly: bool) {
+ // Build all crates for each supported target.
+ for &target in TARGETS {
+ // Filters crate features, keeping only those that are supported.
+ // Relies on all crates in this repo to use the same convention.
+ let should_use_feature = |feat: &str| {
+ match feat {
+ // This is nightly-only, so don't use it on stable.
+ "inline-asm" => is_nightly,
+ // This only affects thumbv7em targets.
+ "cm7-r0p1" => target.starts_with("thumbv7em"),
+
+ _ => true,
+ }
+ };
+
+ for (package, all_features) in PACKAGE_FEATURES {
+ // Every crate must build with the default feature set.
+ build(package, target, &[]);
+
+ let used_features = &*all_features
+ .iter()
+ .copied()
+ .filter(|feat| should_use_feature(*feat))
+ .collect::<Vec<_>>();
+
+ // (note: we don't test with default features disabled, since we don't use them yet)
+
+ // Every crate must build with each individual feature enabled.
+ for feat in used_features {
+ build(package, target, &[*feat]);
+ }
+
+ // Every crate must build with *all* features enabled.
+ build(package, target, used_features);
+
+ // (technically we should be checking the powerset of all features if we wanted to be
+ // *really* sure, but that takes too much time and isn't very easy to implement)
+ }
+ }
}
fn main() {
@@ -36,22 +94,5 @@ fn main() {
let output = Command::new("rustc").arg("-V").output().unwrap();
let is_nightly = str::from_utf8(&output.stdout).unwrap().contains("nightly");
- // Build `cortex-m` for each supported target.
- for target in TARGETS {
- build(*target, &[]);
-
- if is_nightly {
- // This may fail when nightly breaks. That's fine, the CI job isn't essential.
- build(*target, &["inline-asm"]);
- }
-
- if target.starts_with("thumbv7em") {
- // These can target Cortex-M7s, which have an errata workaround.
- build(*target, &["cm7-r0p1"]);
-
- if is_nightly {
- build(*target, &["inline-asm", "cm7-r0p1"]);
- }
- }
- }
+ check_crates_build(is_nightly);
}