1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 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 __c_m_sh_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")))] () => __c_m_sh_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!(), } }