aboutsummaryrefslogtreecommitdiff
path: root/cortex-m-semihosting
diff options
context:
space:
mode:
authorGravatar bors[bot] <26634292+bors[bot]@users.noreply.github.com> 2020-10-13 23:32:15 +0000
committerGravatar GitHub <noreply@github.com> 2020-10-13 23:32:15 +0000
commit432e5f527430394570d1d07454e26cc41d5b2936 (patch)
treed3cde25a18eabfc5f0342dbf2d31ab767f21bab9 /cortex-m-semihosting
parentf77d64a2d1505335e4a170d03a40993bb066fd02 (diff)
parentb51178fae6373d8dae95f2fb661e0635359e8bc0 (diff)
downloadcortex-m-432e5f527430394570d1d07454e26cc41d5b2936.tar.gz
cortex-m-432e5f527430394570d1d07454e26cc41d5b2936.tar.zst
cortex-m-432e5f527430394570d1d07454e26cc41d5b2936.zip
263: Import cortex-m-semihosting and panic-semihosting into this repo r=adamgreig a=jonas-schievink Motivation: * Allows writing QEMU tests for `cortex-m`'s functionality that use semihosting to control QEMU. Previously these crates would pull in another cortex-m version, which doesn't work. Now they have a `path` dependency on the root crate. * Lets us share the outline-inline-assembly setup and `cargo-xtask` in general. * Lets us share CI and bot setup between more crates. * 2 fewer repos to triage and keep track of (I'll transfer their issues after this is merged). I also want to import cortex-m-rt, but I'll do that in a later PR. CI was updated to build-test all crates with all or most feature combinations, like it did before. Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
Diffstat (limited to 'cortex-m-semihosting')
-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
10 files changed, 851 insertions, 0 deletions
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;
+}