[][src]Crate r3_port_arm

The Arm-A port for the R3 kernel.

Startup Code

use_startup! generates an entry point (with a symbol name start), which is expected to be called by a bootloader. The startup code configures MMU to assign appropriate memory attributes based on the memory map supplied by StartupOptions::MEMORY_MAP and to map an exception vector table at 0x0000_0000 or 0xffff_0000.

Linker Scripts

This crate provides linker scripts that define some standard sections, suitable for use with a bootloader that can handle ELF sections. Put files as follows under your crate's directory:


rustflags = ["-C", "link-arg=-Tlink_ram.x"]


  RAM : ORIGIN = 0x20000000, LENGTH = 10240K


This example is not tested
fn main() {
    // Use the linker script `memory.x` at the crate root

The following linker scripts are provided:

  • link_ram.x places all sections in RAM.
  • link_ram_harvard.x places .text in RAM_CODE and all remaining sections in RAM_DATA. Combined with an approriate MMU configuration, this can be used to implement the W⊕X (write xor execute) memory policy for enhanced security. It might also lead to a performance improvement on a processor having separate buses for instruction and data access.

Kernel Timing

As far as kernel timing is concerned, there is no universal solution for a Cortex-A system. This crate provides a port timer driver for Arm PrimeCell SP804 Dual Timer, which can be instantiated by use_sp804!.

Interrupt Controller

Your system type should be combined with an interrupt controller driver by implementing PortInterrupts and InterruptController. Most systems are equipped with Arm Generic Interrupt Controller (GIC), whose driver is provided by use_gic!.

The maximum possible range of valid interrupt numbers is 0..1020 (the upper bound varies across implementations). The range is statically partitioned as follows:

  • 0..16 is used for SGIs (Software-Generated Interrupts), which are used for inter-processor communication. SGIs don't support enabling, disabling, or changing their trigger modes.
  • 16..32 is used for PPIs (Private Peripheral Interrupts), which are peripheral interrupts specific to a single processor.
  • 32.. is used for SPIs (Shared Peripheral Interrupts), which are peripheral interrupts that the Distributor can route to a specified set of processors. The current implementation of the GIC driver routes all interrupts to CPU 0, assuming that's where the application runs.

The valid priority range is 0..255. All priorities are managed - unmanaged interrupts aren't supported yet.

The GIC driver exposes additional operations on interrupt lines through Gic implemented on your system type.


Context state

The state of an interrupted thread is stored to the interrupted thread's stack in the following form:

This example is not tested
struct ContextState {
    // Second-level state
    // Includes everything that is not included in the first-level state. These
    // are moved between memory and registers only when switching tasks.
    // TODO: Floating-point registers
    r4: u32,
    r5: u32,
    r6: u32,
    r7: u32,
    r8: u32,
    r9: u32,
    r10: u32,
    r11: u32,

    // First-level state
    // This was designed after Arm-M's exception frame.
    // The GPR potion is comprised of caller-saved registers. In an exception
    // handler, saving/restoring this set of registers at entry and exit allows
    // it to call Rust functions.
    // `{pc, cpsr}` is the sequence of registers that the RFE (return from
    // exception) instruction expects to be in memory in this exact order.
    r0: u32,
    r1: u32,
    r2: u32,
    r3: u32,
    r12: u32,
    lr: u32,
    pc: u32,
    cpsr: u32,

sp is stored in TaskCb::port_task_state.

When a task is activated, a new context state is created inside the task's stack. By default, only essential registers are preloaded with known values. The preload-registers Cargo feature enables preloading for all GPRs, which might help in debugging at the cost of performance and code size.

For the idle task, saving and restoring the context store is essentially replaced with no-op or loads of hard-coded values. In particular, pc is always “restored” with the entry point of the idle task.

Processor Modes

  • System: Task context. The idle task (the implicit task that runs when *running_task_ptr().is_none()) uses this mode with sp_usr == 0 (no other tasks or non-task contexts use sp == 0, so this is straightforward to detect).
  • Supervisor: Non-task context
  • IRQ: The processor enters this mode when it takes an exception. This state lasts only briefly because the IRQ handler switches to Supervisor as soon as possible to allow reentry. sp_irq is only used as a scratch register.



Implement PortInterrupts, InterruptController, and Gic on the given system type using the General Interrupt Controller (GIC) on the target. Requires GicOptions.


Define a system type implementing PortThreading and EntryPoint. Requires ThreadingOptions, InterruptController, and Timer.


Attach the implementation of PortTimer that is based on Arm PrimeCell SP804 Dual Timer to a given system type. This macro also implements Timer on the system type. Requires Sp804Options.


Generate startup code. Requires StartupOptions and EntryPoint to be implemented.





Specifies the type of signal transition that pends an interrupt.




Defines the entry points of a port instantiation. Implemented by use_port!.


Provides access to a system-global GIC instance. Implemented by use_gic!.


The options for use_gic!.


An abstract interface to an interrupt controller. Implemented by use_gic!.


The options for use_sp804!.


The options for use_startup!.


The configuration of the port.


An abstract inferface to a port timer driver. Implemented by use_sp804!.