diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b28d81b3..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-01-26 - rustup component add rust-src --toolchain nightly-2026-01-26 - rustup default nightly-2026-01-26 + 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-01-26 - rustup component add rust-src --toolchain nightly-2026-01-26 - rustup default nightly-2026-01-26 + 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 7e4d9e6c..34345179 100644 --- a/examples/mps3-an536/rust-toolchain.toml +++ b/examples/mps3-an536/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2026-01-26" +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 c3656806..da7d5ad8 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); @@ -92,16 +82,15 @@ 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"); } 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; @@ -109,11 +98,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 28138597..eef716e7 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); @@ -92,16 +81,15 @@ 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"); } 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; @@ -109,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/el2_hello.rs b/examples/mps3-an536/src/bin/el2_hello.rs index c1a52365..d495195f 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 // 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 6a12bcba..e89a2ebd 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"); @@ -64,12 +55,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!( + panic!( "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 69a2a356..78d86468 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) -> ! { @@ -63,15 +55,15 @@ 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. println!("caught bkpt_from_t32"); } else { - println!( + panic!( "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..c6addfff 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!( + panic!( "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..8a8a3c75 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,15 +46,15 @@ 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. println!("caught udf_from_t32"); } else { - println!( + panic!( "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 1373346c..c939921d 100644 --- a/examples/versatileab/rust-toolchain.toml +++ b/examples/versatileab/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2026-01-26" +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 c0fbb404..eb914600 100644 --- a/examples/versatileab/src/bin/abt-exception-a32.rs +++ b/examples/versatileab/src/bin/abt-exception-a32.rs @@ -24,10 +24,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. @@ -38,26 +37,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); @@ -93,12 +83,12 @@ 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"); } else { - println!( + panic!( "Bad fault address {:08x} is not {:08x}", addr, expect_fault_at ); @@ -109,7 +99,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 ); diff --git a/examples/versatileab/src/bin/abt-exception-t32.rs b/examples/versatileab/src/bin/abt-exception-t32.rs index 25bdafe4..8fc7bc39 100644 --- a/examples/versatileab/src/bin/abt-exception-t32.rs +++ b/examples/versatileab/src/bin/abt-exception-t32.rs @@ -24,10 +24,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. @@ -38,26 +37,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); @@ -93,12 +83,12 @@ 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 + 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"); } else { - println!( + panic!( "Bad fault address {:08x} is not {:08x}", addr, expect_fault_at ); @@ -109,7 +99,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 ); diff --git a/examples/versatileab/src/bin/prefetch-exception-a32.rs b/examples/versatileab/src/bin/prefetch-exception-a32.rs index 0d582f63..7485351b 100644 --- a/examples/versatileab/src/bin/prefetch-exception-a32.rs +++ b/examples/versatileab/src/bin/prefetch-exception-a32.rs @@ -22,33 +22,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) -> ! { @@ -70,12 +62,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/versatileab/src/bin/prefetch-exception-t32.rs b/examples/versatileab/src/bin/prefetch-exception-t32.rs index f41128d9..aa1c03f7 100644 --- a/examples/versatileab/src/bin/prefetch-exception-t32.rs +++ b/examples/versatileab/src/bin/prefetch-exception-t32.rs @@ -22,33 +22,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) -> ! { @@ -70,7 +62,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. @@ -78,7 +70,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..49568e5c 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!( + panic!( "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..328583cc 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,15 +47,15 @@ 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. println!("caught udf_from_t32"); } else { - println!( + panic!( "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 9c03eff5..8af411f7 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-01-26" +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