From 3bb36900eca847045fc5c752eff574c2899c117f Mon Sep 17 00:00:00 2001 From: phil Date: Sun, 29 Sep 2024 20:54:41 +0200 Subject: [PATCH] Add support for the 32 Bit Arm architectures. --- Makefile | 8 ++++ README.md | 4 +- src/aarch32.rs | 75 ++++++++++++++++++++++++++++++++ src/lib.rs | 18 ++++++++ tests/armv7m_mps2an500/link.ld | 22 ++++++++++ tests/armv7m_mps2an500/mod.rs | 16 +++++++ tests/armv7m_mps2an500/startup.S | 12 +++++ tests/exit_13.rs | 10 +++++ tests/test_runner_wrapper.sh | 4 +- 9 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 src/aarch32.rs create mode 100644 tests/armv7m_mps2an500/link.ld create mode 100644 tests/armv7m_mps2an500/mod.rs create mode 100644 tests/armv7m_mps2an500/startup.S diff --git a/Makefile b/Makefile index 24e92b6..733855a 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,20 @@ TARGET_AARCH64 := aarch64-unknown-none-softfloat +TARGET_AARCH32 := thumbv7em-none-eabi LINKER_SCRIPT_AARCH64 := tests/aarch64_raspi3/link.ld +LINKER_SCRIPT_AARCH32 := tests/armv7m_mps2an500/link.ld TARGET_RISCV64 := riscv64gc-unknown-none-elf LINKER_SCRIPT_RISCV64 := tests/riscv64_virt/link.ld default: cargo build --target $(TARGET_AARCH64) --release + cargo build --target $(TARGET_AARCH32) --release cargo build --target $(TARGET_RISCV64) --release cargo build --release clippy: cargo clippy --target $(TARGET_AARCH64) + cargo clippy --target $(TARGET_AARCH32) cargo clippy --target $(TARGET_RISCV64) cargo clippy @@ -19,6 +23,10 @@ test: cargo test \ --target $(TARGET_AARCH64) \ --release + RUSTFLAGS="-C link-arg=-T$(LINKER_SCRIPT_AARCH32) -Clink-args=-Map=/tmp/qemuexit-mapfile.map" \ + cargo test \ + --target $(TARGET_AARCH32) \ + --release RUSTFLAGS="-C link-arg=-T$(LINKER_SCRIPT_RISCV64)" \ cargo test \ --target $(TARGET_RISCV64) \ diff --git a/README.md b/README.md index d061bc7..b63bae7 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,13 @@ qemu_exit_handle.exit_failure(); ## Architecture Specific Configuration -### AArch64 +### AArch64/AArch32 Pass the `-semihosting` argument to the QEMU invocation, e.g.: ``` qemu-system-aarch64 -M raspi3 -serial stdio -semihosting -kernel kernel8.img +qemu-system-arm -nographic -M mps2-an500 -cpu cortex-m7 -serial mon:stdio -semihosting -kernel +kernel.img ``` ### RISCV64 diff --git a/src/aarch32.rs b/src/aarch32.rs new file mode 100644 index 0000000..2f86d51 --- /dev/null +++ b/src/aarch32.rs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2024 Philipp Schulz + +//! AArch32. + +use crate::QEMUExit; +use core::arch::asm; + +const EXIT_SUCCESS: u32 = 0; +const EXIT_FAILURE: u32 = 1; + +#[allow(non_upper_case_globals)] +const ADP_Stopped_ApplicationExit: u32 = 0x20026; + +/// The parameter block layout that is expected by QEMU. +/// +/// If QEMU finds `ADP_Stopped_ApplicationExit` in the first parameter, it uses the second parameter +/// as exit code. +/// +/// If first paraemter != `ADP_Stopped_ApplicationExit`, exit code `1` is used. +#[repr(C)] +struct QEMUParameterBlock { + arg0: u32, + arg1: u32, +} + +/// AArch32 configuration. +pub struct AArch32 {} + +/// A Semihosting call using `0x20` - `SYS_EXIT_EXTENDED`. +fn semihosting_sys_exit_call(block: &QEMUParameterBlock) -> ! { + unsafe { + asm!( + "bkpt #0xab", + in("r0") 0x20, + in("r1") block as *const _ as u32, + options(nostack) + ); + + // For the case that the QEMU exit attempt did not work, transition into an infinite loop. + // Calling `panic!()` here is unfeasible, since there is a good chance this function here is + // the last expression in the `panic!()` handler itself. This prevents a possible + // infinite loop. + loop { + asm!("wfe", options(nomem, nostack)); + } + } +} + +impl AArch32 { + /// Create an instance. + pub const fn new() -> Self { + AArch32 {} + } +} + +impl QEMUExit for AArch32 { + fn exit(&self, code: u32) -> ! { + let block = QEMUParameterBlock { + arg0: ADP_Stopped_ApplicationExit, + arg1: code as u32, + }; + + semihosting_sys_exit_call(&block) + } + + fn exit_success(&self) -> ! { + self.exit(EXIT_SUCCESS) + } + + fn exit_failure(&self) -> ! { + self.exit(EXIT_FAILURE) + } +} diff --git a/src/lib.rs b/src/lib.rs index 2731de1..31a12de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,9 @@ //! #[cfg(target_arch = "aarch64")] //! let qemu_exit_handle = qemu_exit::AArch64::new(); //! +//! #[cfg(target_arch = "arm")] +//! let qemu_exit_handle = qemu_exit::Aarch32::new(); +//! //! // addr: The address of sifive_test. //! #[cfg(target_arch = "riscv64")] //! let qemu_exit_handle = qemu_exit::RISCV64::new(addr); @@ -36,6 +39,15 @@ //! Pass the `-semihosting` argument to the QEMU invocation, e.g.: //! ``` //! qemu-system-aarch64 -M raspi3 -serial stdio -semihosting -kernel kernel8.img +//! +//! ``` +//! +//! ### AArch32 +//! +//! Pass the `-semihosting` argument to the QEMU invocation, e.g.: +//! ``` +//! qemu-system-arm -m 16M -nographic -M mps2-an500 -cpu cortex-m7 -serial mon:stdio -semihosting -kernel kernel.img +//! //! ``` //! //! ### RISCV64 @@ -78,6 +90,12 @@ pub mod aarch64; #[cfg(target_arch = "aarch64")] pub use aarch64::*; +#[cfg(target_arch = "arm")] +pub mod aarch32; + +#[cfg(target_arch = "arm")] +pub use aarch32::*; + #[cfg(target_arch = "riscv64")] pub mod riscv64; diff --git a/tests/armv7m_mps2an500/link.ld b/tests/armv7m_mps2an500/link.ld new file mode 100644 index 0000000..e46b951 --- /dev/null +++ b/tests/armv7m_mps2an500/link.ld @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: MIT OR Apache-2.0 + * + * Copyright (c) 2024 Philipp Schulz + */ +ENTRY(_vectors) + +SECTIONS +{ + . = 0x0; + .boot : + { + KEEP(*(.text.boot)) + KEEP(*(.data.boot)) + } + + .text : + { + *(.vectors) + *(.text._start) + *(.text) + } +} diff --git a/tests/armv7m_mps2an500/mod.rs b/tests/armv7m_mps2an500/mod.rs new file mode 100644 index 0000000..b6f7047 --- /dev/null +++ b/tests/armv7m_mps2an500/mod.rs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2024 Philipp Schulz + +//! AArch32 specific setup code. + +use core::arch::global_asm; + +global_asm!( + include_str!("./startup.S") +); + +#[no_mangle] +extern "C" fn entry() { + super::test_main() +} diff --git a/tests/armv7m_mps2an500/startup.S b/tests/armv7m_mps2an500/startup.S new file mode 100644 index 0000000..00507f7 --- /dev/null +++ b/tests/armv7m_mps2an500/startup.S @@ -0,0 +1,12 @@ +.section ".text.boot","ax" + +.thumb +.thumb_func +.globl _vectors +_vectors: +.word 0x2000 +.word reset + +.thumb_func +reset: + bl entry diff --git a/tests/exit_13.rs b/tests/exit_13.rs index 0883764..cec0490 100644 --- a/tests/exit_13.rs +++ b/tests/exit_13.rs @@ -20,6 +20,16 @@ const QEMU_EXIT_HANDLE: qemu_exit::AArch64 = qemu_exit::AArch64::new(); #[cfg(target_arch = "aarch64")] mod aarch64_raspi3; +//-------------------------------------------------------------------------------------------------- +// Aarch32 +//-------------------------------------------------------------------------------------------------- + +#[cfg(target_arch = "arm")] +const QEMU_EXIT_HANDLE: qemu_exit::AArch32 = qemu_exit::AArch32::new(); + +#[cfg(target_arch = "arm")] +mod armv7m_mps2an500; + //-------------------------------------------------------------------------------------------------- // RISCV64 //-------------------------------------------------------------------------------------------------- diff --git a/tests/test_runner_wrapper.sh b/tests/test_runner_wrapper.sh index f80bb6c..3b2a18e 100755 --- a/tests/test_runner_wrapper.sh +++ b/tests/test_runner_wrapper.sh @@ -1,9 +1,11 @@ #!/usr/bin/env bash TIMEOUT="timeout 10" - if [[ $1 == *"aarch64"* ]]; then $TIMEOUT qemu-system-aarch64 -M raspi3b -display none -semihosting -kernel $1 +elif [[ $1 == *"thumbv7em"* ]]; then + arm-none-eabi-objcopy -O binary $1 /tmp/qemu_exit_armv7em.bin + qemu-system-arm -m 16M -nographic -M mps2-an500 -cpu cortex-m7 -serial mon:stdio -semihosting -kernel /tmp/qemu_exit_armv7em.bin elif [[ $1 == *"riscv64"* ]]; then $TIMEOUT qemu-system-riscv64 -M virt -bios tests/riscv64_virt/fw_jump.elf -display none -kernel $1 fi