Rust on Zephyr RTOS
Overview
Zephyr module for building a Cargo project and linking it into a Zephyr image. Add this directory to ZEPHYR_EXTRA_MODULES to build a Cargo library project (located in the Zephyr app's source directory by default) and link it into the Zephyr app.
Version Compatibility
Zephyr: v2.3, v2.4, v2.5
Rust: exactly 1.68.0
Features
- Generated bindings for all syscalls
- Safe wrappers for some Zephyr APIs (mutex, semaphore, timers, k_poll, UART)
- Basic libstd port (no_std not necessary)
- Heap (std::alloc) see CONFIG_RUST_ALLOC_POOL
- Thread-local storage
- Kernel or user-mode Rust
- Rust globals and heap in a Rust-specific memory segment that can be granted to specific threads
- Syscalls compile to direct C function calls when !CONFIG_USERSPACE
- Note: running kernel and user-mode Rust at the same time could pose a security risk, since there is one shared global allocator
- Minimal std::futures executor
- Supports dynamic tasks and timers
- Currently single-threaded
- async/await UART example
- Implemented as a Zephyr module for inclusion in existing Zephyr projects
- No modifications to Zephyr source
Building and Running
Clone the repo
Make sure to clone the submodules recursively. This points to modified Rust libstd.
git clone --recurse-submodules https://github.com/tylerwhall/zephyr-rust.git
Zephyr setup
Refer to the Zephyr getting started guide. This includes installing west, getting Zephyr source, and the Zephyr toolchain. Make sure you can build a C sample within Zephyr.
See above for tested compatible Zephyr releases. Please try a release if master does not work. Due to differences in the syscall header generation, v1.14 LTS is no longer supported. See issue 16.
Rust toolchain
The compiler version must exactly match the version of standard library included as a submodule of this project. In practice, using a different compiler version often fails to compile because of Rust internally making heavy use of unstable compiler features.
The current base is stable-1.68.0. Rustup is the default workflow, and the rust-toolchain file in this repo should cause rustup to automatically install and use the right version. If not, manually install:
rustup toolchain install 1.68.0
If supplying your own rustc and cargo, make sure they are the version above. The build will fail if it detects a version mismatch.
Also install clang from your distro. This is required by bindgen to generate syscall bindings. Else you will get this error
thread 'main' panicked at 'Unable to find libclang: "couldn\'t find any valid shared libraries matching: [\'libclang.so\', \'libclang-*.so\', \'libclang.so.*\']
Build
west build -p auto -b <board name> samples/rust-app/
Native:
west build -p auto -b native_posix samples/rust-app/
qemu_x86:
west build -p auto -b qemu_x86 samples/rust-app/
ARM Cortex-M:
west build -p auto -b qemu_cortex_m3 samples/rust-app/
These errors are normal. Needs investigation, but the binary is still created successfully.
x86_64-zephyr-elf-objdump: DWARF error: mangled line number section (bad file number)
Run (QEMU targets):
cd build
ninja run
Sample Output
*** Booting Zephyr OS build zephyr-v2.2.0 ***
Hello Rust println
Hello from Rust kernel with direct kernel call
Hello from Rust kernel with runtime-detect syscall
Hello from second thread
second thread: f = 1
second thread: now f = 55
Time InstantMs(20)
Time Instant(InstantMs(20))
Locking
Unlocking
No device
Boxed value 1
main thread: f = 1
main thread: now f = 2
Hello from Rust userspace with forced user-mode syscall
Locking
Unlocking
INFO app: TEST: info!()
WARN app: TEST: warn!()
ERROR app: TEST: error!()
main thread: f = 2
main thread: now f = 3
Hello from Rust userspace with forced user-mode syscall
Hello from Rust userspace with runtime-detect syscall
Next call will crash if userspace is working.
FAILED: zephyr/CMakeFiles/run
Failure is from an intentional crash at the end of the sample.
Testing
The Zephyr test runner can be used:
$ZEPHYR_BASE/scripts/sanitycheck --testcase-root tests -p native_posix -N
Or you can build and run the test manually:
west build -p auto -b native_posix tests/rust
cd build
ninja run
Supported Architectures
- native_posix
- x86
- armv7m
- armv7r
- thumbv7em
Really anything that works with Zephyr and Rust should work. Only need to define a target.json and add a case for it in CMakelists.
TODO
- Figure out how to fail tests through assertions in code
- Support #[test]
- CI
- Ability to build multiple independent apps
- Investigate DWARF errors in final link
- More safe bindings (e.g. GPIO)
Features Not Planned to Support
- std::thread. Requires thread resources to be dynamically allocated. This is possible, but not common for Zephyr.
- Defining static threads in Rust. Zephyr uses many layers of architecture-specific C macros that would not be wise to try to duplicate exactly in Rust. Possibly could generate C code like in the "cpp" crate, but for now just define threads in C and point them at a Rust FFI entry point.
- std::sync::{Mutex, RwLock}. Mutex should work when built without userspace support. Userspace would require (at least) CONFIG_DYNAMIC_OBJECTS. While this is possible, I don't want to require it to use libstd. May revisit. The small number of uses in libstd are patched out.
License
Licensed under either of
- Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0
- MIT license 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.