Rust on RTEMS
Frank Kühndel
frank.kuehndel at embedded-brains.de
Fri Dec 22 13:40:23 UTC 2023
Hello Joel,
On 12/20/23 23:37, Joel Sherrill wrote:
> Karel Gardas posted in February that he has Rust on RTEMS on an arm.
This is one email from that thread:
https://lists.rtems.org/pipermail/devel/2023-March/074532.html
>
> Frank. Are there instructions on building the tools chain somewhere?
I attach a file with instructions which I just compiled from my notices
(my Christmas gift to you ;-) ). I have not tested these instructions
(again) but I hope they will give you a hint how it did it. I attach
them as file to avoid my mail program inserts line breaks.
In case you or anyone else gives these instructions a try, I will be
happy for all feedback. The documentation I want to write for the User
Manual (if no one else does it before me or has a better approach) would
closely follow the steps in the attached file.
>
> And is Jan Sommer part of the DLR Rust on RTEMS project?
I met Jan at ESA ADCSS 2023 at Nordwijk. I prefer Jan answers your
question as I do not want to make anything public without prior
agreement from DLR.
Greetings,
fk
>
> --joel
>
> On Wed, Dec 20, 2023, 3:59 PM Frank Kühndel <
> frank.kuehndel at embedded-brains.de> wrote:
>
>> Hello Dwaine,
>>
>> On 12/20/23 20:41, Molock, Dwaine S. (GSFC-5820) wrote:
>> > Hello,
>> >
>> > Has anyone been able to execute Rust on RTEMS?
>>
>> Yes – to use RTEMS from within a Rust application, with
>>
>> #![no_std]
>> #![no_main]
>>
>> >
>> > If so, is there a how to guide and what architecture and development
>> hardware was used?
>>
>> I have not yet found time to write a documentation despite I want to do
>> so. Sorry for this. Ferrous Systems first extended Rust to run on
>> Gaisler SPARC bare metal and then they figured it works with RTEMS 5
>> from Gaisler, too. Their documentation is here:
>> https://github.com/ferrous-systems/sparc-experiments/
>>
>> I did run my examples only on simulators. I tried two architectures with
>> RTEMS 6: Leon3 and RISC-V.
>>
>> Greetings,
>> Frank
>>
>> --
>> embedded brains GmbH & Co. KG
>> Herr Frank KÜHNDEL
>> Dornierstr. 4
>> 82178 Puchheim
>> Germany
>> email:frank.kuehndel at embedded-brains.de
>> phone: +49-89-18 94 741 - 23
>> mobile: +49-176-15 22 06 - 11
>>
>> Registergericht: Amtsgericht München
>> Registernummer: HRA 117265
>> Vertretungsberechtigte Geschäftsführer: Peter Rasmussen, Thomas Dörfler
>> Unsere Datenschutzerklärung finden Sie hier:
>> https://embedded-brains.de/datenschutzerklaerung/
>> _______________________________________________
>> users mailing list
>> users at rtems.org
>> http://lists.rtems.org/mailman/listinfo/users
>
--
embedded brains GmbH & Co. KG
Herr Frank KÜHNDEL
Dornierstr. 4
82178 Puchheim
Germany
email: frank.kuehndel at embedded-brains.de
phone: +49-89-18 94 741 - 23
mobile: +49-176-15 22 06 - 11
Registergericht: Amtsgericht München
Registernummer: HRA 117265
Vertretungsberechtigte Geschäftsführer: Peter Rasmussen, Thomas Dörfler
Unsere Datenschutzerklärung finden Sie hier:
https://embedded-brains.de/datenschutzerklaerung/
-------------- next part --------------
Use Rust with RTEMS
===================
2023-Dec-22 Frank Kühndel, embedded brains GmbH & Co. KG
This text describes how I used Rust with RTEMS.
This is from my notices, I have not checked these instructions
again and they come without any grantees.
I intent to write a (better) how-to for the RTEMS User Manual
as soon as I find some time for it.
These instructions are for two Hello-World programs, one for
SPARC Leon3 and the other for RISC-V. In both cases, the
Rust program use `printk()` from RTEMS to print text to the
console.
The basic steps are these:
* Compile the Rust code containing `main()` into a
static library using the Rust compiler.
* Compile the RTEMS configuration in `Init.c` into an object
file using the gcc from the RTEMS tool chain.
* Finally link the static library with the Rust code,
the RTEMS init configuration and the RTEMS OS
together into one single executable.
* Run the executable on a simulator.
Note, the Rust SPARC support for Leon3/4/5 is rather new and
requires the nightly build of the Rust compiler.
My whole work was triggered and inspired by a presentation
made by Jonathan Pallant from Ferrous Systems GmbH on
ESA Software Product Assurance Workshop 2023:
"Using Rust for mission critical systems"
https://www.cosmos.esa.int/documents/10939403/13948935/1_Jonathan_Pallant_Using+Rust+for+Mission+Critical+Systems+v5.pdf/12be67e0-de49-3baf-4c00-d797784c6b6a?t=1695808956535
Moreover, he created https://github.com/ferrous-systems/sparc-experiments/
Essentially, Ferrous Systems has certified the open source Rust
compiler for ISO 26262 (ASIL D) as a product named ferrocene.
There is one known Bug in the examples below: `panic()`
always prints "panic occured!" instead of the panic message.
If you have a fix, I will be happy to use it for the documentation.
Setup RTEMS and Rust
====================
I used an OpenSUSE Leap 15.5 container to have clean environment.
I installed the patterns devel_basis and devel_C_C++ as well as
the packages gzip, python3 and qemu-extra. To use Python 3 as
`python` OpenSUSE requires this command:
```
# update-alternatives --install /usr/bin/python python /usr/bin/python3 20
```
Moreover, I created a user "ferris" to avoid working as root:
```
# useradd -c "Build User" -g "users" -d "/home/ferris" --create-home "ferris"
```
Install RTEMS Tools
-------------------
I needed to install the RTEMS 6 tool chain for "sparc" and "riscv"
using the RTEMS source builder. I assume you how know how to
do this. I installed to prefix `/opt/rtems/6` and added it to the
PATH environment variable:
```
# su ferris
> echo "export PATH=/opt/rtems/6/bin:${PATH}" >>~/.bashrc
```
Install and Setup Rust
----------------------
I installed rust from the web-page:
```
# su ferris
> curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Rustup metadata and toolchains will be installed into the Rustup
home directory, located at:
/home/ferris/.rustup
This can be modified with the RUSTUP_HOME environment variable.
The Cargo home directory is located at:
/home/ferris/.cargo
This can be modified with the CARGO_HOME environment variable.
The cargo, rustc, rustup and other commands will be added to
Cargo's bin directory, located at:
/home/ferris/.cargo/bin
This path will then be added to your PATH environment variable by
modifying the profile files located at:
/home/ferris/.profile
/home/ferris/.bashrc
You can uninstall at any time with rustup self uninstall and
these changes will be reverted.
```
At this point it is the best to logout and login again.
```
# su ferris
> rustup update
> cargo --version
cargo 1.73.0 (9c4383fb5 2023-08-26)
> sparc-rtems6-gcc --version
sparc-rtems6-gcc (GCC) 12.3.1 20231012 (RTEMS 6, RSB 103006fc0bdc6eff7760cb74f15bc16ac4238087-modified, Newlib fbc5496)
[...]
```
Install RTEMS
-------------
```
# su ferris
> cd
> git clone git://git.rtems.org/rtems.git
> cd rtems
> cat >config.ini <<"EOF"
[sparc/leon3]
RTEMS_SMP = True
[riscv/rv64imafdc]
EOF
> ./waf configure --prefix=/opt/rtems/6
> ./waf
> ./waf install
```
I did run some RTEMS tests to make sure the installation
and the simulators are working:
```
> sparc-rtems6-sis -leon3 -nouartrx -r m 4 build/sparc/leon3/testsuites/samples/hello.exe
> sparc-rtems6-sis -leon3 -nouartrx -r m 4 build/sparc/leon3/testsuites/samples/ticker.exe
> qemu-system-riscv64 -M virt -nographic -bios build/riscv/rv64imafdc/testsuites/samples/hello.exe
> qemu-system-riscv64 -M virt -nographic -bios build/riscv/rv64imafdc/testsuites/samples/ticker.exe
```
Rust Hello World with RTEMS on SPARC
====================================
I needed to use the Rust nightly build because the support for
Gaisler LEON3/4/5 was added Jul 2023 and is not yet available
in the stable Rust:
```
# su ferris
> cd
> rustup toolchain add nightly
> rustup component add rust-src --toolchain=nightly
```
I created a simple RTEMS `init.c` to configure RTEMS in a
new directory:
```
# su ferris
> cd
> mkdir example-rust
> cd example-rust
> cat >init.c <<"EOF"
/*
* Simple RTEMS configuration
*/
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
#define CONFIGURE_UNLIMITED_OBJECTS
#define CONFIGURE_UNIFIED_WORK_AREAS
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_INIT
#include <rtems/confdefs.h>
EOF
```
I created a new Rust project (still in `~/example-rust`):
```
> cargo new --lib --vcs=none rust-sparc
> cd rust-sparc
> sed -i '/^#/ a \\n[lib]\ncrate-type = ["staticlib"]' Cargo.toml
```
I created the Rust application code (still in
`~/example-rust/rust-sparc`):
```
> cat >src/lib.rs <<"EOF"
#![no_std]
#![no_main]
use core::fmt::Write;
use core::ffi::c_char;
extern "C" {
fn printk(fmt: *const core::ffi::c_char, ...) -> core::ffi::c_int;
fn rtems_panic(fmt: *const core::ffi::c_char, ...) -> !;
}
/// Write text to the console using RTEMS `printk` function
struct Console;
impl core::fmt::Write for Console {
fn write_str(&mut self, message: &str) -> core::fmt::Result {
const FORMAT_STR: &core::ffi::CStr = {
let Ok(s) = core::ffi::CStr::from_bytes_with_nul(b"%.*s\0") else {
panic!()
};
s
};
if message.len() != 0 {
unsafe {
printk(FORMAT_STR.as_ptr(), message.len() as core::ffi::c_int, message.as_ptr());
}
}
Ok(())
}
}
/// Our [`Init`] calls [`rust_main`] and handles errors
#[no_mangle]
pub extern "C" fn Init() {
if let Err(e) = rust_main() {
panic!("Main returned {:?}", e);
}
}
/// This is the `main` function of this program
fn rust_main() -> Result<(), core::fmt::Error> {
let mut console = Console;
writeln!(console, "Hello from Rust")?;
Ok(())
}
/// Handle panic by forwarding it to the `rtems_panic()` handler
#[panic_handler]
fn panic(panic: &core::panic::PanicInfo) -> ! {
let mut message = "panic occured!";
if let Some(s) = panic.payload().downcast_ref::<&str>() {
message = *s;
}
const FORMAT_PTR: *const c_char = {
const BYTES: &[u8] = b"%.*s\n\0";
BYTES.as_ptr().cast()
};
unsafe {
rtems_panic(FORMAT_PTR, message.len() as core::ffi::c_int, message.as_ptr());
}
}
EOF
```
I created a configuration file for Cargo (still in
`~/example-rust/rust-sparc`):
```
> mkdir .cargo
> cat >.cargo/config.toml <<"EOF"
# Available in rust nightly from 2023-07-18
[target.sparc-unknown-none-elf]
# Either kind should work as a linker
linker = "sparc-rtems6-gcc"
# linker = "sparc-rtems6-clang"
rustflags = [
# The target is LEON3
"-Ctarget-cpu=leon3",
# The linker is a gcc compatible C Compiler
"-Clinker-flavor=gcc",
# Pass these options to the linker
"-Clink-arg=-mcpu=leon3",
# Rust needs libatomic.a to satisfy Rust's compiler-builtin library
"-Clink-arg=-latomic",
]
runner = "sparc-rtems6-sis -leon3 -nouartrx -r m 4"
[build]
target = ["sparc-unknown-none-elf"]
[unstable]
build-std = ["core"]
EOF
```
Build and Run on SPARC
----------------------
I compiled the Rust source file into a static library (still in
`~/example-rust/rust-sparc`):
```
> cargo +nightly build --target=sparc-unknown-none-elf
```
It should create `~/example-rust/rust-sparc/target/sparc-unknown-none-elf/debug/librust_sparc.a`
I compiled the RTEMS `init.c` file and linked everything
together into an executable:
```
> cd ~/example-rust
> export PKG_CONFIG_SPARC=/opt/rtems/6/lib/pkgconfig/sparc-rtems6-leon3.pc
> sparc-rtems6-gcc -Wall -Wextra -O2 -g -fdata-sections -ffunction-sections $(pkg-config --cflags ${PKG_CONFIG_SPARC}) init.c -c -o init_sparc.o
> sparc-rtems6-gcc -qnolinkcmds -T linkcmds.leon3 init_sparc.o -Lrust-sparc/target/sparc-unknown-none-elf/debug -lrust_sparc -orust-sparc.exe $(pkg-config --libs ${PKG_CONFIG_SPARC})
```
Then I was able to run the executable (still in
`~/example-rust`):
```
> rtems-run --rtems-bsp=leon3-sis rust-sparc.exe
```
Rust Hello World with RTEMS on RISC-V
=====================================
This is very similar to the SPARC variant above with tiny changes.
Moreover, one does not need a nightly build of the Rust tools.
I reused the directory `~/example-rust` and the file RTEMS
`init.c` which I created at the beginning of the SPARC
variant above. Next, I created a new Rust project:
```
# su ferris
> cd ~/example-rust
> cargo new --lib --vcs=none rust-riscv
> cd rust-riscv
> sed -i '/^#/ a \\n[lib]\ncrate-type = ["staticlib"]' Cargo.toml
```
I created the Rust application code (still in
`~/example-rust/rust-riscv`):
```
> cat >src/lib.rs <<"EOF"
#![no_std]
#![no_main]
use core::fmt::Write;
use core::ffi::c_char;
extern "C" {
fn printk(fmt: *const core::ffi::c_char, ...) -> core::ffi::c_int;
fn rtems_panic(fmt: *const core::ffi::c_char, ...) -> !;
fn rtems_shutdown_executive(fatal_code: u32);
}
/// Write text to the console using RTEMS `printk` function
struct Console;
impl core::fmt::Write for Console {
fn write_str(&mut self, message: &str) -> core::fmt::Result {
const FORMAT_STR: &core::ffi::CStr = {
let Ok(s) = core::ffi::CStr::from_bytes_with_nul(b"%.*s\0") else {
panic!()
};
s
};
if message.len() != 0 {
unsafe {
printk(FORMAT_STR.as_ptr(), message.len() as core::ffi::c_int, message.as_ptr());
}
}
Ok(())
}
}
/// Our [`Init`] calls [`rust_main`] and handles errors
#[no_mangle]
pub extern "C" fn Init() {
if let Err(e) = rust_main() {
panic!("Main returned {:?}", e);
}
unsafe {
rtems_shutdown_executive( 0 );
}
}
/// This is the `main` function of this program
fn rust_main() -> Result<(), core::fmt::Error> {
let mut console = Console;
writeln!(console, "Hello from Rust")?;
Ok(())
}
/// Handle panic by forwarding it to the `rtems_panic()` handler
#[panic_handler]
fn panic(panic: &core::panic::PanicInfo) -> ! {
let mut message = "panic occured!";
if let Some(s) = panic.payload().downcast_ref::<&str>() {
message = *s;
}
const FORMAT_PTR: *const c_char = {
const BYTES: &[u8] = b"%.*s\n\0";
BYTES.as_ptr().cast()
};
unsafe {
rtems_panic(FORMAT_PTR, message.len() as core::ffi::c_int, message.as_ptr());
}
}
EOF
```
I created a configuration file for Cargo (still in
`~/example-rust/rust-riscv`):
```
> mkdir .cargo
> cat >.cargo/config.toml <<"EOF"
[target.riscv64gc-unknown-none-elf]
# Either kind should work as a linker
linker = "riscv-rtems6-gcc"
# linker = "riscv-rtems6-clang"
rustflags = [
# See `rustc --target=riscv64gc-unknown-none-elf --print target-cpus`
"-Ctarget-cpu=generic-rv64",
# The linker is a gcc compatible C Compiler
"-Clinker-flavor=gcc",
# Pass these options to the linker
"-Clink-arg=-march=rv64imafdc",
"-Clink-arg=-mabi=lp64d",
"-Clink-arg=-mcmodel=medany",
# Rust needs libatomic.a to satisfy Rust's compiler-builtin library
"-Clink-arg=-latomic",
]
runner = "qemu-system-riscv64 -M virt -nographic -bios "
[build]
target = ["riscv64gc-unknown-none-elf"]
[unstable]
build-std = ["core"]
EOF
```
As a side node, to get an idea what hardware Rust supports
and some more information try these (or similar) commands:
* `rustc --print target-list`
* `rustc --target=riscv64gc-unknown-none-elf --print target-features`
* `rustc --target=riscv64gc-unknown-none-elf --print target-cpus`
Build and Run on RISC-V
-----------------------
First, I needed to download some additional files for this target:
```
> rustup target add riscv64gc-unknown-none-elf
```
I compiled the Rust source file into a static library (still in
`~/example-rust/rust-riscv`):
```
> cargo build --target=riscv64gc-unknown-none-elf
```
It should create
`target/riscv64gc-unknown-none-elf/debug/librust_riscv.a`
I compiled the RTEMS `init.c` file and linked everything
together into an executable:
```
> cd ~/example-rust
> export PKG_CONFIG_RISCV=/opt/rtems/6/lib/pkgconfig/riscv-rtems6-rv64imafdc.pc
> riscv-rtems6-gcc -Wall -Wextra -O2 -g -fdata-sections -ffunction-sections $(pkg-config --cflags ${PKG_CONFIG_RISCV}) init.c -c -o init_riscv.o
> riscv-rtems6-gcc init_riscv.o -Lrust-riscv/target/riscv64gc-unknown-none-elf/debug -lrust_riscv -orust_riscv.exe $(pkg-config --variable=ABI_FLAGS ${PKG_CONFIG_RISCV}) $(pkg-config --libs ${PKG_CONFIG_RISCV})
```
Then I was able to run the executable (still in
`~/example-rust`):
```
> rtems-run --rtems-bsp=rv64imafdc rust_riscv.exe
```
--
embedded brains GmbH & Co. KG
Herr Frank KÜHNDEL
Dornierstr. 4
82178 Puchheim
Germany
email: frank.kuehndel at embedded-brains.de
phone: +49-89-18 94 741 - 23
mobile: +49-176-15 22 06 - 11
Registergericht: Amtsgericht München
Registernummer: HRA 117265
Vertretungsberechtigte Geschäftsführer: Peter Rasmussen, Thomas Dörfler
Unsere Datenschutzerklärung finden Sie hier:
https://embedded-brains.de/datenschutzerklaerung/
More information about the users
mailing list