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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
//! Semihosting for RISCV processors
//!
//! # What is semihosting?
//!
//! "Semihosting is a technique where an application running in a debug or
//! simulation environment can access elements of the system hosting the
//! debugger or simulator including console, file system, time and other
//! functions. This allows for diagnostics, interaction and measurement of a
//! target system without requiring significant infrastructure to exist in that
//! target environment." - RISC-V Semihosting Spec
//!
//! # 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 riscv_semihosting::hio;
//! use core::fmt::Write;
//!
//! // This function will be called by the application
//! fn print() -> Result<(), core::fmt::Error> {
//! let mut stdout = hio::hstdout().map_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 riscv_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
//!
//! ## `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 are patched out.
//!
//! # Reference
//!
//! For documentation about the semihosting operations, check
//! ['Semihosting for AArch32 and AArch64'](https://github.com/ARM-software/abi-aa/blob/main/semihosting/semihosting.rst).
//! The RISC-V Semihosting spec is identical to Arm's with the exception of the
//! assembly sequence necessary to trigger a semihosting call, so their
//! documentation is sufficient.
/// Performs a semihosting operation, takes a pointer to an argument block
///
/// # Safety
///
/// The syscall number must be a valid [semihosting operation],
/// and the arguments must be valid for the associated operation.
///
/// [semihosting operation]: https://developer.arm.com/documentation/dui0471/i/semihosting/semihosting-operations?lang=en
pub unsafe
/// Performs a semihosting operation, takes one integer as an argument
///
/// # Safety
///
/// Same as [`syscall()`].
pub unsafe