diff options
Diffstat (limited to 'cortex-m-semihosting/src/lib.rs')
-rw-r--r-- | cortex-m-semihosting/src/lib.rs | 232 |
1 files changed, 232 insertions, 0 deletions
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!(), + } +} |