From e624ec6f7f57a28414cc13bd67c1031d23c89376 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Mon, 16 Feb 2026 21:48:23 +0000 Subject: [PATCH 1/6] Split out SMP tests into their own folder. Will make it easier to add other tests, like sharing the GIC across cores. --- .cargo/config.toml | 56 --- .gitignore | 2 + .vscode/settings.json | 4 +- Cargo.toml | 1 + examples/mps3-an536-smp/.cargo/config.toml | 10 + examples/mps3-an536-smp/Cargo.toml | 31 ++ examples/mps3-an536-smp/README.md | 106 +++++ examples/mps3-an536-smp/build.rs | 26 ++ examples/mps3-an536-smp/commands.gdb | 13 + examples/mps3-an536-smp/memory.x | 82 ++++ .../reference/smp-test-armv8r-none-eabihf.out | 4 + .../smp-test-thumbv8r-none-eabihf.out | 4 + examples/mps3-an536-smp/rust-toolchain.toml | 6 + examples/mps3-an536-smp/src/bin/smp-test.rs | 113 +++++ examples/mps3-an536-smp/src/lib.rs | 433 ++++++++++++++++++ examples/mps3-an536/.cargo/config.toml | 8 + .../reference/smp_test-armv8r-none-eabihf.out | 1 - .../smp_test-armv8r-none-eabihf_smp2.out | 2 - .../smp_test-thumbv8r-none-eabihf.out | 1 - .../smp_test-thumbv8r-none-eabihf_smp2.out | 1 - examples/mps3-an536/src/bin/smp_test.rs | 354 -------------- examples/versatileab/.cargo/config.toml | 49 ++ justfile | 20 +- 23 files changed, 903 insertions(+), 424 deletions(-) create mode 100644 examples/mps3-an536-smp/.cargo/config.toml create mode 100644 examples/mps3-an536-smp/Cargo.toml create mode 100644 examples/mps3-an536-smp/README.md create mode 100644 examples/mps3-an536-smp/build.rs create mode 100644 examples/mps3-an536-smp/commands.gdb create mode 100644 examples/mps3-an536-smp/memory.x create mode 100644 examples/mps3-an536-smp/reference/smp-test-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536-smp/reference/smp-test-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536-smp/rust-toolchain.toml create mode 100644 examples/mps3-an536-smp/src/bin/smp-test.rs create mode 100644 examples/mps3-an536-smp/src/lib.rs delete mode 100644 examples/mps3-an536/reference/smp_test-armv8r-none-eabihf.out delete mode 100644 examples/mps3-an536/reference/smp_test-armv8r-none-eabihf_smp2.out delete mode 100644 examples/mps3-an536/reference/smp_test-thumbv8r-none-eabihf.out delete mode 100644 examples/mps3-an536/reference/smp_test-thumbv8r-none-eabihf_smp2.out delete mode 100644 examples/mps3-an536/src/bin/smp_test.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index d4d345ef..e69de29b 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,56 +0,0 @@ -[target.armv8r-none-eabihf] -# Note, this requires QEMU 9 or higher -runner = "qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -audio none -kernel" - -[target.thumbv8r-none-eabihf] -# Note, this requires QEMU 9 or higher -runner = "qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -audio none -kernel" - -[target.armv7r-none-eabihf] -runner = "qemu-system-arm -machine versatileab -cpu cortex-r5f -semihosting -nographic -audio none -kernel" - -[target.thumbv7r-none-eabihf] -runner = "qemu-system-arm -machine versatileab -cpu cortex-r5f -semihosting -nographic -audio none -kernel" - -[target.armv7r-none-eabi] -# change '-mcpu=cortex-r5' to '-mcpu=cortex-r5f' if you use eabi-fpu feature, otherwise -# qemu-system-arm will lock up -runner = "qemu-system-arm -machine versatileab -cpu cortex-r5 -semihosting -nographic -audio none -kernel" - -[target.thumbv7r-none-eabi] -# change '-mcpu=cortex-r5' to '-mcpu=cortex-r5f' if you use eabi-fpu feature, otherwise -# qemu-system-arm will lock up -runner = "qemu-system-arm -machine versatileab -cpu cortex-r5 -semihosting -nographic -audio none -kernel" - -[target.armv7a-none-eabihf] -runner = "qemu-system-arm -machine versatileab -cpu cortex-a8 -semihosting -nographic -audio none -kernel" - -[target.thumbv7a-none-eabihf] -runner = "qemu-system-arm -machine versatileab -cpu cortex-a8 -semihosting -nographic -audio none -kernel" - -[target.armv7a-none-eabi] -runner = "qemu-system-arm -machine versatileab -cpu cortex-a8 -semihosting -nographic -audio none -kernel" - -[target.thumbv7a-none-eabi] -runner = "qemu-system-arm -machine versatileab -cpu cortex-a8 -semihosting -nographic -audio none -kernel" - -[target.armv6-none-eabihf] -runner = "qemu-system-arm -machine versatileab -cpu arm1176 -semihosting -nographic -audio none -kernel" - -[target.armv6-none-eabi] -runner = "qemu-system-arm -machine versatileab -cpu arm1176 -semihosting -nographic -audio none -kernel" - -[target.thumbv6-none-eabi] -runner = "qemu-system-arm -machine versatileab -cpu arm1176 -semihosting -nographic -audio none -kernel" - -[target.armv5te-none-eabi] -runner = "qemu-system-arm -machine versatileab -cpu arm926 -semihosting -nographic -audio none -kernel" - -[target.thumbv5te-none-eabi] -runner = "qemu-system-arm -machine versatileab -cpu arm926 -semihosting -nographic -audio none -kernel" - -[target.armv4t-none-eabi] -runner = "qemu-system-arm -machine versatileab -cpu arm926 -semihosting -nographic -audio none -kernel" - -[target.thumbv4t-none-eabi] -runner = "qemu-system-arm -machine versatileab -cpu arm926 -semihosting -nographic -audio none -kernel" diff --git a/.gitignore b/.gitignore index a139b23f..666f29a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ target examples/mps3-an536/target examples/mps3-an536/target-d32 +examples/mps3-an536-smp/target +examples/mps3-an536-smp/target-d32 examples/versatileab/target examples/versatileab/target-d32 Cargo.lock diff --git a/.vscode/settings.json b/.vscode/settings.json index 29d282c9..fbd3ad9b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,9 @@ "rust-analyzer.checkOnSave": true, "rust-analyzer.linkedProjects": [ "./Cargo.toml", + "./arm-targets/Cargo.toml", "examples/versatileab/Cargo.toml", - "examples/mps3-an536/Cargo.toml" + "examples/mps3-an536/Cargo.toml", + "examples/mps3-an536-smp/Cargo.toml", ] } diff --git a/Cargo.toml b/Cargo.toml index 243b915a..2209a19b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ exclude = [ "arm-targets", "examples/versatileab", "examples/mps3-an536", + "examples/mps3-an536-smp", ] members = [ "aarch32-cpu", diff --git a/examples/mps3-an536-smp/.cargo/config.toml b/examples/mps3-an536-smp/.cargo/config.toml new file mode 100644 index 00000000..b3ab14b1 --- /dev/null +++ b/examples/mps3-an536-smp/.cargo/config.toml @@ -0,0 +1,10 @@ +[target.armv8r-none-eabihf] +# Note, this requires QEMU 9 or higher +runner = "qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -audio none -smp 2 -kernel" + +[target.thumbv8r-none-eabihf] +# Note, this requires QEMU 9 or higher +runner = "qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -audio none -smp 2 -kernel" + +[build] +target = "armv8r-none-eabihf" diff --git a/examples/mps3-an536-smp/Cargo.toml b/examples/mps3-an536-smp/Cargo.toml new file mode 100644 index 00000000..6bf5e110 --- /dev/null +++ b/examples/mps3-an536-smp/Cargo.toml @@ -0,0 +1,31 @@ +[package] +authors = [ + "Jonathan Pallant ", + "The Embedded Devices Working Group Arm Team " +] +default-run = "smp-test" +description = "Examples for SMP MPS3-AN536 device (2x Arm Cortex-R52)" +edition = "2024" +homepage = "https://github.com/rust-embedded/aarch32" +license = "MIT OR Apache-2.0" +name = "mps3-an536-smp" +publish = false +readme = "README.md" +repository = "https://github.com/rust-embedded/aarch32.git" +version = "0.0.0" + +[dependencies] +aarch32-cpu = { path = "../../aarch32-cpu", features = ["critical-section-multi-core"] } +aarch32-rt = { path = "../../aarch32-rt" } +arm-gic = { version = "0.7.1" } +critical-section = "1.2.0" +heapless = "0.9.1" +libm = "0.2.15" +semihosting = { version = "0.1.18", features = ["stdio"] } + +[build-dependencies] +arm-targets = {version = "0.4.0", path = "../../arm-targets"} + +[features] +eabi-fpu = ["aarch32-rt/eabi-fpu"] +fpu-d32 = ["aarch32-rt/fpu-d32"] diff --git a/examples/mps3-an536-smp/README.md b/examples/mps3-an536-smp/README.md new file mode 100644 index 00000000..d43a504e --- /dev/null +++ b/examples/mps3-an536-smp/README.md @@ -0,0 +1,106 @@ +# Examples for Arm MPS3-AN536 + +This package contains example binaries for the Arm MPS3-AN536 evaluation system, +featuring one or two Arm Cortex-R52 processor cores. This crate is tested on the +following targets: + +- `armv8r-none-eabihf` - ARMv8-R AArch32, hard-float, Arm mode +- `thumbv8r-none-eabihf` - ARMv8-R AArch32, hard-float, Thumb mode + +The repo-level [`.cargo/config.toml`] will ensure the code runs on the +appropriate QEMU configuration. + +As of Rust 1.92, `armv8r-none-eabihf` is a Tier 2 target and so any stable +release from 1.92 or newer should work for that target. However, +`thumbv8r-none-eabihf` is still a Tier 3 target, which means Nightly Rust is +required. This folder contains a [`rust-toolchain.toml`] which pins us to a +specific release of nightly that is known to work. + +We have only tested this crate on `qemu-system-arm` emulating the Arm +MPS3-AN536, not the real thing. + +[`.cargo/config.toml`]: ../../.cargo/config.toml +[`rust-toolchain.toml`]: ./rust-toolchain.toml + +## Running + +Run these examples as follows: + +```console +$ cargo run + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09s + Running `qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -audio none -smp 2 -kernel target/armv8r-none-eabihf/debug/smp-test` +I am core 0 - Mpidr(80000000) +I am core 1 - Mpidr(80000001) +CAS test passed +CS Mutex test passed +Stack usage report: +SYS0 Stack = 2680 used of 16384 bytes (016%) @ 0x1006bf80..0x1006ff80 +FIQ0 Stack = 0 used of 64 bytes (000%) @ 0x1006ff80..0x1006ffc0 +IRQ0 Stack = 0 used of 64 bytes (000%) @ 0x1006ffc0..0x10070000 +ABT0 Stack = 0 used of 16384 bytes (000%) @ 0x10070000..0x10074000 +SVC0 Stack = 0 used of 16384 bytes (000%) @ 0x10074000..0x10078000 +UND0 Stack = 0 used of 16384 bytes (000%) @ 0x10078000..0x1007c000 +HYP0 Stack = 0 used of 16384 bytes (000%) @ 0x1007c000..0x10080000 +SYS1 Stack = 680 used of 16384 bytes (004%) @ 0x10000018..0x10004018 +FIQ1 Stack = 0 used of 64 bytes (000%) @ 0x10004018..0x10004058 +IRQ1 Stack = 0 used of 64 bytes (000%) @ 0x10004058..0x10004098 +ABT1 Stack = 0 used of 16384 bytes (000%) @ 0x10004098..0x10008098 +SVC1 Stack = 0 used of 16384 bytes (000%) @ 0x10008098..0x1000c098 +UND1 Stack = 0 used of 16384 bytes (000%) @ 0x1000c098..0x10010098 +HYP1 Stack = 0 used of 16384 bytes (000%) @ 0x10010098..0x10014098 +$ cargo run --target thumbv8r-none-eabihf -Zbuild-std=core + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s + Running `qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -audio none -smp 2 -kernel target/thumbv8r-none-eabihf/debug/smp-test` +I am core 0 - Mpidr(80000000) +I am core 1 - Mpidr(80000001) +CAS test passed +CS Mutex test passed +Stack usage report: +SYS0 Stack = 4840 used of 16384 bytes (029%) @ 0x1006bf80..0x1006ff80 +FIQ0 Stack = 0 used of 64 bytes (000%) @ 0x1006ff80..0x1006ffc0 +IRQ0 Stack = 0 used of 64 bytes (000%) @ 0x1006ffc0..0x10070000 +ABT0 Stack = 0 used of 16384 bytes (000%) @ 0x10070000..0x10074000 +SVC0 Stack = 0 used of 16384 bytes (000%) @ 0x10074000..0x10078000 +UND0 Stack = 0 used of 16384 bytes (000%) @ 0x10078000..0x1007c000 +HYP0 Stack = 0 used of 16384 bytes (000%) @ 0x1007c000..0x10080000 +SYS1 Stack = 1568 used of 16384 bytes (009%) @ 0x10000018..0x10004018 +FIQ1 Stack = 0 used of 64 bytes (000%) @ 0x10004018..0x10004058 +IRQ1 Stack = 0 used of 64 bytes (000%) @ 0x10004058..0x10004098 +ABT1 Stack = 0 used of 16384 bytes (000%) @ 0x10004098..0x10008098 +SVC1 Stack = 0 used of 16384 bytes (000%) @ 0x10008098..0x1000c098 +UND1 Stack = 0 used of 16384 bytes (000%) @ 0x1000c098..0x10010098 +HYP1 Stack = 0 used of 16384 bytes (000%) @ 0x10010098..0x10014098 +``` + +## Debugging + +You can start a GDB server by adding `-- -s -S` to the end of the `cargo run` +command, and the connect with GDB as follows: + +```console +$ cargo run --bin hello -- -s -S +# QEMU runs and hangs waiting for a connection. In another terminal run: +$ arm-none-eabi-gdb -x commands.gdb target/armv8r-none-eabihf/debug/hello +# GDB will start and connect to QEMU's GDB server. The commands.gdb file sets up some useful defaults. +``` + +## Minimum Supported Rust Version (MSRV) + +These examples are guaranteed to compile on the version of Rust given in the +[`rust-toolchain.toml`] file. These examples are not version controlled and we +may change the MSRV at any time. + +## Licence + +- Copyright (c) Ferrous Systems +- Copyright (c) The Rust Embedded Devices Working Group developers + +Licensed under either [MIT](../LICENSE-MIT) or [Apache-2.0](../LICENSE-APACHE) at +your option. + +## Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you shall be licensed as above, without any +additional terms or conditions. diff --git a/examples/mps3-an536-smp/build.rs b/examples/mps3-an536-smp/build.rs new file mode 100644 index 00000000..b0b9f098 --- /dev/null +++ b/examples/mps3-an536-smp/build.rs @@ -0,0 +1,26 @@ +//! # Build script for the MPS3-AN536 Examples +//! +//! This script only executes when using `cargo` to build the project. +//! +//! Copyright (c) Ferrous Systems, 2025 + +use std::io::Write; + +fn main() { + arm_targets::process(); + write("memory.x", include_bytes!("memory.x")); + // Use the cortex-m-rt linker script + println!("cargo:rustc-link-arg=-Tlink.x"); +} + +fn write(file: &str, contents: &[u8]) { + // Put linker file in our output directory and ensure it's on the + // linker search path. + let out = &std::path::PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + std::fs::File::create(out.join(file)) + .unwrap() + .write_all(contents) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed={}", file); +} diff --git a/examples/mps3-an536-smp/commands.gdb b/examples/mps3-an536-smp/commands.gdb new file mode 100644 index 00000000..0634363b --- /dev/null +++ b/examples/mps3-an536-smp/commands.gdb @@ -0,0 +1,13 @@ +target extended-remote :1234 +break kmain +break _asm_undefined_handler +break _asm_svc_handler +break _asm_prefetch_abort_handler +break _asm_data_abort_handler +break _asm_irq_handler +break _asm_fiq_handler +layout asm +layout regs +set logging file ./target/debug.log +set logging enabled on +stepi diff --git a/examples/mps3-an536-smp/memory.x b/examples/mps3-an536-smp/memory.x new file mode 100644 index 00000000..0b784d24 --- /dev/null +++ b/examples/mps3-an536-smp/memory.x @@ -0,0 +1,82 @@ +/* +Memory configuration for the MPS3-AN536 machine. + +See https://github.com/qemu/qemu/blob/master/hw/arm/mps3r.c +*/ + +MEMORY { + QSPI : ORIGIN = 0x08000000, LENGTH = 8M + BRAM : ORIGIN = 0x10000000, LENGTH = 512K + DDR : ORIGIN = 0x20000000, LENGTH = 1536M +} + +REGION_ALIAS("VECTORS", QSPI); +REGION_ALIAS("CODE", QSPI); +REGION_ALIAS("DATA", BRAM); +REGION_ALIAS("STACKS", BRAM); + +SECTIONS { + /* ### Interrupt Handler Entries + * + * The IRQ handler walks this section to find registered + * interrupt handlers + */ + .irq_entries : ALIGN(4) + { + /* We put this in the header */ + __irq_entries_start = .; + /* Here are the entries */ + KEEP(*(.irq_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __irq_entries_end = .; + } > CODE +} INSERT AFTER .text; + +SECTIONS { + .core1_stacks (NOLOAD) : ALIGN(8) + { + . = ALIGN(8); + _core1_stacks_low_end = .; + _core1_sys_stack_end = .; + . += _sys_stack_size; + . = ALIGN(8); + _core1_sys_stack = .; + _core1_fiq_stack_end = .; + . += _fiq_stack_size; + . = ALIGN(8); + _core1_fiq_stack = .; + _core1_irq_stack_end = .; + . += _irq_stack_size; + . = ALIGN(8); + _core1_irq_stack = .; + _core1_abt_stack_end = .; + . += _abt_stack_size; + . = ALIGN(8); + _core1_abt_stack = .; + _core1_svc_stack_end = .; + . += _svc_stack_size; + . = ALIGN(8); + _core1_svc_stack = .; + _core1_und_stack_end = .; + . += _und_stack_size; + . = ALIGN(8); + _core1_und_stack = .; + _core1_hyp_stack_end = .; + . += _hyp_stack_size; + . = ALIGN(8); + _core1_hyp_stack = .; + _core1_stacks_high_end = .; + } > STACKS +} INSERT BEFORE .filler; + +PROVIDE(kmain2 = default_kmain2); + +PROVIDE(_hyp_stack_size = 16K); +PROVIDE(_und_stack_size = 16K); +PROVIDE(_svc_stack_size = 16K); +PROVIDE(_abt_stack_size = 16K); +PROVIDE(_irq_stack_size = 64); +PROVIDE(_fiq_stack_size = 64); +PROVIDE(_sys_stack_size = 16K); diff --git a/examples/mps3-an536-smp/reference/smp-test-armv8r-none-eabihf.out b/examples/mps3-an536-smp/reference/smp-test-armv8r-none-eabihf.out new file mode 100644 index 00000000..b157d323 --- /dev/null +++ b/examples/mps3-an536-smp/reference/smp-test-armv8r-none-eabihf.out @@ -0,0 +1,4 @@ +I am core 0 - Mpidr(80000000) +I am core 1 - Mpidr(80000001) +CAS test passed +CS Mutex test passed diff --git a/examples/mps3-an536-smp/reference/smp-test-thumbv8r-none-eabihf.out b/examples/mps3-an536-smp/reference/smp-test-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..b157d323 --- /dev/null +++ b/examples/mps3-an536-smp/reference/smp-test-thumbv8r-none-eabihf.out @@ -0,0 +1,4 @@ +I am core 0 - Mpidr(80000000) +I am core 1 - Mpidr(80000001) +CAS test passed +CS Mutex test passed diff --git a/examples/mps3-an536-smp/rust-toolchain.toml b/examples/mps3-an536-smp/rust-toolchain.toml new file mode 100644 index 00000000..7e4d9e6c --- /dev/null +++ b/examples/mps3-an536-smp/rust-toolchain.toml @@ -0,0 +1,6 @@ +[toolchain] +channel = "nightly-2026-01-26" +targets = [ + "armv8r-none-eabihf", +] +components = ["rust-src", "clippy", "rustfmt"] diff --git a/examples/mps3-an536-smp/src/bin/smp-test.rs b/examples/mps3-an536-smp/src/bin/smp-test.rs new file mode 100644 index 00000000..4364fbbe --- /dev/null +++ b/examples/mps3-an536-smp/src/bin/smp-test.rs @@ -0,0 +1,113 @@ +//! Multi-core hello-world for Arm Cortex-R +//! +//! Runs code on two cores, checking that atomic fetch_add works. + +#![no_std] +#![no_main] + +use core::cell::RefCell; +use core::sync::atomic::{AtomicBool, AtomicU32, Ordering}; + +use aarch32_rt::entry; +use semihosting::println; + +static CORE1_BOOTED: AtomicBool = AtomicBool::new(false); + +static SHARED_VARIABLE: AtomicU32 = AtomicU32::new(0); + +static SHARED_VARIABLE_2: critical_section::Mutex> = + critical_section::Mutex::new(RefCell::new(0)); + +/// How long core 0 waits for core 1 +const CORE0_WILL_WAIT: usize = 1_000_000; + +/// How many CAS loops to run? +const CAS_LOOPS: u32 = 1000; + +/// How many CS Mutex loops to run? +const CS_MUTEX_LOOPS: u32 = 1000; + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up code in `aarch32-rt`. +#[entry] +fn main() -> ! { + println!("I am core 0 - {:08x?}", aarch32_cpu::register::Mpidr::read()); + + mps3_an536_smp::start_core1(); + + // wait some time for core 1 to start + for counter in 0..=CORE0_WILL_WAIT { + if CORE1_BOOTED.load(Ordering::SeqCst) { + break; + } + if counter == CORE0_WILL_WAIT { + println!("CPU 1 is missing?!"); + + mps3_an536_smp::exit(0); + } + } + + for _ in 0..CAS_LOOPS { + SHARED_VARIABLE.fetch_add(1, Ordering::Relaxed); + } + + for _ in 0..CS_MUTEX_LOOPS { + critical_section::with(|cs| { + let mut value_ref = SHARED_VARIABLE_2.borrow_ref_mut(cs); + *value_ref += 1; + }) + } + + // let the other core finish + for _ in 0..CORE0_WILL_WAIT { + aarch32_cpu::asm::nop(); + } + + let mut code = 0; + let total_a = SHARED_VARIABLE.load(Ordering::Relaxed); + if total_a == CAS_LOOPS * 2 { + println!("CAS test passed"); + } else { + println!("CAS test failed, got {} not 2000", total_a); + code = 1; + } + + let total_b = critical_section::with(|cs| { + let value_ref = SHARED_VARIABLE_2.borrow_ref(cs); + *value_ref + }); + + if total_b == CS_MUTEX_LOOPS * 2 { + println!("CS Mutex test passed"); + } else { + println!("CS Mutex test failed, got {} not 2000", total_b); + code = 1; + } + + mps3_an536_smp::exit(code); +} + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up code below, on Core 1. +#[unsafe(no_mangle)] +pub extern "C" fn kmain2() { + println!("I am core 1 - {:08x?}", aarch32_cpu::register::Mpidr::read()); + CORE1_BOOTED.store(true, Ordering::SeqCst); + + for _ in 0..CAS_LOOPS { + SHARED_VARIABLE.fetch_add(1, Ordering::Relaxed); + } + + for _ in 0..CS_MUTEX_LOOPS { + critical_section::with(|cs| { + let mut value_ref = SHARED_VARIABLE_2.borrow_ref_mut(cs); + *value_ref += 1; + }) + } + + loop { + aarch32_cpu::asm::wfi(); + } +} diff --git a/examples/mps3-an536-smp/src/lib.rs b/examples/mps3-an536-smp/src/lib.rs new file mode 100644 index 00000000..2af619ed --- /dev/null +++ b/examples/mps3-an536-smp/src/lib.rs @@ -0,0 +1,433 @@ +//! Common code for all examples +//! +//! ## Interrupt Map +//! +//! | Interrupt ID | Description | +//! |--------------|------------------------------| +//! | `EXTPPI0[0]` | UART 0 Receive Interrupt | +//! | `EXTPPI0[1]` | UART 0 Transmit Interrupt | +//! | `EXTPPI0[2]` | UART 0 Combined Interrupt | +//! | `EXTPPI0[3]` | UART 0 Overflow | +//! | `EXTPPI1[0]` | UART 1 Receive Interrupt | +//! | `EXTPPI1[1]` | UART 1 Transmit Interrupt | +//! | `EXTPPI1[2]` | UART 1 Combined Interrupt | +//! | `EXTPPI1[3]` | UART 1 Overflow | +//! | `SP[0]` | WDG | +//! | `SP[1]` | DualTimer 1 | +//! | `SP[2]` | DualTimer 2 | +//! | `SP[3]` | DualTimer Combined | +//! | `SP[4]` | RTC | +//! | `SP[5]` | UART 2 Receive Interrupt | +//! | `SP[6]` | UART 2 Transmit Interrupt | +//! | `SP[7]` | UART 3 Receive Interrupt | +//! | `SP[8]` | UART 3 Transmit Interrupt | +//! | `SP[9]` | UART 4 Receive Interrupt | +//! | `SP[10]` | UART 4 Transmit Interrupt | +//! | `SP[11]` | UART 5 Receive Interrupt | +//! | `SP[12]` | UART 5 Transmit Interrupt | +//! | `SP[13]` | UART 2 Combined Interrupt | +//! | `SP[14]` | UART 3 Combined Interrupt | +//! | `SP[15]` | UART 4 Combined Interrupt | +//! | `SP[16]` | UART 5 Combined Interrupt | +//! | `SP[17]` | UART Overflow (2, 3, 4 & 5) | +//! | `SP[18]` | Ethernet | +//! | `SP[19]` | USB | +//! | `SP[20]` | FPGA Audio I2S | +//! | `SP[21]` | Touch Screen | +//! | `SP[22]` | SPI ADC | +//! | `SP[23]` | SPI Shield 0 | +//! | `SP[24]` | SPI Shield 1 | +//! | `SP[25]` | HDCLCD Interrupt | +//! | `SP[26]` | GPIO 0 Combined Interrupt | +//! | `SP[27]` | GPIO 1 Combined Interrupt | +//! | `SP[28]` | GPIO 2 Combined Interrupt | +//! | `SP[29]` | GPIO 3 Combined Interrupt | +//! | `SP[30..=45]`| GPIO 0.x Interrupt | +//! | `SP[46..=61]`| GPIO 1.x Interrupt | +//! | `SP[62..=77]`| GPIO 2.x Interrupt | +//! | `SP[78..=93]`| GPIO 3.x Interrupt | +//! +//! * Interrupt ID `SP[x]` are shared across cores +//! * Interrupt ID `EXTPPI0[x]` is only available on Core 0 +//! * Interrupt ID `EXTPPI1[x]` is only available on Core 1 + +#![no_std] + +use aarch32_cpu::register::{Hactlr, Sctlr, Cpsr, cpsr::ProcessorMode}; + +use core::sync::atomic::{AtomicBool, Ordering}; + +/// The PPI for the virutal timer, according to the Cortex-R52 Technical Reference Manual, +/// Table 10-3: PPI assignments. +/// +/// This corresponds to Interrupt ID 27. +pub const VIRTUAL_TIMER_PPI: arm_gic::IntId = arm_gic::IntId::ppi(11); + +#[cfg(not(arm_architecture = "v8-r"))] +compile_error!("This example is only compatible to the ARMv8-R architecture"); + +static WANT_PANIC: AtomicBool = AtomicBool::new(false); + +/// Called when the application raises an unrecoverable `panic!`. +/// +/// Prints the panic to the console and then exits QEMU using a semihosting +/// breakpoint. +#[panic_handler] +#[cfg(target_os = "none")] +fn panic(info: &core::panic::PanicInfo) -> ! { + semihosting::println!("PANIC: {:#?}", info); + if WANT_PANIC.load(Ordering::Relaxed) { + exit(0); + } else { + exit(1); + } +} + +/// Set the panic function as no longer returning a failure code via semihosting +pub fn want_panic() { + WANT_PANIC.store(true, Ordering::Relaxed); +} + +/// Exit from QEMU with code +pub fn exit(code: i32) -> ! { + stack_dump(); + semihosting::process::exit(code) +} + +/// Print stack using to semihosting output for each stack +/// +/// Produces output like: +/// +/// ```text +/// Stack usage report: +/// SYS Stack = 332 used of 16384 bytes (002%) @ 0x1006bf80..0x1006ff80 +/// FIQ Stack = 0 used of 64 bytes (000%) @ 0x1006ff80..0x1006ffc0 +/// IRQ Stack = 0 used of 64 bytes (000%) @ 0x1006ffc0..0x10070000 +/// ABT Stack = 0 used of 16384 bytes (000%) @ 0x10070000..0x10074000 +/// SVC Stack = 0 used of 16384 bytes (000%) @ 0x10074000..0x10078000 +/// UND Stack = 244 used of 16384 bytes (001%) @ 0x10078000..0x1007c000 +/// HYP Stack = 0 used of 16384 bytes (000%) @ 0x1007c000..0x10080000 +/// ``` +fn stack_dump() { + use aarch32_cpu::stacks::stack_used_bytes; + use core::ptr::addr_of; + + unsafe extern "C" { + static _sys_stack_end: u32; + static _sys_stack: u32; + static _fiq_stack_end: u32; + static _fiq_stack: u32; + static _irq_stack_end: u32; + static _irq_stack: u32; + static _abt_stack_end: u32; + static _abt_stack: u32; + static _svc_stack_end: u32; + static _svc_stack: u32; + static _und_stack_end: u32; + static _und_stack: u32; + static _hyp_stack_end: u32; + static _hyp_stack: u32; + + static _core1_sys_stack_end: u32; + static _core1_sys_stack: u32; + static _core1_fiq_stack_end: u32; + static _core1_fiq_stack: u32; + static _core1_irq_stack_end: u32; + static _core1_irq_stack: u32; + static _core1_abt_stack_end: u32; + static _core1_abt_stack: u32; + static _core1_svc_stack_end: u32; + static _core1_svc_stack: u32; + static _core1_und_stack_end: u32; + static _core1_und_stack: u32; + static _core1_hyp_stack_end: u32; + static _core1_hyp_stack: u32; + } + + // these are placed in the order they are in aarch32-rt/link.x + let stacks = [ + ("SYS0", addr_of!(_sys_stack_end)..addr_of!(_sys_stack)), + ("FIQ0", addr_of!(_fiq_stack_end)..addr_of!(_fiq_stack)), + ("IRQ0", addr_of!(_irq_stack_end)..addr_of!(_irq_stack)), + ("ABT0", addr_of!(_abt_stack_end)..addr_of!(_abt_stack)), + ("SVC0", addr_of!(_svc_stack_end)..addr_of!(_svc_stack)), + ("UND0", addr_of!(_und_stack_end)..addr_of!(_und_stack)), + ("HYP0", addr_of!(_hyp_stack_end)..addr_of!(_hyp_stack)), + ("SYS1", addr_of!(_core1_sys_stack_end)..addr_of!(_core1_sys_stack)), + ("FIQ1", addr_of!(_core1_fiq_stack_end)..addr_of!(_core1_fiq_stack)), + ("IRQ1", addr_of!(_core1_irq_stack_end)..addr_of!(_core1_irq_stack)), + ("ABT1", addr_of!(_core1_abt_stack_end)..addr_of!(_core1_abt_stack)), + ("SVC1", addr_of!(_core1_svc_stack_end)..addr_of!(_core1_svc_stack)), + ("UND1", addr_of!(_core1_und_stack_end)..addr_of!(_core1_und_stack)), + ("HYP1", addr_of!(_core1_hyp_stack_end)..addr_of!(_core1_hyp_stack)), + ]; + + semihosting::eprintln!("Stack usage report:"); + + unsafe { + for (name, range) in stacks { + let (total, used) = stack_used_bytes(range.clone()); + let percent = used * 100 / total; + // Send to stderr, so it doesn't mix with expected output on stdout + semihosting::eprintln!( + "{} Stack = {:6} used of {:6} bytes ({:03}%) @ {:08x?}", + name, + used, + total, + percent, + range + ); + } + } +} + +/// Create the ARM GIC driver +/// +/// # Safety +/// +/// Only call this function once, from Core 0. +pub unsafe fn make_gic() -> arm_gic::gicv3::GicV3<'static> { + /// Offset from PERIPHBASE for GIC Distributor + const GICD_BASE_OFFSET: usize = 0x0000_0000usize; + + /// Offset from PERIPHBASE for the first GIC Redistributor + const GICR_BASE_OFFSET: usize = 0x0010_0000usize; + + // Get the GIC address by reading CBAR + let periphbase = aarch32_cpu::register::ImpCbar::read().periphbase(); + semihosting::println!("Found PERIPHBASE {:010p}", periphbase); + let gicd_base = periphbase.wrapping_byte_add(GICD_BASE_OFFSET); + let gicr_base = periphbase.wrapping_byte_add(GICR_BASE_OFFSET); + + // Initialise the GIC. + semihosting::println!( + "Creating GIC driver @ {:010p} / {:010p}", + gicd_base, + gicr_base + ); + // SAFETY: `gicd_base` points to the valid GICD MMIO region as obtained from the + // hardware CBAR register. This pointer is used exclusively by this GIC instance. + let gicd = unsafe { + arm_gic::UniqueMmioPointer::new(core::ptr::NonNull::new(gicd_base.cast()).unwrap()) + }; + let gicr_base = core::ptr::NonNull::new(gicr_base.cast()).unwrap(); + // SAFETY: The GICD and GICR base addresses point to valid GICv3 MMIO regions as + // obtained from the hardware CBAR register. This function is only called once + // (via Board::new()'s atomic guard), ensuring exclusive ownership of the GIC. + let mut gic = unsafe { arm_gic::gicv3::GicV3::new(gicd, gicr_base, 2, false) }; + semihosting::println!("Calling git.setup(0)"); + gic.setup(0); + arm_gic::gicv3::GicCpuInterface::set_priority_mask(0xFF); + gic +} + +/// Release core1 from spin loop +pub fn start_core1() { + let fpga_led = 0xE020_2000 as *mut u32; + unsafe { + // Activate second core by writing to FPGA LEDs. + // We needed a shared register that wasn't in RAM, and this will do. + fpga_led.write_volatile(1); + } +} + +// Start-up code for multi-core Armv8-R, as implemented on the MPS3-AN536. +// +// We boot into EL2, set up a stack pointer, init .data on .bss on core0, and +// run `kmain` in EL1 on all cores. +#[cfg(arm_architecture = "v8-r")] +core::arch::global_asm!( + r#" + .section .text.startup + .align 4 + .arm + + .global _start + .global core1_released + .type _start, %function + _start: + // Read MPIDR into R0 + mrc p15, 0, r0, c0, c0, 5 + ands r0, r0, 0xFF + bne core1 + core0: + ldr pc, =_default_start + core1: + // LED GPIO register base address + ldr r0, =0xE0202000 + mov r1, #0 + core1_spin: + wfe + // spin until an LED0 is on. We use the LED because unlike RAM this register resets to a known value. + ldr r2, [r0] + cmp r1, r2 + beq core1_spin + core1_released: + // now an LED is on, we assume _core1_stack_pointer contains our stack pointer + // First we must exit EL2... + // Set the HVBAR (for EL2) to _vector_table + ldr r0, =_vector_table + mcr p15, 4, r0, c12, c0, 0 + // Configure HACTLR to let us enter EL1 + mrc p15, 4, r0, c1, c0, 1 + mov r1, {hactlr_bits} + orr r0, r0, r1 + mcr p15, 4, r0, c1, c0, 1 + // Program the SPSR - enter system mode (0x1F) in Arm mode with IRQ, FIQ masked + mov r0, {sys_mode} + msr spsr_hyp, r0 + adr r0, 1f + msr elr_hyp, r0 + dsb + isb + eret + 1: + // Allow VFP coprocessor access + mrc p15, 0, r0, c1, c0, 2 + orr r0, r0, #0xF00000 + mcr p15, 0, r0, c1, c0, 2 + // Enable VFP + mov r0, #0x40000000 + vmsr fpexc, r0 + // Set the VBAR (for EL1) to _vector_table. NB: This isn't required on + // Armv7-R because that only supports 'low' (default) or 'high'. + ldr r0, =_vector_table + mcr p15, 0, r0, c12, c0, 0 + // set up our stacks - also switches to SYS mode + bl _core1_stack_setup_preallocated + // Zero all registers before calling kmain2 + mov r0, 0 + mov r1, 0 + mov r2, 0 + mov r3, 0 + mov r4, 0 + mov r5, 0 + mov r6, 0 + mov r7, 0 + mov r8, 0 + mov r9, 0 + mov r10, 0 + mov r11, 0 + mov r12, 0 + // call our kmain2 for core 1 + bl kmain2 + .size _start, . - _start + "#, + hactlr_bits = const { + Hactlr::new_with_raw_value(0) + .with_cpuactlr(true) + .with_cdbgdci(true) + .with_flashifregionr(true) + .with_periphpregionr(true) + .with_qosr(true) + .with_bustimeoutr(true) + .with_intmonr(true) + .with_err(true) + .with_testr1(true) + .raw_value() + }, + sys_mode = const { + Cpsr::new_with_raw_value(0) + .with_mode(ProcessorMode::Sys) + .with_i(true) + .with_f(true) + .raw_value() + }, +); + +// Initialise the stack for Core 1 for each mode +#[cfg(target_arch = "arm")] +core::arch::global_asm!( + r#" + // Work around https://github.com/rust-lang/rust/issues/127269 + .fpu vfp2 + + // Configure a stack for every mode. Leaves you in sys mode. + // + .section .text._core1_stack_setup_preallocated + .arm + .global _core1_stack_setup_preallocated + .type _core1_stack_setup_preallocated, %function + _core1_stack_setup_preallocated: + // Save LR from whatever mode we're currently in + mov r2, lr + // (we might not be in the same mode when we return). + // Set stack pointer and mask interrupts for UND mode (Mode 0x1B) + msr cpsr_c, {und_mode} + ldr r13, =_core1_und_stack + // Set stack pointer (right after) and mask interrupts for SVC mode (Mode 0x13) + msr cpsr_c, {svc_mode} + ldr r13, =_core1_svc_stack + // Set stack pointer (right after) and mask interrupts for ABT mode (Mode 0x17) + msr cpsr_c, {abt_mode} + ldr r13, =_core1_abt_stack + // Set stack pointer (right after) and mask interrupts for IRQ mode (Mode 0x12) + msr cpsr_c, {irq_mode} + ldr r13, =_core1_irq_stack + // Set stack pointer (right after) and mask interrupts for FIQ mode (Mode 0x11) + msr cpsr_c, {fiq_mode} + ldr r13, =_core1_fiq_stack + // Set stack pointer (right after) and mask interrupts for System mode (Mode 0x1F) + msr cpsr_c, {sys_mode} + ldr r13, =_core1_sys_stack + // Clear the Thumb Exception bit because all vector table is written in Arm assembly + // even on Thumb targets. + mrc p15, 0, r1, c1, c0, 0 + bic r1, #{te_bit} + mcr p15, 0, r1, c1, c0, 0 + // return to caller + bx r2 + .size _core1_stack_setup_preallocated, . - _core1_stack_setup_preallocated + "#, + und_mode = const { + Cpsr::new_with_raw_value(0) + .with_mode(ProcessorMode::Und) + .with_i(true) + .with_f(true) + .raw_value() + }, + svc_mode = const { + Cpsr::new_with_raw_value(0) + .with_mode(ProcessorMode::Svc) + .with_i(true) + .with_f(true) + .raw_value() + }, + abt_mode = const { + Cpsr::new_with_raw_value(0) + .with_mode(ProcessorMode::Abt) + .with_i(true) + .with_f(true) + .raw_value() + }, + fiq_mode = const { + Cpsr::new_with_raw_value(0) + .with_mode(ProcessorMode::Fiq) + .with_i(true) + .with_f(true) + .raw_value() + }, + irq_mode = const { + Cpsr::new_with_raw_value(0) + .with_mode(ProcessorMode::Irq) + .with_i(true) + .with_f(true) + .raw_value() + }, + sys_mode = const { + Cpsr::new_with_raw_value(0) + .with_mode(ProcessorMode::Sys) + .with_i(true) + .with_f(true) + .raw_value() + }, + te_bit = const { Sctlr::new_with_raw_value(0).with_te(true).raw_value() } +); + +/// What a second core does when no `kmain2` is supplied. +#[unsafe(no_mangle)] +pub extern "C" fn default_kmain2() { + loop { + aarch32_cpu::asm::wfe(); + } +} diff --git a/examples/mps3-an536/.cargo/config.toml b/examples/mps3-an536/.cargo/config.toml index ec698529..6960eb34 100644 --- a/examples/mps3-an536/.cargo/config.toml +++ b/examples/mps3-an536/.cargo/config.toml @@ -1,2 +1,10 @@ +[target.armv8r-none-eabihf] +# Note, this requires QEMU 9 or higher +runner = "qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -audio none -kernel" + +[target.thumbv8r-none-eabihf] +# Note, this requires QEMU 9 or higher +runner = "qemu-system-arm -machine mps3-an536 -cpu cortex-r52 -semihosting -nographic -audio none -kernel" + [build] target = "armv8r-none-eabihf" \ No newline at end of file diff --git a/examples/mps3-an536/reference/smp_test-armv8r-none-eabihf.out b/examples/mps3-an536/reference/smp_test-armv8r-none-eabihf.out deleted file mode 100644 index 7fe30b87..00000000 --- a/examples/mps3-an536/reference/smp_test-armv8r-none-eabihf.out +++ /dev/null @@ -1 +0,0 @@ -CPU 1 is missing?! diff --git a/examples/mps3-an536/reference/smp_test-armv8r-none-eabihf_smp2.out b/examples/mps3-an536/reference/smp_test-armv8r-none-eabihf_smp2.out deleted file mode 100644 index 08c8c794..00000000 --- a/examples/mps3-an536/reference/smp_test-armv8r-none-eabihf_smp2.out +++ /dev/null @@ -1,2 +0,0 @@ -CAS test passed -CS Mutex test passed diff --git a/examples/mps3-an536/reference/smp_test-thumbv8r-none-eabihf.out b/examples/mps3-an536/reference/smp_test-thumbv8r-none-eabihf.out deleted file mode 100644 index 7fe30b87..00000000 --- a/examples/mps3-an536/reference/smp_test-thumbv8r-none-eabihf.out +++ /dev/null @@ -1 +0,0 @@ -CPU 1 is missing?! diff --git a/examples/mps3-an536/reference/smp_test-thumbv8r-none-eabihf_smp2.out b/examples/mps3-an536/reference/smp_test-thumbv8r-none-eabihf_smp2.out deleted file mode 100644 index 7fe30b87..00000000 --- a/examples/mps3-an536/reference/smp_test-thumbv8r-none-eabihf_smp2.out +++ /dev/null @@ -1 +0,0 @@ -CPU 1 is missing?! diff --git a/examples/mps3-an536/src/bin/smp_test.rs b/examples/mps3-an536/src/bin/smp_test.rs deleted file mode 100644 index 01e1b809..00000000 --- a/examples/mps3-an536/src/bin/smp_test.rs +++ /dev/null @@ -1,354 +0,0 @@ -//! Multi-core hello-world for Arm Cortex-R -//! -//! Runs code on two cores, checking that atomic fetch_add works. -//! -//! Abuses the FPGA LED register as a place to record whether Core 0 has -//! started. -//! -//! Run with `cargo run --bin smp_test --target=armv8r-none-eabihf -- -smp 2`. - -#![no_std] -#![no_main] - -use core::cell::{RefCell, UnsafeCell}; -use core::sync::atomic::{AtomicBool, AtomicU32, Ordering}; - -use aarch32_cpu::register::{Cpsr, Hactlr, Sctlr, cpsr::ProcessorMode}; -use aarch32_rt::entry; -use semihosting::println; - -use mps3_an536 as _; - -#[repr(align(16))] -struct Stack { - contents: UnsafeCell<[u8; LEN_BYTES]>, -} - -impl Stack { - const fn new() -> Self { - Self { - contents: UnsafeCell::new([0u8; LEN_BYTES]), - } - } - - fn stack_top(&self) -> usize { - let stack_start = self.contents.get() as usize; - stack_start + LEN_BYTES - } -} - -unsafe impl Sync for Stack {} - -static CORE1_STACK: Stack<{ 256 * 1024 }> = Stack::new(); - -static CORE1_BOOTED: AtomicBool = AtomicBool::new(false); - -static SHARED_VARIABLE: AtomicU32 = AtomicU32::new(0); - -static SHARED_VARIABLE_2: critical_section::Mutex> = - critical_section::Mutex::new(RefCell::new(0)); - -/// How long core 0 waits for core 1 -const CORE0_WILL_WAIT: usize = 1_000_000; - -/// How many CAS loops to run? -const CAS_LOOPS: u32 = 1000; - -/// How many CS Mutex loops to run? -const CS_MUTEX_LOOPS: u32 = 1000; - -/// The entry-point to the Rust application. -/// -/// It is called by the start-up code in `aarch32-rt`. -#[entry] -fn main() -> ! { - let fpga_led = 0xE020_2000 as *mut u32; - unsafe extern "C" { - static mut _core1_stack_pointer: usize; - } - unsafe { - let p = &raw mut _core1_stack_pointer; - p.write(CORE1_STACK.stack_top()); - } - unsafe { - // Activate second core by writing to FPGA LEDs. - // We needed a shared register that wasn't in RAM, and this will do. - fpga_led.write_volatile(1); - } - - // wait some time for core 1 to start - for counter in 0..=CORE0_WILL_WAIT { - if CORE1_BOOTED.load(Ordering::SeqCst) { - break; - } - if counter == CORE0_WILL_WAIT { - println!("CPU 1 is missing?!"); - - mps3_an536::exit(0); - } - } - - for _ in 0..CAS_LOOPS { - SHARED_VARIABLE.fetch_add(1, Ordering::Relaxed); - } - - for _ in 0..CS_MUTEX_LOOPS { - critical_section::with(|cs| { - let mut value_ref = SHARED_VARIABLE_2.borrow_ref_mut(cs); - *value_ref += 1; - }) - } - - // let the other core finish - for _ in 0..CORE0_WILL_WAIT { - aarch32_cpu::asm::nop(); - } - - let mut code = 0; - let total_a = SHARED_VARIABLE.load(Ordering::Relaxed); - if total_a == CAS_LOOPS * 2 { - println!("CAS test passed"); - } else { - println!("CAS test failed, got {} not 2000", total_a); - code = 1; - } - - let total_b = critical_section::with(|cs| { - let value_ref = SHARED_VARIABLE_2.borrow_ref(cs); - *value_ref - }); - - if total_b == CS_MUTEX_LOOPS * 2 { - println!("CS Mutex test passed"); - } else { - println!("CS Mutex test failed, got {} not 2000", total_b); - code = 1; - } - - mps3_an536::exit(code); -} - -/// The entry-point to the Rust application. -/// -/// It is called by the start-up code below, on Core 1. -#[unsafe(no_mangle)] -pub extern "C" fn kmain2() { - CORE1_BOOTED.store(true, Ordering::SeqCst); - - for _ in 0..CAS_LOOPS { - SHARED_VARIABLE.fetch_add(1, Ordering::Relaxed); - } - - for _ in 0..CS_MUTEX_LOOPS { - critical_section::with(|cs| { - let mut value_ref = SHARED_VARIABLE_2.borrow_ref_mut(cs); - *value_ref += 1; - }) - } - - loop { - core::hint::spin_loop(); - } -} - -// Start-up code for multi-core Armv8-R, as implemented on the MPS3-AN536. -// -// We boot into EL2, set up a stack pointer, init .data on .bss on core0, and -// run `kmain` in EL1 on all cores. -#[cfg(arm_architecture = "v8-r")] -core::arch::global_asm!( - r#" - .section .bss - .align 4 - _core1_stack_pointer: - .word 0 - - .section .text.startup - .align 4 - - .global _start - .global core1_released - .type _start, %function - _start: - // Read MPIDR into R0 - mrc p15, 0, r0, c0, c0, 5 - ands r0, r0, 0xFF - bne core1 - core0: - ldr pc, =_default_start - core1: - ldr r0, =0xE0202000 - mov r1, #0 - core1_spin: - wfe - // spin until an LED0 is on - ldr r2, [r0] - cmp r1, r2 - beq core1_spin - core1_released: - // now an LED is on, we assume _core1_stack_pointer contains our stack pointer - // First we must exit EL2... - // Set the HVBAR (for EL2) to _vector_table - ldr r0, =_vector_table - mcr p15, 4, r0, c12, c0, 0 - // Configure HACTLR to let us enter EL1 - mrc p15, 4, r0, c1, c0, 1 - mov r1, {hactlr_bits} - orr r0, r0, r1 - mcr p15, 4, r0, c1, c0, 1 - // Program the SPSR - enter system mode (0x1F) in Arm mode with IRQ, FIQ masked - mov r0, {sys_mode} - msr spsr_hyp, r0 - adr r0, 1f - msr elr_hyp, r0 - dsb - isb - eret - 1: - // Set the VBAR (for EL1) to _vector_table. NB: This isn't required on - // Armv7-R because that only supports 'low' (default) or 'high'. - ldr r0, =_vector_table - mcr p15, 0, r0, c12, c0, 0 - ldr r0, =_core1_stack_pointer - ldr r0, [r0] - // set up our stacks using that stack pointer - also switches to SYS mode - bl _stack_setup - // Zero all registers before calling kmain2 - mov r0, 0 - mov r1, 0 - mov r2, 0 - mov r3, 0 - mov r4, 0 - mov r5, 0 - mov r6, 0 - mov r7, 0 - mov r8, 0 - mov r9, 0 - mov r10, 0 - mov r11, 0 - mov r12, 0 - // call our kmain2 for core 1 - bl kmain2 - .size _start, . - _start - "#, - hactlr_bits = const { - Hactlr::new_with_raw_value(0) - .with_cpuactlr(true) - .with_cdbgdci(true) - .with_flashifregionr(true) - .with_periphpregionr(true) - .with_qosr(true) - .with_bustimeoutr(true) - .with_intmonr(true) - .with_err(true) - .with_testr1(true) - .raw_value() - }, - sys_mode = const { - Cpsr::new_with_raw_value(0) - .with_mode(ProcessorMode::Sys) - .with_i(true) - .with_f(true) - .raw_value() - } -); - -// Initialise the stack for each mode -#[cfg(target_arch = "arm")] -core::arch::global_asm!( - r#" - // Work around https://github.com/rust-lang/rust/issues/127269 - .fpu vfp2 - - // Configure a stack for every mode. Leaves you in sys mode. - // - // Pass in stack top in r0. - .section .text._stack_setup - .arm - .global _stack_setup - .type _stack_setup, %function - _stack_setup: - // Save LR from whatever mode we're currently in - mov r2, lr - // (we might not be in the same mode when we return). - // Set stack pointer (right after) and mask interrupts for for UND mode (Mode 0x1B) - msr cpsr_c, {und_mode} - mov sp, r0 - ldr r1, =_und_stack_size - sub r0, r0, r1 - // Set stack pointer (right after) and mask interrupts for for SVC mode (Mode 0x13) - msr cpsr_c, {svc_mode} - mov sp, r0 - ldr r1, =_svc_stack_size - sub r0, r0, r1 - // Set stack pointer (right after) and mask interrupts for for ABT mode (Mode 0x17) - msr cpsr_c, {abt_mode} - mov sp, r0 - ldr r1, =_abt_stack_size - sub r0, r0, r1 - // Set stack pointer (right after) and mask interrupts for for IRQ mode (Mode 0x12) - msr cpsr_c, {irq_mode} - mov sp, r0 - ldr r1, =_irq_stack_size - sub r0, r0, r1 - // Set stack pointer (right after) and mask interrupts for for FIQ mode (Mode 0x11) - msr cpsr_c, {fiq_mode} - mov sp, r0 - ldr r1, =_fiq_stack_size - sub r0, r0, r1 - // Set stack pointer (right after) and mask interrupts for for System mode (Mode 0x1F) - msr cpsr_c, {sys_mode} - mov sp, r0 - // Clear the Thumb Exception bit because all our targets are currently - // for Arm (A32) mode - mrc p15, 0, r1, c1, c0, 0 - bic r1, #{te_bit} - mcr p15, 0, r1, c1, c0, 0 - // return to caller - bx r2 - .size _stack_setup, . - _stack_setup - "#, - und_mode = const { - Cpsr::new_with_raw_value(0) - .with_mode(ProcessorMode::Und) - .with_i(true) - .with_f(true) - .raw_value() - }, - svc_mode = const { - Cpsr::new_with_raw_value(0) - .with_mode(ProcessorMode::Svc) - .with_i(true) - .with_f(true) - .raw_value() - }, - abt_mode = const { - Cpsr::new_with_raw_value(0) - .with_mode(ProcessorMode::Abt) - .with_i(true) - .with_f(true) - .raw_value() - }, - fiq_mode = const { - Cpsr::new_with_raw_value(0) - .with_mode(ProcessorMode::Fiq) - .with_i(true) - .with_f(true) - .raw_value() - }, - irq_mode = const { - Cpsr::new_with_raw_value(0) - .with_mode(ProcessorMode::Irq) - .with_i(true) - .with_f(true) - .raw_value() - }, - sys_mode = const { - Cpsr::new_with_raw_value(0) - .with_mode(ProcessorMode::Sys) - .with_i(true) - .with_f(true) - .raw_value() - }, - te_bit = const { Sctlr::new_with_raw_value(0).with_te(true).raw_value() } -); diff --git a/examples/versatileab/.cargo/config.toml b/examples/versatileab/.cargo/config.toml index cb76cb6e..41913a6c 100644 --- a/examples/versatileab/.cargo/config.toml +++ b/examples/versatileab/.cargo/config.toml @@ -1,2 +1,51 @@ +[target.armv7r-none-eabihf] +runner = "qemu-system-arm -machine versatileab -cpu cortex-r5f -semihosting -nographic -audio none -kernel" + +[target.thumbv7r-none-eabihf] +runner = "qemu-system-arm -machine versatileab -cpu cortex-r5f -semihosting -nographic -audio none -kernel" + +[target.armv7r-none-eabi] +# change '-mcpu=cortex-r5' to '-mcpu=cortex-r5f' if you use eabi-fpu feature, otherwise +# qemu-system-arm will lock up +runner = "qemu-system-arm -machine versatileab -cpu cortex-r5 -semihosting -nographic -audio none -kernel" + +[target.thumbv7r-none-eabi] +# change '-mcpu=cortex-r5' to '-mcpu=cortex-r5f' if you use eabi-fpu feature, otherwise +# qemu-system-arm will lock up +runner = "qemu-system-arm -machine versatileab -cpu cortex-r5 -semihosting -nographic -audio none -kernel" + +[target.armv7a-none-eabihf] +runner = "qemu-system-arm -machine versatileab -cpu cortex-a8 -semihosting -nographic -audio none -kernel" + +[target.thumbv7a-none-eabihf] +runner = "qemu-system-arm -machine versatileab -cpu cortex-a8 -semihosting -nographic -audio none -kernel" + +[target.armv7a-none-eabi] +runner = "qemu-system-arm -machine versatileab -cpu cortex-a8 -semihosting -nographic -audio none -kernel" + +[target.thumbv7a-none-eabi] +runner = "qemu-system-arm -machine versatileab -cpu cortex-a8 -semihosting -nographic -audio none -kernel" + +[target.armv6-none-eabihf] +runner = "qemu-system-arm -machine versatileab -cpu arm1176 -semihosting -nographic -audio none -kernel" + +[target.armv6-none-eabi] +runner = "qemu-system-arm -machine versatileab -cpu arm1176 -semihosting -nographic -audio none -kernel" + +[target.thumbv6-none-eabi] +runner = "qemu-system-arm -machine versatileab -cpu arm1176 -semihosting -nographic -audio none -kernel" + +[target.armv5te-none-eabi] +runner = "qemu-system-arm -machine versatileab -cpu arm926 -semihosting -nographic -audio none -kernel" + +[target.thumbv5te-none-eabi] +runner = "qemu-system-arm -machine versatileab -cpu arm926 -semihosting -nographic -audio none -kernel" + +[target.armv4t-none-eabi] +runner = "qemu-system-arm -machine versatileab -cpu arm926 -semihosting -nographic -audio none -kernel" + +[target.thumbv4t-none-eabi] +runner = "qemu-system-arm -machine versatileab -cpu arm926 -semihosting -nographic -audio none -kernel" + [build] target = "armv7r-none-eabihf" diff --git a/justfile b/justfile index cf8f315f..9990aab9 100644 --- a/justfile +++ b/justfile @@ -27,6 +27,8 @@ clean: rm -rf examples/versatileab/target-d32 cd examples/mps3-an536 && cargo clean rm -rf examples/mps3-an536/target-d32 + cd examples/mps3-an536-smp && cargo clean + rm -rf examples/mps3-an536-smp/target-d32 # Builds our workspace for all targets build-all: \ @@ -151,7 +153,7 @@ clippy-host: cd arm-targets && cargo clippy {{verbose}} # Run all the tests -test: test-cargo test-qemu test-smp +test: test-cargo test-qemu # Run the unit tests with cargo test-cargo: @@ -161,7 +163,7 @@ test-cargo: cd arm-targets && cargo test {{verbose}} # Run the integration tests in QEMU -test-qemu: test-qemu-v4t test-qemu-v5te test-qemu-v6 test-qemu-v7a test-qemu-v7r test-qemu-v8r +test-qemu: test-qemu-v4t test-qemu-v5te test-qemu-v6 test-qemu-v7a test-qemu-v7r test-qemu-v8r test-qemu-v8r-smp test-qemu-v4t: #!/bin/bash @@ -214,9 +216,11 @@ test-qemu-v8r: RUSTFLAGS=-Ctarget-cpu=cortex-r52 ./tests.sh examples/mps3-an536 thumbv8r-none-eabihf -Zbuild-std=core --features=fpu-d32 --target-dir=target-d32 {{verbose}} --release || FAIL=1 if [ "${FAIL}" == "1" ]; then exit 1; fi -# Run the special SMP test -# -# You can't run the normal examples with two CPUs because nothing stops the second CPU from running :/. So we have -# a special test for SMP mode on the MPS3-AN536 -test-smp: - cd examples/mps3-an536 && cargo run --target=armv8r-none-eabihf --bin smp_test {{verbose}} -- --smp 2 +test-qemu-v8r-smp: + #!/bin/bash + FAIL=0 + ./tests.sh examples/mps3-an536-smp armv8r-none-eabihf {{verbose}} --release || FAIL=1 + ./tests.sh examples/mps3-an536-smp thumbv8r-none-eabihf -Zbuild-std=core {{verbose}} --release || FAIL=1 + RUSTFLAGS=-Ctarget-cpu=cortex-r52 ./tests.sh examples/mps3-an536-smp armv8r-none-eabihf --features=fpu-d32 --target-dir=target-d32 {{verbose}} --release || FAIL=1 + RUSTFLAGS=-Ctarget-cpu=cortex-r52 ./tests.sh examples/mps3-an536-smp thumbv8r-none-eabihf -Zbuild-std=core --features=fpu-d32 --target-dir=target-d32 {{verbose}} --release || FAIL=1 + if [ "${FAIL}" == "1" ]; then exit 1; fi From e50781a6acc65f7777cf6b363b5e0356da5572a6 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Mon, 16 Feb 2026 22:40:42 +0000 Subject: [PATCH 2/6] Add GIC SGI SMP example. We send a Software Generated Interrupt from Core 0 to Core 1, and then another one back the other way. --- .../reference/gic-armv8r-none-eabihf.out | 16 ++ .../reference/gic-thumbv8r-none-eabihf.out | 16 ++ examples/mps3-an536-smp/src/bin/gic.rs | 168 ++++++++++++++++++ 3 files changed, 200 insertions(+) create mode 100644 examples/mps3-an536-smp/reference/gic-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536-smp/reference/gic-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536-smp/src/bin/gic.rs diff --git a/examples/mps3-an536-smp/reference/gic-armv8r-none-eabihf.out b/examples/mps3-an536-smp/reference/gic-armv8r-none-eabihf.out new file mode 100644 index 00000000..6a49ec52 --- /dev/null +++ b/examples/mps3-an536-smp/reference/gic-armv8r-none-eabihf.out @@ -0,0 +1,16 @@ +I am core 0 - Mpidr(80000000) +Found PERIPHBASE 0xf0000000 +Creating GIC driver @ 0xf0000000 / 0xf0100000 +Calling git.setup(0) +Configure SGI on both cores... +I am core 1 - Mpidr(80000001) +Calling git.init_cpu(1) +Send SGI to other core +> IRQ on Mpidr(80000001) +- handle_interrupt_with_id(SGI 3) +- send SGI back to first core +> IRQ on Mpidr(80000000) +- handle_interrupt_with_id(SGI 3) +< IRQ on Mpidr(80000001) +< IRQ on Mpidr(80000000) +Got pong diff --git a/examples/mps3-an536-smp/reference/gic-thumbv8r-none-eabihf.out b/examples/mps3-an536-smp/reference/gic-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..6a49ec52 --- /dev/null +++ b/examples/mps3-an536-smp/reference/gic-thumbv8r-none-eabihf.out @@ -0,0 +1,16 @@ +I am core 0 - Mpidr(80000000) +Found PERIPHBASE 0xf0000000 +Creating GIC driver @ 0xf0000000 / 0xf0100000 +Calling git.setup(0) +Configure SGI on both cores... +I am core 1 - Mpidr(80000001) +Calling git.init_cpu(1) +Send SGI to other core +> IRQ on Mpidr(80000001) +- handle_interrupt_with_id(SGI 3) +- send SGI back to first core +> IRQ on Mpidr(80000000) +- handle_interrupt_with_id(SGI 3) +< IRQ on Mpidr(80000001) +< IRQ on Mpidr(80000000) +Got pong diff --git a/examples/mps3-an536-smp/src/bin/gic.rs b/examples/mps3-an536-smp/src/bin/gic.rs new file mode 100644 index 00000000..af2ad191 --- /dev/null +++ b/examples/mps3-an536-smp/src/bin/gic.rs @@ -0,0 +1,168 @@ +//! # Cross-core GIC example for Arm Cortex-R52 on an MPS2-AN536 + +#![no_std] +#![no_main] + +use core::cell::RefCell; +use core::sync::atomic::{AtomicBool, Ordering}; + +use arm_gic::{ + IntId, + gicv3::{GicCpuInterface, Group, InterruptGroup, SgiTarget, SgiTargetGroup}, +}; +use critical_section::Mutex; +use semihosting::println; + +use aarch32_rt::entry; + +static CORE1_BOOTED: AtomicBool = AtomicBool::new(false); + +static PING_PONG_COMPLETE: AtomicBool = AtomicBool::new(false); + +/// How long core 0 waits for core 1 +const CORE0_WILL_WAIT: usize = 100_000_000; + +/// Shared interrupt controller driver +static GLOBAL_GIC: Mutex>>> = + Mutex::new(RefCell::new(None)); + +const SGI_INTID: IntId = IntId::sgi(3); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up code in `aarch32-rt`. +#[entry] +fn main() -> ! { + println!( + "I am core 0 - {:08x?}", + aarch32_cpu::register::Mpidr::read() + ); + + let mut gic = unsafe { mps3_an536_smp::make_gic() }; + + // Configure two Software Generated Interrupts for Core 0 + println!("Configure SGI on both cores..."); + gic.set_interrupt_priority(SGI_INTID, Some(0), 0x31) + .unwrap(); + gic.set_group(SGI_INTID, Some(0), Group::Group1NS).unwrap(); + gic.enable_interrupt(SGI_INTID, Some(0), true).unwrap(); + + gic.set_interrupt_priority(SGI_INTID, Some(1), 0x31) + .unwrap(); + gic.set_group(SGI_INTID, Some(1), Group::Group1NS).unwrap(); + gic.enable_interrupt(SGI_INTID, Some(1), true).unwrap(); + + unsafe { + aarch32_cpu::interrupt::enable(); + } + + critical_section::with(|cs| { + let mut global_gic = GLOBAL_GIC.borrow_ref_mut(cs); + global_gic.replace(gic); + }); + + mps3_an536_smp::start_core1(); + + // wait some time for core 1 to start + for counter in 0..=CORE0_WILL_WAIT { + if CORE1_BOOTED.load(Ordering::SeqCst) { + break; + } + if counter == CORE0_WILL_WAIT { + println!("CPU 1 is missing?!"); + + mps3_an536_smp::exit(0); + } + } + + // Send it + println!("Send SGI to other core"); + GicCpuInterface::send_sgi( + SGI_INTID, + SgiTarget::List { + affinity3: 0, + affinity2: 0, + affinity1: 0, + target_list: 0b10, + }, + SgiTargetGroup::CurrentGroup1, + ) + .unwrap(); + + // let the other core finish + for _ in 0..CORE0_WILL_WAIT { + aarch32_cpu::asm::wfi(); + if PING_PONG_COMPLETE.load(Ordering::Relaxed) { + println!("Got pong"); + break; + } + } + + mps3_an536_smp::exit(0); +} + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up code below, on Core 1. +#[unsafe(no_mangle)] +pub extern "C" fn kmain2() { + println!( + "I am core 1 - {:08x?}", + aarch32_cpu::register::Mpidr::read() + ); + + unsafe { + aarch32_cpu::interrupt::enable(); + } + + critical_section::with(|cs| { + let mut global_gic = GLOBAL_GIC.borrow_ref_mut(cs); + let global_gic = global_gic.as_mut().unwrap(); + semihosting::println!("Calling git.init_cpu(1)"); + global_gic.init_cpu(1); + }); + GicCpuInterface::enable_group1(true); + GicCpuInterface::set_priority_mask(0xFF); + + CORE1_BOOTED.store(true, Ordering::SeqCst); + + loop { + aarch32_cpu::asm::wfi(); + } +} + +/// Called when either Arm CPU gets an IRQ +/// +/// Talks to the GICv3 to find out which interrupts are pending, +/// handles the interrupt, and then tells the GICv3 it has been handled. +#[aarch32_rt::irq] +fn irq_handler() { + let id = aarch32_cpu::register::Mpidr::read(); + println!("> IRQ on {:08x?}", id); + while let Some(next_int_id) = + GicCpuInterface::get_and_acknowledge_interrupt(InterruptGroup::Group1) + { + // handle the interrupt + println!("- handle_interrupt_with_id({:?})", next_int_id); + + if id.0 == 0x8000_0001 { + println!("- send SGI back to first core"); + GicCpuInterface::send_sgi( + SGI_INTID, + SgiTarget::List { + affinity3: 0, + affinity2: 0, + affinity1: 0, + target_list: 0b01, + }, + SgiTargetGroup::CurrentGroup1, + ) + .unwrap(); + } else { + PING_PONG_COMPLETE.store(true, Ordering::Relaxed); + } + + GicCpuInterface::end_interrupt(next_int_id, InterruptGroup::Group1); + } + println!("< IRQ on {:08x?}", id); +} From feddd77917fc02960e1258d3f9d1ee4f40c58b3e Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sat, 21 Feb 2026 12:03:42 +0000 Subject: [PATCH 3/6] Allocate stacks for all cores * The `_num_cores` symbol controls how many copies of each stack you get * Added better docs to link.x * Added `enum Stack` to aarch32-rt, which can fetch information about each stack * Simplified the SMP start-up code * Stacks and stack related code are now all in the same order If you want to put stacks into TCM, you'll need custom routines. This code assumes stacks all live in the same region of RAM. --- aarch32-rt/link.x | 212 ++++++++++++++++++----- aarch32-rt/src/lib.rs | 126 +++++++++----- aarch32-rt/src/stacks.rs | 136 +++++++++++++++ examples/mps3-an536-smp/memory.x | 65 +------ examples/mps3-an536-smp/src/lib.rs | 195 ++++----------------- examples/mps3-an536/src/bin/el2_hello.rs | 2 +- examples/mps3-an536/src/lib.rs | 72 +++----- examples/versatileab/src/lib.rs | 72 +++----- 8 files changed, 467 insertions(+), 413 deletions(-) create mode 100644 aarch32-rt/src/stacks.rs diff --git a/aarch32-rt/link.x b/aarch32-rt/link.x index bff28f54..09f77f0f 100644 --- a/aarch32-rt/link.x +++ b/aarch32-rt/link.x @@ -1,11 +1,41 @@ /* Basic AArch32 linker script. -You must supply a file called `memory.x` which defines the memory regions -'VECTORS', 'CODE', 'DATA', 'STACKS'. +You must supply a file called `memory.x` in your linker search path. It must +define Region Aliases 'VECTORS', 'CODE', 'DATA', 'STACKS'. -The stacks will be at the top of the STACKS region by default, use `_pack_stacks` -to overwrite default behaviour. +Here is an example `memory.x` file: + +------------- +MEMORY { + FLASH : ORIGIN = 0x08000000, LENGTH = 2M + SRAM : ORIGIN = 0x10000000, LENGTH = 512K +} + +REGION_ALIAS("VECTORS", FLASH); +REGION_ALIAS("CODE", FLASH); +REGION_ALIAS("DATA", SRAM); +REGION_ALIAS("STACKS", SRAM); +------------- + +The AArch32 platform uses seven separate stacks. The default sizes for each are +given at the bottom of this file. However, your `memory.x` can provide an +alternative size for any (or all) of them, provided that size is a multiple of +eight bytes. For example, your `memory.x` might include: + +------------- +PROVIDE(_und_stack_size = 3456); +PROVIDE(_svc_stack_size = 3456); +PROVIDE(_abt_stack_size = 3456); +PROVIDE(_hyp_stack_size = 3456); +PROVIDE(_irq_stack_size = 3456); +PROVIDE(_fiq_stack_size = 3456); +PROVIDE(_sys_stack_size = 3456); +------------- + +The stacks will be located at the top of the STACKS region by default. Use +`PROVIDE(_pack_stacks = 0)` to remove the padding and locate the stacks at the +bottom of that region instead. Based upon the linker script from https://github.com/rust-embedded/cortex-m */ @@ -18,26 +48,55 @@ EXTERN(_start); EXTERN(_default_handler); SECTIONS { + /* # Vector Table + * + * Our ARM interrupt vector table, consisting of branch instructions to + * each exception handler. + * + * May include FIQ handler code at the end. + */ .vector_table ORIGIN(VECTORS) : { - /* The vector table must come first */ *(.vector_table) } > VECTORS + /* # Text + * + * Our executable code. + */ .text : { - /* Now the rest of the code */ + __stext = .; + *(.text .text*) + + __etext = .; } > CODE + /* # Text + * + * Our constants. + */ .rodata : { + __srodata = .; + *(.rodata .rodata*) + + __erodata = .; } > CODE + /* # Data + * + * Our global variables that are not initialised to zero. + */ .data : ALIGN(4) { . = ALIGN(4); __sdata = .; + *(.data .data.*); + . = ALIGN(4); + /* NB: __edata defined lower down */ } > DATA AT>CODE + /* * Allow sections from user `memory.x` injected using `INSERT AFTER .data` to * use the .data loading mechanism by pushing __edata. Note: do not change @@ -49,12 +108,20 @@ SECTIONS { /* LMA of .data */ __sidata = LOADADDR(.data); + /* # Block Starting Symbol (BSS) + * + * Our global variables that *are* initialised to zero. + */ .bss (NOLOAD) : ALIGN(4) { . = ALIGN(4); __sbss = .; + *(.bss .bss* COMMON) + . = ALIGN(4); + /* NB: __ebss defined lower down */ } > DATA + /* * Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to * use the .bss zeroing mechanism by pushing __ebss. Note: do not change @@ -62,15 +129,28 @@ SECTIONS { */ __ebss = .; + /* # Uninitialised Data + * + * Our global variables that have no defined initial value. + */ .uninit (NOLOAD) : ALIGN(4) { . = ALIGN(4); __suninit = .; + *(.uninit .uninit.*); + . = ALIGN(4); __euninit = .; } > DATA + + /* # Stack Padding + * + * A padding region to push the stacks to the top of the STACKS region. + * If `_pack_stacks == 0`, this is forced to be zero size, putting the + * stacks at the bottom of the STACK region. + */ .filler (NOLOAD) : { /* Move the .stacks section to the end of the STACKS memory region */ _next_region = ORIGIN(STACKS) + LENGTH(STACKS); @@ -80,61 +160,81 @@ SECTIONS { . = _start_stacks; } > STACKS + /* # Stacks + * + * Space for all seven stacks. + */ .stacks (NOLOAD) : ALIGN(8) { . = ALIGN(8); + + /* Lowest address of allocated stack */ _stacks_low_end = .; - _sys_stack_end = .; - . += _sys_stack_size; - . = ALIGN(8); - _sys_stack = .; - _fiq_stack_end = .; - . += _fiq_stack_size; - . = ALIGN(8); - _fiq_stack = .; - _irq_stack_end = .; - . += _irq_stack_size; - . = ALIGN(8); - _irq_stack = .; - _abt_stack_end = .; - . += _abt_stack_size; - . = ALIGN(8); - _abt_stack = .; - _svc_stack_end = .; - . += _svc_stack_size; - . = ALIGN(8); - _svc_stack = .; - _und_stack_end = .; - . += _und_stack_size; - . = ALIGN(8); - _und_stack = .; - _hyp_stack_end = .; - . += _hyp_stack_size; - . = ALIGN(8); - _hyp_stack = .; + + /* Stack for UND mode */ + _und_stack_low_end = .; + . += (_und_stack_size * _num_cores); + _und_stack_high_end = .; + + /* Stack for SVC mode */ + _svc_stack_low_end = .; + . += (_svc_stack_size * _num_cores); + _svc_stack_high_end = .; + + /* Stack for ABT mode */ + _abt_stack_low_end = .; + . += (_abt_stack_size * _num_cores); + _abt_stack_high_end = .; + + /* Stack for HYP mode */ + _hyp_stack_low_end = .; + . += (_hyp_stack_size * _num_cores); + _hyp_stack_high_end = .; + + /* Stack for IRQ mode */ + _irq_stack_low_end = .; + . += (_irq_stack_size * _num_cores); + _irq_stack_high_end = .; + + /* Stack for FIQ mode */ + _fiq_stack_low_end = .; + . += (_fiq_stack_size * _num_cores); + _fiq_stack_high_end = .; + + /* Stack for SYS mode */ + _sys_stack_low_end = .; + . += (_sys_stack_size * _num_cores); + _sys_stack_high_end = .; + + /* Highest address of allocated stack */ _stacks_high_end = .; } > STACKS /DISCARD/ : { + /* Discard any notes */ *(.note .note*) + /* Discard these unwinding/exception related symbols, they are not used */ *(.ARM.exidx* .gnu.linkonce.armexidx.*) + /* Discard these exception related symbols, they are not used */ *(.ARM.extab* .gnu.linkonce.armextab.*) } } -/* We provide default sizes for the stacks to be overwritten in memory.x */ -PROVIDE(_stack_top = _stacks_high_end); /* deprecated, use _xxx_stack labels as defined in .stacks section */ -PROVIDE(_hyp_stack_size = 0x400); -PROVIDE(_und_stack_size = 0x400); -PROVIDE(_svc_stack_size = 0x400); -PROVIDE(_abt_stack_size = 0x400); -PROVIDE(_irq_stack_size = 0x400); -PROVIDE(_fiq_stack_size = 0x400); -PROVIDE(_sys_stack_size = 0x2000); -PROVIDE(_pack_stacks = 0); /* set this to 1 to remove the filler section pushing the stacks to the end of STACKS. */ +/* We provide default sizes for the stacks for any not specified in memory.x (which was loaded first) */ +PROVIDE(_und_stack_size = 16K); +PROVIDE(_svc_stack_size = 16K); +PROVIDE(_abt_stack_size = 16K); +PROVIDE(_hyp_stack_size = 16K); +PROVIDE(_irq_stack_size = 64); +PROVIDE(_fiq_stack_size = 64); +PROVIDE(_sys_stack_size = 16K); +/* Default to one CPU core (i.e. one copy of each stack) */ +PROVIDE(_num_cores = 1); +/* Set this to 1 in memory.x to remove the filler section pushing the stacks to the end of STACKS. */ +PROVIDE(_pack_stacks = 0); /* Weak aliases for ASM default handlers */ PROVIDE(_start = _default_start); @@ -143,6 +243,7 @@ PROVIDE(_asm_svc_handler = _asm_default_svc_handler); PROVIDE(_asm_hvc_handler = _asm_default_hvc_handler); PROVIDE(_asm_prefetch_abort_handler = _asm_default_prefetch_abort_handler); PROVIDE(_asm_data_abort_handler = _asm_default_data_abort_handler); +/* TODO: Hyp handler goes here */ PROVIDE(_asm_irq_handler = _asm_default_irq_handler); PROVIDE(_asm_fiq_handler = _asm_default_fiq_handler); @@ -152,5 +253,26 @@ PROVIDE(_svc_handler = _default_handler); PROVIDE(_hvc_handler = _default_handler); PROVIDE(_prefetch_abort_handler = _default_handler); PROVIDE(_data_abort_handler = _default_handler); +/* TODO: Hyp handler goes here */ PROVIDE(_irq_handler = _default_handler); -/* There is no default C-language FIQ handler */ +/* NB: There is no default C-language FIQ handler */ + +/* Check the stack sizes are all a multiple of eight bytes */ +ASSERT(_und_stack_size % 8 == 0, " +ERROR(aarch32-rt): UND stack size (_und_stack_size) is not a multiple of 8 bytes"); +ASSERT(_svc_stack_size % 8 == 0, " +ERROR(aarch32-rt): SVC stack size (_svc_stack_size) is not a multiple of 8 bytes"); +ASSERT(_abt_stack_size % 8 == 0, " +ERROR(aarch32-rt): ABT stack size (_abt_stack_size) is not a multiple of 8 bytes"); +ASSERT(_hyp_stack_size % 8 == 0, " +ERROR(aarch32-rt): HYP stack size (_hyp_stack_size) is not a multiple of 8 bytes"); +ASSERT(_irq_stack_size % 8 == 0, " +ERROR(aarch32-rt): IRQ stack size (_irq_stack_size) is not a multiple of 8 bytes"); +ASSERT(_fiq_stack_size % 8 == 0, " +ERROR(aarch32-rt): FIQ stack size (_fiq_stack_size) is not a multiple of 8 bytes"); +ASSERT(_sys_stack_size % 8 == 0, " +ERROR(aarch32-rt): SYS stack size (_sys_stack_size) is not a multiple of 8 bytes"); +ASSERT(_num_cores != 0, " +ERROR(aarch32-rt): Number of cores cannot be zero"); + +/* End of file */ diff --git a/aarch32-rt/src/lib.rs b/aarch32-rt/src/lib.rs index 0ca26461..a6445963 100644 --- a/aarch32-rt/src/lib.rs +++ b/aarch32-rt/src/lib.rs @@ -39,6 +39,8 @@ //! //! ## Constants //! +//! * `_num_cores` - the number of CPU core (and hence the number of copies of +//! each stack). Must be > 0. //! * `__sbss` - the start of zero-initialised data in RAM. Must be 4-byte //! aligned. //! * `__ebss` - the end of zero-initialised data in RAM. Must be 4-byte @@ -70,29 +72,29 @@ //! //! Stacks are located in `.stacks` section which is mapped to the `STACKS` //! memory region. Per default, the stacks are pushed to the end of the `STACKS` -//! by a filler section. +//! by a filler section. We allocate stacks for each core, based on the +//! `_num_cores` linker symbol. //! //! The stacks look like: //! //! ```text -//! +------------------+ <----_stack_top equals ORIGIN(STACKS) + LENGTH(STACKS) -//! | HYP Stack | } _hyp_stack_size bytes (Armv8-R only) +//! +------------------+ <---- ORIGIN(STACKS) + LENGTH(STACKS) +//! | SYS Stack | } _sys_stack_size * _num_cores bytes //! +------------------+ -//! | UND Stack | } _und_stack_size bytes +//! | FIQ Stack | } _fiq_stack_size * _num_cores bytes //! +------------------+ -//! | SVC Stack | } _svc_stack_size bytes +//! | IRQ Stack | } _irq_stack_size * _num_cores bytes //! +------------------+ -//! | ABT Stack | } _abt_stack_size bytes +//! | HYP Stack | } _hyp_stack_size * _num_cores bytes (only used on Armv8-R) //! +------------------+ -//! | IRQ Stack | } _irq_stack_size bytes +//! | ABT Stack | } _abt_stack_size * _num_cores bytes //! +------------------+ -//! | FIQ Stack | } _fiq_stack_size bytes +//! | SVC Stack | } _svc_stack_size * _num_cores bytes //! +------------------+ -//! | SYS Stack | } _sys_stack_size bytes +//! | UND Stack | } _und_stack_size * _num_cores bytes //! +------------------+ -//! | filler section | } calculated so that _stack_top equals end of STACKS -//! +------------------+ <----either ORIGIN(STACKS) or the end of previous -//! section located in STACKS or its alias. +//! | filler section | +//! +------------------+ <---- ORIGIN(STACKS) //! ``` //! //! Our linker script PROVIDEs a symbol `_pack_stacks`. By setting this symbol @@ -117,8 +119,9 @@ //! You can also create a 'kmain' function by using the `#[entry]` attribute on //! a normal Rust function. The function will be renamed in such a way that the //! start-up assembly code can find it, but normal Rust code cannot. Therefore -//! you can be assured that the function will only be called once (unless someone -//! resorts to `unsafe` Rust to import the `kmain` symbol as an `extern "C" fn`). +//! you can be assured that the function will only be called once (unless +//! someone resorts to `unsafe` Rust to import the `kmain` symbol as an `extern +//! "C" fn`). //! //! ```rust //! use aarch32_rt::entry; @@ -502,26 +505,23 @@ //! `_irq_handler` //! * `_asm_default_fiq_handler` - an FIQ handler that just spins //! * `_default_handler` - a C compatible function that spins forever. -//! * `_init_segments` - initialises `.bss` and `.data` +//! * `_init_segments` - initialises `.bss` and `.data` and zeroes the stacks //! * `_stack_setup_preallocated` - initialises UND, SVC, ABT, IRQ, FIQ and SYS //! stacks from the `.stacks` section defined in link.x, based on -//! _xxx_stack_size values -//! * `_xxx_stack` and `_xxx_stack_end` where the former is the top and the latter -//! the bottom of the stack for each mode (`und`, `svc`, `abt`, `irq`, `fiq`, `sys`) -//! * `_stack_top` - the address of the top of the STACKS region that contains -//! the reseved stacks, with eight-byte alignment. -//! Using this symbol is deprecated, stacks should be initialized by their -//! individual _xxx_stack symbols -//! -//! The assembly language trampolines are required because AArch32 -//! processors do not save a great deal of state on entry to an exception -//! handler, unlike Armv7-M (and other M-Profile) processors. We must therefore -//! save this state to the stack using assembly language, before transferring to -//! an `extern "C"` function. We do not change modes before entering that -//! `extern "C"` function - that's for the handler to deal with as it wishes. -//! Because FIQ is often performance-sensitive, we don't supply an FIQ -//! trampoline; if you want to use FIQ, you have to write your own assembly -//! routine, allowing you to preserve only whatever state is important to you. +//! _xxx_stack_size values, and the core number given in `r0` +//! * `_xxx_stack_high_end` and `_xxx_stack_low_end` where the former is the top +//! and the latter the bottom of the stack for each mode (`und`, `svc`, `abt`, +//! `irq`, `fiq`, `sys`) +//! +//! The assembly language trampolines are required because AArch32 processors do +//! not save a great deal of state on entry to an exception handler, unlike +//! Armv7-M (and other M-Profile) processors. We must therefore save this state +//! to the stack using assembly language, before transferring to an `extern "C"` +//! function. We do not change modes before entering that `extern "C"` function +//! - that's for the handler to deal with as it wishes. Because FIQ is often +//! performance-sensitive, we don't supply an FIQ trampoline; if you want to use +//! FIQ, you have to write your own assembly routine, allowing you to preserve +//! only whatever state is important to you. //! //! ## Examples //! @@ -558,6 +558,8 @@ mod arch_v7; ))] mod arch_v4; +pub mod stacks; + /// Our default exception handler. /// /// We end up here if an exception fires and the weak 'PROVIDE' in the link.x @@ -795,38 +797,58 @@ core::arch::global_asm!( // Configure a stack for every mode. Leaves you in sys mode. // + // Pass the core number in r0 .section .text._stack_setup_preallocated .global _stack_setup_preallocated + .arm .type _stack_setup_preallocated, %function _stack_setup_preallocated: // Save LR from whatever mode we're currently in - mov r2, lr + mov r3, lr // (we might not be in the same mode when we return). // Set stack pointer and mask interrupts for UND mode (Mode 0x1B) msr cpsr_c, {und_mode} - ldr r13, =_und_stack + ldr r2, =_und_stack_high_end + ldr r1, =_und_stack_size + muls r1, r1, r0 + subs sp, r2, r1 // Set stack pointer (right after) and mask interrupts for SVC mode (Mode 0x13) msr cpsr_c, {svc_mode} - ldr r13, =_svc_stack + ldr r2, =_svc_stack_high_end + ldr r1, =_svc_stack_size + muls r1, r1, r0 + subs sp, r2, r1 // Set stack pointer (right after) and mask interrupts for ABT mode (Mode 0x17) msr cpsr_c, {abt_mode} - ldr r13, =_abt_stack + ldr r2, =_abt_stack_high_end + ldr r1, =_abt_stack_size + muls r1, r1, r0 + subs sp, r2, r1 // Set stack pointer (right after) and mask interrupts for IRQ mode (Mode 0x12) msr cpsr_c, {irq_mode} - ldr r13, =_irq_stack + ldr r2, =_irq_stack_high_end + ldr r1, =_irq_stack_size + muls r1, r1, r0 + subs sp, r2, r1 // Set stack pointer (right after) and mask interrupts for FIQ mode (Mode 0x11) msr cpsr_c, {fiq_mode} - ldr r13, =_fiq_stack + ldr r2, =_fiq_stack_high_end + ldr r1, =_fiq_stack_size + muls r1, r1, r0 + subs sp, r2, r1 // Set stack pointer (right after) and mask interrupts for System mode (Mode 0x1F) msr cpsr_c, {sys_mode} - ldr r13, =_sys_stack + ldr r2, =_sys_stack_high_end + ldr r1, =_sys_stack_size + muls r1, r1, r0 + subs sp, r2, r1 // Clear the Thumb Exception bit because all vector table is written in Arm assembly // even on Thumb targets. mrc p15, 0, r1, c1, c0, 0 bic r1, #{te_bit} mcr p15, 0, r1, c1, c0, 0 // return to caller - bx r2 + bx r3 .size _stack_setup_preallocated, . - _stack_setup_preallocated // Initialises stacks, .data and .bss @@ -835,7 +857,7 @@ core::arch::global_asm!( .global _init_segments .type _init_segments, %function _init_segments: - // Initialise .bss + // Zero .bss ldr r0, =__sbss ldr r1, =__ebss mov r2, 0 @@ -844,6 +866,16 @@ core::arch::global_asm!( beq 1f stm r0!, {{r2}} b 0b + 1: + // Zero the stacks + ldr r0, =_stacks_low_end + ldr r1, =_stacks_high_end + mov r2, 0 + 0: + cmp r1, r0 + beq 1f + stm r0!, {{r2}} + b 0b 1: // Initialise .data ldr r0, =__sdata @@ -923,10 +955,11 @@ core::arch::global_asm!( .global _default_start .type _default_start, %function _default_start: - // Set up stacks. - bl _stack_setup_preallocated // Init .data and .bss bl _init_segments + // Set up stacks. + mov r0, #0 + bl _stack_setup_preallocated "#, fpu_enable!(), r#" @@ -975,7 +1008,7 @@ core::arch::global_asm!( cmp r0, {cpsr_mode_hyp} bne 1f // Set stack pointer - ldr sp, =_hyp_stack + ldr sp, =_hyp_stack_high_end // Set the HVBAR (for EL2) to _vector_table ldr r1, =_vector_table mcr p15, 4, r1, c12, c0, 0 @@ -993,14 +1026,15 @@ core::arch::global_asm!( isb eret 1: - // Set up stacks. - bl _stack_setup_preallocated // Set the VBAR (for EL1) to _vector_table. NB: This isn't required on // Armv7-R because that only supports 'low' (default) or 'high'. ldr r0, =_vector_table mcr p15, 0, r0, c12, c0, 0 // Init .data and .bss bl _init_segments + // Set up stacks. + mov r0, #0 + bl _stack_setup_preallocated "#, fpu_enable!(), r#" diff --git a/aarch32-rt/src/stacks.rs b/aarch32-rt/src/stacks.rs new file mode 100644 index 00000000..6de2b2f5 --- /dev/null +++ b/aarch32-rt/src/stacks.rs @@ -0,0 +1,136 @@ +//! Code for examining linker allocated stacks + +/// Represents one of the AArch32 stacks +#[derive(Debug, Copy, Clone)] +pub enum Stack { + /// UND mode stack + Und, + /// SVC mode stack + Svc, + /// ABT mode stack, for data abort and prefetch abort + Abt, + /// HYP mode stack, for EL2 + Hyp, + /// IRQ mode stack, for interrupts + Irq, + /// FIQ mode stack, for fast interrupts + Fiq, + /// SYS mode stack, for the main thread + Sys, +} + +impl core::fmt::Display for Stack { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!( + f, + "{}", + match self { + Stack::Und => "UND", + Stack::Svc => "SVC", + Stack::Abt => "ABT", + Stack::Hyp => "HYP", + Stack::Irq => "IRQ", + Stack::Fiq => "FIQ", + Stack::Sys => "SYS", + } + ) + } +} + +impl Stack { + /// Create an iterator over all the stacks + pub fn iter() -> impl Iterator { + [ + Stack::Und, + Stack::Svc, + Stack::Abt, + Stack::Hyp, + Stack::Irq, + Stack::Fiq, + Stack::Sys, + ] + .iter() + .cloned() + } + + /// Get the highest address of this stack, for the given core + pub fn top(&self, core: usize) -> Option<*const u32> { + if core > Self::num_cores() { + return None; + } + let top = self.stack_top(); + let core = core as isize; + let per_core_size_words = self.per_core_size_words(); + Some(unsafe { top.offset(-per_core_size_words * core) }) + } + + /// Get the lowest address of this stack, for the given core + pub fn bottom(&self, core: usize) -> Option<*const u32> { + self.top(core) + .map(|p| unsafe { p.offset(-self.per_core_size_words()) }) + } + + /// Get the range of this stack, for the given core + pub fn range(&self, core: usize) -> Option> { + if let (Some(bottom), Some(top)) = (self.bottom(core), self.top(core)) { + Some(bottom..top) + } else { + None + } + } + + /// Get number of cores in this system + pub fn num_cores() -> usize { + unsafe extern "C" { + safe static _num_cores: u8; + } + core::ptr::addr_of!(_num_cores) as usize + } + + /// Get the total size of this stack across all cores + pub fn per_core_size_words(&self) -> isize { + use core::ptr::addr_of; + unsafe extern "C" { + static _und_stack_size: u8; + static _svc_stack_size: u8; + static _abt_stack_size: u8; + static _hyp_stack_size: u8; + static _irq_stack_size: u8; + static _fiq_stack_size: u8; + static _sys_stack_size: u8; + } + let size_bytes = match self { + Stack::Und => addr_of!(_und_stack_size) as isize, + Stack::Svc => addr_of!(_svc_stack_size) as isize, + Stack::Abt => addr_of!(_abt_stack_size) as isize, + Stack::Hyp => addr_of!(_hyp_stack_size) as isize, + Stack::Irq => addr_of!(_irq_stack_size) as isize, + Stack::Fiq => addr_of!(_fiq_stack_size) as isize, + Stack::Sys => addr_of!(_sys_stack_size) as isize, + }; + size_bytes / 4 + } + + /// Get the top address for this stack + fn stack_top(&self) -> *const u32 { + use core::ptr::addr_of; + unsafe extern "C" { + static _und_stack_high_end: u32; + static _svc_stack_high_end: u32; + static _abt_stack_high_end: u32; + static _hyp_stack_high_end: u32; + static _irq_stack_high_end: u32; + static _fiq_stack_high_end: u32; + static _sys_stack_high_end: u32; + } + match self { + Stack::Und => addr_of!(_und_stack_high_end), + Stack::Svc => addr_of!(_svc_stack_high_end), + Stack::Abt => addr_of!(_abt_stack_high_end), + Stack::Hyp => addr_of!(_hyp_stack_high_end), + Stack::Irq => addr_of!(_irq_stack_high_end), + Stack::Fiq => addr_of!(_fiq_stack_high_end), + Stack::Sys => addr_of!(_sys_stack_high_end), + } + } +} diff --git a/examples/mps3-an536-smp/memory.x b/examples/mps3-an536-smp/memory.x index 0b784d24..c4aa51bb 100644 --- a/examples/mps3-an536-smp/memory.x +++ b/examples/mps3-an536-smp/memory.x @@ -15,68 +15,5 @@ REGION_ALIAS("CODE", QSPI); REGION_ALIAS("DATA", BRAM); REGION_ALIAS("STACKS", BRAM); -SECTIONS { - /* ### Interrupt Handler Entries - * - * The IRQ handler walks this section to find registered - * interrupt handlers - */ - .irq_entries : ALIGN(4) - { - /* We put this in the header */ - __irq_entries_start = .; - /* Here are the entries */ - KEEP(*(.irq_entries)); - /* Keep this block a nice round size */ - . = ALIGN(4); - /* We put this in the header */ - __irq_entries_end = .; - } > CODE -} INSERT AFTER .text; - -SECTIONS { - .core1_stacks (NOLOAD) : ALIGN(8) - { - . = ALIGN(8); - _core1_stacks_low_end = .; - _core1_sys_stack_end = .; - . += _sys_stack_size; - . = ALIGN(8); - _core1_sys_stack = .; - _core1_fiq_stack_end = .; - . += _fiq_stack_size; - . = ALIGN(8); - _core1_fiq_stack = .; - _core1_irq_stack_end = .; - . += _irq_stack_size; - . = ALIGN(8); - _core1_irq_stack = .; - _core1_abt_stack_end = .; - . += _abt_stack_size; - . = ALIGN(8); - _core1_abt_stack = .; - _core1_svc_stack_end = .; - . += _svc_stack_size; - . = ALIGN(8); - _core1_svc_stack = .; - _core1_und_stack_end = .; - . += _und_stack_size; - . = ALIGN(8); - _core1_und_stack = .; - _core1_hyp_stack_end = .; - . += _hyp_stack_size; - . = ALIGN(8); - _core1_hyp_stack = .; - _core1_stacks_high_end = .; - } > STACKS -} INSERT BEFORE .filler; - +PROVIDE(_num_cores = 2); PROVIDE(kmain2 = default_kmain2); - -PROVIDE(_hyp_stack_size = 16K); -PROVIDE(_und_stack_size = 16K); -PROVIDE(_svc_stack_size = 16K); -PROVIDE(_abt_stack_size = 16K); -PROVIDE(_irq_stack_size = 64); -PROVIDE(_fiq_stack_size = 64); -PROVIDE(_sys_stack_size = 16K); diff --git a/examples/mps3-an536-smp/src/lib.rs b/examples/mps3-an536-smp/src/lib.rs index 2af619ed..e3a798e2 100644 --- a/examples/mps3-an536-smp/src/lib.rs +++ b/examples/mps3-an536-smp/src/lib.rs @@ -53,7 +53,7 @@ #![no_std] -use aarch32_cpu::register::{Hactlr, Sctlr, Cpsr, cpsr::ProcessorMode}; +use aarch32_cpu::register::{Hactlr, Cpsr, cpsr::ProcessorMode}; use core::sync::atomic::{AtomicBool, Ordering}; @@ -100,83 +100,44 @@ pub fn exit(code: i32) -> ! { /// /// ```text /// Stack usage report: -/// SYS Stack = 332 used of 16384 bytes (002%) @ 0x1006bf80..0x1006ff80 -/// FIQ Stack = 0 used of 64 bytes (000%) @ 0x1006ff80..0x1006ffc0 -/// IRQ Stack = 0 used of 64 bytes (000%) @ 0x1006ffc0..0x10070000 -/// ABT Stack = 0 used of 16384 bytes (000%) @ 0x10070000..0x10074000 -/// SVC Stack = 0 used of 16384 bytes (000%) @ 0x10074000..0x10078000 -/// UND Stack = 244 used of 16384 bytes (001%) @ 0x10078000..0x1007c000 -/// HYP Stack = 0 used of 16384 bytes (000%) @ 0x1007c000..0x10080000 +/// UND1 Stack = 0 used of 16384 bytes (000%) @ 0x10057f00..0x1005bf00 +/// UND0 Stack = 0 used of 16384 bytes (000%) @ 0x1005bf00..0x1005ff00 +/// SVC1 Stack = 0 used of 16384 bytes (000%) @ 0x1005ff00..0x10063f00 +/// SVC0 Stack = 0 used of 16384 bytes (000%) @ 0x10063f00..0x10067f00 +/// ABT1 Stack = 0 used of 16384 bytes (000%) @ 0x10067f00..0x1006bf00 +/// ABT0 Stack = 0 used of 16384 bytes (000%) @ 0x1006bf00..0x1006ff00 +/// HYP1 Stack = 0 used of 16384 bytes (000%) @ 0x1006ff00..0x10073f00 +/// HYP0 Stack = 0 used of 16384 bytes (000%) @ 0x10073f00..0x10077f00 +/// IRQ1 Stack = 0 used of 64 bytes (000%) @ 0x10077f00..0x10077f40 +/// IRQ0 Stack = 0 used of 64 bytes (000%) @ 0x10077f40..0x10077f80 +/// FIQ1 Stack = 0 used of 64 bytes (000%) @ 0x10077f80..0x10077fc0 +/// FIQ0 Stack = 0 used of 64 bytes (000%) @ 0x10077fc0..0x10078000 +/// SYS1 Stack = 808 used of 16384 bytes (004%) @ 0x10078000..0x1007c000 +/// SYS0 Stack = 1432 used of 16384 bytes (008%) @ 0x1007c000..0x10080000 /// ``` fn stack_dump() { use aarch32_cpu::stacks::stack_used_bytes; - use core::ptr::addr_of; - - unsafe extern "C" { - static _sys_stack_end: u32; - static _sys_stack: u32; - static _fiq_stack_end: u32; - static _fiq_stack: u32; - static _irq_stack_end: u32; - static _irq_stack: u32; - static _abt_stack_end: u32; - static _abt_stack: u32; - static _svc_stack_end: u32; - static _svc_stack: u32; - static _und_stack_end: u32; - static _und_stack: u32; - static _hyp_stack_end: u32; - static _hyp_stack: u32; - - static _core1_sys_stack_end: u32; - static _core1_sys_stack: u32; - static _core1_fiq_stack_end: u32; - static _core1_fiq_stack: u32; - static _core1_irq_stack_end: u32; - static _core1_irq_stack: u32; - static _core1_abt_stack_end: u32; - static _core1_abt_stack: u32; - static _core1_svc_stack_end: u32; - static _core1_svc_stack: u32; - static _core1_und_stack_end: u32; - static _core1_und_stack: u32; - static _core1_hyp_stack_end: u32; - static _core1_hyp_stack: u32; - } - - // these are placed in the order they are in aarch32-rt/link.x - let stacks = [ - ("SYS0", addr_of!(_sys_stack_end)..addr_of!(_sys_stack)), - ("FIQ0", addr_of!(_fiq_stack_end)..addr_of!(_fiq_stack)), - ("IRQ0", addr_of!(_irq_stack_end)..addr_of!(_irq_stack)), - ("ABT0", addr_of!(_abt_stack_end)..addr_of!(_abt_stack)), - ("SVC0", addr_of!(_svc_stack_end)..addr_of!(_svc_stack)), - ("UND0", addr_of!(_und_stack_end)..addr_of!(_und_stack)), - ("HYP0", addr_of!(_hyp_stack_end)..addr_of!(_hyp_stack)), - ("SYS1", addr_of!(_core1_sys_stack_end)..addr_of!(_core1_sys_stack)), - ("FIQ1", addr_of!(_core1_fiq_stack_end)..addr_of!(_core1_fiq_stack)), - ("IRQ1", addr_of!(_core1_irq_stack_end)..addr_of!(_core1_irq_stack)), - ("ABT1", addr_of!(_core1_abt_stack_end)..addr_of!(_core1_abt_stack)), - ("SVC1", addr_of!(_core1_svc_stack_end)..addr_of!(_core1_svc_stack)), - ("UND1", addr_of!(_core1_und_stack_end)..addr_of!(_core1_und_stack)), - ("HYP1", addr_of!(_core1_hyp_stack_end)..addr_of!(_core1_hyp_stack)), - ]; + use aarch32_rt::stacks::Stack; semihosting::eprintln!("Stack usage report:"); unsafe { - for (name, range) in stacks { - let (total, used) = stack_used_bytes(range.clone()); - let percent = used * 100 / total; - // Send to stderr, so it doesn't mix with expected output on stdout - semihosting::eprintln!( - "{} Stack = {:6} used of {:6} bytes ({:03}%) @ {:08x?}", - name, - used, - total, - percent, - range - ); + for stack in Stack::iter() { + for core in (0..Stack::num_cores()).rev() { + let core_range = stack.range(core).unwrap(); + let (total, used) = stack_used_bytes(core_range.clone()); + let percent = used * 100 / total; + // Send to stderr, so it doesn't mix with expected output on stdout + semihosting::eprintln!( + "{}{} Stack = {:6} used of {:6} bytes ({:03}%) @ {:08x?}", + stack, + core, + used, + total, + percent, + core_range + ); + } } } } @@ -294,7 +255,8 @@ core::arch::global_asm!( ldr r0, =_vector_table mcr p15, 0, r0, c12, c0, 0 // set up our stacks - also switches to SYS mode - bl _core1_stack_setup_preallocated + movs r0, #1 + bl _stack_setup_preallocated // Zero all registers before calling kmain2 mov r0, 0 mov r1, 0 @@ -335,95 +297,6 @@ core::arch::global_asm!( }, ); -// Initialise the stack for Core 1 for each mode -#[cfg(target_arch = "arm")] -core::arch::global_asm!( - r#" - // Work around https://github.com/rust-lang/rust/issues/127269 - .fpu vfp2 - - // Configure a stack for every mode. Leaves you in sys mode. - // - .section .text._core1_stack_setup_preallocated - .arm - .global _core1_stack_setup_preallocated - .type _core1_stack_setup_preallocated, %function - _core1_stack_setup_preallocated: - // Save LR from whatever mode we're currently in - mov r2, lr - // (we might not be in the same mode when we return). - // Set stack pointer and mask interrupts for UND mode (Mode 0x1B) - msr cpsr_c, {und_mode} - ldr r13, =_core1_und_stack - // Set stack pointer (right after) and mask interrupts for SVC mode (Mode 0x13) - msr cpsr_c, {svc_mode} - ldr r13, =_core1_svc_stack - // Set stack pointer (right after) and mask interrupts for ABT mode (Mode 0x17) - msr cpsr_c, {abt_mode} - ldr r13, =_core1_abt_stack - // Set stack pointer (right after) and mask interrupts for IRQ mode (Mode 0x12) - msr cpsr_c, {irq_mode} - ldr r13, =_core1_irq_stack - // Set stack pointer (right after) and mask interrupts for FIQ mode (Mode 0x11) - msr cpsr_c, {fiq_mode} - ldr r13, =_core1_fiq_stack - // Set stack pointer (right after) and mask interrupts for System mode (Mode 0x1F) - msr cpsr_c, {sys_mode} - ldr r13, =_core1_sys_stack - // Clear the Thumb Exception bit because all vector table is written in Arm assembly - // even on Thumb targets. - mrc p15, 0, r1, c1, c0, 0 - bic r1, #{te_bit} - mcr p15, 0, r1, c1, c0, 0 - // return to caller - bx r2 - .size _core1_stack_setup_preallocated, . - _core1_stack_setup_preallocated - "#, - und_mode = const { - Cpsr::new_with_raw_value(0) - .with_mode(ProcessorMode::Und) - .with_i(true) - .with_f(true) - .raw_value() - }, - svc_mode = const { - Cpsr::new_with_raw_value(0) - .with_mode(ProcessorMode::Svc) - .with_i(true) - .with_f(true) - .raw_value() - }, - abt_mode = const { - Cpsr::new_with_raw_value(0) - .with_mode(ProcessorMode::Abt) - .with_i(true) - .with_f(true) - .raw_value() - }, - fiq_mode = const { - Cpsr::new_with_raw_value(0) - .with_mode(ProcessorMode::Fiq) - .with_i(true) - .with_f(true) - .raw_value() - }, - irq_mode = const { - Cpsr::new_with_raw_value(0) - .with_mode(ProcessorMode::Irq) - .with_i(true) - .with_f(true) - .raw_value() - }, - sys_mode = const { - Cpsr::new_with_raw_value(0) - .with_mode(ProcessorMode::Sys) - .with_i(true) - .with_f(true) - .raw_value() - }, - te_bit = const { Sctlr::new_with_raw_value(0).with_te(true).raw_value() } -); - /// What a second core does when no `kmain2` is supplied. #[unsafe(no_mangle)] pub extern "C" fn default_kmain2() { diff --git a/examples/mps3-an536/src/bin/el2_hello.rs b/examples/mps3-an536/src/bin/el2_hello.rs index c1a52365..e10a979c 100644 --- a/examples/mps3-an536/src/bin/el2_hello.rs +++ b/examples/mps3-an536/src/bin/el2_hello.rs @@ -44,7 +44,7 @@ core::arch::global_asm!( .type _start, %function _start: // Set stack pointer - ldr sp, =_hyp_stack + ldr sp, =_hyp_stack_high_end // Set the HVBAR (for EL2) to _vector_table ldr r1, =_vector_table mcr p15, 4, r1, c12, c0, 0 diff --git a/examples/mps3-an536/src/lib.rs b/examples/mps3-an536/src/lib.rs index 72596eba..e3eb26b5 100644 --- a/examples/mps3-an536/src/lib.rs +++ b/examples/mps3-an536/src/lib.rs @@ -105,61 +105,37 @@ pub fn exit(code: i32) -> ! { /// /// ```text /// Stack usage report: -/// SYS Stack = 332 used of 16384 bytes (002%) @ 0x1006bf80..0x1006ff80 -/// FIQ Stack = 0 used of 64 bytes (000%) @ 0x1006ff80..0x1006ffc0 -/// IRQ Stack = 0 used of 64 bytes (000%) @ 0x1006ffc0..0x10070000 -/// ABT Stack = 0 used of 16384 bytes (000%) @ 0x10070000..0x10074000 -/// SVC Stack = 0 used of 16384 bytes (000%) @ 0x10074000..0x10078000 -/// UND Stack = 244 used of 16384 bytes (001%) @ 0x10078000..0x1007c000 -/// HYP Stack = 0 used of 16384 bytes (000%) @ 0x1007c000..0x10080000 +/// UND0 Stack = 0 used of 16384 bytes (000%) @ 0x1006bf80..0x1006ff80 +/// SVC0 Stack = 0 used of 16384 bytes (000%) @ 0x1006ff80..0x10073f80 +/// ABT0 Stack = 0 used of 16384 bytes (000%) @ 0x10073f80..0x10077f80 +/// HYP0 Stack = 0 used of 16384 bytes (000%) @ 0x10077f80..0x1007bf80 +/// IRQ0 Stack = 0 used of 64 bytes (000%) @ 0x1007bf80..0x1007bfc0 +/// FIQ0 Stack = 0 used of 64 bytes (000%) @ 0x1007bfc0..0x1007c000 +/// SYS0 Stack = 2416 used of 16384 bytes (014%) @ 0x1007c000..0x10080000 /// ``` fn stack_dump() { use aarch32_cpu::stacks::stack_used_bytes; - use core::ptr::addr_of; - - unsafe extern "C" { - static _sys_stack_end: u32; - static _sys_stack: u32; - static _fiq_stack_end: u32; - static _fiq_stack: u32; - static _irq_stack_end: u32; - static _irq_stack: u32; - static _abt_stack_end: u32; - static _abt_stack: u32; - static _svc_stack_end: u32; - static _svc_stack: u32; - static _und_stack_end: u32; - static _und_stack: u32; - static _hyp_stack_end: u32; - static _hyp_stack: u32; - } - - // these are placed in the order they are in aarch32-rt/link.x - let stacks = [ - ("SYS", addr_of!(_sys_stack_end)..addr_of!(_sys_stack)), - ("FIQ", addr_of!(_fiq_stack_end)..addr_of!(_fiq_stack)), - ("IRQ", addr_of!(_irq_stack_end)..addr_of!(_irq_stack)), - ("ABT", addr_of!(_abt_stack_end)..addr_of!(_abt_stack)), - ("SVC", addr_of!(_svc_stack_end)..addr_of!(_svc_stack)), - ("UND", addr_of!(_und_stack_end)..addr_of!(_und_stack)), - ("HYP", addr_of!(_hyp_stack_end)..addr_of!(_hyp_stack)), - ]; + use aarch32_rt::stacks::Stack; semihosting::eprintln!("Stack usage report:"); unsafe { - for (name, range) in stacks { - let (total, used) = stack_used_bytes(range.clone()); - let percent = (used * 100).checked_div(total).unwrap_or(999); - // Send to stderr, so it doesn't mix with expected output on stdout - semihosting::eprintln!( - "{} Stack = {:6} used of {:6} bytes ({:03}%) @ {:08x?}", - name, - used, - total, - percent, - range - ); + for stack in Stack::iter() { + for core in (0..Stack::num_cores()).rev() { + let core_range = stack.range(core).unwrap(); + let (total, used) = stack_used_bytes(core_range.clone()); + let percent = used * 100 / total; + // Send to stderr, so it doesn't mix with expected output on stdout + semihosting::eprintln!( + "{}{} Stack = {:6} used of {:6} bytes ({:03}%) @ {:08x?}", + stack, + core, + used, + total, + percent, + core_range + ); + } } } } diff --git a/examples/versatileab/src/lib.rs b/examples/versatileab/src/lib.rs index 7b3f9969..3d6ff6e9 100644 --- a/examples/versatileab/src/lib.rs +++ b/examples/versatileab/src/lib.rs @@ -69,61 +69,37 @@ pub fn exit(code: i32) -> ! { /// /// ```text /// Stack usage report: -/// SYS Stack = 332 used of 16384 bytes (002%) @ 0x1006bf80..0x1006ff80 -/// FIQ Stack = 0 used of 64 bytes (000%) @ 0x1006ff80..0x1006ffc0 -/// IRQ Stack = 0 used of 64 bytes (000%) @ 0x1006ffc0..0x10070000 -/// ABT Stack = 0 used of 16384 bytes (000%) @ 0x10070000..0x10074000 -/// SVC Stack = 0 used of 16384 bytes (000%) @ 0x10074000..0x10078000 -/// UND Stack = 244 used of 16384 bytes (001%) @ 0x10078000..0x1007c000 -/// HYP Stack = 0 used of 16384 bytes (000%) @ 0x1007c000..0x10080000 +/// UND0 Stack = 0 used of 16384 bytes (000%) @ 0x1006bf80..0x1006ff80 +/// SVC0 Stack = 0 used of 16384 bytes (000%) @ 0x1006ff80..0x10073f80 +/// ABT0 Stack = 0 used of 16384 bytes (000%) @ 0x10073f80..0x10077f80 +/// HYP0 Stack = 0 used of 16384 bytes (000%) @ 0x10077f80..0x1007bf80 +/// IRQ0 Stack = 0 used of 64 bytes (000%) @ 0x1007bf80..0x1007bfc0 +/// FIQ0 Stack = 0 used of 64 bytes (000%) @ 0x1007bfc0..0x1007c000 +/// SYS0 Stack = 2416 used of 16384 bytes (014%) @ 0x1007c000..0x10080000 /// ``` fn stack_dump() { use aarch32_cpu::stacks::stack_used_bytes; - use core::ptr::addr_of; - - unsafe extern "C" { - static _sys_stack_end: u32; - static _sys_stack: u32; - static _fiq_stack_end: u32; - static _fiq_stack: u32; - static _irq_stack_end: u32; - static _irq_stack: u32; - static _abt_stack_end: u32; - static _abt_stack: u32; - static _svc_stack_end: u32; - static _svc_stack: u32; - static _und_stack_end: u32; - static _und_stack: u32; - static _hyp_stack_end: u32; - static _hyp_stack: u32; - } - - // these are placed in the order they are in aarch32-rt/link.x - let stacks = [ - ("SYS", addr_of!(_sys_stack_end)..addr_of!(_sys_stack)), - ("FIQ", addr_of!(_fiq_stack_end)..addr_of!(_fiq_stack)), - ("IRQ", addr_of!(_irq_stack_end)..addr_of!(_irq_stack)), - ("ABT", addr_of!(_abt_stack_end)..addr_of!(_abt_stack)), - ("SVC", addr_of!(_svc_stack_end)..addr_of!(_svc_stack)), - ("UND", addr_of!(_und_stack_end)..addr_of!(_und_stack)), - ("HYP", addr_of!(_hyp_stack_end)..addr_of!(_hyp_stack)), - ]; + use aarch32_rt::stacks::Stack; semihosting::eprintln!("Stack usage report:"); unsafe { - for (name, range) in stacks { - let (total, used) = stack_used_bytes(range.clone()); - let percent = (used * 100).checked_div(total).unwrap_or(999); - // Send to stderr, so it doesn't mix with expected output on stdout - semihosting::eprintln!( - "{} Stack = {:6} used of {:6} bytes ({:03}%) @ {:08x?}", - name, - used, - total, - percent, - range - ); + for stack in Stack::iter() { + for core in (0..Stack::num_cores()).rev() { + let core_range = stack.range(core).unwrap(); + let (total, used) = stack_used_bytes(core_range.clone()); + let percent = used * 100 / total; + // Send to stderr, so it doesn't mix with expected output on stdout + semihosting::eprintln!( + "{}{} Stack = {:6} used of {:6} bytes ({:03}%) @ {:08x?}", + stack, + core, + used, + total, + percent, + core_range + ); + } } } } From 740a71da048ef0b82874f28d87d11a4e62bbb5af Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 6 Mar 2026 20:27:03 +0000 Subject: [PATCH 4/6] Adjust comment text to avoid markdown warning The parser thought the hyphen started a bullet list. --- aarch32-rt/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aarch32-rt/src/lib.rs b/aarch32-rt/src/lib.rs index a6445963..03d49290 100644 --- a/aarch32-rt/src/lib.rs +++ b/aarch32-rt/src/lib.rs @@ -517,8 +517,8 @@ //! not save a great deal of state on entry to an exception handler, unlike //! Armv7-M (and other M-Profile) processors. We must therefore save this state //! to the stack using assembly language, before transferring to an `extern "C"` -//! function. We do not change modes before entering that `extern "C"` function -//! - that's for the handler to deal with as it wishes. Because FIQ is often +//! function. We do not change modes before entering that `extern "C"` function - +//! that's for the handler to deal with as it wishes. Because FIQ is often //! performance-sensitive, we don't supply an FIQ trampoline; if you want to use //! FIQ, you have to write your own assembly routine, allowing you to preserve //! only whatever state is important to you. From 374029aa0c5e41612d75b64927811f45f746445d Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 6 Mar 2026 22:44:50 +0000 Subject: [PATCH 5/6] Make it easier to set up the MPU * Add an API to get the various linker output sections (aarch32_rt::region) * Adjust link.x to allow for adjustable alignment of regions * Adjust link.x to allow for adjustable padding between stacks * Add an example that programs the MPU --- aarch32-cpu/src/pmsav8.rs | 10 +- aarch32-rt/link.x | 101 ++++++--- aarch32-rt/src/lib.rs | 1 + aarch32-rt/src/regions.rs | 130 +++++++++++ aarch32-rt/src/stacks.rs | 36 +-- examples/mps3-an536/memory.x | 6 +- .../mpu_setup-armv8r-none-eabihf.out | 27 +++ .../mpu_setup-thumbv8r-none-eabihf.out | 27 +++ examples/mps3-an536/src/bin/mpu_setup.rs | 206 ++++++++++++++++++ 9 files changed, 494 insertions(+), 50 deletions(-) create mode 100644 aarch32-rt/src/regions.rs create mode 100644 examples/mps3-an536/reference/mpu_setup-armv8r-none-eabihf.out create mode 100644 examples/mps3-an536/reference/mpu_setup-thumbv8r-none-eabihf.out create mode 100644 examples/mps3-an536/src/bin/mpu_setup.rs diff --git a/aarch32-cpu/src/pmsav8.rs b/aarch32-cpu/src/pmsav8.rs index 0f82f65b..91be34ed 100644 --- a/aarch32-cpu/src/pmsav8.rs +++ b/aarch32-cpu/src/pmsav8.rs @@ -23,7 +23,7 @@ pub enum Error { /// Found an invalid MAIR selector (only 0..=7 is valid) InvalidMair(u8), /// Found a region with invalid alignment - UnalignedRegion(core::ops::RangeInclusive<*mut u8>), + UnalignedRegion(core::ops::RangeInclusive<*const u8>), } /// Represents our PMSAv8-32 EL1 MPU @@ -224,8 +224,8 @@ impl El2Mpu { register::Hprselr::write(register::Hprselr(idx as u32)); let hprbar = register::Hprbar::read(); let hprlar = register::Hprlar::read(); - let start_addr = (hprbar.base().value() << 6) as *mut u8; - let end_addr = ((hprlar.limit().value() << 6) | 0x3F) as *mut u8; + let start_addr = (hprbar.base().value() << 6) as *const u8; + let end_addr = ((hprlar.limit().value() << 6) | 0x3F) as *const u8; Some(El2Region { range: start_addr..=end_addr, shareability: hprbar.shareability(), @@ -385,7 +385,7 @@ pub struct El1Region { /// /// * The first address must be a multiple of 32. /// * The length must be a multiple of 32. - pub range: core::ops::RangeInclusive<*mut u8>, + pub range: core::ops::RangeInclusive<*const u8>, /// Shareability of the region pub shareability: El1Shareability, /// Access for the region @@ -430,7 +430,7 @@ pub struct El2Region { /// /// * The first address must be a multiple of 32. /// * The length must be a multiple of 32. - pub range: core::ops::RangeInclusive<*mut u8>, + pub range: core::ops::RangeInclusive<*const u8>, /// Shareability of the region pub shareability: El2Shareability, /// Access for the region diff --git a/aarch32-rt/link.x b/aarch32-rt/link.x index 09f77f0f..309e3bca 100644 --- a/aarch32-rt/link.x +++ b/aarch32-rt/link.x @@ -56,18 +56,26 @@ SECTIONS { * May include FIQ handler code at the end. */ .vector_table ORIGIN(VECTORS) : { + __svector = .; + *(.vector_table) + + . = ALIGN(_region_alignment); + + __evector = .; } > VECTORS /* # Text * * Our executable code. */ - .text : { + .text : ALIGN(_region_alignment) { __stext = .; *(.text .text*) + . = ALIGN(_region_alignment); + __etext = .; } > CODE @@ -75,11 +83,13 @@ SECTIONS { * * Our constants. */ - .rodata : { + .rodata : ALIGN(_region_alignment) { __srodata = .; *(.rodata .rodata*) + . = ALIGN(_region_alignment); + __erodata = .; } > CODE @@ -87,13 +97,13 @@ SECTIONS { * * Our global variables that are not initialised to zero. */ - .data : ALIGN(4) { - . = ALIGN(4); + .data : ALIGN(_region_alignment) { + . = ALIGN(_region_alignment); __sdata = .; *(.data .data.*); - . = ALIGN(4); + . = ALIGN(_region_alignment); /* NB: __edata defined lower down */ } > DATA AT>CODE @@ -102,7 +112,7 @@ SECTIONS { * use the .data loading mechanism by pushing __edata. Note: do not change * output region or load region in those user sections! */ - . = ALIGN(4); + . = ALIGN(_region_alignment); __edata = .; /* LMA of .data */ @@ -112,13 +122,13 @@ SECTIONS { * * Our global variables that *are* initialised to zero. */ - .bss (NOLOAD) : ALIGN(4) { - . = ALIGN(4); + .bss (NOLOAD) : ALIGN(_region_alignment) { + . = ALIGN(_region_alignment); __sbss = .; *(.bss .bss* COMMON) - . = ALIGN(4); + . = ALIGN(_region_alignment); /* NB: __ebss defined lower down */ } > DATA @@ -133,18 +143,17 @@ SECTIONS { * * Our global variables that have no defined initial value. */ - .uninit (NOLOAD) : ALIGN(4) + .uninit (NOLOAD) : ALIGN(_region_alignment) { - . = ALIGN(4); + . = ALIGN(_region_alignment); __suninit = .; *(.uninit .uninit.*); - . = ALIGN(4); + . = ALIGN(_region_alignment); __euninit = .; } > DATA - /* # Stack Padding * * A padding region to push the stacks to the top of the STACKS region. @@ -164,9 +173,9 @@ SECTIONS { * * Space for all seven stacks. */ - .stacks (NOLOAD) : ALIGN(8) + .stacks (NOLOAD) : ALIGN(_stack_alignment) { - . = ALIGN(8); + . = ALIGN(_stack_alignment); /* Lowest address of allocated stack */ _stacks_low_end = .; @@ -176,31 +185,43 @@ SECTIONS { . += (_und_stack_size * _num_cores); _und_stack_high_end = .; + . += _inter_stack_padding; + /* Stack for SVC mode */ _svc_stack_low_end = .; . += (_svc_stack_size * _num_cores); _svc_stack_high_end = .; + . += _inter_stack_padding; + /* Stack for ABT mode */ _abt_stack_low_end = .; . += (_abt_stack_size * _num_cores); _abt_stack_high_end = .; + . += _inter_stack_padding; + /* Stack for HYP mode */ _hyp_stack_low_end = .; . += (_hyp_stack_size * _num_cores); _hyp_stack_high_end = .; + . += _inter_stack_padding; + /* Stack for IRQ mode */ _irq_stack_low_end = .; . += (_irq_stack_size * _num_cores); _irq_stack_high_end = .; + . += _inter_stack_padding; + /* Stack for FIQ mode */ _fiq_stack_low_end = .; . += (_fiq_stack_size * _num_cores); _fiq_stack_high_end = .; + . += _inter_stack_padding; + /* Stack for SYS mode */ _sys_stack_low_end = .; . += (_sys_stack_size * _num_cores); @@ -233,6 +254,16 @@ PROVIDE(_sys_stack_size = 16K); /* Default to one CPU core (i.e. one copy of each stack) */ PROVIDE(_num_cores = 1); +/* Default stack alignment. You can over-align if you want to set up MPU regions for the stacks */ +PROVIDE(_stack_alignment = 8); + +/* Default region alignment. You can over-align if you want to set up MPU regions for the stacks */ +PROVIDE(_region_alignment = 4); + +/* Default to no padding between stacks. You might want padding if you want turn on the MPU and */ +/* only have a single core (so the stacks are otherwise contiguous) */ +PROVIDE(_inter_stack_padding = 0); + /* Set this to 1 in memory.x to remove the filler section pushing the stacks to the end of STACKS. */ PROVIDE(_pack_stacks = 0); @@ -257,21 +288,31 @@ PROVIDE(_data_abort_handler = _default_handler); PROVIDE(_irq_handler = _default_handler); /* NB: There is no default C-language FIQ handler */ -/* Check the stack sizes are all a multiple of eight bytes */ -ASSERT(_und_stack_size % 8 == 0, " -ERROR(aarch32-rt): UND stack size (_und_stack_size) is not a multiple of 8 bytes"); -ASSERT(_svc_stack_size % 8 == 0, " -ERROR(aarch32-rt): SVC stack size (_svc_stack_size) is not a multiple of 8 bytes"); -ASSERT(_abt_stack_size % 8 == 0, " -ERROR(aarch32-rt): ABT stack size (_abt_stack_size) is not a multiple of 8 bytes"); -ASSERT(_hyp_stack_size % 8 == 0, " -ERROR(aarch32-rt): HYP stack size (_hyp_stack_size) is not a multiple of 8 bytes"); -ASSERT(_irq_stack_size % 8 == 0, " -ERROR(aarch32-rt): IRQ stack size (_irq_stack_size) is not a multiple of 8 bytes"); -ASSERT(_fiq_stack_size % 8 == 0, " -ERROR(aarch32-rt): FIQ stack size (_fiq_stack_size) is not a multiple of 8 bytes"); -ASSERT(_sys_stack_size % 8 == 0, " -ERROR(aarch32-rt): SYS stack size (_sys_stack_size) is not a multiple of 8 bytes"); +/* Check the values are all reasonable */ +ASSERT(_region_alignment % 4 == 0, " +ERROR(aarch32-rt): Region alignment (_region_alignment) is not a multiple of 4 bytes"); +ASSERT(_region_alignment >= 4, " +ERROR(aarch32-rt): Region alignment (_region_alignment) is not at least eight bytes"); +ASSERT(_stack_alignment % 8 == 0, " +ERROR(aarch32-rt): Stack alignment (_stack_alignment) is not a multiple of 8 bytes"); +ASSERT(_stack_alignment >= 8, " +ERROR(aarch32-rt): Stack alignment (_stack_alignment) is not at least eight bytes"); +ASSERT(_inter_stack_padding % _stack_alignment == 0, " +ERROR(aarch32-rt): Inter-Stack padding (_inter_stack_padding) is not a multiple of of the stack alignment"); +ASSERT(_und_stack_size % _stack_alignment == 0, " +ERROR(aarch32-rt): UND stack size (_und_stack_size) is not a multiple of the stack alignment"); +ASSERT(_svc_stack_size % _stack_alignment == 0, " +ERROR(aarch32-rt): SVC stack size (_svc_stack_size) is not a multiple of the stack alignment"); +ASSERT(_abt_stack_size % _stack_alignment == 0, " +ERROR(aarch32-rt): ABT stack size (_abt_stack_size) is not a multiple of the stack alignment"); +ASSERT(_hyp_stack_size % _stack_alignment == 0, " +ERROR(aarch32-rt): HYP stack size (_hyp_stack_size) is not a multiple of the stack alignment"); +ASSERT(_irq_stack_size % _stack_alignment == 0, " +ERROR(aarch32-rt): IRQ stack size (_irq_stack_size) is not a multiple of the stack alignment"); +ASSERT(_fiq_stack_size % _stack_alignment == 0, " +ERROR(aarch32-rt): FIQ stack size (_fiq_stack_size) is not a multiple of the stack alignment"); +ASSERT(_sys_stack_size % _stack_alignment == 0, " +ERROR(aarch32-rt): SYS stack size (_sys_stack_size) is not a multiple of the stack alignment"); ASSERT(_num_cores != 0, " ERROR(aarch32-rt): Number of cores cannot be zero"); diff --git a/aarch32-rt/src/lib.rs b/aarch32-rt/src/lib.rs index 03d49290..82a2676c 100644 --- a/aarch32-rt/src/lib.rs +++ b/aarch32-rt/src/lib.rs @@ -558,6 +558,7 @@ mod arch_v7; ))] mod arch_v4; +pub mod regions; pub mod stacks; /// Our default exception handler. diff --git a/aarch32-rt/src/regions.rs b/aarch32-rt/src/regions.rs new file mode 100644 index 00000000..69a39331 --- /dev/null +++ b/aarch32-rt/src/regions.rs @@ -0,0 +1,130 @@ +//! Code to get the memory regions from the linker script +//! +//! This is useful if you want to set up the MPU + +/// Represents one of the memory regions in the linker script +#[derive(Debug, Copy, Clone)] +pub enum Region { + /// The .vector_table section + /// + /// Contains the reset and exception vectors + VectorTable, + /// The .text section + /// + /// Contains the executable code + Text, + /// The .rodata section + /// + /// Contains read-only static data + Rodata, + /// The .bss section + /// + /// Contains zero-initialised static data + Bss, + /// The .data section + /// + /// Contains non-zero-initialised static data + Data, + /// The .uninit section + /// + /// Contains non-initialised static data + Uninit, +} + +impl core::fmt::Display for Region { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.pad(match self { + Region::VectorTable => ".vector_table", + Region::Text => ".text", + Region::Rodata => ".rodata", + Region::Bss => ".bss", + Region::Data => ".data", + Region::Uninit => ".uninit", + }) + } +} + +impl Region { + /// Create an iterator over all the regions + pub fn iter() -> impl Iterator { + [ + Region::VectorTable, + Region::Text, + Region::Rodata, + Region::Bss, + Region::Data, + Region::Uninit, + ] + .iter() + .cloned() + } + + /// Get the highest address of this region + /// + /// Technically it gets *one past the end* of the region. + pub fn top(&self) -> *const u8 { + use core::ptr::addr_of; + unsafe extern "C" { + static __evector: u8; + static __etext: u8; + static __erodata: u8; + static __ebss: u8; + static __edata: u8; + static __euninit: u8; + } + match self { + Region::VectorTable => addr_of!(__evector), + Region::Text => addr_of!(__etext), + Region::Rodata => addr_of!(__erodata), + Region::Bss => addr_of!(__ebss), + Region::Data => addr_of!(__edata), + Region::Uninit => addr_of!(__euninit), + } + } + + /// Get the lowest address of this region + pub fn bottom(&self) -> *const u8 { + use core::ptr::addr_of; + unsafe extern "C" { + static __svector: u8; + static __stext: u8; + static __srodata: u8; + static __sbss: u8; + static __sdata: u8; + static __suninit: u8; + } + match self { + Region::VectorTable => addr_of!(__svector), + Region::Text => addr_of!(__stext), + Region::Rodata => addr_of!(__srodata), + Region::Bss => addr_of!(__sbss), + Region::Data => addr_of!(__sdata), + Region::Uninit => addr_of!(__suninit), + } + } + + /// Get the range of this region. + pub fn range(&self) -> Option> { + let bottom = self.bottom(); + let top = self.top(); + if bottom != top { + Some(bottom..top) + } else { + None + } + } + + /// Get the inclusive range of this region. + /// + /// This is the range you need to give to the PMSAv8 MPU code. + pub fn mpu_range(&self) -> Option> { + let bottom = self.bottom(); + let top = self.top(); + let top_under = unsafe { top.offset(-1) }; + if bottom != top { + Some(bottom..=top_under) + } else { + None + } + } +} diff --git a/aarch32-rt/src/stacks.rs b/aarch32-rt/src/stacks.rs index 6de2b2f5..a2e37bd5 100644 --- a/aarch32-rt/src/stacks.rs +++ b/aarch32-rt/src/stacks.rs @@ -21,19 +21,15 @@ pub enum Stack { impl core::fmt::Display for Stack { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - write!( - f, - "{}", - match self { - Stack::Und => "UND", - Stack::Svc => "SVC", - Stack::Abt => "ABT", - Stack::Hyp => "HYP", - Stack::Irq => "IRQ", - Stack::Fiq => "FIQ", - Stack::Sys => "SYS", - } - ) + f.pad(match self { + Stack::Und => "UND", + Stack::Svc => "SVC", + Stack::Abt => "ABT", + Stack::Hyp => "HYP", + Stack::Irq => "IRQ", + Stack::Fiq => "FIQ", + Stack::Sys => "SYS", + }) } } @@ -79,6 +75,20 @@ impl Stack { } } + /// Get the inclusive range of this stack, for the given core + /// + /// This is the range you need to give to the PMSAv8 MPU code. + pub fn mpu_range(&self, core: usize) -> Option> { + if let (Some(bottom), Some(top)) = (self.bottom(core), self.top(core)) { + let top = top as *const u8; + let bottom = bottom as *const u8; + let top_under = unsafe { top.offset(-1) }; + Some(bottom..=top_under) + } else { + None + } + } + /// Get number of cores in this system pub fn num_cores() -> usize { unsafe extern "C" { diff --git a/examples/mps3-an536/memory.x b/examples/mps3-an536/memory.x index 00158077..78257ebb 100644 --- a/examples/mps3-an536/memory.x +++ b/examples/mps3-an536/memory.x @@ -32,8 +32,7 @@ SECTIONS { /* We put this in the header */ __irq_entries_end = .; } > CODE -} INSERT AFTER .text; - +} INSERT AFTER .rodata; PROVIDE(_hyp_stack_size = 16K); PROVIDE(_und_stack_size = 16K); @@ -42,3 +41,6 @@ PROVIDE(_abt_stack_size = 16K); PROVIDE(_irq_stack_size = 64); PROVIDE(_fiq_stack_size = 64); PROVIDE(_sys_stack_size = 16K); +PROVIDE(_stack_alignment = 64); +PROVIDE(_inter_stack_padding = 64); +PROVIDE(_region_alignment = 64K); diff --git a/examples/mps3-an536/reference/mpu_setup-armv8r-none-eabihf.out b/examples/mps3-an536/reference/mpu_setup-armv8r-none-eabihf.out new file mode 100644 index 00000000..7c873eaf --- /dev/null +++ b/examples/mps3-an536/reference/mpu_setup-armv8r-none-eabihf.out @@ -0,0 +1,27 @@ + UND @ 0x1006be00..=0x1006fdff + SVC @ 0x1006fe40..=0x10073e3f + ABT @ 0x10073e80..=0x10077e7f + HYP @ 0x10077ec0..=0x1007bebf + IRQ @ 0x1007bf00..=0x1007bf3f + FIQ @ 0x1007bf80..=0x1007bfbf + SYS @ 0x1007c000..=0x1007ffff +.vector_table @ 0x08000000..=0x0800ffff + .text @ 0x08010000..=0x0801ffff + .rodata @ 0x08020000..=0x0802ffff + .data @ 0x10000000..=0x0fffffff + .bss @ 0x10000000..=0x1000ffff + .uninit @ 0x10010000..=0x1000ffff +Region 00: El1Region { range: 0x1006be00..=0x1006fdff, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 01: El1Region { range: 0x1006fe40..=0x10073e3f, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 02: El1Region { range: 0x10073e80..=0x10077e7f, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 03: El1Region { range: 0x10077ec0..=0x1007bebf, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 04: El1Region { range: 0x1007bf00..=0x1007bf3f, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 05: El1Region { range: 0x1007bf80..=0x1007bfbf, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 06: El1Region { range: 0x1007c000..=0x1007ffff, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 07: El1Region { range: 0x08000000..=0x0800ffff, shareability: NonShareable, access: ReadOnly, no_exec: false , mair: 0000000000, enable: true } +Region 08: El1Region { range: 0x08010000..=0x0801ffff, shareability: NonShareable, access: ReadOnly, no_exec: false , mair: 0000000000, enable: true } +Region 09: El1Region { range: 0x08020000..=0x0802ffff, shareability: NonShareable, access: ReadOnly, no_exec: true , mair: 0000000000, enable: true } +Region 10: El1Region { range: 0x10000000..=0x0fffffff, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 11: El1Region { range: 0x10000000..=0x1000ffff, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 12: El1Region { range: 0x10010000..=0x1000ffff, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 13: El1Region { range: 0xe0000000..=0xffffffff, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000002, enable: true } diff --git a/examples/mps3-an536/reference/mpu_setup-thumbv8r-none-eabihf.out b/examples/mps3-an536/reference/mpu_setup-thumbv8r-none-eabihf.out new file mode 100644 index 00000000..7c873eaf --- /dev/null +++ b/examples/mps3-an536/reference/mpu_setup-thumbv8r-none-eabihf.out @@ -0,0 +1,27 @@ + UND @ 0x1006be00..=0x1006fdff + SVC @ 0x1006fe40..=0x10073e3f + ABT @ 0x10073e80..=0x10077e7f + HYP @ 0x10077ec0..=0x1007bebf + IRQ @ 0x1007bf00..=0x1007bf3f + FIQ @ 0x1007bf80..=0x1007bfbf + SYS @ 0x1007c000..=0x1007ffff +.vector_table @ 0x08000000..=0x0800ffff + .text @ 0x08010000..=0x0801ffff + .rodata @ 0x08020000..=0x0802ffff + .data @ 0x10000000..=0x0fffffff + .bss @ 0x10000000..=0x1000ffff + .uninit @ 0x10010000..=0x1000ffff +Region 00: El1Region { range: 0x1006be00..=0x1006fdff, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 01: El1Region { range: 0x1006fe40..=0x10073e3f, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 02: El1Region { range: 0x10073e80..=0x10077e7f, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 03: El1Region { range: 0x10077ec0..=0x1007bebf, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 04: El1Region { range: 0x1007bf00..=0x1007bf3f, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 05: El1Region { range: 0x1007bf80..=0x1007bfbf, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 06: El1Region { range: 0x1007c000..=0x1007ffff, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 07: El1Region { range: 0x08000000..=0x0800ffff, shareability: NonShareable, access: ReadOnly, no_exec: false , mair: 0000000000, enable: true } +Region 08: El1Region { range: 0x08010000..=0x0801ffff, shareability: NonShareable, access: ReadOnly, no_exec: false , mair: 0000000000, enable: true } +Region 09: El1Region { range: 0x08020000..=0x0802ffff, shareability: NonShareable, access: ReadOnly, no_exec: true , mair: 0000000000, enable: true } +Region 10: El1Region { range: 0x10000000..=0x0fffffff, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 11: El1Region { range: 0x10000000..=0x1000ffff, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 12: El1Region { range: 0x10010000..=0x1000ffff, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000001, enable: true } +Region 13: El1Region { range: 0xe0000000..=0xffffffff, shareability: NonShareable, access: ReadWrite, no_exec: true , mair: 0000000002, enable: true } diff --git a/examples/mps3-an536/src/bin/mpu_setup.rs b/examples/mps3-an536/src/bin/mpu_setup.rs new file mode 100644 index 00000000..eaadf8f8 --- /dev/null +++ b/examples/mps3-an536/src/bin/mpu_setup.rs @@ -0,0 +1,206 @@ +//! Stack-overflow check for Arm Cortex-R +//! +//! Uses the MPU to protect each stack + +#![no_std] +#![no_main] + +use aarch32_cpu::pmsav8::{ + Cacheable, El1AccessPerms, El1Mpu, El1Region, El1Shareability, MemAttr, RwAllocPolicy, +}; +use aarch32_rt::{entry, regions::Region, stacks::Stack}; +use semihosting::println; + +use mps3_an536 as _; + +unsafe extern "C" { + static __stext: u8; + static __etext: u8; + static __srodata: u8; + static __erodata: u8; + static __sdata: u8; + static __edata: u8; + static __sbss: u8; + static __ebss: u8; +} + +const MAIR_READ_ONLY: u8 = 0; +const MAIR_READ_WRITE: u8 = 1; +const MAIR_DEVICE: u8 = 2; + +static MEM_ATTRS: [MemAttr; 8] = [ + // Read-only Code RAM + MemAttr::NormalMemory { + outer: Cacheable::WriteThroughNonTransient(RwAllocPolicy::R), + inner: Cacheable::WriteThroughNonTransient(RwAllocPolicy::R), + }, + // Read-write RAM + MemAttr::NormalMemory { + outer: Cacheable::WriteBackNonTransient(RwAllocPolicy::W), + inner: Cacheable::WriteBackNonTransient(RwAllocPolicy::W), + }, + // Device Memory + MemAttr::DeviceMemory, + // Spare entries + MemAttr::DeviceMemory, + MemAttr::DeviceMemory, + MemAttr::DeviceMemory, + MemAttr::DeviceMemory, + MemAttr::DeviceMemory, +]; + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up code in `aarch32-rt`. +#[entry] +fn main() -> ! { + let mut mpu = unsafe { El1Mpu::new() }; + + mpu.set_attributes(&MEM_ATTRS); + + let mut last = 0; + for (idx, stack) in Stack::iter().enumerate() { + let range = stack.mpu_range(0).unwrap(); + println!("{:>13} @ {:010x?}", stack, range); + + let region = El1Region { + range, + shareability: El1Shareability::NonShareable, + access: El1AccessPerms::ReadWrite, + no_exec: true, + mair: MAIR_READ_WRITE, + enable: true, + }; + mpu.set_region(idx as u8, ®ion).unwrap(); + last = idx as u8; + } + + let mut next = last + 1; + + if let Some(range) = Region::VectorTable.mpu_range() { + println!("{:>13} @ {:010x?}", Region::VectorTable, range); + mpu.set_region( + next, + &El1Region { + range, + shareability: El1Shareability::NonShareable, + access: El1AccessPerms::ReadOnly, + no_exec: false, + mair: MAIR_READ_ONLY, + enable: true, + }, + ) + .unwrap(); + next += 1; + } + + if let Some(range) = Region::Text.mpu_range() { + println!("{:>13} @ {:010x?}", Region::Text, range); + mpu.set_region( + next, + &El1Region { + range, + shareability: El1Shareability::NonShareable, + access: El1AccessPerms::ReadOnly, + no_exec: false, + mair: MAIR_READ_ONLY, + enable: true, + }, + ) + .unwrap(); + next += 1; + } + + if let Some(range) = Region::Rodata.mpu_range() { + println!("{:>13} @ {:010x?}", Region::Rodata, range); + mpu.set_region( + next, + &El1Region { + range, + shareability: El1Shareability::NonShareable, + access: El1AccessPerms::ReadOnly, + no_exec: true, + mair: MAIR_READ_ONLY, + enable: true, + }, + ) + .unwrap(); + next += 1; + } + + if let Some(range) = Region::Data.mpu_range() { + println!("{:>13} @ {:010x?}", Region::Data, range); + mpu.set_region( + next, + &El1Region { + range, + shareability: El1Shareability::NonShareable, + access: El1AccessPerms::ReadWrite, + no_exec: true, + mair: MAIR_READ_WRITE, + enable: true, + }, + ) + .unwrap(); + next += 1; + } + + if let Some(range) = Region::Bss.mpu_range() { + println!("{:>13} @ {:010x?}", Region::Bss, range); + mpu.set_region( + next, + &El1Region { + range, + shareability: El1Shareability::NonShareable, + access: El1AccessPerms::ReadWrite, + no_exec: true, + mair: MAIR_READ_WRITE, + enable: true, + }, + ) + .unwrap(); + next += 1; + } + + if let Some(range) = Region::Uninit.mpu_range() { + println!("{:>13} @ {:010x?}", Region::Uninit, range); + mpu.set_region( + next, + &El1Region { + range, + shareability: El1Shareability::NonShareable, + access: El1AccessPerms::ReadWrite, + no_exec: true, + mair: MAIR_READ_WRITE, + enable: true, + }, + ) + .unwrap(); + next += 1; + } + + mpu.set_region( + next, + &El1Region { + range: 0xE000_0000 as *const u8..=0xFFFF_FFFF as *const u8, + shareability: El1Shareability::NonShareable, + access: El1AccessPerms::ReadWrite, + no_exec: true, + mair: MAIR_DEVICE, + enable: true, + }, + ) + .unwrap(); + + mpu.enable(); + + for idx in 0..mpu.num_regions() { + if let Some(region) = mpu.get_region(idx) + && region.enable + { + println!("Region {:02}: {:010x?}", idx, region); + } + } + + mps3_an536::exit(0); +} From 3b92c3dfdba483e36682fb97574572805be16609 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 6 Mar 2026 22:44:59 +0000 Subject: [PATCH 6/6] Resolve some clippy lints --- examples/mps3-an536/src/bin/hvc-a32.rs | 2 +- examples/mps3-an536/src/bin/hvc-t32.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/mps3-an536/src/bin/hvc-a32.rs b/examples/mps3-an536/src/bin/hvc-a32.rs index 2e48231b..7dad76a4 100644 --- a/examples/mps3-an536/src/bin/hvc-a32.rs +++ b/examples/mps3-an536/src/bin/hvc-a32.rs @@ -27,7 +27,7 @@ fn main() -> ! { fn hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 { let hsr = aarch32_cpu::register::Hsr::new_with_raw_value(hsr); println!("In hvc_handler, with {:08x?}, {:08x?}", hsr, frame); - return 0x12345678; + 0x12345678 } #[instruction_set(arm::a32)] diff --git a/examples/mps3-an536/src/bin/hvc-t32.rs b/examples/mps3-an536/src/bin/hvc-t32.rs index 4d0ce587..631bde18 100644 --- a/examples/mps3-an536/src/bin/hvc-t32.rs +++ b/examples/mps3-an536/src/bin/hvc-t32.rs @@ -27,7 +27,7 @@ fn main() -> ! { fn hvc_handler(hsr: u32, frame: &aarch32_rt::Frame) -> u32 { let hsr = aarch32_cpu::register::Hsr::new_with_raw_value(hsr); println!("In hvc_handler, with {:08x?}, {:08x?}", hsr, frame); - return 0x12345678; + 0x12345678 } #[instruction_set(arm::t32)]