From 7338915eb8caccdfd78fdb4ca088d76ea9c98181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 16 Oct 2026 15:56:08 +0000 Subject: [PATCH 1/4] Fix compiler ordering around interrupt-mask writes Declare asm memory effects and retain existing compiler fences. Add Release before and Acquire after BASEPRI writes, and Acquire after BASEPRI_MAX, to work around rust-lang/rust#144351. --- cortex-m/CHANGELOG.md | 1 + cortex-m/src/interrupt.rs | 8 ++++++-- cortex-m/src/register/basepri.rs | 15 +++++++++++---- cortex-m/src/register/basepri_max.rs | 12 ++++++++---- cortex-m/src/register/primask.rs | 4 +++- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/cortex-m/CHANGELOG.md b/cortex-m/CHANGELOG.md index 45fd2aac..40c5a4a2 100644 --- a/cortex-m/CHANGELOG.md +++ b/cortex-m/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - The separate `#[cfg(not(cortex_m))]` stub for `interrupt::free`. ### Fixed +- Fix compiler ordering around interrupt-mask writes. - `NVIC`'s interrupt-bitmap accessors no longer emit a bounds check and a panic path. diff --git a/cortex-m/src/interrupt.rs b/cortex-m/src/interrupt.rs index 07da650b..bdf63ded 100644 --- a/cortex-m/src/interrupt.rs +++ b/cortex-m/src/interrupt.rs @@ -38,10 +38,12 @@ unsafe impl InterruptNumber for T { } /// Disables all interrupts +/// +/// Forms a read/write memory barrier for the compiler. #[inline] #[asm_cfg(cortex_m)] pub fn disable() { - unsafe { asm!("cpsid i", options(nomem, nostack, preserves_flags)) }; + unsafe { asm!("cpsid i", options(nostack, preserves_flags)) }; // Ensure no subsequent memory accesses are reordered to before interrupts are disabled. compiler_fence(Ordering::SeqCst); @@ -49,6 +51,8 @@ pub fn disable() { /// Enables all the interrupts /// +/// Forms a read/write memory barrier for the compiler. +/// /// # Safety /// /// - Do not call this function inside an `interrupt::free` critical section @@ -58,7 +62,7 @@ pub unsafe fn enable() { // Ensure no preceeding memory accesses are reordered to after interrupts are enabled. compiler_fence(Ordering::SeqCst); - unsafe { asm!("cpsie i", options(nomem, nostack, preserves_flags)) }; + unsafe { asm!("cpsie i", options(nostack, preserves_flags)) }; } /// Execute closure `f` in an interrupt-free context. diff --git a/cortex-m/src/register/basepri.rs b/cortex-m/src/register/basepri.rs index 86eb4bf2..abf8dd34 100644 --- a/cortex-m/src/register/basepri.rs +++ b/cortex-m/src/register/basepri.rs @@ -2,6 +2,8 @@ #[cfg(any(armv7m, armv8m))] use core::arch::asm; +#[cfg(any(armv7m, armv8m))] +use core::sync::atomic::{Ordering, compiler_fence}; use cortex_m_macros::asm_cfg; /// Reads the CPU register @@ -15,16 +17,20 @@ pub fn read() -> u8 { /// Writes to the CPU register /// +/// Forms a read/write memory barrier for the compiler. +/// /// **IMPORTANT** If you are using a Cortex-M7 device with revision r0p1 you MUST enable the /// `cm7-r0p1` Cargo feature or this function WILL misbehave. #[inline] #[asm_cfg(any(armv7m, armv8m_main))] pub unsafe fn write(basepri: u8) { + // Release before enabling interrupts; acquire after disabling them. + // Work around missing asm synchronization effects: rust-lang/rust#144351. + compiler_fence(Ordering::Release); + #[cfg(not(feature = "cm7-r0p1"))] { - unsafe { - asm!("msr BASEPRI, {}", in(reg) basepri, options(nomem, nostack, preserves_flags)) - }; + unsafe { asm!("msr BASEPRI, {}", in(reg) basepri, options(nostack, preserves_flags)) }; } #[cfg(feature = "cm7-r0p1")] @@ -40,8 +46,9 @@ pub unsafe fn write(basepri: u8) { "cpsie i", in(reg) basepri, out(reg) _, - options(nomem, nostack, preserves_flags), + options(nostack, preserves_flags), ) }; } + compiler_fence(Ordering::Acquire); } diff --git a/cortex-m/src/register/basepri_max.rs b/cortex-m/src/register/basepri_max.rs index f59ae421..2971bdd6 100644 --- a/cortex-m/src/register/basepri_max.rs +++ b/cortex-m/src/register/basepri_max.rs @@ -2,6 +2,8 @@ #[cfg(any(armv7m, armv8m))] use core::arch::asm; +#[cfg(any(armv7m, armv8m))] +use core::sync::atomic::{Ordering, compiler_fence}; use cortex_m_macros::asm_cfg; /// Writes to BASEPRI *if* @@ -9,6 +11,8 @@ use cortex_m_macros::asm_cfg; /// - `basepri != 0` AND `basepri::read() == 0`, OR /// - `basepri != 0` AND `basepri < basepri::read()` /// +/// Forms a read/write memory barrier for the compiler. +/// /// **IMPORTANT** If you are using a Cortex-M7 device with revision r0p1 you MUST enable the /// `cm7-r0p1` Cargo feature or this function WILL misbehave. #[inline] @@ -16,9 +20,7 @@ use cortex_m_macros::asm_cfg; pub fn write(basepri: u8) { #[cfg(not(feature = "cm7-r0p1"))] { - unsafe { - asm!("msr BASEPRI_MAX, {}", in(reg) basepri, options(nomem, nostack, preserves_flags)) - }; + unsafe { asm!("msr BASEPRI_MAX, {}", in(reg) basepri, options(nostack, preserves_flags)) }; } #[cfg(feature = "cm7-r0p1")] @@ -34,8 +36,10 @@ pub fn write(basepri: u8) { "cpsie i", in(reg) basepri, out(reg) _, - options(nomem, nostack, preserves_flags), + options(nostack, preserves_flags), ) }; } + // Acquire after disabling interrupts. See rust-lang/rust#144351. + compiler_fence(Ordering::Acquire); } diff --git a/cortex-m/src/register/primask.rs b/cortex-m/src/register/primask.rs index 163fee95..1effa364 100644 --- a/cortex-m/src/register/primask.rs +++ b/cortex-m/src/register/primask.rs @@ -51,6 +51,8 @@ pub fn read_raw() -> u32 { /// Writes the entire PRIMASK register /// +/// Forms a read/write memory barrier for the compiler. +/// /// Note that bits `[31:1]` are reserved and SBZP (Should-Be-Zero-or-Preserved) /// /// # Safety @@ -64,7 +66,7 @@ pub fn read_raw() -> u32 { pub unsafe fn write_raw(r: u32) { // Ensure no preceeding memory accesses are reordered to after interrupts are possibly enabled. compiler_fence(Ordering::SeqCst); - unsafe { asm!("msr PRIMASK, {}", in(reg) r, options(nomem, nostack, preserves_flags)) }; + unsafe { asm!("msr PRIMASK, {}", in(reg) r, options(nostack, preserves_flags)) }; // Ensure no subsequent memory accesses are reordered to before interrupts are possibly disabled. compiler_fence(Ordering::SeqCst); } From 130a2ae2f36dcd7335fdc44146b4e3314ceabb06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 16 Oct 2026 15:56:23 +0000 Subject: [PATCH 2/4] Declare semihosting syscall memory effects The host can access memory through the argument pointer. Remove nomem so those accesses are visible to the compiler. --- cortex-m/CHANGELOG.md | 1 + cortex-m/src/asm.rs | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cortex-m/CHANGELOG.md b/cortex-m/CHANGELOG.md index 40c5a4a2..dfe951f6 100644 --- a/cortex-m/CHANGELOG.md +++ b/cortex-m/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - The separate `#[cfg(not(cortex_m))]` stub for `interrupt::free`. ### Fixed +- Preserve memory accesses around semihosting syscalls. - Fix compiler ordering around interrupt-mask writes. - `NVIC`'s interrupt-bitmap accessors no longer emit a bounds check and a panic path. diff --git a/cortex-m/src/asm.rs b/cortex-m/src/asm.rs index 208ac5ed..3b86789f 100644 --- a/cortex-m/src/asm.rs +++ b/cortex-m/src/asm.rs @@ -242,9 +242,7 @@ pub unsafe fn bx_ns(addr: u32) { #[inline] #[asm_cfg(cortex_m)] pub unsafe fn semihosting_syscall(mut nr: u32, arg: u32) -> u32 { - unsafe { - asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nomem, nostack, preserves_flags)) - }; + unsafe { asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nostack, preserves_flags)) }; nr } From c65c7443dbfdae322df9486e2ce4b453bcedc696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 16 Oct 2026 15:56:27 +0000 Subject: [PATCH 3/4] Declare memory effects of execution-context changes Remove nomem from the CONTROL write and ISB, and from bootstrap. Memory accesses must use the intended execution context, and the bootstrap destination can read the initialized stack. --- cortex-m/CHANGELOG.md | 2 ++ cortex-m/src/asm.rs | 2 +- cortex-m/src/register/control.rs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cortex-m/CHANGELOG.md b/cortex-m/CHANGELOG.md index dfe951f6..a53056ad 100644 --- a/cortex-m/CHANGELOG.md +++ b/cortex-m/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - The separate `#[cfg(not(cortex_m))]` stub for `interrupt::free`. ### Fixed +- Preserve memory accesses across `control::write`, `asm::bootstrap`, and + `asm::bootload`. - Preserve memory accesses around semihosting syscalls. - Fix compiler ordering around interrupt-mask writes. - `NVIC`'s interrupt-bitmap accessors no longer emit a bounds check and a panic diff --git a/cortex-m/src/asm.rs b/cortex-m/src/asm.rs index 3b86789f..08a2a11e 100644 --- a/cortex-m/src/asm.rs +++ b/cortex-m/src/asm.rs @@ -361,7 +361,7 @@ pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! { spsel = in(reg) 2, msp = in(reg) msp, rv = in(reg) rv, - options(noreturn, nomem, nostack), + options(noreturn, nostack), ) }; } diff --git a/cortex-m/src/register/control.rs b/cortex-m/src/register/control.rs index 52e8f665..f6a7e618 100644 --- a/cortex-m/src/register/control.rs +++ b/cortex-m/src/register/control.rs @@ -199,7 +199,7 @@ pub unsafe fn write(control: Control) { "msr CONTROL, {}", "isb", in(reg) control, - options(nomem, nostack, preserves_flags), + options(nostack, preserves_flags), ); compiler_fence(Ordering::SeqCst); } From 540959fdc81786be46948b23a5d75f1c86b9f5a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 16 Oct 2026 15:56:42 +0000 Subject: [PATCH 4/4] Declare memory effects of coprocessor transfers Cortex-M33 TRM B6.6 permits memory-mapped coprocessor state accessed through loads and stores. --- cortex-m/CHANGELOG.md | 1 + cortex-m/src/asm.rs | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cortex-m/CHANGELOG.md b/cortex-m/CHANGELOG.md index a53056ad..0c7d273a 100644 --- a/cortex-m/CHANGELOG.md +++ b/cortex-m/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - The separate `#[cfg(not(cortex_m))]` stub for `interrupt::free`. ### Fixed +- Account for memory-mapped coprocessor state in coprocessor transfers. - Preserve memory accesses across `control::write`, `asm::bootstrap`, and `asm::bootload`. - Preserve memory accesses around semihosting syscalls. diff --git a/cortex-m/src/asm.rs b/cortex-m/src/asm.rs index 08a2a11e..ac66b46f 100644 --- a/cortex-m/src/asm.rs +++ b/cortex-m/src/asm.rs @@ -488,7 +488,7 @@ pub unsafe fn mcr(a: u32, b: u32 cp = const CP, op1 = const OP1, crm = const CRM, - options(nostack, nomem) + options(nostack) ) }; } @@ -581,7 +581,7 @@ pub unsafe fn mrrc() -> (u32, u32 cp = const CP, opc = const OPC, crm = const CRM, - options(nostack, nomem) + options(nostack) ) };