From d565c51522ccc2c3a255c8f1a42c69d7233b3122 Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Thu, 26 Feb 2026 15:46:49 +0000 Subject: [PATCH 1/2] Switch the examples to use naked functions. We cannot use this on the main library, because it means we'd need to use the instruction_set attribute (rather than a .arm or .thumb directive) and that isn't available on stable for most of our targets. --- .github/workflows/build.yml | 12 ++--- examples/mps3-an536/rust-toolchain.toml | 2 +- .../mps3-an536/src/bin/abt-exception-a32.rs | 36 +++++-------- .../mps3-an536/src/bin/abt-exception-t32.rs | 35 +++++------- examples/mps3-an536/src/bin/el2_hello.rs | 53 ++++++++++--------- .../src/bin/prefetch-exception-a32.rs | 35 +++++------- .../src/bin/prefetch-exception-t32.rs | 34 +++++------- .../mps3-an536/src/bin/undef-exception-a32.rs | 35 +++++------- .../mps3-an536/src/bin/undef-exception-t32.rs | 35 +++++------- examples/versatileab/rust-toolchain.toml | 2 +- .../versatileab/src/bin/abt-exception-a32.rs | 36 +++++-------- .../versatileab/src/bin/abt-exception-t32.rs | 36 +++++-------- .../src/bin/prefetch-exception-a32.rs | 34 +++++------- .../src/bin/prefetch-exception-t32.rs | 34 +++++------- .../src/bin/undef-exception-a32.rs | 35 +++++------- .../src/bin/undef-exception-t32.rs | 35 +++++------- justfile | 2 +- 17 files changed, 191 insertions(+), 300 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index afc8b3ff..0c4007ab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,9 +63,9 @@ jobs: uses: taiki-e/install-action@just - name: Install Rust run: | - rustup install nightly-2026-02-21 - rustup component add rust-src --toolchain nightly-2026-02-21 - rustup default nightly-2026-02-21 + rustup install nightly-2026-02-26 + rustup component add rust-src --toolchain nightly-2026-02-26 + rustup default nightly-2026-02-26 - name: Build run: | just build-tier3 ${{ matrix.target }} @@ -89,9 +89,9 @@ jobs: uses: taiki-e/install-action@just - name: Install Rust run: | - rustup install nightly-2026-02-21 - rustup component add rust-src --toolchain nightly-2026-02-21 - rustup default nightly-2026-02-21 + rustup install nightly-2026-02-26 + rustup component add rust-src --toolchain nightly-2026-02-26 + rustup default nightly-2026-02-26 - name: Build run: | just build-tier3-no-atomics ${{ matrix.target }} diff --git a/examples/mps3-an536/rust-toolchain.toml b/examples/mps3-an536/rust-toolchain.toml index 37f3532e..34345179 100644 --- a/examples/mps3-an536/rust-toolchain.toml +++ b/examples/mps3-an536/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2026-02-21" +channel = "nightly-2026-02-26" targets = [ "armv8r-none-eabihf", ] diff --git a/examples/mps3-an536/src/bin/abt-exception-a32.rs b/examples/mps3-an536/src/bin/abt-exception-a32.rs index da48bb02..4cef1971 100644 --- a/examples/mps3-an536/src/bin/abt-exception-a32.rs +++ b/examples/mps3-an536/src/bin/abt-exception-a32.rs @@ -23,10 +23,9 @@ fn main() -> ! { enable_alignment_check(); println!("Hello, this is an data abort exception example"); - unsafe { - // Unaligned read - unaligned_from_a32(); - } + + // Unaligned read + unaligned_from_a32(); // turn it off before we do the stack dump on exit, because println! has been // observed to do unaligned reads. @@ -37,26 +36,17 @@ fn main() -> ! { mps3_an536::exit(0); } -// These functions are written in assembly -unsafe extern "C" { - fn unaligned_from_a32(); +#[unsafe(naked)] +#[instruction_set(arm::a32)] +extern "C" fn unaligned_from_a32() { + core::arch::naked_asm!( + "ldr r0, =COUNTER", + "adds r0, r0, 1", + "ldr r0, [r0]", + "bx lr", + ); } -core::arch::global_asm!( - r#" - // fn unaligned_from_a32(); - .arm - .global unaligned_from_a32 - .type unaligned_from_a32, %function - unaligned_from_a32: - ldr r0, =COUNTER - add r0, r0, 1 - ldr r0, [r0] - bx lr - .size unaligned_from_a32, . - unaligned_from_a32 -"# -); - fn enable_alignment_check() { let mut sctrl = Sctlr::read(); sctrl.set_a(true); @@ -91,7 +81,7 @@ unsafe fn data_abort_handler(addr: usize) -> usize { enable_alignment_check(); // note the fault isn't at the start of the function - let expect_fault_at = unaligned_from_a32 as unsafe extern "C" fn() as usize + 8; + let expect_fault_at = unaligned_from_a32 as extern "C" fn() as usize + 8; if addr == expect_fault_at { println!("caught unaligned_from_a32"); diff --git a/examples/mps3-an536/src/bin/abt-exception-t32.rs b/examples/mps3-an536/src/bin/abt-exception-t32.rs index 21093494..ac677fa5 100644 --- a/examples/mps3-an536/src/bin/abt-exception-t32.rs +++ b/examples/mps3-an536/src/bin/abt-exception-t32.rs @@ -23,10 +23,8 @@ fn main() -> ! { enable_alignment_check(); println!("Hello, this is an data abort exception example"); - unsafe { - // Unaligned read - unaligned_from_t32(); - } + // Unaligned read + unaligned_from_t32(); // turn it off before we do the stack dump on exit, because println! has been // observed to do unaligned reads. @@ -37,26 +35,17 @@ fn main() -> ! { mps3_an536::exit(0); } -// These functions are written in assembly -unsafe extern "C" { - fn unaligned_from_t32(); +#[unsafe(naked)] +#[instruction_set(arm::t32)] +extern "C" fn unaligned_from_t32() { + core::arch::naked_asm!( + "ldr r0, =COUNTER", + "adds r0, r0, 1", + "ldr r0, [r0]", + "bx lr", + ); } -core::arch::global_asm!( - r#" - // fn unaligned_from_t32(); - .thumb - .global unaligned_from_t32 - .type unaligned_from_t32, %function - unaligned_from_t32: - ldr r0, =COUNTER - add r0, r0, 1 - ldr r0, [r0] - bx lr - .size unaligned_from_t32, . - unaligned_from_t32 -"# -); - fn enable_alignment_check() { let mut sctrl = Sctlr::read(); sctrl.set_a(true); @@ -91,7 +80,7 @@ unsafe fn data_abort_handler(addr: usize) -> usize { enable_alignment_check(); // note the fault isn't at the start of the function - let expect_fault_at = unaligned_from_t32 as unsafe extern "C" fn() as usize + 5; + let expect_fault_at = unaligned_from_t32 as extern "C" fn() as usize + 3; if addr == expect_fault_at { println!("caught unaligned_from_t32"); diff --git a/examples/mps3-an536/src/bin/el2_hello.rs b/examples/mps3-an536/src/bin/el2_hello.rs index e10a979c..4c3d1746 100644 --- a/examples/mps3-an536/src/bin/el2_hello.rs +++ b/examples/mps3-an536/src/bin/el2_hello.rs @@ -33,16 +33,17 @@ fn main() -> ! { // // Unlike the default routine, it does not initialise any other stacks, or // switch to EL1 mode. -core::arch::global_asm!( - r#" - // Work around https://github.com/rust-lang/rust/issues/127269 - .fpu vfp3-d16 - - .section .text.start - - .global _start - .type _start, %function - _start: +// +/// # Safety +/// +/// This function should not be called manually. It should only be called on reset +/// from the reset vector. +#[unsafe(naked)] +#[unsafe(no_mangle)] +#[instruction_set(arm::t32)] +pub unsafe extern "C" fn _start() { + core::arch::naked_asm!( + r#" // Set stack pointer ldr sp, =_hyp_stack_high_end // Set the HVBAR (for EL2) to _vector_table @@ -80,19 +81,19 @@ core::arch::global_asm!( bl kmain // In case the application returns, loop forever b . - .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() - }, -); + "#, + 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() + }, + ); +} diff --git a/examples/mps3-an536/src/bin/prefetch-exception-a32.rs b/examples/mps3-an536/src/bin/prefetch-exception-a32.rs index 40ea9104..67e98fa2 100644 --- a/examples/mps3-an536/src/bin/prefetch-exception-a32.rs +++ b/examples/mps3-an536/src/bin/prefetch-exception-a32.rs @@ -21,35 +21,26 @@ fn main() -> ! { // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. // See p. 2038 of ARMv7-M Architecture Reference Manual - unsafe { - // trigger an prefetch abort exception, from A32 (Arm) mode - bkpt_from_a32(); - } + + // trigger an prefetch abort exception, from A32 (Arm) mode + bkpt_from_a32(); println!("Recovered from fault OK!"); mps3_an536::exit(0); } -// These functions are written in assembly -unsafe extern "C" { - fn bkpt_from_a32(); -} - -core::arch::global_asm!( - r#" - // fn bkpt_from_a32(); - .arm - .global bkpt_from_a32 - .type bkpt_from_a32, %function - bkpt_from_a32: +#[unsafe(naked)] +#[instruction_set(arm::a32)] +extern "C" fn bkpt_from_a32() { + core::arch::naked_asm!( + r#" bkpt #0 bx lr - .size bkpt_from_a32, . - bkpt_from_a32 -"# -); + "# + ); +} -// Custom link sections are allowed as well. #[exception(Undefined)] fn undefined_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); @@ -63,12 +54,12 @@ unsafe fn prefetch_abort_handler(addr: usize) -> usize { let ifar = Ifar::read(); println!("IFAR (Faulting Address Register): {:?}", ifar); - if addr == bkpt_from_a32 as unsafe extern "C" fn() as usize { + if addr == bkpt_from_a32 as extern "C" fn() as usize { println!("caught bkpt_from_a32"); } else { println!( "Bad fault address {:08x} is not {:08x}", - addr, bkpt_from_a32 as unsafe extern "C" fn() as usize + addr, bkpt_from_a32 as extern "C" fn() as usize ); } diff --git a/examples/mps3-an536/src/bin/prefetch-exception-t32.rs b/examples/mps3-an536/src/bin/prefetch-exception-t32.rs index 88717095..1bd5de80 100644 --- a/examples/mps3-an536/src/bin/prefetch-exception-t32.rs +++ b/examples/mps3-an536/src/bin/prefetch-exception-t32.rs @@ -21,33 +21,25 @@ fn main() -> ! { // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. // See p. 2038 of ARMv7-M Architecture Reference Manual - unsafe { - // trigger an prefetch abort exception, from T32 (Thumb) mode - bkpt_from_t32(); - } + + // trigger an prefetch abort exception, from T32 (Thumb) mode + bkpt_from_t32(); println!("Recovered from fault OK!"); mps3_an536::exit(0); } -// These functions are written in assembly -unsafe extern "C" { - fn bkpt_from_t32(); -} - -core::arch::global_asm!( - r#" - // fn bkpt_from_t32(); - .thumb - .global bkpt_from_t32 - .type bkpt_from_t32, %function - bkpt_from_t32: +#[unsafe(naked)] +#[instruction_set(arm::t32)] +extern "C" fn bkpt_from_t32() { + core::arch::naked_asm!( + r#" bkpt #0 bx lr - .size bkpt_from_t32, . - bkpt_from_t32 -"# -); + "# + ); +} #[exception(Undefined)] fn undefined_handler(_addr: usize) -> ! { @@ -62,7 +54,7 @@ unsafe fn prefetch_abort_handler(addr: usize) -> usize { let ifar = Ifar::read(); println!("IFAR (Faulting Address Register): {:?}", ifar); - if (addr + 1) == bkpt_from_t32 as unsafe extern "C" fn() as usize { + if (addr + 1) == bkpt_from_t32 as extern "C" fn() as usize { // note that thumb functions have their LSB set, despite always being a // multiple of two - that's how the CPU knows they are written in T32 // machine code. @@ -70,7 +62,7 @@ unsafe fn prefetch_abort_handler(addr: usize) -> usize { } else { println!( "Bad fault address {:08x} is not {:08x}", - addr, bkpt_from_t32 as unsafe extern "C" fn() as usize + addr, bkpt_from_t32 as extern "C" fn() as usize ); } diff --git a/examples/mps3-an536/src/bin/undef-exception-a32.rs b/examples/mps3-an536/src/bin/undef-exception-a32.rs index 5ef8c2c6..54f2eac6 100644 --- a/examples/mps3-an536/src/bin/undef-exception-a32.rs +++ b/examples/mps3-an536/src/bin/undef-exception-a32.rs @@ -18,34 +18,25 @@ static COUNTER: AtomicU32 = AtomicU32::new(0); fn main() -> ! { println!("Hello, this is a undef exception example"); - unsafe { - // trigger an Undefined exception, from A32 (Arm) mode - udf_from_a32(); - } + // trigger an Undefined exception, from A32 (Arm) mode + udf_from_a32(); println!("Recovered from fault OK!"); mps3_an536::exit(0); } -// These functions are written in assembly -unsafe extern "C" { - fn udf_from_a32(); +#[unsafe(naked)] +#[instruction_set(arm::a32)] +extern "C" fn udf_from_a32() { + core::arch::naked_asm!( + // Do a UDF + "udf #0", + // Return + "bx lr", + ); } -core::arch::global_asm!( - r#" - // fn udf_from_a32(); - .arm - .global udf_from_a32 - .type udf_from_a32, %function - udf_from_a32: - udf #0 - bx lr - .size udf_from_a32, . - udf_from_a32 -"# -); - #[exception(PrefetchAbort)] fn prefetch_abort_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); @@ -55,12 +46,12 @@ fn prefetch_abort_handler(_addr: usize) -> ! { unsafe fn undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); - if addr == udf_from_a32 as unsafe extern "C" fn() as usize { + if addr == udf_from_a32 as extern "C" fn() as usize { println!("caught udf_from_a32"); } else { println!( "Bad fault address {:08x} is not {:08x}", - addr, udf_from_a32 as unsafe extern "C" fn() as usize + addr, udf_from_a32 as extern "C" fn() as usize ); } diff --git a/examples/mps3-an536/src/bin/undef-exception-t32.rs b/examples/mps3-an536/src/bin/undef-exception-t32.rs index aacbf55c..89c93453 100644 --- a/examples/mps3-an536/src/bin/undef-exception-t32.rs +++ b/examples/mps3-an536/src/bin/undef-exception-t32.rs @@ -18,34 +18,25 @@ static COUNTER: AtomicU32 = AtomicU32::new(0); fn main() -> ! { println!("Hello, this is a undef exception example"); - unsafe { - // trigger an Undefined exception, from T32 (Thumb) mode - udf_from_t32(); - } + // trigger an Undefined exception, from T32 (Thumb) mode + udf_from_t32(); println!("Recovered from fault OK!"); mps3_an536::exit(0); } -// These functions are written in assembly -unsafe extern "C" { - fn udf_from_t32(); +#[unsafe(naked)] +#[instruction_set(arm::t32)] +extern "C" fn udf_from_t32() { + core::arch::naked_asm!( + // Do a UDF + "udf #0", + // Return + "bx lr", + ); } -core::arch::global_asm!( - r#" - // fn udf_from_t32(); - .thumb - .global udf_from_t32 - .type udf_from_t32, %function - udf_from_t32: - udf #0 - bx lr - .size udf_from_t32, . - udf_from_t32 -"# -); - #[exception(PrefetchAbort)] fn prefetch_abort_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); @@ -55,7 +46,7 @@ fn prefetch_abort_handler(_addr: usize) -> ! { unsafe fn undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); - if (addr + 1) == udf_from_t32 as unsafe extern "C" fn() as usize { + if (addr + 1) == udf_from_t32 as extern "C" fn() as usize { // note that thumb functions have their LSB set, despite always being a // multiple of two - that's how the CPU knows they are written in T32 // machine code. @@ -63,7 +54,7 @@ unsafe fn undefined_handler(addr: usize) -> usize { } else { println!( "Bad fault address {:08x} is not {:08x}", - addr, udf_from_t32 as unsafe extern "C" fn() as usize + addr, udf_from_t32 as extern "C" fn() as usize ); } diff --git a/examples/versatileab/rust-toolchain.toml b/examples/versatileab/rust-toolchain.toml index 0042f181..c939921d 100644 --- a/examples/versatileab/rust-toolchain.toml +++ b/examples/versatileab/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2026-02-21" +channel = "nightly-2026-02-26" targets = [ "armv7r-none-eabi", "armv7r-none-eabihf", diff --git a/examples/versatileab/src/bin/abt-exception-a32.rs b/examples/versatileab/src/bin/abt-exception-a32.rs index 80922828..58bb554b 100644 --- a/examples/versatileab/src/bin/abt-exception-a32.rs +++ b/examples/versatileab/src/bin/abt-exception-a32.rs @@ -26,10 +26,9 @@ fn main() -> ! { enable_alignment_check(); println!("Hello, this is an data abort exception example"); - unsafe { - // Unaligned read - unaligned_from_a32(); - } + + // Unaligned read + unaligned_from_a32(); // turn it off before we do the stack dump on exit, because println! has been // observed to do unaligned reads. @@ -40,26 +39,17 @@ fn main() -> ! { versatileab::exit(0); } -// These functions are written in assembly -unsafe extern "C" { - fn unaligned_from_a32(); +#[unsafe(naked)] +#[instruction_set(arm::a32)] +extern "C" fn unaligned_from_a32() { + core::arch::naked_asm!( + "ldr r0, =COUNTER", + "adds r0, r0, 1", + "ldr r0, [r0]", + "bx lr", + ); } -core::arch::global_asm!( - r#" - // fn unaligned_from_a32(); - .arm - .global unaligned_from_a32 - .type unaligned_from_a32, %function - unaligned_from_a32: - ldr r0, =COUNTER - adds r0, r0, 1 - ldr r0, [r0] - bx lr - .size unaligned_from_a32, . - unaligned_from_a32 -"# -); - fn enable_alignment_check() { let mut sctrl = Sctlr::read(); sctrl.set_a(true); @@ -109,7 +99,7 @@ unsafe fn data_abort_handler(addr: usize) -> usize { } // note the fault isn't at the start of the function - let expect_fault_at = unaligned_from_a32 as unsafe extern "C" fn() as usize + 8; + let expect_fault_at = unaligned_from_a32 as extern "C" fn() as usize + 8; if addr == expect_fault_at { println!("caught unaligned_from_a32"); diff --git a/examples/versatileab/src/bin/abt-exception-t32.rs b/examples/versatileab/src/bin/abt-exception-t32.rs index bc44ded7..46cde4fa 100644 --- a/examples/versatileab/src/bin/abt-exception-t32.rs +++ b/examples/versatileab/src/bin/abt-exception-t32.rs @@ -26,10 +26,9 @@ fn main() -> ! { enable_alignment_check(); println!("Hello, this is an data abort exception example"); - unsafe { - // Unaligned read - unaligned_from_t32(); - } + + // Unaligned read + unaligned_from_t32(); // turn it off before we do the stack dump on exit, because println! has been // observed to do unaligned reads. @@ -40,26 +39,17 @@ fn main() -> ! { versatileab::exit(0); } -// These functions are written in assembly -unsafe extern "C" { - fn unaligned_from_t32(); +#[unsafe(naked)] +#[instruction_set(arm::t32)] +extern "C" fn unaligned_from_t32() { + core::arch::naked_asm!( + "ldr r0, =COUNTER", + "adds r0, r0, 1", + "ldr r0, [r0]", + "bx lr", + ); } -core::arch::global_asm!( - r#" - // fn unaligned_from_t32(); - .thumb - .global unaligned_from_t32 - .type unaligned_from_t32, %function - unaligned_from_t32: - ldr r0, =COUNTER - adds r0, r0, 1 - ldr r0, [r0] - bx lr - .size unaligned_from_t32, . - unaligned_from_t32 -"# -); - fn enable_alignment_check() { let mut sctrl = Sctlr::read(); sctrl.set_a(true); @@ -109,7 +99,7 @@ unsafe fn data_abort_handler(addr: usize) -> usize { } // note the fault isn't at the start of the function - let expect_fault_at = unaligned_from_t32 as unsafe extern "C" fn() as usize + 3; + let expect_fault_at = unaligned_from_t32 as extern "C" fn() as usize + 3; if addr == expect_fault_at { println!("caught unaligned_from_t32"); diff --git a/examples/versatileab/src/bin/prefetch-exception-a32.rs b/examples/versatileab/src/bin/prefetch-exception-a32.rs index 4dde185b..d9c78adc 100644 --- a/examples/versatileab/src/bin/prefetch-exception-a32.rs +++ b/examples/versatileab/src/bin/prefetch-exception-a32.rs @@ -25,33 +25,25 @@ fn main() -> ! { // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. // See p. 2038 of ARMv7-M Architecture Reference Manual - unsafe { - // trigger an prefetch abort exception, from A32 (Arm) mode - bkpt_from_a32(); - } + + // trigger an prefetch abort exception, from A32 (Arm) mode + bkpt_from_a32(); println!("Recovered from fault OK!"); versatileab::exit(0); } -// These functions are written in assembly -unsafe extern "C" { - fn bkpt_from_a32(); -} - -core::arch::global_asm!( - r#" - // fn bkpt_from_a32(); - .arm - .global bkpt_from_a32 - .type bkpt_from_a32, %function - bkpt_from_a32: +#[unsafe(naked)] +#[instruction_set(arm::a32)] +extern "C" fn bkpt_from_a32() { + core::arch::naked_asm!( + r#" bkpt #0 bx lr - .size bkpt_from_a32, . - bkpt_from_a32 -"# -); + "# + ); +} #[exception(Undefined)] fn undefined_handler(addr: usize) -> ! { @@ -73,12 +65,12 @@ unsafe fn prefetch_abort_handler(addr: usize) -> usize { println!("IFAR (Faulting Address Register): {:?}", ifar); } - if addr == bkpt_from_a32 as unsafe extern "C" fn() as usize { + if addr == bkpt_from_a32 as extern "C" fn() as usize { println!("caught bkpt_from_a32"); } else { println!( "Bad fault address {:08x} is not {:08x}", - addr, bkpt_from_a32 as unsafe extern "C" fn() as usize + addr, bkpt_from_a32 as extern "C" fn() as usize ); } diff --git a/examples/versatileab/src/bin/prefetch-exception-t32.rs b/examples/versatileab/src/bin/prefetch-exception-t32.rs index 129368ac..c5d6f306 100644 --- a/examples/versatileab/src/bin/prefetch-exception-t32.rs +++ b/examples/versatileab/src/bin/prefetch-exception-t32.rs @@ -25,33 +25,25 @@ fn main() -> ! { // A BKPT instruction triggers a Prefetch Abort except when Halting debug-mode is enabled. // See p. 2038 of ARMv7-M Architecture Reference Manual - unsafe { - // trigger an prefetch abort exception, from T32 (Thumb) mode - bkpt_from_t32(); - } + + // trigger an prefetch abort exception, from T32 (Thumb) mode + bkpt_from_t32(); println!("Recovered from fault OK!"); versatileab::exit(0); } -// These functions are written in assembly -unsafe extern "C" { - fn bkpt_from_t32(); -} - -core::arch::global_asm!( - r#" - // fn bkpt_from_t32(); - .thumb - .global bkpt_from_t32 - .type bkpt_from_t32, %function - bkpt_from_t32: +#[unsafe(naked)] +#[instruction_set(arm::t32)] +extern "C" fn bkpt_from_t32() { + core::arch::naked_asm!( + r#" bkpt #0 bx lr - .size bkpt_from_t32, . - bkpt_from_t32 -"# -); + "# + ); +} #[exception(Undefined)] fn undefined_handler(_addr: usize) -> ! { @@ -73,7 +65,7 @@ unsafe fn prefetch_abort_handler(addr: usize) -> usize { println!("IFAR (Faulting Address Register): {:?}", ifar); } - if (addr + 1) == bkpt_from_t32 as unsafe extern "C" fn() as usize { + if (addr + 1) == bkpt_from_t32 as extern "C" fn() as usize { // note that thumb functions have their LSB set, despite always being a // multiple of two - that's how the CPU knows they are written in T32 // machine code. @@ -81,7 +73,7 @@ unsafe fn prefetch_abort_handler(addr: usize) -> usize { } else { println!( "Bad fault address {:08x} is not {:08x}", - addr, bkpt_from_t32 as unsafe extern "C" fn() as usize + addr, bkpt_from_t32 as extern "C" fn() as usize ); } diff --git a/examples/versatileab/src/bin/undef-exception-a32.rs b/examples/versatileab/src/bin/undef-exception-a32.rs index 8e5c8f4c..a3d98362 100644 --- a/examples/versatileab/src/bin/undef-exception-a32.rs +++ b/examples/versatileab/src/bin/undef-exception-a32.rs @@ -19,34 +19,25 @@ fn main() -> ! { versatileab::init(); println!("Hello, this is a undef exception example"); - unsafe { - // trigger an Undefined exception, from A32 (Arm) mode - udf_from_a32(); - } + // trigger an Undefined exception, from A32 (Arm) mode + udf_from_a32(); println!("Recovered from fault OK!"); versatileab::exit(0); } -// These functions are written in assembly -unsafe extern "C" { - fn udf_from_a32(); +#[unsafe(naked)] +#[instruction_set(arm::a32)] +extern "C" fn udf_from_a32() { + core::arch::naked_asm!( + // Do a UDF + "udf #0", + // Return + "bx lr", + ); } -core::arch::global_asm!( - r#" - // fn udf_from_a32(); - .arm - .global udf_from_a32 - .type udf_from_a32, %function - udf_from_a32: - udf #0 - bx lr - .size udf_from_a32, . - udf_from_a32 -"# -); - #[exception(PrefetchAbort)] fn prefetch_abort_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); @@ -56,12 +47,12 @@ fn prefetch_abort_handler(_addr: usize) -> ! { unsafe fn undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); - if addr == udf_from_a32 as unsafe extern "C" fn() as usize { + if addr == udf_from_a32 as extern "C" fn() as usize { println!("caught udf_from_a32"); } else { println!( "Bad fault address {:08x} is not {:08x}", - addr, udf_from_a32 as unsafe extern "C" fn() as usize + addr, udf_from_a32 as extern "C" fn() as usize ); } diff --git a/examples/versatileab/src/bin/undef-exception-t32.rs b/examples/versatileab/src/bin/undef-exception-t32.rs index 121e25fb..47987a98 100644 --- a/examples/versatileab/src/bin/undef-exception-t32.rs +++ b/examples/versatileab/src/bin/undef-exception-t32.rs @@ -19,34 +19,25 @@ fn main() -> ! { versatileab::init(); println!("Hello, this is a undef exception example"); - unsafe { - // trigger an Undefined exception, from T32 (Thumb) mode - udf_from_t32(); - } + // trigger an Undefined exception, from T32 (Thumb) mode + udf_from_t32(); println!("Recovered from fault OK!"); versatileab::exit(0); } -// These functions are written in assembly -unsafe extern "C" { - fn udf_from_t32(); +#[unsafe(naked)] +#[instruction_set(arm::t32)] +extern "C" fn udf_from_t32() { + core::arch::naked_asm!( + // Do a UDF + "udf #0", + // Return + "bx lr", + ); } -core::arch::global_asm!( - r#" - // fn udf_from_t32(); - .thumb - .global udf_from_t32 - .type udf_from_t32, %function - udf_from_t32: - udf #0 - bx lr - .size udf_from_t32, . - udf_from_t32 -"# -); - #[exception(PrefetchAbort)] fn prefetch_abort_handler(_addr: usize) -> ! { panic!("unexpected undefined exception"); @@ -56,7 +47,7 @@ fn prefetch_abort_handler(_addr: usize) -> ! { unsafe fn undefined_handler(addr: usize) -> usize { println!("undefined abort occurred"); - if (addr + 1) == udf_from_t32 as unsafe extern "C" fn() as usize { + if (addr + 1) == udf_from_t32 as extern "C" fn() as usize { // note that thumb functions have their LSB set, despite always being a // multiple of two - that's how the CPU knows they are written in T32 // machine code. @@ -64,7 +55,7 @@ unsafe fn undefined_handler(addr: usize) -> usize { } else { println!( "Bad fault address {:08x} is not {:08x}", - addr, udf_from_t32 as unsafe extern "C" fn() as usize + addr, udf_from_t32 as extern "C" fn() as usize ); } diff --git a/justfile b/justfile index 9990aab9..d6501bcf 100644 --- a/justfile +++ b/justfile @@ -11,7 +11,7 @@ export RUSTC_BOOTSTRAP := "1" # If you run with `just --set v 1` then we make cargo run in verbose mode v := "0" verbose := if v == "1" { "--verbose" } else { "" } -nightly := "nightly-2026-02-21" +nightly := "nightly-2026-02-26" # Our default target. It does everything that you might want to do pre-checkin. check: build-all build-all-examples fmt-check clippy-examples clippy-targets clippy-host test From bf81a024b1708eae125f676e06de22448e8a058d Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Thu, 26 Feb 2026 16:25:50 +0000 Subject: [PATCH 2/2] Use panic to exit on test mismatch --- examples/mps3-an536/src/bin/abt-exception-a32.rs | 6 ++---- examples/mps3-an536/src/bin/abt-exception-t32.rs | 6 ++---- examples/mps3-an536/src/bin/prefetch-exception-a32.rs | 2 +- examples/mps3-an536/src/bin/prefetch-exception-t32.rs | 2 +- examples/mps3-an536/src/bin/undef-exception-a32.rs | 2 +- examples/mps3-an536/src/bin/undef-exception-t32.rs | 2 +- examples/versatileab/src/bin/abt-exception-a32.rs | 4 ++-- examples/versatileab/src/bin/abt-exception-t32.rs | 4 ++-- examples/versatileab/src/bin/undef-exception-a32.rs | 2 +- examples/versatileab/src/bin/undef-exception-t32.rs | 2 +- 10 files changed, 14 insertions(+), 18 deletions(-) diff --git a/examples/mps3-an536/src/bin/abt-exception-a32.rs b/examples/mps3-an536/src/bin/abt-exception-a32.rs index 4cef1971..43c81145 100644 --- a/examples/mps3-an536/src/bin/abt-exception-a32.rs +++ b/examples/mps3-an536/src/bin/abt-exception-a32.rs @@ -86,11 +86,10 @@ unsafe fn data_abort_handler(addr: usize) -> usize { if addr == expect_fault_at { println!("caught unaligned_from_a32"); } else { - println!( + panic!( "Bad fault address {:08x} is not {:08x}", addr, expect_fault_at ); - semihosting::process::abort(); } let expect_fault_from = core::ptr::addr_of!(COUNTER) as usize + 1; @@ -98,11 +97,10 @@ unsafe fn data_abort_handler(addr: usize) -> usize { if dfar.0 as usize == expect_fault_from { println!("caught fault on COUNTER"); } else { - println!( + panic!( "Bad DFAR address {:08x} is not {:08x}", dfar.0, expect_fault_from ); - semihosting::process::abort(); } match COUNTER.fetch_add(1, Ordering::Relaxed) { diff --git a/examples/mps3-an536/src/bin/abt-exception-t32.rs b/examples/mps3-an536/src/bin/abt-exception-t32.rs index ac677fa5..033290ac 100644 --- a/examples/mps3-an536/src/bin/abt-exception-t32.rs +++ b/examples/mps3-an536/src/bin/abt-exception-t32.rs @@ -85,11 +85,10 @@ unsafe fn data_abort_handler(addr: usize) -> usize { if addr == expect_fault_at { println!("caught unaligned_from_t32"); } else { - println!( + panic!( "Bad fault address {:08x} is not {:08x}", addr, expect_fault_at ); - semihosting::process::abort(); } let expect_fault_from = core::ptr::addr_of!(COUNTER) as usize + 1; @@ -97,11 +96,10 @@ unsafe fn data_abort_handler(addr: usize) -> usize { if dfar.0 as usize == expect_fault_from { println!("caught fault on COUNTER"); } else { - println!( + panic!( "Bad DFAR address {:08x} is not {:08x}", dfar.0, expect_fault_from ); - semihosting::process::abort(); } match COUNTER.fetch_add(1, Ordering::Relaxed) { diff --git a/examples/mps3-an536/src/bin/prefetch-exception-a32.rs b/examples/mps3-an536/src/bin/prefetch-exception-a32.rs index 67e98fa2..ffbebd1f 100644 --- a/examples/mps3-an536/src/bin/prefetch-exception-a32.rs +++ b/examples/mps3-an536/src/bin/prefetch-exception-a32.rs @@ -57,7 +57,7 @@ unsafe fn prefetch_abort_handler(addr: usize) -> usize { if addr == bkpt_from_a32 as extern "C" fn() as usize { println!("caught bkpt_from_a32"); } else { - println!( + panic!( "Bad fault address {:08x} is not {:08x}", addr, bkpt_from_a32 as extern "C" fn() as usize ); diff --git a/examples/mps3-an536/src/bin/prefetch-exception-t32.rs b/examples/mps3-an536/src/bin/prefetch-exception-t32.rs index 1bd5de80..42caee0d 100644 --- a/examples/mps3-an536/src/bin/prefetch-exception-t32.rs +++ b/examples/mps3-an536/src/bin/prefetch-exception-t32.rs @@ -60,7 +60,7 @@ unsafe fn prefetch_abort_handler(addr: usize) -> usize { // machine code. println!("caught bkpt_from_t32"); } else { - println!( + panic!( "Bad fault address {:08x} is not {:08x}", addr, bkpt_from_t32 as extern "C" fn() as usize ); diff --git a/examples/mps3-an536/src/bin/undef-exception-a32.rs b/examples/mps3-an536/src/bin/undef-exception-a32.rs index 54f2eac6..c6addfff 100644 --- a/examples/mps3-an536/src/bin/undef-exception-a32.rs +++ b/examples/mps3-an536/src/bin/undef-exception-a32.rs @@ -49,7 +49,7 @@ unsafe fn undefined_handler(addr: usize) -> usize { if addr == udf_from_a32 as extern "C" fn() as usize { println!("caught udf_from_a32"); } else { - println!( + panic!( "Bad fault address {:08x} is not {:08x}", addr, udf_from_a32 as extern "C" fn() as usize ); diff --git a/examples/mps3-an536/src/bin/undef-exception-t32.rs b/examples/mps3-an536/src/bin/undef-exception-t32.rs index 89c93453..8a8a3c75 100644 --- a/examples/mps3-an536/src/bin/undef-exception-t32.rs +++ b/examples/mps3-an536/src/bin/undef-exception-t32.rs @@ -52,7 +52,7 @@ unsafe fn undefined_handler(addr: usize) -> usize { // machine code. println!("caught udf_from_t32"); } else { - println!( + panic!( "Bad fault address {:08x} is not {:08x}", addr, udf_from_t32 as extern "C" fn() as usize ); diff --git a/examples/versatileab/src/bin/abt-exception-a32.rs b/examples/versatileab/src/bin/abt-exception-a32.rs index 58bb554b..80f9f483 100644 --- a/examples/versatileab/src/bin/abt-exception-a32.rs +++ b/examples/versatileab/src/bin/abt-exception-a32.rs @@ -91,7 +91,7 @@ unsafe fn data_abort_handler(addr: usize) -> usize { if dfar.0 as usize == expect_fault_from { println!("caught fault on COUNTER"); } else { - println!( + panic!( "Bad DFAR address {:08x} is not {:08x}", dfar.0, expect_fault_from ); @@ -104,7 +104,7 @@ unsafe fn data_abort_handler(addr: usize) -> usize { if addr == expect_fault_at { println!("caught unaligned_from_a32"); } else { - println!( + panic!( "Bad fault address {:08x} is not {:08x}", addr, expect_fault_at ); diff --git a/examples/versatileab/src/bin/abt-exception-t32.rs b/examples/versatileab/src/bin/abt-exception-t32.rs index 46cde4fa..99d33874 100644 --- a/examples/versatileab/src/bin/abt-exception-t32.rs +++ b/examples/versatileab/src/bin/abt-exception-t32.rs @@ -91,7 +91,7 @@ unsafe fn data_abort_handler(addr: usize) -> usize { if dfar.0 as usize == expect_fault_from { println!("caught fault on COUNTER"); } else { - println!( + panic!( "Bad DFAR address {:08x} is not {:08x}", dfar.0, expect_fault_from ); @@ -104,7 +104,7 @@ unsafe fn data_abort_handler(addr: usize) -> usize { if addr == expect_fault_at { println!("caught unaligned_from_t32"); } else { - println!( + panic!( "Bad fault address {:08x} is not {:08x}", addr, expect_fault_at ); diff --git a/examples/versatileab/src/bin/undef-exception-a32.rs b/examples/versatileab/src/bin/undef-exception-a32.rs index a3d98362..49568e5c 100644 --- a/examples/versatileab/src/bin/undef-exception-a32.rs +++ b/examples/versatileab/src/bin/undef-exception-a32.rs @@ -50,7 +50,7 @@ unsafe fn undefined_handler(addr: usize) -> usize { if addr == udf_from_a32 as extern "C" fn() as usize { println!("caught udf_from_a32"); } else { - println!( + panic!( "Bad fault address {:08x} is not {:08x}", addr, udf_from_a32 as extern "C" fn() as usize ); diff --git a/examples/versatileab/src/bin/undef-exception-t32.rs b/examples/versatileab/src/bin/undef-exception-t32.rs index 47987a98..328583cc 100644 --- a/examples/versatileab/src/bin/undef-exception-t32.rs +++ b/examples/versatileab/src/bin/undef-exception-t32.rs @@ -53,7 +53,7 @@ unsafe fn undefined_handler(addr: usize) -> usize { // machine code. println!("caught udf_from_t32"); } else { - println!( + panic!( "Bad fault address {:08x} is not {:08x}", addr, udf_from_t32 as extern "C" fn() as usize );