#aarch64 #arm #cortex-a

no-std aarch64-rt

Startup code and exception vector for aarch64 Cortex-A processors

1 unstable release

new 0.1.0 Mar 10, 2025

#1864 in Embedded development

Download history 91/week @ 2025-03-05

91 downloads per month

MIT/Apache

25KB
353 lines

Startup code for bare-metal aarch64

crates.io page docs.rs page

This crate provides entry point and exception handling for bare-metal Rust binaries on aarch64 Cortex-A processors.

This is not an officially supported Google product.

Usage

Use the entry! macro to mark your main function:

use aarch64_rt::entry;

entry!(main);
fn main(arg0: u64, arg1: u64, arg2: u64, arg3: u64) -> ! {
    // ...
}

arg0 through arg3 will contain the initial values of registers x0x3. These are often used to pass arguments from the previous-stage bootloader, such as the address of the device tree.

You'll need to provide the image origin (which will be the entry point address) and maximum size in a linker script, e.g.:

MEMORY
{
    image : ORIGIN = 0x40080000, LENGTH = 2M
}

You'll need to tell Rust to use both this and the aarch64-rt linker script in your build.rs. Assuming your linker script is called memory.ld:

fn main() {
    println!("cargo:rustc-link-arg=-Timage.ld");
    println!("cargo:rustc-link-arg=-Tmemory.ld");
    println!("cargo:rerun-if-changed=memory.ld");
}

Features

el1, exceptions and initial-pagetable are enabled by default.

el1

If the exceptions feature is also enabled then uses vbar_el1 for the exception vector. If initial-pagetable is also enabled then uses ttbr0_el1 for the page table, and other EL1 MMU configuration registers.

el2

If the exceptions feature is also enabled then uses vbar_el2 for the exception vector. If initial-pagetable is also enabled then uses ttbr0_el2 for the page table, and other EL2 MMU configuration registers.

el3

If the exceptions feature is also enabled then uses vbar_el3 for the exception vector. If initial-pagetable is also enabled then uses ttbr0_el3 for the page table, and other EL3 MMU configuration registers.

exceptions

Provides an exception vector table, and sets it in the appropriate vbar system register for the selected exception level. You must provide handlers for each exception like so:

#[unsafe(no_mangle)]
extern "C" fn sync_exception_current(_elr: u64, _spsr: u64) {
}

#[unsafe(no_mangle)]
extern "C" fn irq_current(_elr: u64, _spsr: u64) {
}

#[unsafe(no_mangle)]
extern "C" fn fiq_current(_elr: u64, _spsr: u64) {
}

#[unsafe(no_mangle)]
extern "C" fn serr_current(_elr: u64, _spsr: u64) {
}

#[unsafe(no_mangle)]
extern "C" fn sync_lower(_elr: u64, _spsr: u64) {
}

#[unsafe(no_mangle)]
extern "C" fn irq_lower(_elr: u64, _spsr: u64) {
}

#[unsafe(no_mangle)]
extern "C" fn fiq_lower(_elr: u64, _spsr: u64) {
}

#[unsafe(no_mangle)]
extern "C" fn serr_lower(_elr: u64, _spsr: u64) {
}

initial-pagetable

Sets an initial pagetable in the appropriate TTBR and enables the MMU and cache before running any Rust code or writing to any memory.

This is especially important if running at EL1 in a VM, as accessing memory with the cache disabled while the hypervisor or host has cacheable aliases to the same memory can lead to cache coherency issues. Even if the host doesn't explicitly access the memory, speculative accesses can lead to cache fills.

License

Licensed under either of

at your option.

Contributing

If you want to contribute to the project, see details of how we accept contributions.

Dependencies