Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
392 lines
19 KiB
Rust
392 lines
19 KiB
Rust
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
|
|
/*!
|
|
<!-- Note: Document from sync-markdown-to-rustdoc:start through sync-markdown-to-rustdoc:end
|
|
is synchronized from README.md. Any changes to that range are not preserved. -->
|
|
<!-- tidy:sync-markdown-to-rustdoc:start -->
|
|
|
|
Semihosting for AArch64, Arm, RISC-V, LoongArch, 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/2025Q1/semihosting/semihosting.rst) for more information on semihosting.
|
|
|
|
APIs are categorized into the following four types:
|
|
|
|
- The top-level API (`semihosting::{io,fs,..}`) provides a subset of the standard library's similar APIs.
|
|
- `io`: Provide no-std io traits and `std{in,out,err}`. (`std{in,out,err}` requires `stdio` feature, others are unconditionally provided)
|
|
- `fs`: Provide methods to manipulate the contents of the host filesystem. (requires `fs` feature)
|
|
- `process`: Provide `abort` and `exit`.
|
|
- `dbg!`/`print{,ln}!`/`eprint{,ln}!`: macros to output to stdout/stderr. (requires `stdio` feature)
|
|
|
|
Note that some APIs are not strictly a subset of the standard library.
|
|
- API that uses types not available in `core` such as `Path` (technically, the same thing could be implemented, but it makes sense to use `CStr` directly, because when converting a long `Path`/`OsStr` to `CStr`, it needs to either [do an allocation](https://github.com/rust-lang/rust/blob/1.84.0/library/std/src/sys/pal/common/small_c_string.rs#L25-L26) or return an error)
|
|
- API that panics on failure in `std` (in no-std it makes sense to return `Result` since `panic=abort` is default)
|
|
|
|
- Helpers that are useful when using this library.
|
|
- `c!`: `CStr` literal macro. (Since Rust 1.77, this macro is soft-deprecated in favor of C string literals (`c"..."`).)
|
|
|
|
- `semihosting::sys` module provides low-level access to platform-specific semihosting interfaces.
|
|
|
|
- `semihosting::experimental` module provides experimental APIs. See [optional features](#optional-features) for more.
|
|
- `env`: Provide `args`.
|
|
- `time`: Provide `Instant` and `SystemTime`.
|
|
- `random`: Provide `fill_bytes` and `fill_uninit_bytes`.
|
|
- `panic`: Provide `catch_unwind`.
|
|
|
|
Additionally, this library provides a panic handler for semihosting, `-C panic=unwind` support, backtrace support, via [optional features](#optional-features).
|
|
|
|
## Platform Support
|
|
|
|
The following target architectures are supported:
|
|
|
|
| target_arch | Specification | `semihosting::sys` module | Note |
|
|
| ----------- | ------------- | ------------------------- | ---- |
|
|
| aarch64 | [Semihosting for AArch32 and AArch64](https://github.com/ARM-software/abi-aa/blob/2025Q1/semihosting/semihosting.rst) | `sys::arm_compat` | |
|
|
| arm | [Semihosting for AArch32 and AArch64](https://github.com/ARM-software/abi-aa/blob/2025Q1/semihosting/semihosting.rst) | `sys::arm_compat` | use `SVC` on A+R profile by default based on Arm's recommendation but it can be changed by [`trap-hlt` feature](#optional-features-trap-hlt). |
|
|
| riscv32/riscv64 | [RISC-V Semihosting](https://github.com/riscv-non-isa/riscv-semihosting/blob/1.0/riscv-semihosting.adoc) | `sys::arm_compat` | |
|
|
| loongarch32/loongarch64 | | `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 [`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.
|
|
|
|
The following targets have been tested on CI. (qemu-system has been tested on Linux, macOS, and Windows hosts, and qemu-user on Linux host.)
|
|
|
|
| target | qemu-system | qemu-user | note |
|
|
| ----------------------------------------- | ---------------- | -------------- | --------- |
|
|
| `aarch64-unknown-none{,-softfloat}` | ✓ | ✓ | |
|
|
| `aarch64_be-unknown-none{,-softfloat}` | ✓ | ✓ | \[2] |
|
|
| `{arm,thumb}v4t-none-eabi` | ✓ | ✓ | |
|
|
| `{arm,thumb}v5te-none-eabi` | ✓ | ✓ | |
|
|
| `armv6-none-eabi{,hf}` | ✓ | ✓ | |
|
|
| `thumbv6-none-eabi` | ✓ | ✓ | |
|
|
| `{arm,thumb}v7a-none-eabi{,hf}` | ✓ | ✓ | |
|
|
| `{arm,thumb}v7r-none-eabi{,hf}` | ✓ | ✓ | |
|
|
| `armebv7r-none-eabi{,hf}` | * | ✓ | \[1] |
|
|
| `{arm,thumb}v8r-none-eabihf` | ✓ | ✓ | |
|
|
| `armebv8r-none-eabihf` | * | ✓ | \[1] |
|
|
| `thumbv6m-none-eabi` | ✓ | ✓ | |
|
|
| `thumbv7m-none-eabi` | ✓ | ✓ | |
|
|
| `thumbv7em-none-eabi{,hf}` | ✓ | ✓ | |
|
|
| `thumbv8m.base-none-eabi` | ✓ | ✓ | |
|
|
| `thumbv8m.main-none-eabi{,hf}` | ✓ | ✓ | |
|
|
| `riscv32*-unknown-none-elf` | ✓ | ✓ | |
|
|
| `riscv64*-unknown-none-elf` | ✓ | ✓ | |
|
|
| `loongarch32-unknown-none{,-softfloat}` | ✓ | N/A | \[2] \[3] |
|
|
| `loongarch64-unknown-none{,-softfloat}` | ✓ | ✓ | \[2] |
|
|
| `mips{,el}-unknown-none` | ✓ | N/A | \[2] \[4] |
|
|
| `mips{,el}-mti-none-elf` | ✓ | N/A | \[2] \[4] |
|
|
| `mips64{,el}-unknown-none` | ✓ | N/A | \[2] \[4] |
|
|
| `mipsisa32r6{,el}-unknown-none` | ✓ | N/A | \[2] \[4] |
|
|
| `mipsisa64r6{,el}-unknown-none` | ✓ | N/A | \[2] \[4] |
|
|
|
|
\[1] Due to [qemu-system bug about big-endian Arm](https://github.com/taiki-e/semihosting/issues/18), operations that contains reading/writing of strings such as file open (`stdio`, `fs`, `random`), `args`, and `SYS_SYSTEM` don't work.<br>
|
|
\[2] Tested with patched QEMU ([aarch64_be](https://patchew.org/QEMU/20260106-semihosting-cpu-tswap-v1-0-646576c25f56@eonerc.rwth-aachen.de/), [loongarch](https://lore.kernel.org/qemu-devel/20241222-semihosting-v1-1-8a770df60e9c@flygoat.com/), [mips](https://github.com/taiki-e/qemu/commit/c2d1c8dbc5cb878fbcce824180aa812213785124)). QEMU binaries that we are using for test are available on our QEMU fork ([aarch64_be/mips](https://github.com/taiki-e/qemu/releases/tag/patched-10.2.0), [loongarch](https://github.com/taiki-e/qemu/releases/tag/loongarch-semi-10.1.3)).<br>
|
|
\[3] Requires Rust 1.91+.<br>
|
|
\[4] Requires nightly due to `#![feature(asm_experimental_arch)]`.<br>
|
|
|
|
## Optional features
|
|
|
|
All features are disabled by default.
|
|
|
|
In general use cases, you probably only need the `stdio` feature that enables print-related macros and/or the `panic-handler` feature that exits with a non-zero error code on panic.
|
|
|
|
```toml
|
|
[dependencies]
|
|
semihosting = { version = "0.1", features = ["stdio", "panic-handler"] }
|
|
```
|
|
|
|
- **`alloc`**<br>
|
|
Use `alloc`.
|
|
|
|
- **`stdio`**<br>
|
|
Enable `semihosting::io::{stdin,stdout,stderr}` and `semihosting::{print*,eprint*,dbg}`.
|
|
|
|
- **`fs`**<br>
|
|
Enable `semihosting::fs`.
|
|
|
|
- **`panic-handler`**<br>
|
|
Provide panic handler based on `semihosting::process::exit`.
|
|
|
|
If the `stdio` feature is also enabled, this attempt to output panic message and
|
|
location to stderr.
|
|
|
|
- <a name="optional-features-trap-hlt"></a>**`trap-hlt`**<br>
|
|
Arm-specific: Use HLT instruction (except for M-profile architecture).
|
|
|
|
[Arm documentation](https://github.com/ARM-software/abi-aa/blob/2025Q1/semihosting/semihosting.rst#the-semihosting-interface) says:
|
|
|
|
> The `HLT` encodings are new in version 2.0 of the semihosting specification.
|
|
> Where possible, have semihosting callers continue to use the previously existing
|
|
> trap instructions to ensure compatibility with legacy semihosting implementations.
|
|
> These trap instructions are `HLT` for A64, `SVC` on A+R profile A32 or T32, and
|
|
> `BKPT` on M profile. However, it is necessary to change from SVC to HLT instructions
|
|
> to support AArch32 semihosting properly in a mixed AArch32/AArch64 system.
|
|
>
|
|
> ARM encourages semihosting callers to implement support for trapping using `HLT`
|
|
> on A32 and T32 as a configurable option. ARM strongly discourages semihosting
|
|
> callers from mixing the `HLT` and `SVC` mechanisms within the same executable.
|
|
|
|
Based on the Arm's recommendation, this is implemented as an optional feature.
|
|
|
|
Note:
|
|
- Enabling this feature on architectures other than Arm or on Arm M-profile architecture will result in a compile error.
|
|
|
|
- <a name="optional-features-openocd-semihosting"></a>**`openocd-semihosting`**<br>
|
|
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-esp32](https://github.com/espressif/newlib-esp32/blob/esp-4.3.0_20240530/libgloss/xtensa/syscalls.c#L21).)
|
|
|
|
When this feature is enabled, this crate uses the latter interface.
|
|
|
|
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.
|
|
|
|
Note:
|
|
- Enabling this feature on architectures other than Xtensa will result in a compile error.
|
|
|
|
- **`portable-atomic`**<br>
|
|
Use [portable-atomic]'s atomic types.
|
|
|
|
portable-atomic provides atomic CAS on targets where the standard library does not provide atomic CAS.
|
|
To use the `panic-unwind` feature on such targets (e.g., RISC-V without A-extension), you need to enable this feature.
|
|
|
|
See [its documentation](https://github.com/taiki-e/portable-atomic#optional-features-critical-section) for details.
|
|
|
|
- **`args`**<br>
|
|
Enable `semihosting::experimental::env::args`.
|
|
|
|
Note:
|
|
- This feature is experimental (tracking issue: [#1](https://github.com/taiki-e/semihosting/issues/1))
|
|
and outside of the normal semver guarantees and minor or patch versions of semihosting may make
|
|
breaking changes to them at any time.
|
|
|
|
- **`time`**<br>
|
|
Enable `semihosting::experimental::time`.
|
|
|
|
Note:
|
|
- This feature is experimental (tracking issue: [#2](https://github.com/taiki-e/semihosting/issues/2))
|
|
and outside of the normal semver guarantees and minor or patch versions of semihosting may make
|
|
breaking changes to them at any time.
|
|
|
|
- **`random`**<br>
|
|
Enable `semihosting::experimental::random`.
|
|
|
|
Note:
|
|
- This feature is experimental (tracking issue: [#22](https://github.com/taiki-e/semihosting/issues/22))
|
|
and outside of the normal semver guarantees and minor or patch versions of semihosting may make
|
|
breaking changes to them at any time.
|
|
- This implicitly enables the `fs` features.
|
|
- This is optimized on targets atomic CAS. You may want to use `portable-atomic` feature together if your target doesn't support atomic CAS (e.g., RISC-V without A-extension).
|
|
|
|
- **`panic-unwind`**<br>
|
|
Provide `-C panic=unwind` support for panic handler and enable
|
|
`semihosting::experimental::panic::catch_unwind`.
|
|
|
|
This currently uses [unwinding] crate to support unwinding.
|
|
See its documentation for supported platforms and requirements.
|
|
|
|
Note:
|
|
- This feature is experimental (tracking issue: [#3](https://github.com/taiki-e/semihosting/issues/3))
|
|
and outside of the normal semver guarantees and minor or patch versions of semihosting may make
|
|
breaking changes to them at any time.
|
|
- This currently requires nightly compiler.
|
|
- This implicitly enables the `alloc` and `panic-handler` features.
|
|
- This uses atomic CAS. You need to use `portable-atomic` feature together if your target doesn't support atomic CAS (e.g., RISC-V without A-extension).
|
|
- When enabling this feature, you may need to rebuild the standard library with
|
|
`-C panic=unwind` for `catch_unwind` to work properly. The recommended way to
|
|
rebuild the standard library is passing `-Z build-std="core,alloc"` option to cargo.
|
|
|
|
- **`backtrace`**<br>
|
|
Provide backtrace support for panic handler.
|
|
|
|
This currently uses [unwinding] crate to support backtrace.
|
|
See its documentation for supported platforms and requirements.
|
|
|
|
Note:
|
|
- This feature is experimental (tracking issue: [#3](https://github.com/taiki-e/semihosting/issues/3))
|
|
and outside of the normal semver guarantees and minor or patch versions of semihosting may make
|
|
breaking changes to them at any time.
|
|
- This implicitly enables the `stdio` feature.
|
|
- When enabling this, it is recommended to also enable the `panic-unwind` feature. Otherwise, a decent backtrace will not be displayed at this time. (Using [`-C force-unwind-tables`](https://doc.rust-lang.org/rustc/codegen-options/index.html#force-unwind-tables) may work, but has not been tested yet.)
|
|
- Currently, the backtrace generated is not human-readable.
|
|
|
|
```text
|
|
panicked at 'a', src/main.rs:86:13
|
|
stack backtrace:
|
|
0x84dc0
|
|
0x8ed80
|
|
0x8332c
|
|
0x83654
|
|
0x80644
|
|
0x803cc
|
|
0x809dc
|
|
0x800bc
|
|
```
|
|
|
|
You can use `addr2line` to resolve the addresses and [rustfilt] to demangle Rust symbols.
|
|
For example, run the following command (please replace `<path/to/binary>` with your binary path), then paste the addresses:
|
|
|
|
```sh
|
|
llvm-addr2line -fipe <path/to/binary> | rustfilt
|
|
```
|
|
|
|
[portable-atomic]: https://github.com/taiki-e/portable-atomic
|
|
[rustfilt]: https://github.com/luser/rustfilt
|
|
[unwinding]: https://github.com/nbdd0121/unwinding
|
|
|
|
<!-- tidy:sync-markdown-to-rustdoc:end -->
|
|
*/
|
|
|
|
#![no_std]
|
|
#![doc(test(
|
|
no_crate_inject,
|
|
attr(
|
|
deny(warnings, rust_2018_idioms, single_use_lifetimes),
|
|
allow(dead_code, unused_variables)
|
|
)
|
|
))]
|
|
#![warn(unsafe_op_in_unsafe_fn)]
|
|
#![warn(
|
|
// Lints that may help when writing public library.
|
|
missing_debug_implementations,
|
|
missing_docs,
|
|
clippy::alloc_instead_of_core,
|
|
clippy::exhaustive_enums,
|
|
clippy::exhaustive_structs,
|
|
clippy::impl_trait_in_params,
|
|
clippy::std_instead_of_alloc,
|
|
clippy::std_instead_of_core,
|
|
// clippy::missing_inline_in_public_items,
|
|
)]
|
|
#![allow(
|
|
clippy::inline_always,
|
|
clippy::len_without_is_empty,
|
|
clippy::missing_panics_doc,
|
|
clippy::must_use_candidate,
|
|
clippy::new_without_default
|
|
)]
|
|
#![cfg_attr(
|
|
any(
|
|
all(target_arch = "loongarch32", semihosting_no_asm),
|
|
target_arch = "mips",
|
|
target_arch = "mips32r6",
|
|
target_arch = "mips64",
|
|
target_arch = "mips64r6",
|
|
target_arch = "xtensa",
|
|
),
|
|
feature(asm_experimental_arch)
|
|
)]
|
|
// docs.rs only (cfg is enabled by docs.rs, not build script)
|
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
|
#![cfg_attr(
|
|
docsrs,
|
|
doc(auto_cfg(hide(semihosting_no_duration_checked_float, semihosting_no_error_in_core)))
|
|
)]
|
|
#![cfg_attr(semihosting_no_strict_provenance, allow(unstable_name_collisions))]
|
|
|
|
// 64-bit architecture's 32-bit ABI (e.g., AArch64 ILP32 ABI) are also
|
|
// unsupported yet (is there a semihosting interface defined for those ABIs?).
|
|
#[cfg(not(any(
|
|
all(target_arch = "aarch64", target_pointer_width = "64"),
|
|
target_arch = "arm",
|
|
target_arch = "riscv32",
|
|
all(target_arch = "riscv64", target_pointer_width = "64"),
|
|
target_arch = "loongarch32",
|
|
all(target_arch = "loongarch64", target_pointer_width = "64"),
|
|
target_arch = "mips",
|
|
target_arch = "mips32r6",
|
|
all(target_arch = "mips64", target_pointer_width = "64"),
|
|
all(target_arch = "mips64r6", target_pointer_width = "64"),
|
|
target_arch = "xtensa",
|
|
)))]
|
|
compile_error!(
|
|
"unsupported target; if you need support for this target, \
|
|
please submit an issue at <https://github.com/taiki-e/semihosting>"
|
|
);
|
|
#[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, probe-rs, etc.\n\
|
|
see <https://docs.rs/semihosting/latest/semihosting/#optional-features-openocd-semihosting> for more."
|
|
);
|
|
#[cfg(not(target_arch = "xtensa"))]
|
|
#[cfg(feature = "openocd-semihosting")]
|
|
compile_error!("`openocd-semihosting` feature is only available on Xtensa");
|
|
#[cfg(not(all(
|
|
target_arch = "arm",
|
|
not(any(target_feature = "mclass", semihosting_target_feature = "mclass")),
|
|
)))]
|
|
#[cfg(feature = "trap-hlt")]
|
|
compile_error!("`trap-hlt` feature is only available on Arm A+R profile");
|
|
|
|
#[cfg(feature = "alloc")]
|
|
extern crate alloc;
|
|
#[cfg(doc)]
|
|
extern crate self as semihosting;
|
|
#[cfg(test)]
|
|
extern crate std;
|
|
|
|
#[macro_use]
|
|
mod utils;
|
|
|
|
#[cfg(any(
|
|
feature = "panic-unwind",
|
|
all(feature = "random", any(target_has_atomic = "32", feature = "portable-atomic")),
|
|
))]
|
|
cfg_sel!({
|
|
#[cfg(feature = "portable-atomic")]
|
|
{
|
|
use portable_atomic as atomic;
|
|
}
|
|
#[cfg(else)]
|
|
{
|
|
use core::sync::atomic;
|
|
}
|
|
});
|
|
|
|
#[macro_use]
|
|
mod macros;
|
|
|
|
#[macro_use]
|
|
mod c_str;
|
|
|
|
#[macro_use]
|
|
pub mod fd;
|
|
|
|
#[macro_use]
|
|
pub mod io;
|
|
|
|
#[cfg(any(feature = "args", feature = "panic-unwind", feature = "time", feature = "random"))]
|
|
pub mod experimental;
|
|
#[cfg(feature = "fs")]
|
|
pub mod fs;
|
|
#[cfg(feature = "panic-handler")]
|
|
mod panicking;
|
|
pub mod process;
|
|
pub mod sys;
|
|
|
|
#[cfg(feature = "stdio")]
|
|
mod sealed {
|
|
#[allow(unknown_lints, unnameable_types)] // Not public API. unnameable_types is available on Rust 1.79+
|
|
pub trait Sealed {}
|
|
}
|
|
|
|
// Not public API.
|
|
#[doc(hidden)]
|
|
pub mod __private {
|
|
#[doc(hidden)]
|
|
pub use core::{
|
|
concat,
|
|
ffi::CStr,
|
|
file, line,
|
|
result::Result::{Err, Ok},
|
|
stringify, write, writeln,
|
|
};
|
|
|
|
#[doc(hidden)]
|
|
pub use crate::c_str::const_c_str_check;
|
|
}
|