Hello, World!
Run the hello world example:
cd workspaces/root-task/hello-world
make simulate
Press ctrl-a x
to exit QEMU.
Here is its source:
#![no_std]
#![no_main]
use sel4_root_task::root_task;
#[root_task]
fn main(_bootinfo: &sel4::BootInfoPtr) -> ! {
sel4::debug_println!("Hello, World!");
sel4::init_thread::suspend_self()
}
The Rust standard library is divided into three layers:
core
: dependency-free foundationalloc
: implements heap-backed data structures, but requires a runtime that provides a heap allocatorstd
includescore
andalloc
, and adds APIs that depend on OS services such as networking and filesystems
The high-level std
doesn't support the low-level seL4 root task target.
#![no_std]
declares that this crate does not depend on std
, and prevents rustc from automatically importing it.
Our language runtime will handle the program's entrypoint differently than a typical Rust program.
#![no_main]
informs rustc of this fact.
The sel4
crate binds the seL4 API.
It is generated from source (.xml
, .bf
, and .h
) in libsel4
.
We will cover the contents of this crate in future chapters.
The sel4_root_task
crate implements a Rust language runtime for the root task environment.
The #[root_task]
attribute macro declares a function to be the root task's entrypoint.
The entrypoint function must have a signature of the form:
fn(&sel4::BootInfoPtr) -> T
where
T: sel4_root_task::Termination
(Rustdoc for BootInfoPtr
and Termination
)
The root task has no way to exit, so, to terminate cleanly, it must suspend its own thread.
sel4::init_thread::suspend_self()
does exactly this.
Step 2.A (exercise)
Exercise: Cause a panic.
Step 2.B (exercise)
Exercise: Catch the panic using sel4_root_task::panicking::catch_unwind()
.
Step 2.C (exercise)
You can set a custom panic hook with sel4_root_task::panicking::PanicHook
.
The default hook just prints the panic's ExternalPanicInfo
.
Exercise: Set a custom panic hook.
Step 2.D (exercise)
Exercise: Cause a stack overflow.
Step 2.E (exercise)
The #[root_task]
attribute macro accepts a named stack_size
parameter, which can be any expression of type usize
and whose value is interpreted as the root task's initial thread's stack size, in bytes.
For example:
#[root_task(stack_size = 13 * 37)]
The default stack size is sel4_root_task::DEFAULT_STACK_SIZE
.
Exercise: Adjust the root task's initial thread's stack size to prevent the stack overflow you just caused.
Step 2.F (exercise)
By default, the sel4_root_task
runtime does not include a heap.
Any attempt to use the alloc
crate will result in a link-time failure.
The #[root_task]
attribute macro accepts a heap_size
parameter, which can be any expression of type usize
and whose value is interpreted as the root task's heap size, in bytes.
Note that heap_size
must come after stack_size
in the case where both are present.
For example:
#[root_task(heap_size = 0xf00d)]
or
#[root_task(stack_size = 13 * 37, heap_size = 0xf00d)]
Exercise: Add a heap and use it.
Step 2.G
The sel4_logging
crate builds on top of the log crate to add utilities for initializing simple loggers in minimal environments, such as a seL4 root task.
This step demonstrates one way to initialize a logger using this crate:
static LOGGER: Logger = LoggerBuilder::const_default()
.level_filter(LevelFilter::Info)
.write(|s| sel4::debug_print!("{}", s))
.build();
LOGGER.set().unwrap();