cortex_m_semihosting/
lib.rs

1//! Semihosting for ARM Cortex-M processors
2//!
3//! # What is semihosting?
4//!
5//! "Semihosting is a mechanism that enables code running on an ARM target to communicate and use
6//! the Input/Output facilities on a host computer that is running a debugger." - ARM
7//!
8//! # Interface
9//!
10//! This crate provides implementations of
11//! [`core::fmt::Write`](https://doc.rust-lang.org/core/fmt/trait.Write.html), so you can use it,
12//! in conjunction with
13//! [`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.
14//!
15//! Since semihosting operations are modeled as [system calls][sc], this crate exposes an untyped
16//! `syscall!` interface just like the [`sc`] crate does.
17//!
18//! [sc]: https://en.wikipedia.org/wiki/System_call
19//! [`sc`]: https://crates.io/crates/sc
20//!
21//! # Forewarning
22//!
23//! Semihosting operations are *very* slow. Like, each WRITE operation can take hundreds of
24//! milliseconds.
25//!
26//! # Example
27//!
28//! ## Using `hio::hstdout`
29//!
30//! This example will demonstrate how to print formatted strings.
31//!
32//! ```no_run
33//! use cortex_m_semihosting::hio;
34//! use core::fmt::Write;
35//!
36//! // This function will be called by the application
37//! fn print() -> Result<(), core::fmt::Error> {
38//!     let mut stdout = hio::hstdout().map_err(|_| core::fmt::Error)?;
39//!     let language = "Rust";
40//!     let ranking = 1;
41//!
42//!     write!(stdout, "{} on embedded is #{}!", language, ranking)?;
43//!
44//!     Ok(())
45//! }
46//! ```
47//!
48//! On the host side:
49//!
50//! ``` text
51//! $ openocd -f $INTERFACE -f $TARGET -l /tmp/openocd.log
52//! Open On-Chip Debugger 0.9.0 (2016-04-27-23:18)
53//! Licensed under GNU GPL v2
54//! For bug reports, read
55//!         http://openocd.org/doc/doxygen/bugs.html
56//! # the command will block at this point
57//! ```
58//!
59//! The OpenOCD logs will be redirected to `/tmp/openocd.log`. You can view those logs in "real
60//! time" using `tail`
61//!
62//! ``` text
63//! $ tail -f /tmp/openocd.log
64//! Info : Unable to match requested speed 1000 kHz, using 950 kHz
65//! Info : Unable to match requested speed 1000 kHz, using 950 kHz
66//! Info : clock speed 950 kHz
67//! Info : STLINK v1 JTAG v11 API v2 SWIM v0 VID 0x0483 PID 0x3744
68//! Info : using stlink api v2
69//! Info : nrf51.cpu: hardware has 4 breakpoints, 2 watchpoints
70//! ```
71//!
72//! Alternatively you could omit the `-l` flag from the `openocd` call, and the `tail -f` command
73//! but the OpenOCD output will have intermingled in it logs from its normal operation.
74//!
75//! Then, we run the program:
76//!
77//! ``` text
78//! $ arm-none-eabi-gdb hello-world
79//! (gdb) # Connect to OpenOCD
80//! (gdb) target remote :3333
81//!
82//! (gdb) # Enable OpenOCD's semihosting support
83//! (gdb) monitor arm semihosting enable
84//!
85//! (gdb) # Flash the program
86//! (gdb) load
87//!
88//! (gdb) # Run the program
89//! (gdb) continue
90//! ```
91//!
92//! And you'll see the output under OpenOCD's terminal
93//!
94//! ``` text
95//! # openocd -f $INTERFACE -f $TARGET -l /tmp/openocd.log
96//! (..)
97//! Rust on embedded is #1!
98//! ```
99//! ## Using the syscall interface
100//!
101//! This example will show how to print "Hello, world!" on the host.
102//!
103//! Target program:
104//!
105//! ```no_run
106//! use cortex_m_semihosting::syscall;
107//!
108//! // This function will be called by the application
109//! fn print() {
110//!     // File descriptor (on the host)
111//!     const STDOUT: usize = 1; // NOTE the host stdout may not always be fd 1
112//!     static MSG: &'static [u8] = b"Hello, world!\n";
113//!
114//!     // Signature: fn write(fd: usize, ptr: *const u8, len: usize) -> usize
115//!     let r = unsafe { syscall!(WRITE, STDOUT, MSG.as_ptr(), MSG.len()) };
116//! }
117//! ```
118//! Output and monitoring proceed as in the above example.
119//!
120//! ## The `dbg!` macro
121//!
122//! Analogous to [`std::dbg`](https://doc.rust-lang.org/std/macro.dbg.html) the macro
123//! `dbg!` returns a given expression and prints it using `heprintln!` including context
124//! for quick and dirty debugging.
125//!
126//! Panics if `heprintln!` returns an error.
127//!
128//! Example:
129//!
130//! ```no_run
131//! const UUID: *mut u32 = 0x0009_FC70 as *mut u32;
132//! dbg!(UUID);
133//! let mut uuid: [u32; 4] = [0; 4];
134//! for i in 0..4 {
135//!     dbg!(i);
136//!     uuid[i] = unsafe { dbg!(UUID.offset(i as isize).read_volatile()) };
137//! }
138//! ```
139//! outputs
140//! ```text
141//! [examples/semihosting.rs:37] UUID = 0x0009fc70
142//! [examples/semihosting.rs:40] i = 0
143//! [examples/semihosting.rs:41] UUID.offset(i as isize).read_volatile() = 3370045464
144//! [examples/semihosting.rs:40] i = 1
145//! [examples/semihosting.rs:41] UUID.offset(i as isize).read_volatile() = 1426218275
146//! [examples/semihosting.rs:40] i = 2
147//! [examples/semihosting.rs:41] UUID.offset(i as isize).read_volatile() = 2422621116
148//! [examples/semihosting.rs:40] i = 3
149//! [examples/semihosting.rs:41] UUID.offset(i as isize).read_volatile() = 1044138593
150//! ```
151//!
152//! # Optional features
153//!
154//! ## `jlink-quirks`
155//!
156//! When this feature is enabled, return values above `0xfffffff0` from semihosting operation
157//! `SYS_WRITE` (0x05) are interpreted as if the entire buffer had been written. The current
158//! latest version 6.48b of J-Link exhibits such behaviour, causing a panic if this feature
159//! is not enabled.
160//!
161//! ## `no-semihosting`
162//!
163//! When this feature is enabled, the underlying system calls to `bkpt` are patched out.
164//!
165//! # Reference
166//!
167//! For documentation about the semihosting operations, check:
168//!
169//! 'Chapter 8 - Semihosting' of the ['ARM Compiler toolchain Version 5.0'][pdf]
170//! manual.
171//!
172//! [pdf]: http://infocenter.arm.com/help/topic/com.arm.doc.dui0471e/DUI0471E_developing_for_arm_processors.pdf
173
174#![deny(missing_docs)]
175#![no_std]
176
177#[macro_use]
178mod macros;
179
180pub mod debug;
181#[doc(hidden)]
182pub mod export;
183pub mod hio;
184pub mod nr;
185
186/// Performs a semihosting operation, takes a pointer to an argument block
187#[inline(always)]
188pub unsafe fn syscall<T>(nr: usize, arg: &T) -> usize {
189    syscall1(nr, arg as *const T as usize)
190}
191
192/// Performs a semihosting operation, takes one integer as an argument
193#[inline(always)]
194pub unsafe fn syscall1(_nr: usize, _arg: usize) -> usize {
195    match () {
196        #[cfg(all(thumb, not(feature = "no-semihosting")))]
197        () => {
198            use core::arch::asm;
199            let mut nr = _nr as u32;
200            let arg = _arg as u32;
201            asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nostack, preserves_flags));
202            nr as usize
203        }
204        #[cfg(all(thumb, feature = "no-semihosting"))]
205        () => 0,
206        #[cfg(not(thumb))]
207        () => unimplemented!(),
208    }
209}