From 372c6081bb9329e0ae6158da9b3562f18660157c Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 22 Mar 2024 00:26:32 +0900 Subject: [PATCH] Support Xtensa (OpenOCD Semihosting) --- .github/.cspell/project-dictionary.txt | 4 +++ .github/workflows/ci.yml | 14 ++++++++- Cargo.toml | 7 ++++- README.md | 23 +++++++++++---- src/experimental/env.rs | 1 + src/experimental/time.rs | 1 + src/lib.rs | 35 +++++++++++++++++++---- src/sys/arm_compat/mod.rs | 1 + src/sys/arm_compat/syscall/mod.rs | 1 + src/sys/arm_compat/syscall/xtensa.rs | 39 ++++++++++++++++++++++++++ src/sys/mod.rs | 3 ++ src/sys/reg.rs | 3 ++ 12 files changed, 118 insertions(+), 14 deletions(-) create mode 100644 src/sys/arm_compat/syscall/xtensa.rs diff --git a/.github/.cspell/project-dictionary.txt b/.github/.cspell/project-dictionary.txt index 523ddb4..b86ba05 100644 --- a/.github/.cspell/project-dictionary.txt +++ b/.github/.cspell/project-dictionary.txt @@ -11,6 +11,7 @@ ebreak EINTR EINVAL errno +espup fipe FLEN fopen @@ -39,6 +40,7 @@ nlink norvc nsec OMAP +openocd opensbi pcnet plog @@ -55,12 +57,14 @@ requirechecksums rustfilt sdbbp Seekable +simcall slli srai subarch subcode syscall TDMI +Tensilica tickfreq timespec TMPNAM diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4406402..2124de4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,7 +93,7 @@ jobs: - run: cargo test --workspace --features $TEST_FEATURES --release --target ${{ matrix.target }} $DOCTEST_XCOMPILE $BUILD_STD - run: cargo careful test --workspace --features $TEST_FEATURES --target ${{ matrix.target }} $DOCTEST_XCOMPILE $BUILD_STD if: startsWith(matrix.rust, 'nightly') - - run: cargo hack build --workspace --no-private --feature-powerset --depth 2 --optional-deps=portable-atomic --exclude-features panic-unwind,backtrace --target ${{ matrix.target }} $BUILD_STD + - run: cargo hack build --workspace --no-private --feature-powerset --depth 2 --optional-deps=portable-atomic --exclude-features panic-unwind,backtrace,openocd-semihosting --target ${{ matrix.target }} $BUILD_STD - run: cargo minimal-versions build --workspace --no-private --detach-path-deps=skip-exact --features $TEST_FEATURES --target ${{ matrix.target }} $BUILD_STD - run: cargo minimal-versions build --workspace --no-private --detach-path-deps=skip-exact --features $TEST_FEATURES --target ${{ matrix.target }} $BUILD_STD --direct @@ -117,6 +117,12 @@ jobs: - uses: taiki-e/checkout-action@v1 - name: Install Rust run: rustup toolchain add ${{ matrix.rust }} --no-self-update --component rust-src && rustup default ${{ matrix.rust }} + - uses: taiki-e/install-action@v2 + with: + tool: espup + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if: startsWith(matrix.rust, 'nightly') && (startsWith(matrix.os, 'ubuntu') || matrix.os == '') - run: | set -eEuxo pipefail sudo apt-get -o Acquire::Retries=10 -qq update && sudo apt-get -o Acquire::Retries=10 -o Dpkg::Use-Pty=0 install -y --no-install-recommends \ @@ -164,6 +170,12 @@ jobs: echo "C:\Program Files\qemu" >>"${GITHUB_PATH}" "C:\Program Files\qemu\qemu-system-arm" --version if: startsWith(matrix.os, 'windows') + - run: espup install --targets esp32 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if: startsWith(matrix.rust, 'nightly') && (startsWith(matrix.os, 'ubuntu') || matrix.os == '') - run: tools/no-std.sh - run: TEST_RUNNER=qemu-user tools/no-std.sh if: startsWith(matrix.os, 'ubuntu') || matrix.os == '' + - run: cargo +esp build --target xtensa-esp32-none-elf -Z build-std=core,alloc --features $TEST_FEATURES,openocd-semihosting + if: startsWith(matrix.rust, 'nightly') && (startsWith(matrix.os, 'ubuntu') || matrix.os == '') diff --git a/Cargo.toml b/Cargo.toml index a91c210..907131e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ keywords = ["qemu"] categories = ["embedded", "hardware-support", "no-std", "no-std::no-alloc"] exclude = ["/.*", "/tools", "/target-specs"] description = """ -Semihosting for AArch64, ARM, RISC-V, MIPS32, and MIPS64. +Semihosting for AArch64, ARM, RISC-V, MIPS32, MIPS64, and Xtensa. """ [package.metadata.docs.rs] @@ -77,6 +77,11 @@ backtrace = ["stdio", "unwinding", "unwinding/hide-trace"] # See readme for details. portable-atomic = ["dep:portable-atomic"] +# Xtensa-specific: Use OpenOCD Semihosting. +# +# See readme for details. +openocd-semihosting = [] + [dependencies] portable-atomic = { version = "1.3", optional = true, default-features = false, features = ["require-cas"] } unwinding = { version = "0.2", optional = true, default-features = false, features = ["unwinder"] } diff --git a/README.md b/README.md index 1ad3e24..c4206ae 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![github actions](https://img.shields.io/github/actions/workflow/status/taiki-e/semihosting/ci.yml?branch=main&style=flat-square&logo=github)](https://github.com/taiki-e/semihosting/actions) -Semihosting for AArch64, ARM, RISC-V, MIPS32, and MIPS64. +Semihosting for AArch64, ARM, RISC-V, MIPS32, MIPS64, and Xtensa. This library provides access to semihosting, a mechanism for programs running on the real or virtual (e.g., QEMU) target to communicate with I/O facilities on the host system. See the [ARM documentation](https://github.com/ARM-software/abi-aa/blob/HEAD/semihosting/semihosting.rst) for more information on semihosting. @@ -36,11 +36,12 @@ Additionally, this library provides a panic handler for semihosting, `-C panic=u The following target architectures are supported: -| target_arch | Specification | `semihosting::sys` module | -| ----------- | ------------- | ------------------------- | -| arm/aarch64 | [Semihosting for AArch32 and AArch64](https://github.com/ARM-software/abi-aa/blob/HEAD/semihosting/semihosting.rst) | `sys::arm_compat` | -| riscv32/riscv64 | [RISC-V Semihosting](https://github.com/riscv-software-src/riscv-semihosting/blob/HEAD/riscv-semihosting-spec.adoc) | `sys::arm_compat` | -| mips/mips32r6/mips64/mips64r6 | Unified Hosting Interface (MD01069) | `sys::mips` | +| target_arch | Specification | `semihosting::sys` module | Note | +| ----------- | ------------- | ------------------------- | ---- | +| arm/aarch64 | [Semihosting for AArch32 and AArch64](https://github.com/ARM-software/abi-aa/blob/HEAD/semihosting/semihosting.rst) | `sys::arm_compat` | | +| riscv32/riscv64 | [RISC-V Semihosting](https://github.com/riscv-software-src/riscv-semihosting/blob/HEAD/riscv-semihosting-spec.adoc) | `sys::arm_compat` | | +| xtensa | [OpenOCD Semihosting](https://github.com/espressif/openocd-esp32/blob/HEAD/src/target/espressif/esp_xtensa_semihosting.c) | `sys::arm_compat` | requires the [`openocd-semihosting` feature](#optional-features-openocd-semihosting) | +| mips/mips32r6/mips64/mips64r6 | Unified Hosting Interface (MD01069) | `sys::mips` | | The host must be running an emulator or a debugger attached to the target. @@ -98,6 +99,16 @@ semihosting = { version = "0.1", features = ["stdio", "panic-handler"] } If the `stdio` feature is also enabled, this attempt to output panic message and location to stderr. +- **`openocd-semihosting`**
+ Xtensa-specific: Use OpenOCD Semihosting. + + Xtensa has two semihosting interfaces: + + - Tensilica ISS SIMCALL used in Cadence tools and [QEMU](https://www.qemu.org/docs/master/about/emulation.html#supported-targets). + - ARM-semihosting-compatible semihosting interface used in [OpenOCD](https://github.com/espressif/openocd-esp32/blob/HEAD/src/target/espressif/esp_xtensa_semihosting.c) and [probe-rs](https://github.com/probe-rs/probe-rs/pull/2303). (This crate calls it "OpenOCD Semihosting", which is the same as the option name in [newlib](https://github.com/espressif/newlib-esp32/blob/esp_based_on_4_1_0/libgloss/xtensa/syscalls.c#L23).) + + This crate does not currently support SIMCALL-based semihosting, but users need to explicitly enable the feature to avoid accidentally selecting a different one than one actually want to use. + - **`portable-atomic`**
Use [portable-atomic]'s atomic types. diff --git a/src/experimental/env.rs b/src/experimental/env.rs index 7e2cb30..9b95d9c 100644 --- a/src/experimental/env.rs +++ b/src/experimental/env.rs @@ -81,6 +81,7 @@ mod sys { target_arch = "arm", target_arch = "riscv32", target_arch = "riscv64", + all(target_arch = "xtensa", feature = "openocd-semihosting"), ))] mod imp { use core::cell::Cell; diff --git a/src/experimental/time.rs b/src/experimental/time.rs index d2ed8b3..a5d7b1b 100644 --- a/src/experimental/time.rs +++ b/src/experimental/time.rs @@ -206,6 +206,7 @@ mod sys { target_arch = "arm", target_arch = "riscv32", target_arch = "riscv64", + all(target_arch = "xtensa", feature = "openocd-semihosting"), ))] mod inner { use super::{SystemTime, Timespec}; diff --git a/src/lib.rs b/src/lib.rs index e5b01db..d818c7a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ /*! -Semihosting for AArch64, ARM, RISC-V, MIPS32, and MIPS64. +Semihosting for AArch64, ARM, RISC-V, MIPS32, MIPS64, and Xtensa. This library provides access to semihosting, a mechanism for programs running on the real or virtual (e.g., QEMU) target to communicate with I/O facilities on the host system. See the [ARM documentation](https://github.com/ARM-software/abi-aa/blob/HEAD/semihosting/semihosting.rst) for more information on semihosting. @@ -31,11 +31,12 @@ Additionally, this library provides a panic handler for semihosting, `-C panic=u The following target architectures are supported: -| target_arch | Specification | `semihosting::sys` module | -| ----------- | ------------- | ------------------------- | -| arm/aarch64 | [Semihosting for AArch32 and AArch64](https://github.com/ARM-software/abi-aa/blob/HEAD/semihosting/semihosting.rst) | `sys::arm_compat` | -| riscv32/riscv64 | [RISC-V Semihosting](https://github.com/riscv-software-src/riscv-semihosting/blob/HEAD/riscv-semihosting-spec.adoc) | `sys::arm_compat` | -| mips/mips32r6/mips64/mips64r6 | Unified Hosting Interface (MD01069) | `sys::mips` | +| target_arch | Specification | `semihosting::sys` module | Note | +| ----------- | ------------- | ------------------------- | ---- | +| arm/aarch64 | [Semihosting for AArch32 and AArch64](https://github.com/ARM-software/abi-aa/blob/HEAD/semihosting/semihosting.rst) | `sys::arm_compat` | | +| riscv32/riscv64 | [RISC-V Semihosting](https://github.com/riscv-software-src/riscv-semihosting/blob/HEAD/riscv-semihosting-spec.adoc) | `sys::arm_compat` | | +| xtensa | [OpenOCD Semihosting](https://github.com/espressif/openocd-esp32/blob/HEAD/src/target/espressif/esp_xtensa_semihosting.c) | `sys::arm_compat` | requires the [`openocd-semihosting` feature](#optional-features-openocd-semihosting) | +| mips/mips32r6/mips64/mips64r6 | Unified Hosting Interface (MD01069) | `sys::mips` | | The host must be running an emulator or a debugger attached to the target. @@ -93,6 +94,16 @@ semihosting = { version = "0.1", features = ["stdio", "panic-handler"] } If the `stdio` feature is also enabled, this attempt to output panic message and location to stderr. +- **`openocd-semihosting`**
+ Xtensa-specific: Use OpenOCD Semihosting. + + Xtensa has two semihosting interfaces: + + - Tensilica ISS SIMCALL used in Cadence tools and [QEMU](https://www.qemu.org/docs/master/about/emulation.html#supported-targets). + - ARM-semihosting-compatible semihosting interface used in [OpenOCD](https://github.com/espressif/openocd-esp32/blob/HEAD/src/target/espressif/esp_xtensa_semihosting.c) and [probe-rs](https://github.com/probe-rs/probe-rs/pull/2303). (This crate calls it "OpenOCD Semihosting", which is the same as the option name in [newlib](https://github.com/espressif/newlib-esp32/blob/esp_based_on_4_1_0/libgloss/xtensa/syscalls.c#L23).) + + This crate does not currently support SIMCALL-based semihosting, but users need to explicitly enable the feature to avoid accidentally selecting a different one than one actually want to use. + - **`portable-atomic`**
Use [portable-atomic]'s atomic types. @@ -212,6 +223,7 @@ semihosting = { version = "0.1", features = ["stdio", "panic-handler"] } target_arch = "mips32r6", target_arch = "mips64", target_arch = "mips64r6", + target_arch = "xtensa", ), feature(asm_experimental_arch) )] @@ -227,8 +239,19 @@ semihosting = { version = "0.1", features = ["stdio", "panic-handler"] } target_arch = "mips32r6", target_arch = "mips64", target_arch = "mips64r6", + target_arch = "xtensa", )))] compile_error!("unsupported target"); +#[cfg(target_arch = "xtensa")] +#[cfg(not(feature = "openocd-semihosting"))] +compile_error!( + "xtensa has two semihosting interfaces so you have to select implementation;\n\ + please enable `openocd-semihosting` feature if you want to use OpenOCD Semihosting used in OpenOCD and probe-rs.\n\ + see for more." +); +#[cfg(not(target_arch = "xtensa"))] +#[cfg(feature = "openocd-semihosting")] +compile_error!("`openocd-semihosting` feature does not compatible with this target"); #[cfg(feature = "alloc")] extern crate alloc; diff --git a/src/sys/arm_compat/mod.rs b/src/sys/arm_compat/mod.rs index 7e92f61..7d11822 100644 --- a/src/sys/arm_compat/mod.rs +++ b/src/sys/arm_compat/mod.rs @@ -7,6 +7,7 @@ //! - Semihosting for AArch32 and AArch64 //! - RISC-V Semihosting //! - +//! - #![allow(clippy::missing_safety_doc)] // TODO diff --git a/src/sys/arm_compat/syscall/mod.rs b/src/sys/arm_compat/syscall/mod.rs index f88ae4c..4a9f096 100644 --- a/src/sys/arm_compat/syscall/mod.rs +++ b/src/sys/arm_compat/syscall/mod.rs @@ -8,6 +8,7 @@ pub use arch::{syscall, syscall_readonly}; #[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")] #[cfg_attr(target_arch = "arm", path = "arm.rs")] #[cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), path = "riscv.rs")] +#[cfg_attr(target_arch = "xtensa", path = "xtensa.rs")] mod arch; pub use crate::sys::reg::{ParamRegR, ParamRegW, RetReg}; diff --git a/src/sys/arm_compat/syscall/xtensa.rs b/src/sys/arm_compat/syscall/xtensa.rs new file mode 100644 index 0000000..db817fa --- /dev/null +++ b/src/sys/arm_compat/syscall/xtensa.rs @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use core::arch::asm; + +use super::{OperationNumber, ParamRegR, ParamRegW, RetReg}; + +/// Raw semihosting call with a parameter that will be read + modified by the host +#[inline] +pub unsafe fn syscall(number: OperationNumber, parameter: ParamRegW<'_>) -> RetReg { + unsafe { + let r; + asm!( + "break 1, 14", + inout("a2") number.0 as usize => r, // OPERATION NUMBER REGISTER => RETURN REGISTER + // Use inout because operation such as SYS_ELAPSED suggest that + // PARAMETER REGISTER may be changed. + inout("a3") parameter.0 => _, // PARAMETER REGISTER + options(nostack, preserves_flags), + ); + RetReg(r) + } +} + +/// Raw semihosting call with a parameter that will be read (but not modified) by the host +#[inline] +pub unsafe fn syscall_readonly(number: OperationNumber, parameter: ParamRegR<'_>) -> RetReg { + unsafe { + let r; + asm!( + "break 1, 14", + inout("a2") number.0 as usize => r, // OPERATION NUMBER REGISTER => RETURN REGISTER + // Use inout because operation such as SYS_ELAPSED suggest that + // PARAMETER REGISTER may be changed. + inout("a3") parameter.0 => _, // PARAMETER REGISTER + options(nostack, preserves_flags, readonly), + ); + RetReg(r) + } +} diff --git a/src/sys/mod.rs b/src/sys/mod.rs index b6aec6d..17bc084 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -14,6 +14,7 @@ target_arch = "arm", target_arch = "riscv32", target_arch = "riscv64", + all(target_arch = "xtensa", feature = "openocd-semihosting"), ))] use arm_compat as arch; #[cfg(any( @@ -21,6 +22,7 @@ use arm_compat as arch; target_arch = "arm", target_arch = "riscv32", target_arch = "riscv64", + all(target_arch = "xtensa", feature = "openocd-semihosting"), ))] #[cfg_attr( semihosting_doc_cfg, @@ -29,6 +31,7 @@ use arm_compat as arch; target_arch = "arm", target_arch = "riscv32", target_arch = "riscv64", + all(target_arch = "xtensa", feature = "openocd-semihosting"), ))) )] pub mod arm_compat; diff --git a/src/sys/reg.rs b/src/sys/reg.rs index ba88d3d..6aa8ce6 100644 --- a/src/sys/reg.rs +++ b/src/sys/reg.rs @@ -51,6 +51,7 @@ impl<'a> ParamRegW<'a> { target_arch = "arm", target_arch = "riscv32", target_arch = "riscv64", + all(target_arch = "xtensa", feature = "openocd-semihosting"), ))] impl<'a> ParamRegW<'a> { #[inline] @@ -100,6 +101,7 @@ impl<'a> ParamRegR<'a> { target_arch = "arm", target_arch = "riscv32", target_arch = "riscv64", + all(target_arch = "xtensa", feature = "openocd-semihosting"), ))] impl<'a> ParamRegR<'a> { #[inline] @@ -157,6 +159,7 @@ impl RetReg { target_arch = "arm", target_arch = "riscv32", target_arch = "riscv64", + all(target_arch = "xtensa", feature = "openocd-semihosting"), ))] impl RetReg { #[allow(clippy::cast_possible_truncation)]