From a8c058f0eec44c69f8f50542eb30959ff4248c44 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 22 Jan 2017 13:04:15 -0500 Subject: [PATCH 01/41] initial commit --- .gitignore | 2 + Cargo.toml | 11 +++ LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE-MIT | 25 ++++++ README.md | 24 ++++++ src/io.rs | 45 +++++++++++ src/lib.rs | 124 ++++++++++++++++++++++++++++++ src/macros.rs | 32 ++++++++ src/nr.rs | 24 ++++++ 9 files changed, 488 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 README.md create mode 100644 src/io.rs create mode 100644 src/lib.rs create mode 100644 src/macros.rs create mode 100644 src/nr.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a9d37c56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..6d927f43 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +authors = ["Jorge Aparicio "] +description = "Semihosting for ARM Cortex-M processors" +documentation = "https://docs.rs/cortex-m-semihosting" +keywords = ["semihosting", "arm", "cortex-m"] +license = "MIT OR Apache-2.0" +name = "cortex-m-semihosting" +repository = "https://github.com/japaric/cortex-m-semihosting" +version = "0.1.0" + +[dependencies] diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 00000000..16fe87b0 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 00000000..a128ba40 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2017 Jorge Aparicio + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..805c5f62 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +[![crates.io](https://img.shields.io/crates/v/cortex-m-semihosting.svg)](https://crates.io/crates/cortex-m-semihosting) +[![crates.io](https://img.shields.io/crates/d/cortex-m-semihosting.svg)](https://crates.io/crates/cortex-m-semihosting) + +# `cortex-m-semihosting` + +> Semihosting for ARM Cortex-M processors + +## [Documentation](https://docs.rs/cortex-m-semihosting) + +## License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 00000000..37a69998 --- /dev/null +++ b/src/io.rs @@ -0,0 +1,45 @@ +//! Host I/O + +use core::{fmt, slice}; +use core::fmt::Write; + +/// File descriptor +const STDOUT: usize = 1; + +/// Host's standard output +struct Stdout; + +impl Stdout { + fn write_all(&mut self, mut buffer: &[u8]) { + while !buffer.is_empty() { + match unsafe { + syscall!(WRITE, STDOUT, buffer.as_ptr(), buffer.len()) + } { + // Done + 0 => return, + // `n` bytes were not written + n => { + buffer = unsafe { + slice::from_raw_parts(buffer.as_ptr(), buffer.len() - n) + } + } + } + } + } +} + +impl Write for Stdout { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_all(s.as_bytes()); + Ok(()) + } +} + +pub fn _write_str(s: &str) { + Stdout.write_str(s).ok(); +} + +pub fn _write_fmt(args: fmt::Arguments) { + + Stdout.write_fmt(args).ok(); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..b14762bd --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,124 @@ +//! 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 +//! +//! 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 +//! +//! And since the most used semihosting operation is writing to the host's +//! stdout, convenience `hprint` and `hprintln` macros are provided. +//! +//! # Forewarning +//! +//! Semihosting operations are *very* slow. Like, each WRITE operation can take +//! hundreds of milliseconds. +//! +//! # Example +//! +//! This example will show how to print "Hello, world!" on the host. +//! +//! Target program: +//! +//! ``` +//! fn main() { +//! // File descriptor (on the host) +//! const STDOUT: usize = 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()) }; +//! +//! // (Or you could have just written `hprintln!("Hello, world!")`) +//! // .. +//! } +//! ``` +//! +//! 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 `watch` + `tail` +//! +//! ``` text +//! $ watch 'tail /tmp/openocd.log' +//! Every 2.0s: tail /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 +//! ``` +//! +//! Then, we run the program: +//! +//! ``` text +//! $ arm-none-eabi-gdb hello-world +//! # Connect to OpenOCD +//! (gdb) target remote :3333 +//! +//! # Enable OpenOCD's semihosting support +//! (gdb) monitor arm semihosting enable +//! +//! # Flash the program +//! (gdb) load +//! +//! # 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 +//! (..) +//! Hello, world! +//! ``` +//! +//! # 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 + +#![feature(asm)] +#![no_std] + +#[macro_use] +mod macros; +#[doc(hidden)] +pub mod io; + +pub mod nr; + +/// Performs a semihosting operation +#[inline(always)] +pub unsafe fn syscall(mut nr: usize, arg: &T) -> usize { + asm!("bkpt 0xAB" + : "+{r0}"(nr) + : "{r1}"(arg) + : "memory" + : "volatile"); + nr +} diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 00000000..7a3d4117 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,32 @@ +/// Variable argument version of `syscall` +#[macro_export] +macro_rules! syscall { + ($nr:ident, $a1:expr, $a2:expr) => { + $crate::syscall($crate::nr::$nr, &($a1 as usize)) + }; + ($nr:ident, $a1:expr, $a2:expr) => { + $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize]) + }; + ($nr:ident, $a1:expr, $a2:expr, $a3:expr) => { + $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize, + $a3 as usize]) + }; + ($nr:ident, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => { + $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize, + $a3 as usize, $a4 as usize]) + }; +} +/// Macro for printing to the **host's** standard output +#[macro_export] +macro_rules! hprint { + ($s:expr) => ($crate::io::_write_str($s)); + ($($arg:tt)*) => ($crate::io::_write_fmt(format_args!($($arg)*))); +} + +/// Macro for printing to the **host's** standard output, with a newline. +#[macro_export] +macro_rules! hprintln { + () => (print!("\n")); + ($fmt:expr) => (hprint!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (hprint!(concat!($fmt, "\n"), $($arg)*)); +} diff --git a/src/nr.rs b/src/nr.rs new file mode 100644 index 00000000..d7bd2c1b --- /dev/null +++ b/src/nr.rs @@ -0,0 +1,24 @@ +//! Semihosting operations + +pub const CLOCK: usize = 0x10; +pub const CLOSE: usize = 0x05; +pub const ELAPSED: usize = 0x30; +pub const ERRNO: usize = 0x13; +pub const FLEN: usize = 0x0c; +pub const GET_CMDLINE: usize = 0x15; +pub const HEAPINFO: usize = 0x16; +pub const ISERROR: usize = 0x08; +pub const ISTTY: usize = 0x09; +pub const OPEN: usize = 0x01; +pub const READ: usize = 0x06; +pub const READC: usize = 0x07; +pub const REMOVE: usize = 0x0e; +pub const RENAME: usize = 0x0f; +pub const SEEK: usize = 0x0a; +pub const SYSTEM: usize = 0x12; +pub const TICKFREQ: usize = 0x31; +pub const TIME: usize = 0x11; +pub const TMPNAM: usize = 0x0d; +pub const WRITE0: usize = 0x04; +pub const WRITE: usize = 0x05; +pub const WRITEC: usize = 0x03; From 18420a875a552ffea4067b74456bd0d32d438c31 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 22 Jan 2017 13:06:54 -0500 Subject: [PATCH 02/41] hax to make this crate compilable for x86 needed to cargo publish --- src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index b14762bd..70dc2dfe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,6 +114,7 @@ pub mod nr; /// Performs a semihosting operation #[inline(always)] +#[cfg(target_arch = "arm")] pub unsafe fn syscall(mut nr: usize, arg: &T) -> usize { asm!("bkpt 0xAB" : "+{r0}"(nr) @@ -122,3 +123,8 @@ pub unsafe fn syscall(mut nr: usize, arg: &T) -> usize { : "volatile"); nr } + +#[cfg(not(target_arch = "arm"))] +pub unsafe fn syscall(_nr: usize, _arg: &T) -> usize { + 0 +} From 0524b9528ef6797af289c7f22f3018e1663d8269 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 22 Jan 2017 20:43:43 -0500 Subject: [PATCH 03/41] add `write` functions to write to the host's stdout --- CHANGELOG.md | 17 +++++++++++++++++ src/io.rs | 14 ++++++++++---- src/lib.rs | 3 +-- src/macros.rs | 4 ++-- 4 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..2b4d489d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +## [Unreleased] + +### Added + +- Expose a family of `write` functions to write to the host's stdout without + going through the `hprint!` macros. + +## v0.1.0 - 2017-01-22 + +- Initial release + +[Unreleased]: https://github.com/japaric/trust/compare/v0.1.0...HEAD diff --git a/src/io.rs b/src/io.rs index 37a69998..f886b824 100644 --- a/src/io.rs +++ b/src/io.rs @@ -35,11 +35,17 @@ impl Write for Stdout { } } -pub fn _write_str(s: &str) { - Stdout.write_str(s).ok(); +/// Write a `buffer` to the host's stdout +pub fn write(buffer: &[u8]) { + Stdout.write_all(buffer) } -pub fn _write_fmt(args: fmt::Arguments) { - +/// Write `fmt::Arguments` to the host's stdout +pub fn write_fmt(args: fmt::Arguments) { Stdout.write_fmt(args).ok(); } + +/// Write a `string` to the host's stdout +pub fn write_str(string: &str) { + Stdout.write_all(string.as_bytes()) +} diff --git a/src/lib.rs b/src/lib.rs index 70dc2dfe..b9ee8d1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,9 +107,8 @@ #[macro_use] mod macros; -#[doc(hidden)] -pub mod io; +pub mod io; pub mod nr; /// Performs a semihosting operation diff --git a/src/macros.rs b/src/macros.rs index 7a3d4117..f264f3a2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -19,8 +19,8 @@ macro_rules! syscall { /// Macro for printing to the **host's** standard output #[macro_export] macro_rules! hprint { - ($s:expr) => ($crate::io::_write_str($s)); - ($($arg:tt)*) => ($crate::io::_write_fmt(format_args!($($arg)*))); + ($s:expr) => ($crate::io::write_str($s)); + ($($arg:tt)*) => ($crate::io::write_fmt(format_args!($($arg)*))); } /// Macro for printing to the **host's** standard output, with a newline. From 82aa4306e60fabba162b664571285fc2d2bebf3c Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 22 Jan 2017 20:45:23 -0500 Subject: [PATCH 04/41] release v0.1.1 --- CHANGELOG.md | 2 ++ Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b4d489d..f16d4269 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.1.1] - 2017-01-22 + ### Added - Expose a family of `write` functions to write to the host's stdout without diff --git a/Cargo.toml b/Cargo.toml index 6d927f43..94894c08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,6 @@ keywords = ["semihosting", "arm", "cortex-m"] license = "MIT OR Apache-2.0" name = "cortex-m-semihosting" repository = "https://github.com/japaric/cortex-m-semihosting" -version = "0.1.0" +version = "0.1.1" [dependencies] From 419094e8f271f301eedf24b874569291a4e3f61b Mon Sep 17 00:00:00 2001 From: Jascha Date: Wed, 15 Feb 2017 15:01:24 +0100 Subject: [PATCH 05/41] fix typo in hprintln! --- src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index f264f3a2..7443070b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -26,7 +26,7 @@ macro_rules! hprint { /// Macro for printing to the **host's** standard output, with a newline. #[macro_export] macro_rules! hprintln { - () => (print!("\n")); + () => (hprint!("\n")); ($fmt:expr) => (hprint!(concat!($fmt, "\n"))); ($fmt:expr, $($arg:tt)*) => (hprint!(concat!($fmt, "\n"), $($arg)*)); } From adf036333f1cbb6059f4a975d9b89f095a2e89cf Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 15 Feb 2017 12:30:54 -0500 Subject: [PATCH 06/41] release v0.1.2 --- CHANGELOG.md | 10 +++++++++- Cargo.toml | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f16d4269..06b3b12e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.1.2] - 2017-02-15 + +### Fixed + +- the `hprintln!` macro when called without arguments. + ## [v0.1.1] - 2017-01-22 ### Added @@ -16,4 +22,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Initial release -[Unreleased]: https://github.com/japaric/trust/compare/v0.1.0...HEAD +[Unreleased]: https://github.com/japaric/trust/compare/v0.1.2...HEAD +[v0.1.2]: https://github.com/japaric/trust/compare/v0.1.1...v0.1.2 +[v0.1.1]: https://github.com/japaric/trust/compare/v0.1.0...v0.1.1 diff --git a/Cargo.toml b/Cargo.toml index 94894c08..52c1cf91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,6 @@ keywords = ["semihosting", "arm", "cortex-m"] license = "MIT OR Apache-2.0" name = "cortex-m-semihosting" repository = "https://github.com/japaric/cortex-m-semihosting" -version = "0.1.1" +version = "0.1.2" [dependencies] From 0d42a81fbd35270d004a47796604e6cfe74ef3be Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 27 Feb 2017 15:46:43 -0500 Subject: [PATCH 07/41] add a macro to write to the host stderr also fix `write_all` logic when the a single write doesn't write all the bytes --- src/io.rs | 63 +++++++++++++++++++++++++++++++++++++++------------ src/macros.rs | 16 +++++++++++++ 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/src/io.rs b/src/io.rs index f886b824..cda6a8f9 100644 --- a/src/io.rs +++ b/src/io.rs @@ -3,31 +3,51 @@ use core::{fmt, slice}; use core::fmt::Write; -/// File descriptor +/// File descriptors const STDOUT: usize = 1; +const STDERR: usize = 2; + +/// Host's standard error +struct Stderr; /// Host's standard output struct Stdout; -impl Stdout { - fn write_all(&mut self, mut buffer: &[u8]) { - while !buffer.is_empty() { - match unsafe { - syscall!(WRITE, STDOUT, buffer.as_ptr(), buffer.len()) - } { - // Done - 0 => return, - // `n` bytes were not written - n => { - buffer = unsafe { - slice::from_raw_parts(buffer.as_ptr(), buffer.len() - n) - } +fn write_all(fd: usize, mut buffer: &[u8]) { + while !buffer.is_empty() { + match unsafe { syscall!(WRITE, fd, buffer.as_ptr(), buffer.len()) } { + // Done + 0 => return, + // `n` bytes were not written + n => { + let offset = (buffer.len() - n) as isize; + buffer = unsafe { + slice::from_raw_parts(buffer.as_ptr().offset(offset as isize), n) } } } } } +impl Stderr { + fn write_all(&mut self, buffer: &[u8]) { + write_all(STDERR, buffer); + } +} + +impl Stdout { + fn write_all(&mut self, buffer: &[u8]) { + write_all(STDOUT, buffer); + } +} + +impl Write for Stderr { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_all(s.as_bytes()); + Ok(()) + } +} + impl Write for Stdout { fn write_str(&mut self, s: &str) -> fmt::Result { self.write_all(s.as_bytes()); @@ -35,6 +55,21 @@ impl Write for Stdout { } } +/// Write a `buffer` to the host's stderr +pub fn ewrite(buffer: &[u8]) { + Stderr.write_all(buffer) +} + +/// Write `fmt::Arguments` to the host's stderr +pub fn ewrite_fmt(args: fmt::Arguments) { + Stderr.write_fmt(args).ok(); +} + +/// Write a `string` to the host's stderr +pub fn ewrite_str(string: &str) { + Stderr.write_all(string.as_bytes()) +} + /// Write a `buffer` to the host's stdout pub fn write(buffer: &[u8]) { Stdout.write_all(buffer) diff --git a/src/macros.rs b/src/macros.rs index 7443070b..d8d68b5e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -16,6 +16,22 @@ macro_rules! syscall { $a3 as usize, $a4 as usize]) }; } + +/// Macro for printing to the **host's** standard stderr +#[macro_export] +macro_rules! ehprint { + ($s:expr) => ($crate::io::ewrite_str($s)); + ($($arg:tt)*) => ($crate::io::ewrite_fmt(format_args!($($arg)*))); +} + +/// Macro for printing to the **host's** standard error, with a newline. +#[macro_export] +macro_rules! ehprintln { + () => (ehprint!("\n")); + ($fmt:expr) => (ehprint!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (ehprint!(concat!($fmt, "\n"), $($arg)*)); +} + /// Macro for printing to the **host's** standard output #[macro_export] macro_rules! hprint { From 36ae9a29c2cd8094f9a1c68028c038401b8f1060 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 27 Feb 2017 15:49:36 -0500 Subject: [PATCH 08/41] release v0.1.3 --- CHANGELOG.md | 18 +++++++++++++++--- Cargo.toml | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06b3b12e..a073d6b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.1.3] - 2017-02-27 + +### Added + +- A family of `ewrite` functions and `ehprint!` macros to write to the host's + stderr. + +### Fixed + +- `write_all` logic when a single write doesn't write all the buffer bytes + ## [v0.1.2] - 2017-02-15 ### Fixed @@ -22,6 +33,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Initial release -[Unreleased]: https://github.com/japaric/trust/compare/v0.1.2...HEAD -[v0.1.2]: https://github.com/japaric/trust/compare/v0.1.1...v0.1.2 -[v0.1.1]: https://github.com/japaric/trust/compare/v0.1.0...v0.1.1 +[Unreleased]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.3...HEAD +[v0.1.3]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.2...v0.1.3 +[v0.1.2]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.1...v0.1.2 +[v0.1.1]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.0...v0.1.1 diff --git a/Cargo.toml b/Cargo.toml index 52c1cf91..606b6c31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,6 @@ keywords = ["semihosting", "arm", "cortex-m"] license = "MIT OR Apache-2.0" name = "cortex-m-semihosting" repository = "https://github.com/japaric/cortex-m-semihosting" -version = "0.1.2" +version = "0.1.3" [dependencies] From ad2fb8f8169742ca97ba95e9f9c40da2f4a894b8 Mon Sep 17 00:00:00 2001 From: Vadzim Dambrouski Date: Mon, 13 Mar 2017 01:55:04 +0300 Subject: [PATCH 09/41] ignore failing test --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index b9ee8d1f..ee9a0686 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ //! //! Target program: //! -//! ``` +//! ```rust,ignore //! fn main() { //! // File descriptor (on the host) //! const STDOUT: usize = 1; From db5951b5f11f8afb3b8da605e6eec7fa56cf7c21 Mon Sep 17 00:00:00 2001 From: Vadzim Dambrouski Date: Mon, 13 Mar 2017 02:35:34 +0300 Subject: [PATCH 10/41] fix syscall macros --- src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index d8d68b5e..f80a5d3a 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,7 +1,7 @@ /// Variable argument version of `syscall` #[macro_export] macro_rules! syscall { - ($nr:ident, $a1:expr, $a2:expr) => { + ($nr:ident, $a1:expr) => { $crate::syscall($crate::nr::$nr, &($a1 as usize)) }; ($nr:ident, $a1:expr, $a2:expr) => { From 02cb500a5a80986d18ed13f655d5130c55532342 Mon Sep 17 00:00:00 2001 From: Vadzim Dambrouski Date: Mon, 13 Mar 2017 04:00:36 +0300 Subject: [PATCH 11/41] add `exit` and `report_exception` syscalls --- src/debug.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 +++-- src/macros.rs | 5 ++++- src/nr.rs | 2 ++ 4 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 src/debug.rs diff --git a/src/debug.rs b/src/debug.rs new file mode 100644 index 00000000..610be091 --- /dev/null +++ b/src/debug.rs @@ -0,0 +1,52 @@ +//! Interacting with debugging agent + +pub enum Exception { + // Hardware reason codes + BranchThroughZero = 0x20000, + UndefinedInstr = 0x20001, + SoftwareInterrupt = 0x20002, + PrefetchAbort = 0x20003, + DataAbort = 0x20004, + AddressException = 0x20005, + IRQ = 0x20006, + FIQ = 0x20007, + // Software reason codes + BreakPoint = 0x20020, + WatchPoint = 0x20021, + StepComplete = 0x20022, + RunTimeErrorUnknown = 0x20023, + InternalError = 0x20024, + UserInterruption = 0x20025, + ApplicationExit = 0x20026, + StackOverflow = 0x20027, + DivisionByZero = 0x20028, + OSSpecific = 0x20029, +} + +/// Reports to the debugger that the execution has completed. +/// +/// If `status` is not 0 then an error is reported. +/// This call may not return. +/// +pub fn exit(status: i8) { + if status == 0 { + report_exception(Exception::ApplicationExit); + } else { + report_exception(Exception::RunTimeErrorUnknown); + } +} + +/// Report an exception to the debugger directly. +/// +/// This call may not return. +/// +/// # Arguments +/// +/// * `reason` - A reason code reported back to the debugger +/// +pub fn report_exception(reason: Exception) { + let code = reason as usize; + unsafe { + syscall!(REPORT_EXCEPTION, code); + } +} diff --git a/src/lib.rs b/src/lib.rs index ee9a0686..efe960ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,11 +110,12 @@ mod macros; pub mod io; pub mod nr; +pub mod debug; /// Performs a semihosting operation #[inline(always)] #[cfg(target_arch = "arm")] -pub unsafe fn syscall(mut nr: usize, arg: &T) -> usize { +pub unsafe fn syscall(mut nr: usize, arg: T) -> usize { asm!("bkpt 0xAB" : "+{r0}"(nr) : "{r1}"(arg) @@ -124,6 +125,6 @@ pub unsafe fn syscall(mut nr: usize, arg: &T) -> usize { } #[cfg(not(target_arch = "arm"))] -pub unsafe fn syscall(_nr: usize, _arg: &T) -> usize { +pub unsafe fn syscall(_nr: usize, _arg: T) -> usize { 0 } diff --git a/src/macros.rs b/src/macros.rs index f80a5d3a..02f1fdf7 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,8 +1,11 @@ /// Variable argument version of `syscall` #[macro_export] macro_rules! syscall { + ($nr:ident) => { + $crate::syscall($crate::nr::$nr, 0usize) + }; ($nr:ident, $a1:expr) => { - $crate::syscall($crate::nr::$nr, &($a1 as usize)) + $crate::syscall($crate::nr::$nr, $a1 as usize) }; ($nr:ident, $a1:expr, $a2:expr) => { $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize]) diff --git a/src/nr.rs b/src/nr.rs index d7bd2c1b..d0372734 100644 --- a/src/nr.rs +++ b/src/nr.rs @@ -22,3 +22,5 @@ pub const TMPNAM: usize = 0x0d; pub const WRITE0: usize = 0x04; pub const WRITE: usize = 0x05; pub const WRITEC: usize = 0x03; +pub const ENTER_SVC: usize = 0x17; +pub const REPORT_EXCEPTION: usize = 0x18; From 22fb4a780b457775820ecf20a48517fc76cc6e6b Mon Sep 17 00:00:00 2001 From: Vadzim Dambrouski Date: Sun, 19 Mar 2017 22:37:50 +0300 Subject: [PATCH 12/41] Fix issues found by code review, add more documentation. --- src/debug.rs | 43 ++++++++++++++++++++++++++++++++++++++++--- src/lib.rs | 30 ++++++++++++++++++++++++++---- src/macros.rs | 12 ++++++++++-- 3 files changed, 76 insertions(+), 9 deletions(-) diff --git a/src/debug.rs b/src/debug.rs index 610be091..54bf7fe4 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -1,5 +1,31 @@ //! Interacting with debugging agent +//! +//! # Example +//! +//! This example will show how to terminate the QEMU session. The program +//! should be running under QEMU with semihosting enabled +//! (use `-semihosting` flag). +//! +//! Target program: +//! +//! ``` +//! #[macro_use] +//! extern crate cortex_m_semihosting; +//! use cortex_m_semihosting::debug; +//! +//! fn main() { +//! if 2 == 2 { +//! // report success +//! debug::exit(0); +//! } else { +//! // report failure +//! debug::exit(1); +//! } +//! } +//! +/// This values are taken from section 5.5.2 of +/// "ADS Debug Target Guide" (DUI0058) pub enum Exception { // Hardware reason codes BranchThroughZero = 0x20000, @@ -26,7 +52,13 @@ pub enum Exception { /// Reports to the debugger that the execution has completed. /// /// If `status` is not 0 then an error is reported. -/// This call may not return. +/// +/// This call can be used to terminate QEMU session, and report back success +/// or failure. +/// +/// This call should not return. However, it is possible for the debugger +/// to request that the application continue. In that case this call +/// returns normally. /// pub fn exit(status: i8) { if status == 0 { @@ -38,7 +70,12 @@ pub fn exit(status: i8) { /// Report an exception to the debugger directly. /// -/// This call may not return. +/// Exception handlers can use this SWI at the end of handler chains +/// as the default action, to indicate that the exception has not been handled. +/// +/// This call should not return. However, it is possible for the debugger +/// to request that the application continue. In that case this call +/// returns normally. /// /// # Arguments /// @@ -47,6 +84,6 @@ pub fn exit(status: i8) { pub fn report_exception(reason: Exception) { let code = reason as usize; unsafe { - syscall!(REPORT_EXCEPTION, code); + syscall1!(REPORT_EXCEPTION, code); } } diff --git a/src/lib.rs b/src/lib.rs index efe960ac..dc3797bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,10 @@ //! //! Target program: //! -//! ```rust,ignore +//! ``` +//! #[macro_use] +//! extern crate cortex_m_semihosting; +//! //! fn main() { //! // File descriptor (on the host) //! const STDOUT: usize = 1; @@ -112,10 +115,28 @@ pub mod io; pub mod nr; pub mod debug; -/// Performs a semihosting operation +/// Performs a semihosting operation, takes a pointer to an argument block +#[inline(always)] +#[cfg(target_arch = "arm")] +pub unsafe fn syscall(mut nr: usize, arg: &T) -> usize { + asm!("bkpt 0xAB" + : "+{r0}"(nr) + : "{r1}"(arg) + : "memory" + : "volatile"); + nr +} + +/// Performs a semihosting operation, takes a pointer to an argument block +#[cfg(not(target_arch = "arm"))] +pub unsafe fn syscall(_nr: usize, _arg: &T) -> usize { + 0 +} + +/// Performs a semihosting operation, takes one integer as an argument #[inline(always)] #[cfg(target_arch = "arm")] -pub unsafe fn syscall(mut nr: usize, arg: T) -> usize { +pub unsafe fn syscall1(mut nr: usize, arg: usize) -> usize { asm!("bkpt 0xAB" : "+{r0}"(nr) : "{r1}"(arg) @@ -124,7 +145,8 @@ pub unsafe fn syscall(mut nr: usize, arg: T) -> usize { nr } +/// Performs a semihosting operation, takes one integer as an argument #[cfg(not(target_arch = "arm"))] -pub unsafe fn syscall(_nr: usize, _arg: T) -> usize { +pub unsafe fn syscall1(_nr: usize, _arg: usize) -> usize { 0 } diff --git a/src/macros.rs b/src/macros.rs index 02f1fdf7..184d13d6 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -2,10 +2,10 @@ #[macro_export] macro_rules! syscall { ($nr:ident) => { - $crate::syscall($crate::nr::$nr, 0usize) + $crate::syscall1($crate::nr::$nr, 0) }; ($nr:ident, $a1:expr) => { - $crate::syscall($crate::nr::$nr, $a1 as usize) + $crate::syscall($crate::nr::$nr, &[$a1 as usize]) }; ($nr:ident, $a1:expr, $a2:expr) => { $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize]) @@ -20,6 +20,14 @@ macro_rules! syscall { }; } +/// Macro version of `syscall1` +#[macro_export] +macro_rules! syscall1 { + ($nr:ident, $a1:expr) => { + $crate::syscall1($crate::nr::$nr, $a1 as usize) + }; +} + /// Macro for printing to the **host's** standard stderr #[macro_export] macro_rules! ehprint { From 6a585c18968c2647bcbb937ed67338391b9a5e5b Mon Sep 17 00:00:00 2001 From: Vadzim Dambrouski Date: Tue, 21 Mar 2017 00:51:23 +0300 Subject: [PATCH 13/41] Use enum for exit status. --- src/debug.rs | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/debug.rs b/src/debug.rs index 54bf7fe4..590dd1e5 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -11,21 +11,21 @@ //! ``` //! #[macro_use] //! extern crate cortex_m_semihosting; -//! use cortex_m_semihosting::debug; +//! use cortex_m_semihosting::debug::{self, EXIT_SUCCESS, EXIT_FAILURE}; //! //! fn main() { //! if 2 == 2 { //! // report success -//! debug::exit(0); +//! debug::exit(EXIT_SUCCESS); //! } else { //! // report failure -//! debug::exit(1); +//! debug::exit(EXIT_FAILURE); //! } //! } //! /// This values are taken from section 5.5.2 of -/// "ADS Debug Target Guide" (DUI0058) +/// ADS Debug Target Guide (DUI0058). pub enum Exception { // Hardware reason codes BranchThroughZero = 0x20000, @@ -49,22 +49,29 @@ pub enum Exception { OSSpecific = 0x20029, } +/// Status enum for `exit` syscall. +pub type ExitStatus = Result<(), ()>; + +/// Successful execution of a program. +pub const EXIT_SUCCESS: ExitStatus = Ok(()); + +/// Unsuccessful execution of a program. +pub const EXIT_FAILURE: ExitStatus = Err(()); + /// Reports to the debugger that the execution has completed. /// -/// If `status` is not 0 then an error is reported. -/// -/// This call can be used to terminate QEMU session, and report back success -/// or failure. +/// This call can be used to terminate QEMU session and report back success +/// or failure. If you need to pass more than one type of error, consider +/// using `report_exception` syscall instead. /// /// This call should not return. However, it is possible for the debugger /// to request that the application continue. In that case this call /// returns normally. /// -pub fn exit(status: i8) { - if status == 0 { - report_exception(Exception::ApplicationExit); - } else { - report_exception(Exception::RunTimeErrorUnknown); +pub fn exit(status: ExitStatus) { + match status { + EXIT_SUCCESS => report_exception(Exception::ApplicationExit), + EXIT_FAILURE => report_exception(Exception::RunTimeErrorUnknown), } } @@ -79,7 +86,7 @@ pub fn exit(status: i8) { /// /// # Arguments /// -/// * `reason` - A reason code reported back to the debugger +/// * `reason` - A reason code reported back to the debugger. /// pub fn report_exception(reason: Exception) { let code = reason as usize; From 9562a580f6f3217f70f020f39eca52336cb98023 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 15 May 2017 12:06:32 -0500 Subject: [PATCH 14/41] watch tail -> tail -f note that it's possible to skip the separate log file. closes #9 --- src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dc3797bb..cd5e0152 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,12 +57,10 @@ //! ``` //! //! The OpenOCD logs will be redirected to `/tmp/openocd.log`. You can view -//! those logs in "real time" using `watch` + `tail` +//! those logs in "real time" using `tail` //! //! ``` text -//! $ watch 'tail /tmp/openocd.log' -//! Every 2.0s: tail /tmp/openocd.log -//! +//! $ 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 @@ -71,6 +69,10 @@ //! 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 From 6deed404f45bf9cfa7957ba232f55799618c8c96 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Fri, 16 Jun 2017 20:53:04 +0200 Subject: [PATCH 15/41] WIP: proof of concept for stdout() and stderr() functions to open streams --- src/io.rs | 68 +++++++++++++++++++++++++++++++++++-------------------- src/nr.rs | 15 ++++++++++++ 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/src/io.rs b/src/io.rs index cda6a8f9..f6b2ca77 100644 --- a/src/io.rs +++ b/src/io.rs @@ -2,85 +2,105 @@ use core::{fmt, slice}; use core::fmt::Write; - -/// File descriptors -const STDOUT: usize = 1; -const STDERR: usize = 2; +use nr; /// Host's standard error -struct Stderr; +pub struct Stderr { + fd: usize, +} + +/// Construct a new handle to the host's standard error. +pub fn stderr() -> Stderr { + Stderr { fd: open(":tt\0", nr::open::W_APPEND).unwrap() } +} /// Host's standard output -struct Stdout; +pub struct Stdout { + fd: usize, +} + +/// Construct a new handle to the host's standard output. +pub fn stdout() -> Stdout { + Stdout { fd: open(":tt\0", nr::open::W_TRUNC).unwrap() } +} + +fn open(name: &str, mode: usize) -> Result { + let name = name.as_bytes(); + match unsafe { syscall!(OPEN, name.as_ptr(), mode, name.len() - 1) } as isize { + -1 => Err(()), + fd => Ok(fd as usize), + } +} -fn write_all(fd: usize, mut buffer: &[u8]) { +fn write_all(fd: usize, mut buffer: &[u8]) -> fmt::Result { while !buffer.is_empty() { match unsafe { syscall!(WRITE, fd, buffer.as_ptr(), buffer.len()) } { // Done - 0 => return, + 0 => return Ok(()), // `n` bytes were not written - n => { + n if n <= buffer.len() => { let offset = (buffer.len() - n) as isize; buffer = unsafe { - slice::from_raw_parts(buffer.as_ptr().offset(offset as isize), n) + slice::from_raw_parts(buffer.as_ptr().offset(offset), n) } } + // Error + _ => return Err(fmt::Error::default()), } } + Ok(()) } impl Stderr { - fn write_all(&mut self, buffer: &[u8]) { - write_all(STDERR, buffer); + fn write_all(&mut self, buffer: &[u8]) -> fmt::Result { + write_all(self.fd, buffer) } } impl Stdout { - fn write_all(&mut self, buffer: &[u8]) { - write_all(STDOUT, buffer); + fn write_all(&mut self, buffer: &[u8]) -> fmt::Result { + write_all(self.fd, buffer) } } impl Write for Stderr { fn write_str(&mut self, s: &str) -> fmt::Result { - self.write_all(s.as_bytes()); - Ok(()) + self.write_all(s.as_bytes()) } } impl Write for Stdout { fn write_str(&mut self, s: &str) -> fmt::Result { - self.write_all(s.as_bytes()); - Ok(()) + self.write_all(s.as_bytes()) } } /// Write a `buffer` to the host's stderr pub fn ewrite(buffer: &[u8]) { - Stderr.write_all(buffer) + stderr().write_all(buffer).ok(); } /// Write `fmt::Arguments` to the host's stderr pub fn ewrite_fmt(args: fmt::Arguments) { - Stderr.write_fmt(args).ok(); + stderr().write_fmt(args).ok(); } /// Write a `string` to the host's stderr pub fn ewrite_str(string: &str) { - Stderr.write_all(string.as_bytes()) + stderr().write_all(string.as_bytes()).ok(); } /// Write a `buffer` to the host's stdout pub fn write(buffer: &[u8]) { - Stdout.write_all(buffer) + stdout().write_all(buffer).ok(); } /// Write `fmt::Arguments` to the host's stdout pub fn write_fmt(args: fmt::Arguments) { - Stdout.write_fmt(args).ok(); + stdout().write_fmt(args).ok(); } /// Write a `string` to the host's stdout pub fn write_str(string: &str) { - Stdout.write_all(string.as_bytes()) + stdout().write_all(string.as_bytes()).ok(); } diff --git a/src/nr.rs b/src/nr.rs index d0372734..e546b08d 100644 --- a/src/nr.rs +++ b/src/nr.rs @@ -24,3 +24,18 @@ pub const WRITE: usize = 0x05; pub const WRITEC: usize = 0x03; pub const ENTER_SVC: usize = 0x17; pub const REPORT_EXCEPTION: usize = 0x18; + +pub mod open { + pub const R: usize = 0; + pub const R_TEXT: usize = 1; + pub const RW: usize = 2; + pub const RW_TEXT: usize = 3; + pub const W_TRUNC: usize = 4; + pub const W_TRUNC_TEXT: usize = 5; + pub const RW_TRUNC: usize = 6; + pub const RW_TRUNC_TEXT: usize = 7; + pub const W_APPEND: usize = 8; + pub const W_APPEND_TEXT: usize = 9; + pub const RW_APPEND: usize = 10; + pub const RW_APPEND_TEXT: usize = 11; +} From a295a7301c52d1743c438cb8362a4c105b74880d Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 2 Jul 2017 20:54:48 -0500 Subject: [PATCH 16/41] drop write macro in favor of IO objects + fmt::Write closes #4 closes #5 closes #6 closes #7 closes #11 --- src/debug.rs | 2 + src/hio.rs | 78 +++++++++++++++++++++++++++++++++++++ src/io.rs | 106 -------------------------------------------------- src/lib.rs | 8 ++-- src/macros.rs | 30 -------------- src/nr.rs | 3 ++ 6 files changed, 88 insertions(+), 139 deletions(-) create mode 100644 src/hio.rs delete mode 100644 src/io.rs diff --git a/src/debug.rs b/src/debug.rs index 590dd1e5..15ba91be 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -26,6 +26,8 @@ /// This values are taken from section 5.5.2 of /// ADS Debug Target Guide (DUI0058). +// TODO document +#[allow(missing_docs)] pub enum Exception { // Hardware reason codes BranchThroughZero = 0x20000, diff --git a/src/hio.rs b/src/hio.rs new file mode 100644 index 00000000..ac1240ee --- /dev/null +++ b/src/hio.rs @@ -0,0 +1,78 @@ +//! Host I/O + +use core::{fmt, slice}; +use nr; + +/// Host's standard error +pub struct HStderr { + fd: usize, +} + +impl HStderr { + /// Attempts to write an entire `buffer` into this sink + pub fn write_all(&mut self, buffer: &[u8]) -> Result<(), ()> { + write_all(self.fd, buffer) + } +} + +impl fmt::Write for HStderr { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_all(s.as_bytes()).map_err(|_| fmt::Error) + } +} + +/// Host's standard output +pub struct HStdout { + fd: usize, +} + +impl HStdout { + /// Attempts to write an entire `buffer` into this sink + pub fn write_all(&mut self, buffer: &[u8]) -> Result<(), ()> { + write_all(self.fd, buffer) + } +} + +impl fmt::Write for HStdout { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_all(s.as_bytes()).map_err(|_| fmt::Error) + } +} + +/// Construct a new handle to the host's standard error. +pub fn hstderr() -> Result { + open(":tt\0", nr::open::W_APPEND).map(|fd| HStderr { fd }) +} + +/// Construct a new handle to the host's standard output. +pub fn hstdout() -> Result { + open(":tt\0", nr::open::W_TRUNC).map(|fd| HStdout { fd }) +} + +fn open(name: &str, mode: usize) -> Result { + let name = name.as_bytes(); + match unsafe { syscall!(OPEN, name.as_ptr(), mode, name.len() - 1) } as + isize { + -1 => Err(()), + fd => Ok(fd as usize), + } +} + +fn write_all(fd: usize, mut buffer: &[u8]) -> Result<(), ()> { + while !buffer.is_empty() { + match unsafe { syscall!(WRITE, fd, buffer.as_ptr(), buffer.len()) } { + // Done + 0 => return Ok(()), + // `n` bytes were not written + n if n <= buffer.len() => { + let offset = (buffer.len() - n) as isize; + buffer = unsafe { + slice::from_raw_parts(buffer.as_ptr().offset(offset), n) + } + } + // Error + _ => return Err(()), + } + } + Ok(()) +} diff --git a/src/io.rs b/src/io.rs deleted file mode 100644 index f6b2ca77..00000000 --- a/src/io.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! Host I/O - -use core::{fmt, slice}; -use core::fmt::Write; -use nr; - -/// Host's standard error -pub struct Stderr { - fd: usize, -} - -/// Construct a new handle to the host's standard error. -pub fn stderr() -> Stderr { - Stderr { fd: open(":tt\0", nr::open::W_APPEND).unwrap() } -} - -/// Host's standard output -pub struct Stdout { - fd: usize, -} - -/// Construct a new handle to the host's standard output. -pub fn stdout() -> Stdout { - Stdout { fd: open(":tt\0", nr::open::W_TRUNC).unwrap() } -} - -fn open(name: &str, mode: usize) -> Result { - let name = name.as_bytes(); - match unsafe { syscall!(OPEN, name.as_ptr(), mode, name.len() - 1) } as isize { - -1 => Err(()), - fd => Ok(fd as usize), - } -} - -fn write_all(fd: usize, mut buffer: &[u8]) -> fmt::Result { - while !buffer.is_empty() { - match unsafe { syscall!(WRITE, fd, buffer.as_ptr(), buffer.len()) } { - // Done - 0 => return Ok(()), - // `n` bytes were not written - n if n <= buffer.len() => { - let offset = (buffer.len() - n) as isize; - buffer = unsafe { - slice::from_raw_parts(buffer.as_ptr().offset(offset), n) - } - } - // Error - _ => return Err(fmt::Error::default()), - } - } - Ok(()) -} - -impl Stderr { - fn write_all(&mut self, buffer: &[u8]) -> fmt::Result { - write_all(self.fd, buffer) - } -} - -impl Stdout { - fn write_all(&mut self, buffer: &[u8]) -> fmt::Result { - write_all(self.fd, buffer) - } -} - -impl Write for Stderr { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.write_all(s.as_bytes()) - } -} - -impl Write for Stdout { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.write_all(s.as_bytes()) - } -} - -/// Write a `buffer` to the host's stderr -pub fn ewrite(buffer: &[u8]) { - stderr().write_all(buffer).ok(); -} - -/// Write `fmt::Arguments` to the host's stderr -pub fn ewrite_fmt(args: fmt::Arguments) { - stderr().write_fmt(args).ok(); -} - -/// Write a `string` to the host's stderr -pub fn ewrite_str(string: &str) { - stderr().write_all(string.as_bytes()).ok(); -} - -/// Write a `buffer` to the host's stdout -pub fn write(buffer: &[u8]) { - stdout().write_all(buffer).ok(); -} - -/// Write `fmt::Arguments` to the host's stdout -pub fn write_fmt(args: fmt::Arguments) { - stdout().write_fmt(args).ok(); -} - -/// Write a `string` to the host's stdout -pub fn write_str(string: &str) { - stdout().write_all(string.as_bytes()).ok(); -} diff --git a/src/lib.rs b/src/lib.rs index cd5e0152..6f76b308 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,7 @@ //! //! fn main() { //! // File descriptor (on the host) -//! const STDOUT: usize = 1; +//! 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 @@ -107,15 +107,17 @@ //! //! [pdf]: http://infocenter.arm.com/help/topic/com.arm.doc.dui0471e/DUI0471E_developing_for_arm_processors.pdf +#![deny(missing_docs)] +#![deny(warnings)] #![feature(asm)] #![no_std] #[macro_use] mod macros; -pub mod io; -pub mod nr; pub mod debug; +pub mod hio; +pub mod nr; /// Performs a semihosting operation, takes a pointer to an argument block #[inline(always)] diff --git a/src/macros.rs b/src/macros.rs index 184d13d6..8287b4ab 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -27,33 +27,3 @@ macro_rules! syscall1 { $crate::syscall1($crate::nr::$nr, $a1 as usize) }; } - -/// Macro for printing to the **host's** standard stderr -#[macro_export] -macro_rules! ehprint { - ($s:expr) => ($crate::io::ewrite_str($s)); - ($($arg:tt)*) => ($crate::io::ewrite_fmt(format_args!($($arg)*))); -} - -/// Macro for printing to the **host's** standard error, with a newline. -#[macro_export] -macro_rules! ehprintln { - () => (ehprint!("\n")); - ($fmt:expr) => (ehprint!(concat!($fmt, "\n"))); - ($fmt:expr, $($arg:tt)*) => (ehprint!(concat!($fmt, "\n"), $($arg)*)); -} - -/// Macro for printing to the **host's** standard output -#[macro_export] -macro_rules! hprint { - ($s:expr) => ($crate::io::write_str($s)); - ($($arg:tt)*) => ($crate::io::write_fmt(format_args!($($arg)*))); -} - -/// Macro for printing to the **host's** standard output, with a newline. -#[macro_export] -macro_rules! hprintln { - () => (hprint!("\n")); - ($fmt:expr) => (hprint!(concat!($fmt, "\n"))); - ($fmt:expr, $($arg:tt)*) => (hprint!(concat!($fmt, "\n"), $($arg)*)); -} diff --git a/src/nr.rs b/src/nr.rs index e546b08d..a45cb7ef 100644 --- a/src/nr.rs +++ b/src/nr.rs @@ -1,5 +1,8 @@ //! Semihosting operations +// TODO document +#![allow(missing_docs)] + pub const CLOCK: usize = 0x10; pub const CLOSE: usize = 0x05; pub const ELAPSED: usize = 0x30; From f7428a759348782c2a1e44472c8d835da86ed267 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Tue, 4 Jul 2017 21:21:48 +0200 Subject: [PATCH 17/41] Drop references to removed hprint(ln)! macros --- src/lib.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6f76b308..9e8977cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,9 +14,6 @@ //! [sc]: https://en.wikipedia.org/wiki/System_call //! [`sc`]: https://crates.io/crates/sc //! -//! And since the most used semihosting operation is writing to the host's -//! stdout, convenience `hprint` and `hprintln` macros are provided. -//! //! # Forewarning //! //! Semihosting operations are *very* slow. Like, each WRITE operation can take @@ -39,9 +36,6 @@ //! //! // Signature: fn write(fd: usize, ptr: *const u8, len: usize) -> usize //! let r = unsafe { syscall!(WRITE, STDOUT, MSG.as_ptr(), MSG.len()) }; -//! -//! // (Or you could have just written `hprintln!("Hello, world!")`) -//! // .. //! } //! ``` //! From 3a1f763e8c86b65fa6f9b878504eac71faee7842 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Tue, 4 Jul 2017 21:31:16 +0200 Subject: [PATCH 18/41] Document nr::open, fix text/binary inversion --- src/nr.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/nr.rs b/src/nr.rs index a45cb7ef..7a4e3b36 100644 --- a/src/nr.rs +++ b/src/nr.rs @@ -28,17 +28,30 @@ pub const WRITEC: usize = 0x03; pub const ENTER_SVC: usize = 0x17; pub const REPORT_EXCEPTION: usize = 0x18; +/// Values for the mode parameter of the OPEN syscall. pub mod open { + /// Mode corresponding to fopen "r" mode. pub const R: usize = 0; - pub const R_TEXT: usize = 1; + /// Mode corresponding to fopen "rb" mode. + pub const R_BINARY: usize = 1; + /// Mode corresponding to fopen "r+" mode. pub const RW: usize = 2; - pub const RW_TEXT: usize = 3; + /// Mode corresponding to fopen "r+b" mode. + pub const RW_BINARY: usize = 3; + /// Mode corresponding to fopen "w" mode. pub const W_TRUNC: usize = 4; - pub const W_TRUNC_TEXT: usize = 5; + /// Mode corresponding to fopen "wb" mode. + pub const W_TRUNC_BINARY: usize = 5; + /// Mode corresponding to fopen "w+" mode. pub const RW_TRUNC: usize = 6; - pub const RW_TRUNC_TEXT: usize = 7; + /// Mode corresponding to fopen "w+b" mode. + pub const RW_TRUNC_BINARY: usize = 7; + /// Mode corresponding to fopen "a" mode. pub const W_APPEND: usize = 8; - pub const W_APPEND_TEXT: usize = 9; + /// Mode corresponding to fopen "ab" mode. + pub const W_APPEND_BINARY: usize = 9; + /// Mode corresponding to fopen "a+" mode. pub const RW_APPEND: usize = 10; - pub const RW_APPEND_TEXT: usize = 11; + /// Mode corresponding to fopen "a+b" mode. + pub const RW_APPEND_BINARY: usize = 11; } From aaa1c60ee3524a943783e6e0e2a5dfe0b8fc57a1 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Tue, 4 Jul 2017 21:39:35 +0200 Subject: [PATCH 19/41] Document the strange usage of W_APPEND mode for stderr --- src/hio.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/hio.rs b/src/hio.rs index ac1240ee..7eac6eb7 100644 --- a/src/hio.rs +++ b/src/hio.rs @@ -41,6 +41,10 @@ impl fmt::Write for HStdout { /// Construct a new handle to the host's standard error. pub fn hstderr() -> Result { + // There is actually no stderr access in ARM Semihosting documentation. Use + // convention used in libgloss. + // See: libgloss/arm/syscalls.c, line 139. + // https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/arm/syscalls.c#l139 open(":tt\0", nr::open::W_APPEND).map(|fd| HStderr { fd }) } From bab5fba9a71721ba178077b3587674c1cf0f54db Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 4 Jul 2017 15:10:07 -0500 Subject: [PATCH 20/41] add CI --- .travis.yml | 36 ++++++++++++++++++++++++++++++++++++ ci/install.sh | 12 ++++++++++++ ci/script.sh | 14 ++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 .travis.yml create mode 100644 ci/install.sh create mode 100644 ci/script.sh diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..f2ddcd9e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,36 @@ +language: rust + +matrix: + include: + - env: TARGET=thumbv7m-none-eabi + rust: nightly + - env: TARGET=x86_64-unknown-linux-gnu + rust: nightly + +before_install: set -e + +install: + - bash ci/install.sh + +script: + - bash ci/script.sh + +after_script: set +e + +after_success: + - bash ci/after_success.sh + +cache: cargo +before_cache: + # Travis can't cache files that are not readable by "others" + - chmod -R a+r $HOME/.cargo + +branches: + only: + - auto + - master + - try + +notifications: + email: + on_success: never diff --git a/ci/install.sh b/ci/install.sh new file mode 100644 index 00000000..43c97604 --- /dev/null +++ b/ci/install.sh @@ -0,0 +1,12 @@ +set -euxo pipefail + +main() { + if [ $TARGET = thumbv7m-none-eabi ]; then + cargo install --list | grep xargo || \ + cargo install xargo + rustup component list | grep 'rust-src.*installed' || \ + rustup component add rust-src + fi +} + +main diff --git a/ci/script.sh b/ci/script.sh new file mode 100644 index 00000000..c9acaaf7 --- /dev/null +++ b/ci/script.sh @@ -0,0 +1,14 @@ +set -euxo pipefail + +main() { + local cargo= + if [ $TARGET = thumbv7m-none-eabi ]; then + cargo=xargo + else + cargo=cargo + fi + + $cargo check --target $TARGET +} + +main From bbe2d651c1b7f7dd5f5e03b86696e005203b2945 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 4 Jul 2017 15:12:19 -0500 Subject: [PATCH 21/41] drop sh ci/after_success.sh --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f2ddcd9e..ce70501e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,9 +17,6 @@ script: after_script: set +e -after_success: - - bash ci/after_success.sh - cache: cargo before_cache: # Travis can't cache files that are not readable by "others" From 4e7d9d41b4176adc0a853f0fdc1097aeb2fcab35 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 4 Jul 2017 15:25:18 -0500 Subject: [PATCH 22/41] don't test master --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ce70501e..7b8f8d9f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,6 @@ before_cache: branches: only: - auto - - master - try notifications: From 71d02218885465f023a750caa9251ef22189695b Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 7 Jul 2017 12:07:33 -0500 Subject: [PATCH 23/41] v0.2.0 --- CHANGELOG.md | 26 +++++++++++++++++++++++++- Cargo.toml | 2 +- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a073d6b3..d0dc339b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,29 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.2.0] - 2017-07-07 + +### Added + +- `exit` and `report_exception` syscalls + +- `HStdout` and `HStderr` structs that represent handles to the host stdout and + stderr stream respectively. + +### Changed + +- [breaking-change] The `io` module has been renamed to `hio` to reflect that + this is I/O *on the host*. + +### Removed + +- [breaking-change] the family of `write` functions in the `io` module. Instead + use `HStdout` / `HStderr` and its `write_all` method and `fmt::Write` + implementation. + +- [breaking-change] the `hprint!` family of macros. Instead use `HStdout` and + the standard `write!` macro. + ## [v0.1.3] - 2017-02-27 ### Added @@ -33,7 +56,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Initial release -[Unreleased]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.3...HEAD +[Unreleased]: https://github.com/japaric/cortex-m-semihosting/compare/v0.2.0...HEAD +[v0.2.0]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.3...v0.2.0 [v0.1.3]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.2...v0.1.3 [v0.1.2]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.1...v0.1.2 [v0.1.1]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.0...v0.1.1 diff --git a/Cargo.toml b/Cargo.toml index 606b6c31..580548e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,6 @@ keywords = ["semihosting", "arm", "cortex-m"] license = "MIT OR Apache-2.0" name = "cortex-m-semihosting" repository = "https://github.com/japaric/cortex-m-semihosting" -version = "0.1.3" +version = "0.2.0" [dependencies] From 28a3d990ebd43fa8a43cff69d14d90952c08520b Mon Sep 17 00:00:00 2001 From: Hiroki Noda Date: Mon, 28 Aug 2017 23:50:15 +0900 Subject: [PATCH 24/41] Fix semihosting's SYS_CLOSE nr. refs: http://www.keil.com/support/man/docs/armcc/armcc_pge1358787051159.htm --- src/nr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nr.rs b/src/nr.rs index 7a4e3b36..5d206dec 100644 --- a/src/nr.rs +++ b/src/nr.rs @@ -4,7 +4,7 @@ #![allow(missing_docs)] pub const CLOCK: usize = 0x10; -pub const CLOSE: usize = 0x05; +pub const CLOSE: usize = 0x02; pub const ELAPSED: usize = 0x30; pub const ERRNO: usize = 0x13; pub const FLEN: usize = 0x0c; From cb1afe4002d576b87bfd4c199f42a43815984ce4 Mon Sep 17 00:00:00 2001 From: David Craven Date: Tue, 27 Feb 2018 16:02:14 +0100 Subject: [PATCH 25/41] Implement semihosting for RISCV. --- Cargo.toml | 12 ++++++------ src/lib.rs | 51 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 580548e2..e898e5b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] authors = ["Jorge Aparicio "] -description = "Semihosting for ARM Cortex-M processors" -documentation = "https://docs.rs/cortex-m-semihosting" -keywords = ["semihosting", "arm", "cortex-m"] +description = "Semihosting for RISCV processors" +documentation = "https://docs.rs/riscv-semihosting" +keywords = ["semihosting", "riscv"] license = "MIT OR Apache-2.0" -name = "cortex-m-semihosting" -repository = "https://github.com/japaric/cortex-m-semihosting" -version = "0.2.0" +name = "riscv-semihosting" +repository = "https://github.com/riscv-rust/riscv-semihosting" +version = "0.0.1" [dependencies] diff --git a/src/lib.rs b/src/lib.rs index 9e8977cc..d606477f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,10 @@ -//! Semihosting for ARM Cortex-M processors +//! Semihosting for RISCV processors //! //! # What is semihosting? //! -//! "Semihosting is a mechanism that enables code running on an ARM target to +//! "Semihosting is a mechanism that enables code running on a RISCV target to //! communicate and use the Input/Output facilities on a host computer that is -//! running a debugger." - ARM +//! running a debugger." - RISCV //! //! # Interface //! @@ -27,7 +27,7 @@ //! //! ``` //! #[macro_use] -//! extern crate cortex_m_semihosting; +//! extern crate riscv_semihosting; //! //! fn main() { //! // File descriptor (on the host) @@ -113,38 +113,57 @@ pub mod debug; pub mod hio; pub mod nr; +/// The hint that differentiates the semihosting call. +// WARNING: This variable is hardcoded in the asm! Don't forget to update +// if it changes. +pub const RISCV_SEMIHOSTING_CALL_NUMBER: usize = 7; + /// Performs a semihosting operation, takes a pointer to an argument block -#[inline(always)] -#[cfg(target_arch = "arm")] +#[inline] +#[cfg(target_arch = "riscv")] pub unsafe fn syscall(mut nr: usize, arg: &T) -> usize { - asm!("bkpt 0xAB" - : "+{r0}"(nr) - : "{r1}"(arg) + // .option push + // .option norvc + asm!(r" + slli x0, x0, 0x1f + ebreak + srai x0, x0, 0x7 + " + : "+{x10}"(nr) + : "{x11}"(arg) : "memory" : "volatile"); + // .option pop nr } /// Performs a semihosting operation, takes a pointer to an argument block -#[cfg(not(target_arch = "arm"))] +#[cfg(not(target_arch = "riscv"))] pub unsafe fn syscall(_nr: usize, _arg: &T) -> usize { 0 } /// Performs a semihosting operation, takes one integer as an argument -#[inline(always)] -#[cfg(target_arch = "arm")] +#[inline] +#[cfg(target_arch = "riscv")] pub unsafe fn syscall1(mut nr: usize, arg: usize) -> usize { - asm!("bkpt 0xAB" - : "+{r0}"(nr) - : "{r1}"(arg) + // .option push + // .option norvc + asm!(r" + slli x0, x0, 0x1f + ebreak + srai x0, x0, 0x7 + " + : "+{x10}"(nr) + : "{x11}"(arg) : "memory" : "volatile"); + // .option pop nr } /// Performs a semihosting operation, takes one integer as an argument -#[cfg(not(target_arch = "arm"))] +#[cfg(not(target_arch = "riscv"))] pub unsafe fn syscall1(_nr: usize, _arg: usize) -> usize { 0 } From 4e8ad013fd3b81080c5b4fcdf1255aa6c698eb48 Mon Sep 17 00:00:00 2001 From: Jonathan Perkin Date: Sun, 2 Jan 2022 08:54:30 +0000 Subject: [PATCH 26/41] Merge upstream cortex-m-semihosting. --- CHANGELOG.md | 106 ++++++++++++++++++++++++-- Cargo.toml | 15 +++- README.md | 20 ++++- src/debug.rs | 6 +- src/export.rs | 51 +++++++++++++ src/hio.rs | 52 +++++-------- src/lib.rs | 207 ++++++++++++++++++++++++++++++++------------------ src/macros.rs | 101 ++++++++++++++++++++++-- 8 files changed, 431 insertions(+), 127 deletions(-) create mode 100644 src/export.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index d0dc339b..0a942cfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,91 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.4.1] - 2020-10-20 + +0.4.1 was yanked because the pre-built binaries contain conflicting symbols +with a supported version of cortex-m. + +- Fix missing prebuilt binaries (#271) + +## [v0.4.0] - 2020-10-14 + +v0.4.0 was yanked because it did not include the required pre-built binaries +in the final crate. + +- Moved into cortex-m repository +- Merge `HStdout` and `HStderr` into one type: `HostStream` +- Support cortex-m v0.7 +- Semihosting macros no longer return a Result, instead errors are ignored. + +## [v0.3.7] - 2020-12-02 + +- Replaces the yanked v0.3.6 by reverting #48, so the semihosting macros + continue to return a Result. + +## [v0.3.6] - 2020-12-01 + +v0.3.6 was yanked because it incorrectly included #48, which was a breaking +change. + +### Added + +- Update cortex-m dependency to support version 0.7. +- Add `no-semihosting` feature to disable all semihosting calls. + +## [v0.3.5] - 2019-08-29 + +### Added + +- Adds a feature to work around JLink quirks +- Adds a dbg! macro using heprintln +- Added thumbv8m.main support on stable + +### Fixed + +- Now Rust 2018 edition + +## [v0.3.4] - 2019-08-13 + +### Fixed + +- Support for thumbv8 mainline hf target + +## [v0.3.3] - 2019-04-22 + +### Added + +- Adds support for thumbv8 and cortex-m v0.6.0 + +## [v0.3.2] - 2018-11-04 + +### Added + +- Added a family of `hprint` macros for printing to the host standard output / + error via globally shared `HStdout` / `HStderr` handles . + +## [v0.3.1] - 2018-08-27 + +### Changed + +- This crate no longer depends on `arm-none-eabi-gcc`. + +## [v0.3.0] - 2018-05-10 + +### Changed + +- [breaking-change] `inline-asm` is no longer a default feature (i.e. a feature that's enabled by + default). The consequence is that this crate now compiles on 1.27 (beta) by default, and opting + into `inline-asm` requires nightly. + +## [v0.2.1] - 2018-04-25 + +### Added + +- An opt-out "inline-asm" Cargo feature. When this feature is disabled semihosting is implemented + using an external assembly file instead of using the unstable inline assembly (`asm!`) feature + meaning that this crate can be compiled on stable. + ## [v0.2.0] - 2017-07-07 ### Added @@ -56,8 +141,19 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Initial release -[Unreleased]: https://github.com/japaric/cortex-m-semihosting/compare/v0.2.0...HEAD -[v0.2.0]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.3...v0.2.0 -[v0.1.3]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.2...v0.1.3 -[v0.1.2]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.1...v0.1.2 -[v0.1.1]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.0...v0.1.1 +[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.4.1...HEAD +[v0.4.1]: https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.4.0...c-m-sh-v0.4.1 +[v0.4.0]: https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.3.5...c-m-sh-v0.4.0 +[v0.3.7]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.6...v0.3.7 +[v0.3.6]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.5...v0.3.6 +[v0.3.5]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.4...v0.3.5 +[v0.3.4]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.3...v0.3.4 +[v0.3.3]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.2...v0.3.3 +[v0.3.2]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.1...v0.3.2 +[v0.3.1]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.0...v0.3.1 +[v0.3.0]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.2.1...v0.3.0 +[v0.2.1]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.2.0...v0.2.1 +[v0.2.0]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.3...v0.2.0 +[v0.1.3]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.2...v0.1.3 +[v0.1.2]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.1...v0.1.2 +[v0.1.1]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.0...v0.1.1 diff --git a/Cargo.toml b/Cargo.toml index e898e5b0..8a7c5714 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,24 @@ [package] -authors = ["Jorge Aparicio "] +authors = [ + "The Cortex-M Team ", + "Jorge Aparicio ", +] description = "Semihosting for RISCV processors" documentation = "https://docs.rs/riscv-semihosting" keywords = ["semihosting", "riscv"] +categories = ["no-std", "embedded"] license = "MIT OR Apache-2.0" name = "riscv-semihosting" +readme = "README.md" repository = "https://github.com/riscv-rust/riscv-semihosting" version = "0.0.1" +edition = "2018" + +[features] +default = ["inline-asm", "jlink-quirks"] +inline-asm = [] +jlink-quirks = [] +no-semihosting = [] [dependencies] +riscv = "0.7.0" diff --git a/README.md b/README.md index 805c5f62..bfbfb44a 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,22 @@ > Semihosting for ARM Cortex-M processors +This project is developed and maintained by the [Cortex-M team][team]. + ## [Documentation](https://docs.rs/cortex-m-semihosting) +# Minimum Supported Rust Version (MSRV) + +This crate is guaranteed to compile on stable Rust 1.33.0 and up. It *might* +compile with older versions but that may change in any new patch release. + ## License Licensed under either of -- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or +- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) -- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) +- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. @@ -22,3 +29,12 @@ at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +## Code of Conduct + +Contribution to this crate is organized under the terms of the [Rust Code of +Conduct][CoC], the maintainer of this crate, the [Cortex-M team][team], promises +to intervene to uphold that code of conduct. + +[CoC]: ../CODE_OF_CONDUCT.md +[team]: https://github.com/rust-embedded/wg#the-cortex-m-team diff --git a/src/debug.rs b/src/debug.rs index 15ba91be..72db715d 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -8,10 +8,8 @@ //! //! Target program: //! -//! ``` -//! #[macro_use] -//! extern crate cortex_m_semihosting; -//! use cortex_m_semihosting::debug::{self, EXIT_SUCCESS, EXIT_FAILURE}; +//! ```no_run +//! use riscv_semihosting::debug::{self, EXIT_SUCCESS, EXIT_FAILURE}; //! //! fn main() { //! if 2 == 2 { diff --git a/src/export.rs b/src/export.rs new file mode 100644 index 00000000..01a7cc0a --- /dev/null +++ b/src/export.rs @@ -0,0 +1,51 @@ +//! IMPLEMENTATION DETAILS USED BY MACROS + +use core::fmt::{self, Write}; + +use riscv::interrupt; + +use crate::hio::{self, HostStream}; + +static mut HSTDOUT: Option = None; + +pub fn hstdout_str(s: &str) { + let _result = interrupt::free(|_| unsafe { + if HSTDOUT.is_none() { + HSTDOUT = Some(hio::hstdout()?); + } + + HSTDOUT.as_mut().unwrap().write_str(s).map_err(drop) + }); +} + +pub fn hstdout_fmt(args: fmt::Arguments) { + let _result = interrupt::free(|_| unsafe { + if HSTDOUT.is_none() { + HSTDOUT = Some(hio::hstdout()?); + } + + HSTDOUT.as_mut().unwrap().write_fmt(args).map_err(drop) + }); +} + +static mut HSTDERR: Option = None; + +pub fn hstderr_str(s: &str) { + let _result = interrupt::free(|_| unsafe { + if HSTDERR.is_none() { + HSTDERR = Some(hio::hstderr()?); + } + + HSTDERR.as_mut().unwrap().write_str(s).map_err(drop) + }); +} + +pub fn hstderr_fmt(args: fmt::Arguments) { + let _result = interrupt::free(|_| unsafe { + if HSTDERR.is_none() { + HSTDERR = Some(hio::hstderr()?); + } + + HSTDERR.as_mut().unwrap().write_fmt(args).map_err(drop) + }); +} diff --git a/src/hio.rs b/src/hio.rs index 7eac6eb7..b6b6c7b7 100644 --- a/src/hio.rs +++ b/src/hio.rs @@ -1,64 +1,46 @@ //! Host I/O +use crate::nr; use core::{fmt, slice}; -use nr; -/// Host's standard error -pub struct HStderr { +/// A byte stream to the host (e.g., host's stdout or stderr). +#[derive(Clone, Copy)] +pub struct HostStream { fd: usize, } -impl HStderr { +impl HostStream { /// Attempts to write an entire `buffer` into this sink pub fn write_all(&mut self, buffer: &[u8]) -> Result<(), ()> { write_all(self.fd, buffer) } } -impl fmt::Write for HStderr { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.write_all(s.as_bytes()).map_err(|_| fmt::Error) - } -} - -/// Host's standard output -pub struct HStdout { - fd: usize, -} - -impl HStdout { - /// Attempts to write an entire `buffer` into this sink - pub fn write_all(&mut self, buffer: &[u8]) -> Result<(), ()> { - write_all(self.fd, buffer) - } -} - -impl fmt::Write for HStdout { +impl fmt::Write for HostStream { fn write_str(&mut self, s: &str) -> fmt::Result { self.write_all(s.as_bytes()).map_err(|_| fmt::Error) } } /// Construct a new handle to the host's standard error. -pub fn hstderr() -> Result { +pub fn hstderr() -> Result { // There is actually no stderr access in ARM Semihosting documentation. Use // convention used in libgloss. // See: libgloss/arm/syscalls.c, line 139. // https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/arm/syscalls.c#l139 - open(":tt\0", nr::open::W_APPEND).map(|fd| HStderr { fd }) + open(":tt\0", nr::open::W_APPEND) } /// Construct a new handle to the host's standard output. -pub fn hstdout() -> Result { - open(":tt\0", nr::open::W_TRUNC).map(|fd| HStdout { fd }) +pub fn hstdout() -> Result { + open(":tt\0", nr::open::W_TRUNC) } -fn open(name: &str, mode: usize) -> Result { +fn open(name: &str, mode: usize) -> Result { let name = name.as_bytes(); - match unsafe { syscall!(OPEN, name.as_ptr(), mode, name.len() - 1) } as - isize { + match unsafe { syscall!(OPEN, name.as_ptr(), mode, name.len() - 1) } as isize { -1 => Err(()), - fd => Ok(fd as usize), + fd => Ok(HostStream { fd: fd as usize }), } } @@ -70,10 +52,12 @@ fn write_all(fd: usize, mut buffer: &[u8]) -> Result<(), ()> { // `n` bytes were not written n if n <= buffer.len() => { let offset = (buffer.len() - n) as isize; - buffer = unsafe { - slice::from_raw_parts(buffer.as_ptr().offset(offset), n) - } + buffer = unsafe { slice::from_raw_parts(buffer.as_ptr().offset(offset), n) } } + #[cfg(feature = "jlink-quirks")] + // Error (-1) - should be an error but JLink can return -1, -2, -3,... + // For good measure, we allow up to negative 15. + n if n > 0xfffffff0 => return Ok(()), // Error _ => return Err(()), } diff --git a/src/lib.rs b/src/lib.rs index d606477f..6b15bddc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,40 +2,46 @@ //! //! # What is semihosting? //! -//! "Semihosting is a mechanism that enables code running on a RISCV target to -//! communicate and use the Input/Output facilities on a host computer that is -//! running a debugger." - RISCV +//! "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 //! -//! Since semihosting operations are modeled as [system calls][sc], this crate -//! exposes an untyped `syscall!` interface just like the [`sc`] crate does. +//! 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. +//! Semihosting operations are *very* slow. Like, each WRITE operation can take hundreds of +//! milliseconds. //! //! # Example //! -//! This example will show how to print "Hello, world!" on the host. +//! ## Using `hio::hstdout` //! -//! Target program: +//! This example will demonstrate how to print formatted strings. //! -//! ``` -//! #[macro_use] -//! extern crate riscv_semihosting; +//! ```no_run +//! use riscv_semihosting::hio; +//! use core::fmt::Write; //! -//! fn main() { -//! // 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"; +//! // 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; //! -//! // Signature: fn write(fd: usize, ptr: *const u8, len: usize) -> usize -//! let r = unsafe { syscall!(WRITE, STDOUT, MSG.as_ptr(), MSG.len()) }; +//! write!(stdout, "{} on embedded is #{}!", language, ranking)?; +//! +//! Ok(()) //! } //! ``` //! @@ -50,8 +56,8 @@ //! # 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` +//! 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 @@ -63,24 +69,23 @@ //! 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. +//! 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 -//! # Connect to OpenOCD +//! (gdb) # Connect to OpenOCD //! (gdb) target remote :3333 //! -//! # Enable OpenOCD's semihosting support +//! (gdb) # Enable OpenOCD's semihosting support //! (gdb) monitor arm semihosting enable //! -//! # Flash the program +//! (gdb) # Flash the program //! (gdb) load //! -//! # Run the program +//! (gdb) # Run the program //! (gdb) continue //! ``` //! @@ -89,9 +94,82 @@ //! ``` text //! # openocd -f $INTERFACE -f $TARGET -l /tmp/openocd.log //! (..) -//! Hello, world! +//! 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 +//! +//! ## `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 are patched out. +//! //! # Reference //! //! For documentation about the semihosting operations, check: @@ -101,69 +179,46 @@ //! //! [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)] -#![deny(warnings)] -#![feature(asm)] #![no_std] #[macro_use] mod macros; pub mod debug; +#[doc(hidden)] +pub mod export; pub mod hio; pub mod nr; -/// The hint that differentiates the semihosting call. -// WARNING: This variable is hardcoded in the asm! Don't forget to update -// if it changes. -pub const RISCV_SEMIHOSTING_CALL_NUMBER: usize = 7; - -/// Performs a semihosting operation, takes a pointer to an argument block -#[inline] -#[cfg(target_arch = "riscv")] -pub unsafe fn syscall(mut nr: usize, arg: &T) -> usize { - // .option push - // .option norvc - asm!(r" - slli x0, x0, 0x1f - ebreak - srai x0, x0, 0x7 - " - : "+{x10}"(nr) - : "{x11}"(arg) - : "memory" - : "volatile"); - // .option pop - nr +#[cfg(not(feature = "inline-asm"))] +extern "C" { + fn __sh_syscall(nr: usize, arg: usize) -> usize; } /// Performs a semihosting operation, takes a pointer to an argument block -#[cfg(not(target_arch = "riscv"))] -pub unsafe fn syscall(_nr: usize, _arg: &T) -> usize { - 0 +#[inline(always)] +pub unsafe fn syscall(nr: usize, arg: &T) -> usize { + syscall1(nr, arg as *const T as usize) } /// Performs a semihosting operation, takes one integer as an argument -#[inline] -#[cfg(target_arch = "riscv")] -pub unsafe fn syscall1(mut nr: usize, arg: usize) -> usize { - // .option push - // .option norvc - asm!(r" - slli x0, x0, 0x1f - ebreak - srai x0, x0, 0x7 - " - : "+{x10}"(nr) - : "{x11}"(arg) - : "memory" - : "volatile"); - // .option pop - nr -} - -/// Performs a semihosting operation, takes one integer as an argument -#[cfg(not(target_arch = "riscv"))] +#[inline(always)] pub unsafe fn syscall1(_nr: usize, _arg: usize) -> usize { - 0 + match () { + #[cfg(all(feature = "inline-asm", not(feature = "no-semihosting")))] + () => { + let mut nr = _nr; + llvm_asm!(" + slli x0, x0, 0x1f + ebreak + srai x0, x0, 0x7 + " : "+{a0}"(nr) : "{a1}"(_arg) :: "volatile"); + nr + } + + #[cfg(feature = "no-semihosting")] + () => 0, + } } diff --git a/src/macros.rs b/src/macros.rs index 8287b4ab..f1cc4f3e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -11,19 +11,110 @@ macro_rules! syscall { $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize]) }; ($nr:ident, $a1:expr, $a2:expr, $a3:expr) => { - $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize, - $a3 as usize]) + $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize, $a3 as usize]) }; ($nr:ident, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => { - $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize, - $a3 as usize, $a4 as usize]) + $crate::syscall( + $crate::nr::$nr, + &[$a1 as usize, $a2 as usize, $a3 as usize, $a4 as usize], + ) }; } -/// Macro version of `syscall1` +/// Macro version of `syscall1`. #[macro_export] macro_rules! syscall1 { ($nr:ident, $a1:expr) => { $crate::syscall1($crate::nr::$nr, $a1 as usize) }; } + +/// Macro for printing to the HOST standard output. +/// +/// This is similar to the `print!` macro in the standard library. Both will panic on any failure to +/// print. +#[macro_export] +macro_rules! hprint { + ($s:expr) => { + $crate::export::hstdout_str($s) + }; + ($($tt:tt)*) => { + $crate::export::hstdout_fmt(format_args!($($tt)*)) + }; +} + +/// Macro for printing to the HOST standard output, with a newline. +/// +/// This is similar to the `println!` macro in the standard library. Both will panic on any failure to +/// print. +#[macro_export] +macro_rules! hprintln { + () => { + $crate::export::hstdout_str("\n") + }; + ($s:expr) => { + $crate::export::hstdout_str(concat!($s, "\n")) + }; + ($s:expr, $($tt:tt)*) => { + $crate::export::hstdout_fmt(format_args!(concat!($s, "\n"), $($tt)*)) + }; +} + +/// Macro for printing to the HOST standard error. +/// +/// This is similar to the `eprint!` macro in the standard library. Both will panic on any failure +/// to print. +#[macro_export] +macro_rules! heprint { + ($s:expr) => { + $crate::export::hstderr_str($s) + }; + ($($tt:tt)*) => { + $crate::export::hstderr_fmt(format_args!($($tt)*)) + }; +} + +/// Macro for printing to the HOST standard error, with a newline. +/// +/// This is similar to the `eprintln!` macro in the standard library. Both will panic on any failure +/// to print. +#[macro_export] +macro_rules! heprintln { + () => { + $crate::export::hstderr_str("\n") + }; + ($s:expr) => { + $crate::export::hstderr_str(concat!($s, "\n")) + }; + ($s:expr, $($tt:tt)*) => { + $crate::export::hstderr_fmt(format_args!(concat!($s, "\n"), $($tt)*)) + }; +} + +/// Macro that prints and returns the value of a given expression for quick and +/// dirty debugging. +/// +/// Works exactly like `dbg!` in the standard library, replacing `eprintln!` +/// with `heprintln!`. +#[macro_export] +macro_rules! dbg { + () => { + $crate::heprintln!("[{}:{}]", file!(), line!()); + }; + ($val:expr) => { + // Use of `match` here is intentional because it affects the lifetimes + // of temporaries - https://stackoverflow.com/a/48732525/1063961 + match $val { + tmp => { + $crate::heprintln!("[{}:{}] {} = {:#?}", + file!(), line!(), stringify!($val), &tmp); + tmp + } + } + }; + // Trailing comma with single argument is ignored + ($val:expr,) => { $crate::dbg!($val) }; + ($($val:expr),+ $(,)?) => { + ($($crate::dbg!($val)),+,) + }; +} From 7c021ec880198f9d73ebca45585504faa927f362 Mon Sep 17 00:00:00 2001 From: Jonathan Perkin Date: Sun, 2 Jan 2022 10:25:33 +0000 Subject: [PATCH 27/41] Fix assembly to ensure uncompressed instructions. Without this the correct sequence is not triggered in OpenOCD and is treated as a regular debugger break. While here update to use asm!() syntax. --- src/lib.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6b15bddc..1c7d1e56 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -179,7 +179,7 @@ //! //! [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))] +#![cfg_attr(feature = "inline-asm", feature(asm))] #![deny(missing_docs)] #![no_std] @@ -210,11 +210,22 @@ pub unsafe fn syscall1(_nr: usize, _arg: usize) -> usize { #[cfg(all(feature = "inline-asm", not(feature = "no-semihosting")))] () => { let mut nr = _nr; - llvm_asm!(" + // The instructions below must always be uncompressed, otherwise + // it will be treated as a regular break, hence the norvc option. + // + // See https://github.com/riscv/riscv-semihosting-spec for more + // details. + asm!(" + .option push + .option norvc slli x0, x0, 0x1f ebreak srai x0, x0, 0x7 - " : "+{a0}"(nr) : "{a1}"(_arg) :: "volatile"); + .option pop + ", + inout("a0") nr, + in("a1") _arg, + ); nr } From 4fde9396f72c76f327893d498efd0082bf97a58c Mon Sep 17 00:00:00 2001 From: Jonathan Perkin Date: Sun, 2 Jan 2022 10:43:38 +0000 Subject: [PATCH 28/41] Update README with a basic introduction. --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bfbfb44a..b0ad0318 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,20 @@ [![crates.io](https://img.shields.io/crates/v/cortex-m-semihosting.svg)](https://crates.io/crates/cortex-m-semihosting) [![crates.io](https://img.shields.io/crates/d/cortex-m-semihosting.svg)](https://crates.io/crates/cortex-m-semihosting) -# `cortex-m-semihosting` +# `riscv-semihosting` -> Semihosting for ARM Cortex-M processors +> Semihosting for RISC-V processors + +This is a fork of the +[cortex-m-semihosting](https://docs.rs/cortex-m-semihosting) crate with minimal +changes to support the RISC-V Semihosting Specification as documented +[here](https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc) + +This crate can be used in exactly the same way as cortex-m-semihosting, simply +by changing calls to `cortex_m_semihosting::*` to `riscv_semihosting::*`. + +The rest of this document is as-is from upstream, and obviously any +ARM-specific sections should be ignored. This project is developed and maintained by the [Cortex-M team][team]. From bf2940f069786cb1830186ae118cee5201834e6e Mon Sep 17 00:00:00 2001 From: Fawaz Tirmizi Date: Mon, 18 Jul 2022 13:47:17 -0700 Subject: [PATCH 29/41] Removed inline asm, bumped MSRV --- Cargo.toml | 5 ++--- README.md | 4 ++-- src/lib.rs | 18 +++--------------- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8a7c5714..91872449 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,10 +15,9 @@ version = "0.0.1" edition = "2018" [features] -default = ["inline-asm", "jlink-quirks"] -inline-asm = [] +default = ["jlink-quirks"] jlink-quirks = [] no-semihosting = [] [dependencies] -riscv = "0.7.0" +riscv = "0.8.0" diff --git a/README.md b/README.md index bfbfb44a..0149fa62 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ This project is developed and maintained by the [Cortex-M team][team]. # Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.33.0 and up. It *might* -compile with older versions but that may change in any new patch release. +This crate is guaranteed to compile on stable Rust 1.59.0 and up. It **won't** +compile with older versions. ## License diff --git a/src/lib.rs b/src/lib.rs index 1c7d1e56..326b7562 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -151,14 +151,6 @@ //! //! # 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 @@ -179,10 +171,11 @@ //! //! [pdf]: http://infocenter.arm.com/help/topic/com.arm.doc.dui0471e/DUI0471E_developing_for_arm_processors.pdf -#![cfg_attr(feature = "inline-asm", feature(asm))] #![deny(missing_docs)] #![no_std] +use core::arch::asm; + #[macro_use] mod macros; @@ -192,11 +185,6 @@ pub mod export; pub mod hio; pub mod nr; -#[cfg(not(feature = "inline-asm"))] -extern "C" { - fn __sh_syscall(nr: usize, arg: usize) -> usize; -} - /// Performs a semihosting operation, takes a pointer to an argument block #[inline(always)] pub unsafe fn syscall(nr: usize, arg: &T) -> usize { @@ -207,7 +195,7 @@ pub unsafe fn syscall(nr: usize, arg: &T) -> usize { #[inline(always)] pub unsafe fn syscall1(_nr: usize, _arg: usize) -> usize { match () { - #[cfg(all(feature = "inline-asm", not(feature = "no-semihosting")))] + #[cfg(not(feature = "no-semihosting"))] () => { let mut nr = _nr; // The instructions below must always be uncompressed, otherwise From e80cec6faa3f8a23ea6ae0bab9d9be0a51670001 Mon Sep 17 00:00:00 2001 From: Fawaz Tirmizi Date: Tue, 19 Jul 2022 14:55:57 -0700 Subject: [PATCH 30/41] Cleaned up documentation Original documentation was largely an exact copy of cortex-m-semihosting at the fork. This commit fixes that, removing unnecessary references to cortex-m-semihosting and clearing out the changelog so that it's relevant to this repository instead. --- CHANGELOG.md | 168 ++++++--------------------------------------------- README.md | 26 +++----- src/lib.rs | 58 ++++++++++-------- src/nr.rs | 6 +- 4 files changed, 66 insertions(+), 192 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a942cfc..a1dde5f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,155 +5,25 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -## [v0.4.1] - 2020-10-20 - -0.4.1 was yanked because the pre-built binaries contain conflicting symbols -with a supported version of cortex-m. - -- Fix missing prebuilt binaries (#271) - -## [v0.4.0] - 2020-10-14 - -v0.4.0 was yanked because it did not include the required pre-built binaries -in the final crate. - -- Moved into cortex-m repository -- Merge `HStdout` and `HStderr` into one type: `HostStream` -- Support cortex-m v0.7 -- Semihosting macros no longer return a Result, instead errors are ignored. - -## [v0.3.7] - 2020-12-02 - -- Replaces the yanked v0.3.6 by reverting #48, so the semihosting macros - continue to return a Result. - -## [v0.3.6] - 2020-12-01 - -v0.3.6 was yanked because it incorrectly included #48, which was a breaking -change. - -### Added - -- Update cortex-m dependency to support version 0.7. -- Add `no-semihosting` feature to disable all semihosting calls. - -## [v0.3.5] - 2019-08-29 - -### Added - -- Adds a feature to work around JLink quirks -- Adds a dbg! macro using heprintln -- Added thumbv8m.main support on stable - -### Fixed - -- Now Rust 2018 edition - -## [v0.3.4] - 2019-08-13 - -### Fixed - -- Support for thumbv8 mainline hf target - -## [v0.3.3] - 2019-04-22 - -### Added - -- Adds support for thumbv8 and cortex-m v0.6.0 - -## [v0.3.2] - 2018-11-04 - -### Added - -- Added a family of `hprint` macros for printing to the host standard output / - error via globally shared `HStdout` / `HStderr` handles . - -## [v0.3.1] - 2018-08-27 - -### Changed - -- This crate no longer depends on `arm-none-eabi-gcc`. - -## [v0.3.0] - 2018-05-10 - -### Changed - -- [breaking-change] `inline-asm` is no longer a default feature (i.e. a feature that's enabled by - default). The consequence is that this crate now compiles on 1.27 (beta) by default, and opting - into `inline-asm` requires nightly. - -## [v0.2.1] - 2018-04-25 - -### Added - -- An opt-out "inline-asm" Cargo feature. When this feature is disabled semihosting is implemented - using an external assembly file instead of using the unstable inline assembly (`asm!`) feature - meaning that this crate can be compiled on stable. - -## [v0.2.0] - 2017-07-07 - -### Added - -- `exit` and `report_exception` syscalls - -- `HStdout` and `HStderr` structs that represent handles to the host stdout and - stderr stream respectively. - -### Changed - -- [breaking-change] The `io` module has been renamed to `hio` to reflect that - this is I/O *on the host*. - -### Removed - -- [breaking-change] the family of `write` functions in the `io` module. Instead - use `HStdout` / `HStderr` and its `write_all` method and `fmt::Write` - implementation. - -- [breaking-change] the `hprint!` family of macros. Instead use `HStdout` and - the standard `write!` macro. - -## [v0.1.3] - 2017-02-27 - -### Added - -- A family of `ewrite` functions and `ehprint!` macros to write to the host's - stderr. - -### Fixed - -- `write_all` logic when a single write doesn't write all the buffer bytes - -## [v0.1.2] - 2017-02-15 - -### Fixed - -- the `hprintln!` macro when called without arguments. - -## [v0.1.1] - 2017-01-22 - -### Added - -- Expose a family of `write` functions to write to the host's stdout without - going through the `hprint!` macros. - -## v0.1.0 - 2017-01-22 +- Bring in API changes from + [cortex-m-semihosting](https://github.com/rust-embedded/cortex-m/tree/master/cortex-m-semihosting), + including: + - Addition of the `hprint`, `hprintln`, `heprint`, `heprintln`, and `dbg` + macros. + - `hprint` and `heprintln` print to host stdout without and with a + newline, respectively. + - `heprint` and `heprintln` do the same, except to host stderr. + - `dbg` works exactly like + [`std::dbg`](https://doc.rust-lang.org/std/macro.dbg.html). + - `HStdout` and `HStderr` have been combined into `HostStream`. + - `inline-asm` feature removed, switched to stabilized inline asm and MSRV + bumped to 1.59.0 +- Clean up documentation, removing unnecessary references to + cortex-m-semihosting and improving clarity. + +## [v0.0.1] - 2018-02-27 - Initial release -[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.4.1...HEAD -[v0.4.1]: https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.4.0...c-m-sh-v0.4.1 -[v0.4.0]: https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.3.5...c-m-sh-v0.4.0 -[v0.3.7]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.6...v0.3.7 -[v0.3.6]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.5...v0.3.6 -[v0.3.5]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.4...v0.3.5 -[v0.3.4]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.3...v0.3.4 -[v0.3.3]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.2...v0.3.3 -[v0.3.2]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.1...v0.3.2 -[v0.3.1]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.0...v0.3.1 -[v0.3.0]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.2.1...v0.3.0 -[v0.2.1]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.2.0...v0.2.1 -[v0.2.0]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.3...v0.2.0 -[v0.1.3]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.2...v0.1.3 -[v0.1.2]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.1...v0.1.2 -[v0.1.1]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.0...v0.1.1 +[Unreleased]: https://github.com/riscv-rust/riscv-semihosting/compare/cb1afe4002d576b87bfd4c199f42a43815984ce4..HEAD +[v0.0.1]: https://github.com/riscv-rust/riscv-semihosting/tree/cb1afe4002d576b87bfd4c199f42a43815984ce4 diff --git a/README.md b/README.md index c67739fe..886c2e1e 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,17 @@ -[![crates.io](https://img.shields.io/crates/v/cortex-m-semihosting.svg)](https://crates.io/crates/cortex-m-semihosting) -[![crates.io](https://img.shields.io/crates/d/cortex-m-semihosting.svg)](https://crates.io/crates/cortex-m-semihosting) - # `riscv-semihosting` > Semihosting for RISC-V processors This is a fork of the -[cortex-m-semihosting](https://docs.rs/cortex-m-semihosting) crate with minimal -changes to support the RISC-V Semihosting Specification as documented +[cortex-m-semihosting](https://docs.rs/cortex-m-semihosting) crate with changes +to support the RISC-V Semihosting Specification as documented [here](https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc) This crate can be used in exactly the same way as cortex-m-semihosting, simply -by changing calls to `cortex_m_semihosting::*` to `riscv_semihosting::*`. - -The rest of this document is as-is from upstream, and obviously any -ARM-specific sections should be ignored. - -This project is developed and maintained by the [Cortex-M team][team]. - -## [Documentation](https://docs.rs/cortex-m-semihosting) +by changing calls to `cortex_m_semihosting::*` to `riscv_semihosting::*`. Given +this, the +[cortex-m-semihosting documentation](https://docs.rs/cortex-m-semihosting) is +generally sufficient for using this library. # Minimum Supported Rust Version (MSRV) @@ -31,7 +24,8 @@ Licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) -- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) +- MIT license ([LICENSE-MIT](../LICENSE-MIT) or + http://opensource.org/licenses/MIT) at your option. @@ -44,8 +38,8 @@ dual licensed as above, without any additional terms or conditions. ## Code of Conduct Contribution to this crate is organized under the terms of the [Rust Code of -Conduct][CoC], the maintainer of this crate, the [Cortex-M team][team], promises +Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises to intervene to uphold that code of conduct. [CoC]: ../CODE_OF_CONDUCT.md -[team]: https://github.com/rust-embedded/wg#the-cortex-m-team +[team]: https://github.com/rust-embedded/wg#the-risc-v-team diff --git a/src/lib.rs b/src/lib.rs index 326b7562..59dad79e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,26 +2,32 @@ //! //! # 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 +//! "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. +//! [`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. +//! 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. +//! Semihosting operations are *very* slow. Like, each WRITE operation can take +//! hundreds of milliseconds. //! //! # Example //! @@ -56,8 +62,8 @@ //! # 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` +//! 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 @@ -69,8 +75,9 @@ //! 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. +//! 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: //! @@ -119,9 +126,9 @@ //! //! ## 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. +//! 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. //! @@ -153,10 +160,10 @@ //! //! ## `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. +//! 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` //! @@ -164,12 +171,11 @@ //! //! # 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 +//! 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. #![deny(missing_docs)] #![no_std] diff --git a/src/nr.rs b/src/nr.rs index 5d206dec..45c3b808 100644 --- a/src/nr.rs +++ b/src/nr.rs @@ -1,6 +1,10 @@ //! Semihosting operations +//! +//! The details of what each operation does can be found in the +//! [ARM Semihosting Specification](https://github.com/ARM-software/abi-aa/blob/main/semihosting/semihosting.rst#semihosting-operations). +//! The RISC-V Semihosting operations are identiacal to ARM's, so their +//! documentation is sufficient. -// TODO document #![allow(missing_docs)] pub const CLOCK: usize = 0x10; From f4c48301dd3c8013e03d8e4748613e6fe17a7d93 Mon Sep 17 00:00:00 2001 From: Fawaz Tirmizi Date: Tue, 19 Jul 2022 15:03:07 -0700 Subject: [PATCH 31/41] Remove cortex-m-semihosting CI The current repository CI was copied from cortex-m-semihosting, so it is not relevant to this one. In the future, it may be worthwhile to add some proper CI for this repo. --- ci/install.sh | 12 ------------ ci/script.sh | 14 -------------- 2 files changed, 26 deletions(-) delete mode 100644 ci/install.sh delete mode 100644 ci/script.sh diff --git a/ci/install.sh b/ci/install.sh deleted file mode 100644 index 43c97604..00000000 --- a/ci/install.sh +++ /dev/null @@ -1,12 +0,0 @@ -set -euxo pipefail - -main() { - if [ $TARGET = thumbv7m-none-eabi ]; then - cargo install --list | grep xargo || \ - cargo install xargo - rustup component list | grep 'rust-src.*installed' || \ - rustup component add rust-src - fi -} - -main diff --git a/ci/script.sh b/ci/script.sh deleted file mode 100644 index c9acaaf7..00000000 --- a/ci/script.sh +++ /dev/null @@ -1,14 +0,0 @@ -set -euxo pipefail - -main() { - local cargo= - if [ $TARGET = thumbv7m-none-eabi ]; then - cargo=xargo - else - cargo=cargo - fi - - $cargo check --target $TARGET -} - -main From b7f5ee4d8778c70e63b3ee7a1f5469886f5d24cc Mon Sep 17 00:00:00 2001 From: Fawaz Tirmizi Date: Tue, 19 Jul 2022 15:05:40 -0700 Subject: [PATCH 32/41] Updated Cargo.toml The old Cargo.toml was a bit out-of-date. This commit adds the RISC-V Team to the authors list, as well as bumping the version and edition to 1.59.0 and 2021. --- Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 91872449..e35b0fe0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ authors = [ "The Cortex-M Team ", "Jorge Aparicio ", + "The RISC-V Team ", ] description = "Semihosting for RISCV processors" documentation = "https://docs.rs/riscv-semihosting" @@ -12,7 +13,8 @@ name = "riscv-semihosting" readme = "README.md" repository = "https://github.com/riscv-rust/riscv-semihosting" version = "0.0.1" -edition = "2018" +edition = "2021" +rust-version = "1.59.0" [features] default = ["jlink-quirks"] From 6002ee50089544448822fc4e297f13ded2859f0b Mon Sep 17 00:00:00 2001 From: Fawaz Tirmizi Date: Wed, 20 Jul 2022 12:54:29 -0700 Subject: [PATCH 33/41] Revert "Remove cortex-m-semihosting CI" This reverts commit f4c48301dd3c8013e03d8e4748613e6fe17a7d93. It has been decided that the CI will be left in for now, and updated to work with this crate in the future. --- ci/install.sh | 12 ++++++++++++ ci/script.sh | 14 ++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 ci/install.sh create mode 100644 ci/script.sh diff --git a/ci/install.sh b/ci/install.sh new file mode 100644 index 00000000..43c97604 --- /dev/null +++ b/ci/install.sh @@ -0,0 +1,12 @@ +set -euxo pipefail + +main() { + if [ $TARGET = thumbv7m-none-eabi ]; then + cargo install --list | grep xargo || \ + cargo install xargo + rustup component list | grep 'rust-src.*installed' || \ + rustup component add rust-src + fi +} + +main diff --git a/ci/script.sh b/ci/script.sh new file mode 100644 index 00000000..c9acaaf7 --- /dev/null +++ b/ci/script.sh @@ -0,0 +1,14 @@ +set -euxo pipefail + +main() { + local cargo= + if [ $TARGET = thumbv7m-none-eabi ]; then + cargo=xargo + else + cargo=cargo + fi + + $cargo check --target $TARGET +} + +main From 45ac4e862f6985c0d41ac28ae6d901b4413f1c50 Mon Sep 17 00:00:00 2001 From: Fawaz Date: Fri, 21 Oct 2022 12:33:43 -0700 Subject: [PATCH 34/41] Replaced old CI with one using GitHub Actions (#4) The old CI was meant for cortex-m-semihosting, so it would not have worked with this repository, and it also used Travis CI, whereas the new one uses GitHub Actions. The CI runs `cargo check` for the following targets: - `riscv32i-unknown-none-elf` - `riscv32imc-unknown-none-elf` - `riscv32imac-unknown-none-elf` - `riscv64imac-unknown-none-elf` - `riscv64gc-unknown-none-elf` It also checks formatting and doc. --- .github/workflows/check.yml | 30 ++++++++++++++++++++++++++++++ .github/workflows/doc.yml | 17 +++++++++++++++++ .github/workflows/fmt.yml | 18 ++++++++++++++++++ .travis.yml | 32 -------------------------------- CHANGELOG.md | 1 + ci/install.sh | 12 ------------ ci/script.sh | 14 -------------- 7 files changed, 66 insertions(+), 58 deletions(-) create mode 100644 .github/workflows/check.yml create mode 100644 .github/workflows/doc.yml create mode 100644 .github/workflows/fmt.yml delete mode 100644 .travis.yml delete mode 100644 ci/install.sh delete mode 100644 ci/script.sh diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 00000000..7deeb66c --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,30 @@ +name: check +on: [push, pull_request] + +jobs: + check: + runs-on: ubuntu-latest + strategy: + matrix: + # All generated code should be running on stable now, MRSV is 1.59.0 + toolchain: [nightly, stable, 1.59.0] + target: [riscv32i-unknown-none-elf, riscv32imc-unknown-none-elf, riscv32imac-unknown-none-elf, riscv64imac-unknown-none-elf, riscv64gc-unknown-none-elf] + + include: + # Nightly is only for reference and allowed to fail + - rust: nightly + experimental: true + + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.toolchain }} + target: ${{ matrix.target }} + override: true + - uses: actions-rs/cargo@v1 + with: + use-cross: true + command: check + args: --verbose --target ${{ matrix.target }} diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml new file mode 100644 index 00000000..454ff23b --- /dev/null +++ b/.github/workflows/doc.yml @@ -0,0 +1,17 @@ +name: doc +on: [push, pull_request] + + +jobs: + doc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: doc diff --git a/.github/workflows/fmt.yml b/.github/workflows/fmt.yml new file mode 100644 index 00000000..00dc59cd --- /dev/null +++ b/.github/workflows/fmt.yml @@ -0,0 +1,18 @@ +name: fmt +on: [push, pull_request] + +jobs: + fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + components: rustfmt + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7b8f8d9f..00000000 --- a/.travis.yml +++ /dev/null @@ -1,32 +0,0 @@ -language: rust - -matrix: - include: - - env: TARGET=thumbv7m-none-eabi - rust: nightly - - env: TARGET=x86_64-unknown-linux-gnu - rust: nightly - -before_install: set -e - -install: - - bash ci/install.sh - -script: - - bash ci/script.sh - -after_script: set +e - -cache: cargo -before_cache: - # Travis can't cache files that are not readable by "others" - - chmod -R a+r $HOME/.cargo - -branches: - only: - - auto - - try - -notifications: - email: - on_success: never diff --git a/CHANGELOG.md b/CHANGELOG.md index a1dde5f5..b74c0e99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). bumped to 1.59.0 - Clean up documentation, removing unnecessary references to cortex-m-semihosting and improving clarity. +- Added GitHub Actions CI ## [v0.0.1] - 2018-02-27 diff --git a/ci/install.sh b/ci/install.sh deleted file mode 100644 index 43c97604..00000000 --- a/ci/install.sh +++ /dev/null @@ -1,12 +0,0 @@ -set -euxo pipefail - -main() { - if [ $TARGET = thumbv7m-none-eabi ]; then - cargo install --list | grep xargo || \ - cargo install xargo - rustup component list | grep 'rust-src.*installed' || \ - rustup component add rust-src - fi -} - -main diff --git a/ci/script.sh b/ci/script.sh deleted file mode 100644 index c9acaaf7..00000000 --- a/ci/script.sh +++ /dev/null @@ -1,14 +0,0 @@ -set -euxo pipefail - -main() { - local cargo= - if [ $TARGET = thumbv7m-none-eabi ]; then - cargo=xargo - else - cargo=cargo - fi - - $cargo check --target $TARGET -} - -main From 6e355849a5d4b73506ee17f6c178358fc1e26853 Mon Sep 17 00:00:00 2001 From: Fawaz Date: Tue, 1 Nov 2022 11:23:12 -0700 Subject: [PATCH 35/41] Added features to select privilege level (#5) Currently, the library attempts to execute some semihosting operations in a completely interrupt-free context. It does this by using `riscv::interrupt::free`, which saves and restores the `mie` field of `mstatus`. As a result, attempts to initiate semihosting calls outside of M mode cause illegal instruction exceptions. This commit provides a solution, by requiring users to choose one of two features, "machine-mode" or "user-mode", which will compile different versions of the functions in src/export.rs that do and do not suspend interrupts, respectively. Failure to do so will throw a compiler error, unless the "no-semihosting" feature was enabled. A "supervisor-mode" feature was left out as the `riscv` crate does not yet have an equivalent of `interrupt::free` for supervisor mode. CI now also checks builds with both of these features. --- .github/workflows/check.yml | 3 +- .github/workflows/doc.yml | 4 ++ CHANGELOG.md | 2 + Cargo.toml | 3 + README.md | 14 ++++- src/export.rs | 110 +++++++++++++++++++++++++++--------- 6 files changed, 104 insertions(+), 32 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 7deeb66c..83e0b0fd 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -9,6 +9,7 @@ jobs: # All generated code should be running on stable now, MRSV is 1.59.0 toolchain: [nightly, stable, 1.59.0] target: [riscv32i-unknown-none-elf, riscv32imc-unknown-none-elf, riscv32imac-unknown-none-elf, riscv64imac-unknown-none-elf, riscv64gc-unknown-none-elf] + privilege: [machine, user] include: # Nightly is only for reference and allowed to fail @@ -27,4 +28,4 @@ jobs: with: use-cross: true command: check - args: --verbose --target ${{ matrix.target }} + args: --verbose --target ${{ matrix.target }} --features=${{matrix.privilege}}-mode diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 454ff23b..4866a136 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -5,6 +5,9 @@ on: [push, pull_request] jobs: doc: runs-on: ubuntu-latest + strategy: + matrix: + privilege: [machine, user] steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 @@ -15,3 +18,4 @@ jobs: - uses: actions-rs/cargo@v1 with: command: doc + args: --features=${{matrix.privilege}}-mode diff --git a/CHANGELOG.md b/CHANGELOG.md index b74c0e99..68d4319a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Clean up documentation, removing unnecessary references to cortex-m-semihosting and improving clarity. - Added GitHub Actions CI +- Add features to select the privilege level the semihosting operations will be + started from ## [v0.0.1] - 2018-02-27 diff --git a/Cargo.toml b/Cargo.toml index e35b0fe0..979c4847 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,11 @@ rust-version = "1.59.0" [features] default = ["jlink-quirks"] +machine-mode = [] +user-mode = [] jlink-quirks = [] no-semihosting = [] [dependencies] riscv = "0.8.0" +cfg-if = "1.0.0" diff --git a/README.md b/README.md index 886c2e1e..797c591c 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,20 @@ This is a fork of the to support the RISC-V Semihosting Specification as documented [here](https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc) -This crate can be used in exactly the same way as cortex-m-semihosting, simply -by changing calls to `cortex_m_semihosting::*` to `riscv_semihosting::*`. Given -this, the +This crate can (almost) be used in exactly the same way as cortex-m-semihosting, +simply by changing calls to `cortex_m_semihosting::*` to `riscv_semihosting::*`. +Given this, the [cortex-m-semihosting documentation](https://docs.rs/cortex-m-semihosting) is generally sufficient for using this library. +A major difference between this library and cortex-m-semihosting is that there +are mandatory features to choose the privilege level at which the semihosting +calls are executed. The "machine-mode" feature will cause the macros in `export` +to execute the semihosting operation in an interrupt-free context, while +"user-mode" causes them to just execute the operation. Failure to select one of +these two features will cause a compiler error. + + # Minimum Supported Rust Version (MSRV) This crate is guaranteed to compile on stable Rust 1.59.0 and up. It **won't** diff --git a/src/export.rs b/src/export.rs index 01a7cc0a..b916367c 100644 --- a/src/export.rs +++ b/src/export.rs @@ -2,50 +2,104 @@ use core::fmt::{self, Write}; +#[cfg(feature = "machine-mode")] use riscv::interrupt; use crate::hio::{self, HostStream}; static mut HSTDOUT: Option = None; -pub fn hstdout_str(s: &str) { - let _result = interrupt::free(|_| unsafe { - if HSTDOUT.is_none() { - HSTDOUT = Some(hio::hstdout()?); +#[cfg(not(feature = "no-semihosting"))] +cfg_if::cfg_if! { + if #[cfg(feature="machine-mode")] { + pub fn hstdout_str(s: &str) { + let _result = interrupt::free(|_| unsafe { + if HSTDOUT.is_none() { + HSTDOUT = Some(hio::hstdout()?); + } + + HSTDOUT.as_mut().unwrap().write_str(s).map_err(drop) + }); } - HSTDOUT.as_mut().unwrap().write_str(s).map_err(drop) - }); -} + pub fn hstdout_fmt(args: fmt::Arguments) { + let _result = interrupt::free(|_| unsafe { + if HSTDOUT.is_none() { + HSTDOUT = Some(hio::hstdout()?); + } -pub fn hstdout_fmt(args: fmt::Arguments) { - let _result = interrupt::free(|_| unsafe { - if HSTDOUT.is_none() { - HSTDOUT = Some(hio::hstdout()?); + HSTDOUT.as_mut().unwrap().write_fmt(args).map_err(drop) + }); } - HSTDOUT.as_mut().unwrap().write_fmt(args).map_err(drop) - }); -} + static mut HSTDERR: Option = None; -static mut HSTDERR: Option = None; + pub fn hstderr_str(s: &str) { + let _result = interrupt::free(|_| unsafe { + if HSTDERR.is_none() { + HSTDERR = Some(hio::hstderr()?); + } -pub fn hstderr_str(s: &str) { - let _result = interrupt::free(|_| unsafe { - if HSTDERR.is_none() { - HSTDERR = Some(hio::hstderr()?); + HSTDERR.as_mut().unwrap().write_str(s).map_err(drop) + }); } - HSTDERR.as_mut().unwrap().write_str(s).map_err(drop) - }); -} + pub fn hstderr_fmt(args: fmt::Arguments) { + let _result = interrupt::free(|_| unsafe { + if HSTDERR.is_none() { + HSTDERR = Some(hio::hstderr()?); + } -pub fn hstderr_fmt(args: fmt::Arguments) { - let _result = interrupt::free(|_| unsafe { - if HSTDERR.is_none() { - HSTDERR = Some(hio::hstderr()?); + HSTDERR.as_mut().unwrap().write_fmt(args).map_err(drop) + }); } + } + else if #[cfg(feature = "user-mode")] { + pub fn hstdout_str(s: &str) { + let _result = unsafe { + if HSTDOUT.is_none() { + HSTDOUT = Some(hio::hstdout().unwrap()); + } - HSTDERR.as_mut().unwrap().write_fmt(args).map_err(drop) - }); + HSTDOUT.as_mut().unwrap().write_str(s).map_err(drop) + }; + } + + pub fn hstdout_fmt(args: fmt::Arguments) { + let _result = unsafe { + if HSTDOUT.is_none() { + HSTDOUT = Some(hio::hstdout().unwrap()); + } + + HSTDOUT.as_mut().unwrap().write_fmt(args).map_err(drop) + }; + } + + static mut HSTDERR: Option = None; + + pub fn hstderr_str(s: &str) { + let _result = unsafe { + if HSTDERR.is_none() { + HSTDERR = Some(hio::hstderr().unwrap()); + } + + HSTDERR.as_mut().unwrap().write_str(s).map_err(drop) + }; + } + + pub fn hstderr_fmt(args: fmt::Arguments) { + let _result = unsafe { + if HSTDERR.is_none() { + HSTDERR = Some(hio::hstderr().unwrap()); + } + + HSTDERR.as_mut().unwrap().write_fmt(args).map_err(drop) + }; + } + } + else { + compile_error!("A privilege level has not been selected. Enable either \ + the machine-mode or user-mode features as appropriate \ + for your use case."); + } } From a1c88e59069484bcb78d49ffa5d39fd81d765542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Thu, 28 Dec 2023 16:38:08 +0100 Subject: [PATCH 36/41] move to riscv-semihosting --- {.github => riscv-semihosting/.github}/workflows/check.yml | 0 {.github => riscv-semihosting/.github}/workflows/doc.yml | 0 {.github => riscv-semihosting/.github}/workflows/fmt.yml | 0 .gitignore => riscv-semihosting/.gitignore | 0 CHANGELOG.md => riscv-semihosting/CHANGELOG.md | 0 Cargo.toml => riscv-semihosting/Cargo.toml | 0 LICENSE-APACHE => riscv-semihosting/LICENSE-APACHE | 0 LICENSE-MIT => riscv-semihosting/LICENSE-MIT | 0 README.md => riscv-semihosting/README.md | 0 {src => riscv-semihosting/src}/debug.rs | 0 {src => riscv-semihosting/src}/export.rs | 0 {src => riscv-semihosting/src}/hio.rs | 0 {src => riscv-semihosting/src}/lib.rs | 0 {src => riscv-semihosting/src}/macros.rs | 0 {src => riscv-semihosting/src}/nr.rs | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename {.github => riscv-semihosting/.github}/workflows/check.yml (100%) rename {.github => riscv-semihosting/.github}/workflows/doc.yml (100%) rename {.github => riscv-semihosting/.github}/workflows/fmt.yml (100%) rename .gitignore => riscv-semihosting/.gitignore (100%) rename CHANGELOG.md => riscv-semihosting/CHANGELOG.md (100%) rename Cargo.toml => riscv-semihosting/Cargo.toml (100%) rename LICENSE-APACHE => riscv-semihosting/LICENSE-APACHE (100%) rename LICENSE-MIT => riscv-semihosting/LICENSE-MIT (100%) rename README.md => riscv-semihosting/README.md (100%) rename {src => riscv-semihosting/src}/debug.rs (100%) rename {src => riscv-semihosting/src}/export.rs (100%) rename {src => riscv-semihosting/src}/hio.rs (100%) rename {src => riscv-semihosting/src}/lib.rs (100%) rename {src => riscv-semihosting/src}/macros.rs (100%) rename {src => riscv-semihosting/src}/nr.rs (100%) diff --git a/.github/workflows/check.yml b/riscv-semihosting/.github/workflows/check.yml similarity index 100% rename from .github/workflows/check.yml rename to riscv-semihosting/.github/workflows/check.yml diff --git a/.github/workflows/doc.yml b/riscv-semihosting/.github/workflows/doc.yml similarity index 100% rename from .github/workflows/doc.yml rename to riscv-semihosting/.github/workflows/doc.yml diff --git a/.github/workflows/fmt.yml b/riscv-semihosting/.github/workflows/fmt.yml similarity index 100% rename from .github/workflows/fmt.yml rename to riscv-semihosting/.github/workflows/fmt.yml diff --git a/.gitignore b/riscv-semihosting/.gitignore similarity index 100% rename from .gitignore rename to riscv-semihosting/.gitignore diff --git a/CHANGELOG.md b/riscv-semihosting/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to riscv-semihosting/CHANGELOG.md diff --git a/Cargo.toml b/riscv-semihosting/Cargo.toml similarity index 100% rename from Cargo.toml rename to riscv-semihosting/Cargo.toml diff --git a/LICENSE-APACHE b/riscv-semihosting/LICENSE-APACHE similarity index 100% rename from LICENSE-APACHE rename to riscv-semihosting/LICENSE-APACHE diff --git a/LICENSE-MIT b/riscv-semihosting/LICENSE-MIT similarity index 100% rename from LICENSE-MIT rename to riscv-semihosting/LICENSE-MIT diff --git a/README.md b/riscv-semihosting/README.md similarity index 100% rename from README.md rename to riscv-semihosting/README.md diff --git a/src/debug.rs b/riscv-semihosting/src/debug.rs similarity index 100% rename from src/debug.rs rename to riscv-semihosting/src/debug.rs diff --git a/src/export.rs b/riscv-semihosting/src/export.rs similarity index 100% rename from src/export.rs rename to riscv-semihosting/src/export.rs diff --git a/src/hio.rs b/riscv-semihosting/src/hio.rs similarity index 100% rename from src/hio.rs rename to riscv-semihosting/src/hio.rs diff --git a/src/lib.rs b/riscv-semihosting/src/lib.rs similarity index 100% rename from src/lib.rs rename to riscv-semihosting/src/lib.rs diff --git a/src/macros.rs b/riscv-semihosting/src/macros.rs similarity index 100% rename from src/macros.rs rename to riscv-semihosting/src/macros.rs diff --git a/src/nr.rs b/riscv-semihosting/src/nr.rs similarity index 100% rename from src/nr.rs rename to riscv-semihosting/src/nr.rs From e70afe47bda763dcc699f2667ccf24b250924484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Thu, 28 Dec 2023 17:22:02 +0100 Subject: [PATCH 37/41] adjust the workspace --- .github/workflows/changelog.yaml | 22 +- .github/workflows/riscv-semihosting.yaml | 46 ++++ Cargo.toml | 1 + README.md | 4 +- riscv-semihosting/.github/workflows/check.yml | 31 --- riscv-semihosting/.github/workflows/doc.yml | 21 -- riscv-semihosting/.github/workflows/fmt.yml | 18 -- riscv-semihosting/.gitignore | 2 - riscv-semihosting/CHANGELOG.md | 1 + riscv-semihosting/Cargo.toml | 4 +- riscv-semihosting/LICENSE-APACHE | 201 ------------------ riscv-semihosting/LICENSE-MIT | 25 --- riscv-semihosting/README.md | 26 ++- 13 files changed, 86 insertions(+), 316 deletions(-) create mode 100644 .github/workflows/riscv-semihosting.yaml delete mode 100644 riscv-semihosting/.github/workflows/check.yml delete mode 100644 riscv-semihosting/.github/workflows/doc.yml delete mode 100644 riscv-semihosting/.github/workflows/fmt.yml delete mode 100644 riscv-semihosting/.gitignore delete mode 100644 riscv-semihosting/LICENSE-APACHE delete mode 100644 riscv-semihosting/LICENSE-MIT diff --git a/.github/workflows/changelog.yaml b/.github/workflows/changelog.yaml index 86541174..25b888f0 100644 --- a/.github/workflows/changelog.yaml +++ b/.github/workflows/changelog.yaml @@ -19,10 +19,12 @@ jobs: filters: | riscv: - 'riscv/**' - riscv-rt: - - 'riscv-rt/**' riscv-pac: - 'riscv-pac/**' + riscv-rt: + - 'riscv-rt/**' + riscv-semihosting: + - 'riscv-semihosting/**' - name: Check for CHANGELOG.md (riscv) if: steps.changes.outputs.riscv == 'true' @@ -32,6 +34,14 @@ jobs: skipLabels: 'skip changelog' missingUpdateErrorMessage: 'Please add a changelog entry in the riscv/CHANGELOG.md file.' + - name: Check for CHANGELOG.md (riscv-pac) + if: steps.changes.outputs.riscv-pac == 'true' + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: ./riscv-pac/CHANGELOG.md + skipLabels: 'skip changelog' + missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-pac/CHANGELOG.md file.' + - name: Check for CHANGELOG.md (riscv-rt) if: steps.changes.outputs.riscv-rt == 'true' uses: dangoslen/changelog-enforcer@v3 @@ -40,10 +50,10 @@ jobs: skipLabels: 'skip changelog' missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-rt/CHANGELOG.md file.' - - name: Check for CHANGELOG.md (riscv-pac) - if: steps.changes.outputs.riscv-pac == 'true' + - name: Check for CHANGELOG.md (riscv-semihosting) + if: steps.changes.outputs.riscv-semihosting == 'true' uses: dangoslen/changelog-enforcer@v3 with: - changeLogPath: ./riscv-pac/CHANGELOG.md + changeLogPath: ./riscv-semihosting/CHANGELOG.md skipLabels: 'skip changelog' - missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-pac/CHANGELOG.md file.' + missingUpdateErrorMessage: 'Please add a changelog entry in the riscv-semihosting/CHANGELOG.md file.' diff --git a/.github/workflows/riscv-semihosting.yaml b/.github/workflows/riscv-semihosting.yaml new file mode 100644 index 00000000..ecbc32bd --- /dev/null +++ b/.github/workflows/riscv-semihosting.yaml @@ -0,0 +1,46 @@ +on: + push: + branches: [ master, semihosting ] + pull_request: + merge_group: + +name: Build check (riscv-semihosting) + +jobs: + # We check that the crate builds and links for all the toolchains and targets. + build-riscv: + strategy: + matrix: + # All generated code should be running on stable now, MRSV is 1.60.0 + toolchain: [ stable, nightly, 1.60.0 ] + target: + - riscv32i-unknown-none-elf + - riscv32imc-unknown-none-elf + - riscv32imac-unknown-none-elf + - riscv64imac-unknown-none-elf + - riscv64gc-unknown-none-elf + include: + # Nightly is only for reference and allowed to fail + - toolchain: nightly + experimental: true + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || false }} + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.toolchain }} + targets: ${{ matrix.target }} + - name: Build (M-mode) + run: cargo build --package riscv-semihosting --target ${{ matrix.target }} --features machine-mode + - name: Build (U-mode) + run: cargo build --package riscv-semihosting --target ${{ matrix.target }} --features=user-mode + + # Job to check that all the builds succeeded + build-check: + needs: + - build-riscv + runs-on: ubuntu-latest + if: always() + steps: + - run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/Cargo.toml b/Cargo.toml index b97d6de3..fe8fdd1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,5 @@ members = [ "riscv", "riscv-pac", "riscv-rt", + "riscv-semihosting", ] diff --git a/README.md b/README.md index d694f26c..f35a5e50 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ This repository contains various crates useful for writing Rust programs on RISC * [`riscv`]: CPU registers access and intrinsics * [`riscv-pac`]: Common traits to be implemented by RISC-V PACs * [`riscv-rt`]: Startup code and interrupt handling - +* [`riscv-semihosting`]: Semihosting for RISC-V processors This project is developed and maintained by the [RISC-V team][team]. @@ -22,6 +22,8 @@ Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises to intervene to uphold that code of conduct. [`riscv`]: https://crates.io/crates/riscv +[`riscv-pac`]: https://crates.io/crates/riscv-pac [`riscv-rt`]: https://crates.io/crates/riscv-rt +[`riscv-semihosting`]: https://crates.io/crates/riscv-semihosting [team]: https://github.com/rust-embedded/wg#the-risc-v-team [CoC]: CODE_OF_CONDUCT.md diff --git a/riscv-semihosting/.github/workflows/check.yml b/riscv-semihosting/.github/workflows/check.yml deleted file mode 100644 index 83e0b0fd..00000000 --- a/riscv-semihosting/.github/workflows/check.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: check -on: [push, pull_request] - -jobs: - check: - runs-on: ubuntu-latest - strategy: - matrix: - # All generated code should be running on stable now, MRSV is 1.59.0 - toolchain: [nightly, stable, 1.59.0] - target: [riscv32i-unknown-none-elf, riscv32imc-unknown-none-elf, riscv32imac-unknown-none-elf, riscv64imac-unknown-none-elf, riscv64gc-unknown-none-elf] - privilege: [machine, user] - - include: - # Nightly is only for reference and allowed to fail - - rust: nightly - experimental: true - - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{ matrix.toolchain }} - target: ${{ matrix.target }} - override: true - - uses: actions-rs/cargo@v1 - with: - use-cross: true - command: check - args: --verbose --target ${{ matrix.target }} --features=${{matrix.privilege}}-mode diff --git a/riscv-semihosting/.github/workflows/doc.yml b/riscv-semihosting/.github/workflows/doc.yml deleted file mode 100644 index 4866a136..00000000 --- a/riscv-semihosting/.github/workflows/doc.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: doc -on: [push, pull_request] - - -jobs: - doc: - runs-on: ubuntu-latest - strategy: - matrix: - privilege: [machine, user] - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: doc - args: --features=${{matrix.privilege}}-mode diff --git a/riscv-semihosting/.github/workflows/fmt.yml b/riscv-semihosting/.github/workflows/fmt.yml deleted file mode 100644 index 00dc59cd..00000000 --- a/riscv-semihosting/.github/workflows/fmt.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: fmt -on: [push, pull_request] - -jobs: - fmt: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - components: rustfmt - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check diff --git a/riscv-semihosting/.gitignore b/riscv-semihosting/.gitignore deleted file mode 100644 index a9d37c56..00000000 --- a/riscv-semihosting/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target -Cargo.lock diff --git a/riscv-semihosting/CHANGELOG.md b/riscv-semihosting/CHANGELOG.md index 68d4319a..df76aa4f 100644 --- a/riscv-semihosting/CHANGELOG.md +++ b/riscv-semihosting/CHANGELOG.md @@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Moved to the `riscv` Cargo workspace - Bring in API changes from [cortex-m-semihosting](https://github.com/rust-embedded/cortex-m/tree/master/cortex-m-semihosting), including: diff --git a/riscv-semihosting/Cargo.toml b/riscv-semihosting/Cargo.toml index 979c4847..29621af4 100644 --- a/riscv-semihosting/Cargo.toml +++ b/riscv-semihosting/Cargo.toml @@ -11,10 +11,10 @@ categories = ["no-std", "embedded"] license = "MIT OR Apache-2.0" name = "riscv-semihosting" readme = "README.md" -repository = "https://github.com/riscv-rust/riscv-semihosting" +repository = "https://github.com/riscv-rust/riscv" version = "0.0.1" edition = "2021" -rust-version = "1.59.0" +rust-version = "1.60.0" [features] default = ["jlink-quirks"] diff --git a/riscv-semihosting/LICENSE-APACHE b/riscv-semihosting/LICENSE-APACHE deleted file mode 100644 index 16fe87b0..00000000 --- a/riscv-semihosting/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/riscv-semihosting/LICENSE-MIT b/riscv-semihosting/LICENSE-MIT deleted file mode 100644 index a128ba40..00000000 --- a/riscv-semihosting/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2017 Jorge Aparicio - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/riscv-semihosting/README.md b/riscv-semihosting/README.md index 797c591c..9a2a8a4f 100644 --- a/riscv-semihosting/README.md +++ b/riscv-semihosting/README.md @@ -1,3 +1,6 @@ +[![crates.io](https://img.shields.io/crates/d/riscv-semihosting.svg)](https://crates.io/crates/riscv-semihosting) +[![crates.io](https://img.shields.io/crates/v/riscv-semihosting.svg)](https://crates.io/crates/riscv-semihosting) + # `riscv-semihosting` > Semihosting for RISC-V processors @@ -10,10 +13,10 @@ to support the RISC-V Semihosting Specification as documented This crate can (almost) be used in exactly the same way as cortex-m-semihosting, simply by changing calls to `cortex_m_semihosting::*` to `riscv_semihosting::*`. Given this, the -[cortex-m-semihosting documentation](https://docs.rs/cortex-m-semihosting) is +[`cortex-m-semihosting documentation`](https://docs.rs/cortex-m-semihosting) is generally sufficient for using this library. -A major difference between this library and cortex-m-semihosting is that there +A major difference between this library and `cortex-m-semihosting` is that there are mandatory features to choose the privilege level at which the semihosting calls are executed. The "machine-mode" feature will cause the macros in `export` to execute the semihosting operation in an interrupt-free context, while @@ -23,19 +26,24 @@ these two features will cause a compiler error. # Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59.0 and up. It **won't** +This crate is guaranteed to compile on stable Rust 1.60.0 and up. It **won't** compile with older versions. ## License -Licensed under either of +Copyright 2018-2023 [RISC-V team][team] -- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) -- MIT license ([LICENSE-MIT](../LICENSE-MIT) or - http://opensource.org/licenses/MIT) +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. -at your option. +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. ### Contribution From 297aa6e1b431f9a2f1282cc01b02f1f7de60b8a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Fri, 29 Dec 2023 10:42:38 +0100 Subject: [PATCH 38/41] m-mode as default --- riscv-semihosting/Cargo.toml | 9 +- riscv-semihosting/build.rs | 9 ++ riscv-semihosting/src/debug.rs | 16 ++- riscv-semihosting/src/export.rs | 186 ++++++++++++++++---------------- riscv-semihosting/src/hio.rs | 3 + riscv-semihosting/src/lib.rs | 22 +++- 6 files changed, 132 insertions(+), 113 deletions(-) create mode 100644 riscv-semihosting/build.rs diff --git a/riscv-semihosting/Cargo.toml b/riscv-semihosting/Cargo.toml index 29621af4..4f483433 100644 --- a/riscv-semihosting/Cargo.toml +++ b/riscv-semihosting/Cargo.toml @@ -17,12 +17,11 @@ edition = "2021" rust-version = "1.60.0" [features] -default = ["jlink-quirks"] -machine-mode = [] -user-mode = [] +u-mode = [] jlink-quirks = [] no-semihosting = [] +default = ["jlink-quirks"] [dependencies] -riscv = "0.8.0" -cfg-if = "1.0.0" +critical-section = "1.0.0" +riscv = {path = "../riscv", version = "0.10.1"} diff --git a/riscv-semihosting/build.rs b/riscv-semihosting/build.rs new file mode 100644 index 00000000..298364fd --- /dev/null +++ b/riscv-semihosting/build.rs @@ -0,0 +1,9 @@ +use std::env; + +fn main() { + let target = env::var("TARGET").unwrap(); + + if target.starts_with("riscv") { + println!("cargo:rustc-cfg=riscv"); + } +} diff --git a/riscv-semihosting/src/debug.rs b/riscv-semihosting/src/debug.rs index 72db715d..1e2a7b06 100644 --- a/riscv-semihosting/src/debug.rs +++ b/riscv-semihosting/src/debug.rs @@ -11,16 +11,14 @@ //! ```no_run //! use riscv_semihosting::debug::{self, EXIT_SUCCESS, EXIT_FAILURE}; //! -//! fn main() { -//! if 2 == 2 { -//! // report success -//! debug::exit(EXIT_SUCCESS); -//! } else { -//! // report failure -//! debug::exit(EXIT_FAILURE); -//! } +//! if 2 == 2 { +//! // report success +//! debug::exit(EXIT_SUCCESS); +//! } else { +//! // report failure +//! debug::exit(EXIT_FAILURE); //! } -//! +//!``` /// This values are taken from section 5.5.2 of /// ADS Debug Target Guide (DUI0058). diff --git a/riscv-semihosting/src/export.rs b/riscv-semihosting/src/export.rs index b916367c..5531b408 100644 --- a/riscv-semihosting/src/export.rs +++ b/riscv-semihosting/src/export.rs @@ -1,105 +1,103 @@ //! IMPLEMENTATION DETAILS USED BY MACROS +use crate::hio::{self, HostStream}; use core::fmt::{self, Write}; -#[cfg(feature = "machine-mode")] -use riscv::interrupt; +static mut HSTDOUT: Option = None; -use crate::hio::{self, HostStream}; +static mut HSTDERR: Option = None; -static mut HSTDOUT: Option = None; +#[cfg(not(feature = "u-mode"))] +mod machine { + use super::*; + + pub fn hstdout_str(s: &str) { + let _result = critical_section::with(|_| unsafe { + if HSTDOUT.is_none() { + HSTDOUT = Some(hio::hstdout()?); + } + + HSTDOUT.as_mut().unwrap().write_str(s).map_err(drop) + }); + } + + pub fn hstdout_fmt(args: fmt::Arguments) { + let _result = critical_section::with(|_| unsafe { + if HSTDOUT.is_none() { + HSTDOUT = Some(hio::hstdout()?); + } + + HSTDOUT.as_mut().unwrap().write_fmt(args).map_err(drop) + }); + } + + pub fn hstderr_str(s: &str) { + let _result = critical_section::with(|_| unsafe { + if HSTDERR.is_none() { + HSTDERR = Some(hio::hstderr()?); + } + + HSTDERR.as_mut().unwrap().write_str(s).map_err(drop) + }); + } + + pub fn hstderr_fmt(args: fmt::Arguments) { + let _result = critical_section::with(|_| unsafe { + if HSTDERR.is_none() { + HSTDERR = Some(hio::hstderr()?); + } + + HSTDERR.as_mut().unwrap().write_fmt(args).map_err(drop) + }); + } +} +#[cfg(not(feature = "u-mode"))] +pub use machine::*; + +#[cfg(feature = "u-mode")] +mod user { + use super::*; + pub fn hstdout_str(s: &str) { + let _result = unsafe { + if HSTDOUT.is_none() { + HSTDOUT = Some(hio::hstdout().unwrap()); + } + + HSTDOUT.as_mut().unwrap().write_str(s).map_err(drop) + }; + } -#[cfg(not(feature = "no-semihosting"))] -cfg_if::cfg_if! { - if #[cfg(feature="machine-mode")] { - pub fn hstdout_str(s: &str) { - let _result = interrupt::free(|_| unsafe { - if HSTDOUT.is_none() { - HSTDOUT = Some(hio::hstdout()?); - } - - HSTDOUT.as_mut().unwrap().write_str(s).map_err(drop) - }); - } - - pub fn hstdout_fmt(args: fmt::Arguments) { - let _result = interrupt::free(|_| unsafe { - if HSTDOUT.is_none() { - HSTDOUT = Some(hio::hstdout()?); - } - - HSTDOUT.as_mut().unwrap().write_fmt(args).map_err(drop) - }); - } - - static mut HSTDERR: Option = None; - - pub fn hstderr_str(s: &str) { - let _result = interrupt::free(|_| unsafe { - if HSTDERR.is_none() { - HSTDERR = Some(hio::hstderr()?); - } - - HSTDERR.as_mut().unwrap().write_str(s).map_err(drop) - }); - } - - pub fn hstderr_fmt(args: fmt::Arguments) { - let _result = interrupt::free(|_| unsafe { - if HSTDERR.is_none() { - HSTDERR = Some(hio::hstderr()?); - } - - HSTDERR.as_mut().unwrap().write_fmt(args).map_err(drop) - }); - } + pub fn hstdout_fmt(args: fmt::Arguments) { + let _result = unsafe { + if HSTDOUT.is_none() { + HSTDOUT = Some(hio::hstdout().unwrap()); + } + + HSTDOUT.as_mut().unwrap().write_fmt(args).map_err(drop) + }; } - else if #[cfg(feature = "user-mode")] { - pub fn hstdout_str(s: &str) { - let _result = unsafe { - if HSTDOUT.is_none() { - HSTDOUT = Some(hio::hstdout().unwrap()); - } - - HSTDOUT.as_mut().unwrap().write_str(s).map_err(drop) - }; - } - - pub fn hstdout_fmt(args: fmt::Arguments) { - let _result = unsafe { - if HSTDOUT.is_none() { - HSTDOUT = Some(hio::hstdout().unwrap()); - } - - HSTDOUT.as_mut().unwrap().write_fmt(args).map_err(drop) - }; - } - - static mut HSTDERR: Option = None; - - pub fn hstderr_str(s: &str) { - let _result = unsafe { - if HSTDERR.is_none() { - HSTDERR = Some(hio::hstderr().unwrap()); - } - - HSTDERR.as_mut().unwrap().write_str(s).map_err(drop) - }; - } - - pub fn hstderr_fmt(args: fmt::Arguments) { - let _result = unsafe { - if HSTDERR.is_none() { - HSTDERR = Some(hio::hstderr().unwrap()); - } - - HSTDERR.as_mut().unwrap().write_fmt(args).map_err(drop) - }; - } + + static mut HSTDERR: Option = None; + + pub fn hstderr_str(s: &str) { + let _result = unsafe { + if HSTDERR.is_none() { + HSTDERR = Some(hio::hstderr().unwrap()); + } + + HSTDERR.as_mut().unwrap().write_str(s).map_err(drop) + }; } - else { - compile_error!("A privilege level has not been selected. Enable either \ - the machine-mode or user-mode features as appropriate \ - for your use case."); + + pub fn hstderr_fmt(args: fmt::Arguments) { + let _result = unsafe { + if HSTDERR.is_none() { + HSTDERR = Some(hio::hstderr().unwrap()); + } + + HSTDERR.as_mut().unwrap().write_fmt(args).map_err(drop) + }; } } +#[cfg(feature = "u-mode")] +pub use user::*; diff --git a/riscv-semihosting/src/hio.rs b/riscv-semihosting/src/hio.rs index b6b6c7b7..e0614ad3 100644 --- a/riscv-semihosting/src/hio.rs +++ b/riscv-semihosting/src/hio.rs @@ -1,5 +1,8 @@ //! Host I/O +// Fixing this lint requires a breaking change that does not add much value +#![allow(clippy::result_unit_err)] + use crate::nr; use core::{fmt, slice}; diff --git a/riscv-semihosting/src/lib.rs b/riscv-semihosting/src/lib.rs index 59dad79e..9c019e18 100644 --- a/riscv-semihosting/src/lib.rs +++ b/riscv-semihosting/src/lib.rs @@ -180,6 +180,7 @@ #![deny(missing_docs)] #![no_std] +#[cfg(all(riscv, not(feature = "no-semihosting")))] use core::arch::asm; #[macro_use] @@ -192,23 +193,33 @@ pub mod hio; pub mod nr; /// 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 #[inline(always)] pub unsafe fn syscall(nr: usize, arg: &T) -> usize { syscall1(nr, arg as *const T as usize) } /// Performs a semihosting operation, takes one integer as an argument +/// +/// # Safety +/// +/// Same as [`syscall`]. #[inline(always)] pub unsafe fn syscall1(_nr: usize, _arg: usize) -> usize { match () { - #[cfg(not(feature = "no-semihosting"))] + #[cfg(all(riscv, not(feature = "no-semihosting")))] () => { let mut nr = _nr; // The instructions below must always be uncompressed, otherwise // it will be treated as a regular break, hence the norvc option. // - // See https://github.com/riscv/riscv-semihosting-spec for more - // details. + // See https://github.com/riscv/riscv-semihosting-spec for more details. asm!(" .option push .option norvc @@ -222,8 +233,9 @@ pub unsafe fn syscall1(_nr: usize, _arg: usize) -> usize { ); nr } - - #[cfg(feature = "no-semihosting")] + #[cfg(all(riscv, feature = "no-semihosting"))] () => 0, + #[cfg(not(riscv))] + () => unimplemented!(), } } From eb29d9640b6e8f1c6c3ed7d55c33a4e4a20f966a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Fri, 29 Dec 2023 10:46:17 +0100 Subject: [PATCH 39/41] modify readme --- riscv-semihosting/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/riscv-semihosting/README.md b/riscv-semihosting/README.md index 9a2a8a4f..75da8aeb 100644 --- a/riscv-semihosting/README.md +++ b/riscv-semihosting/README.md @@ -18,10 +18,10 @@ generally sufficient for using this library. A major difference between this library and `cortex-m-semihosting` is that there are mandatory features to choose the privilege level at which the semihosting -calls are executed. The "machine-mode" feature will cause the macros in `export` +calls are executed. The *machine-mode (M-mode)* feature will cause the macros in `export` to execute the semihosting operation in an interrupt-free context, while -"user-mode" causes them to just execute the operation. Failure to select one of -these two features will cause a compiler error. +*user-mode (U-mode)* causes them to just execute the operation. +By default, M-mode is used. You can activate the U-mode via the `u-mode` feature. # Minimum Supported Rust Version (MSRV) From eade03842e663b0fa11ac862e1a2e30c01961c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Fri, 29 Dec 2023 11:02:07 +0100 Subject: [PATCH 40/41] update CI --- .github/workflows/riscv-semihosting.yaml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/riscv-semihosting.yaml b/.github/workflows/riscv-semihosting.yaml index ecbc32bd..93e76ff6 100644 --- a/.github/workflows/riscv-semihosting.yaml +++ b/.github/workflows/riscv-semihosting.yaml @@ -32,14 +32,31 @@ jobs: toolchain: ${{ matrix.toolchain }} targets: ${{ matrix.target }} - name: Build (M-mode) - run: cargo build --package riscv-semihosting --target ${{ matrix.target }} --features machine-mode + run: cargo build --package riscv-semihosting --target ${{ matrix.target }} - name: Build (U-mode) - run: cargo build --package riscv-semihosting --target ${{ matrix.target }} --features=user-mode + run: cargo build --package riscv-semihosting --target ${{ matrix.target }} --features=u-mode + - name: Build (no semihosting) + run: cargo build --package riscv-semihosting --target ${{ matrix.target }} --features=no-semihosting + + # On MacOS, Ubuntu, and Windows, we at least make sure that the crate builds and links. + build-others: + strategy: + matrix: + os: [ macos-latest, ubuntu-latest, windows-latest ] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - name: Build (no features) + run: cargo build --package riscv-semihosting + - name: Build (all features) + run: cargo build --package riscv-semihosting --all-features # Job to check that all the builds succeeded build-check: needs: - build-riscv + - build-others runs-on: ubuntu-latest if: always() steps: From 4dc63f6743a48f36c09693d77ec04a19b1db23e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas?= Date: Sun, 31 Dec 2023 18:01:19 +0100 Subject: [PATCH 41/41] minor changes --- .github/workflows/riscv-semihosting.yaml | 2 +- riscv-semihosting/src/export.rs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/riscv-semihosting.yaml b/.github/workflows/riscv-semihosting.yaml index 93e76ff6..4f6da8b0 100644 --- a/.github/workflows/riscv-semihosting.yaml +++ b/.github/workflows/riscv-semihosting.yaml @@ -1,6 +1,6 @@ on: push: - branches: [ master, semihosting ] + branches: [ master ] pull_request: merge_group: diff --git a/riscv-semihosting/src/export.rs b/riscv-semihosting/src/export.rs index 5531b408..c56d6d48 100644 --- a/riscv-semihosting/src/export.rs +++ b/riscv-semihosting/src/export.rs @@ -4,7 +4,6 @@ use crate::hio::{self, HostStream}; use core::fmt::{self, Write}; static mut HSTDOUT: Option = None; - static mut HSTDERR: Option = None; #[cfg(not(feature = "u-mode"))] @@ -57,6 +56,7 @@ pub use machine::*; #[cfg(feature = "u-mode")] mod user { use super::*; + pub fn hstdout_str(s: &str) { let _result = unsafe { if HSTDOUT.is_none() { @@ -77,8 +77,6 @@ mod user { }; } - static mut HSTDERR: Option = None; - pub fn hstderr_str(s: &str) { let _result = unsafe { if HSTDERR.is_none() {