lib.rs 7.7 KB

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