From 54abf1a9640710fbac8112470b69db3880fbf897 Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Tue, 12 May 2026 14:58:02 +0100 Subject: [PATCH 1/2] Stop ElXVirtualTimer and ElXPhysicalTimer from implementing Send. These are core-local types and we need to try and stop people moving them into threads running on other cores, at the expense of stopping people moving them into threads running on the same core. If this causes you an issue, you can just unsafely conjure up the values on demand, or use raw CP15 sycalls from the aarch32_cpu::register module. --- aarch32-cpu/src/generic_timer/el0.rs | 35 +++++++++++++++++++++------- aarch32-cpu/src/generic_timer/el1.rs | 22 +++++++++++++---- aarch32-cpu/src/generic_timer/el2.rs | 24 +++++++++++++++---- 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/aarch32-cpu/src/generic_timer/el0.rs b/aarch32-cpu/src/generic_timer/el0.rs index 90dd61f3..192c01b5 100644 --- a/aarch32-cpu/src/generic_timer/el0.rs +++ b/aarch32-cpu/src/generic_timer/el0.rs @@ -1,5 +1,7 @@ //! Code and types for Generic Timer support at EL0 on Armv8-R. +use core::marker::PhantomData; + use crate::register; /// Represents our Generic Physical Timer when we are running at EL0. @@ -7,7 +9,12 @@ use crate::register; /// Note that for most of these APIs to work, EL0 needs to have been granted /// access using methods like /// [El1PhysicalTimer::el0_access_physical_counter](crate::generic_timer::El1PhysicalTimer::el0_access_physical_counter). -pub struct El0PhysicalTimer(); +/// +/// This type is not [Send] because it is a per-core type and should not be moved across +/// cores on an SMP system. +pub struct El0PhysicalTimer { + _phantom: PhantomData<*const u8>, +} impl El0PhysicalTimer { /// Create an EL0 Timer handle for the Physical Timer. @@ -17,11 +24,13 @@ impl El0PhysicalTimer { /// /// # Safety /// - /// Only create one of these at any given time, as they access shared - /// mutable state within the processor and do read-modify-writes on that - /// state. + /// Only create one Physical Timer handle (at any EL) at any given time, as + /// they access shared mutable state within the processor and do + /// read-modify-writes on that state. pub unsafe fn new() -> El0PhysicalTimer { - El0PhysicalTimer() + El0PhysicalTimer { + _phantom: PhantomData, + } } } @@ -80,17 +89,25 @@ impl super::GenericTimer for El0PhysicalTimer { /// Note that for most of these APIs to work, EL0 needs to have been granted /// access using methods like /// [El1VirtualTimer::el0_access_virtual_counter](crate::generic_timer::El1VirtualTimer::el0_access_virtual_counter). -pub struct El0VirtualTimer(); +/// +/// This type is not [Send] because it is a per-core type and should not be moved across +/// cores on an SMP system. +pub struct El0VirtualTimer { + _phantom: PhantomData<*const u8>, +} impl El0VirtualTimer { /// Create an EL0 Timer handle for the Virtual Timer. /// /// # Safety /// - /// Only create one of these at any given time, as they access shared - /// mutable state within the processor and do read-modify-writes on that state. + /// Only create one Virtual Timer handle (at any EL) at any given time, as + /// they access shared mutable state within the processor and do + /// read-modify-writes on that state. pub unsafe fn new() -> El0VirtualTimer { - El0VirtualTimer() + El0VirtualTimer { + _phantom: PhantomData, + } } } diff --git a/aarch32-cpu/src/generic_timer/el1.rs b/aarch32-cpu/src/generic_timer/el1.rs index 9af1d901..dde027fa 100644 --- a/aarch32-cpu/src/generic_timer/el1.rs +++ b/aarch32-cpu/src/generic_timer/el1.rs @@ -5,6 +5,12 @@ use crate::register; use super::{El0PhysicalTimer, El0VirtualTimer, GenericTimer}; /// Represents our Physical Timer when we are running at EL1. +/// +/// This works exactly like [El0PhysicalTimer], but it gives you extra methods +/// for functionality that only processors running at EL1 can access. +/// +/// This type is not [Send] because it is a per-core type and should not be moved across +/// cores on an SMP system. pub struct El1PhysicalTimer(pub(crate) El0PhysicalTimer); impl El1PhysicalTimer { @@ -12,8 +18,9 @@ impl El1PhysicalTimer { /// /// # Safety /// - /// Only create one of these at any given time, as they access shared - /// mutable state within the processor and do read-modify-writes on that state. + /// Only create one Physical Timer handle (at any EL) at any given time, as + /// they access shared mutable state within the processor and do + /// read-modify-writes on that state. pub unsafe fn new() -> El1PhysicalTimer { unsafe { El1PhysicalTimer(El0PhysicalTimer::new()) } } @@ -80,6 +87,12 @@ impl GenericTimer for El1PhysicalTimer { } /// Represents our Virtual Timer when we are running at EL1. +/// +/// This works exactly like [El0VirtualTimer], but it gives you extra methods +/// for functionality that only processors running at EL1 can access. +/// +/// This type is not [Send] because it is a per-core type and should not be moved across +/// cores on an SMP system. pub struct El1VirtualTimer(El0VirtualTimer); impl El1VirtualTimer { @@ -87,8 +100,9 @@ impl El1VirtualTimer { /// /// # Safety /// - /// Only create one of these at any given time, as they access shared - /// mutable state within the processor and do read-modify-writes on that state. + /// Only create one Virtual Timer handle (at any EL) at any given time, as + /// they access shared mutable state within the processor and do + /// read-modify-writes on that state. pub unsafe fn new() -> El1VirtualTimer { unsafe { El1VirtualTimer(El0VirtualTimer::new()) } } diff --git a/aarch32-cpu/src/generic_timer/el2.rs b/aarch32-cpu/src/generic_timer/el2.rs index fd34c915..ad80e701 100644 --- a/aarch32-cpu/src/generic_timer/el2.rs +++ b/aarch32-cpu/src/generic_timer/el2.rs @@ -5,6 +5,12 @@ use crate::register; use super::{El1PhysicalTimer, El1VirtualTimer, GenericTimer}; /// Represents our Physical Timer when we are running at EL2. +/// +/// This works exactly like [El1PhysicalTimer], but it gives you extra methods +/// for functionality that only processors running at EL2 can access. +/// +/// This type is not [Send] because it is a per-core type and should not be moved across +/// cores on an SMP system. pub struct El2PhysicalTimer(El1PhysicalTimer); impl El2PhysicalTimer { @@ -12,8 +18,9 @@ impl El2PhysicalTimer { /// /// # Safety /// - /// Only create one of these at any given time, as they access shared - /// mutable state within the processor and do read-modify-writes on that state. + /// Only create one Physical Timer handle (at any EL) at any given time, as + /// they access shared mutable state within the processor and do + /// read-modify-writes on that state. pub unsafe fn new() -> El2PhysicalTimer { unsafe { El2PhysicalTimer(El1PhysicalTimer::new()) } } @@ -76,15 +83,22 @@ impl GenericTimer for El2PhysicalTimer { } /// Represents our Virtual Timer when we are running at EL1. +/// +/// This works exactly like [El1VirtualTimer], but it gives you extra methods +/// for functionality that only processors running at EL2 can access. +/// +/// This type is not [Send] because it is a per-core type and should not be moved +/// across cores on an SMP system. pub struct El2VirtualTimer(El1VirtualTimer); impl El2VirtualTimer { - /// Create an EL2 Generic Timer handle + /// Create an EL2 Virtual Timer handle /// /// # Safety /// - /// Only create one of these at any given time, as they access shared - /// mutable state within the processor and do read-modify-writes on that state. + /// Only create one Virtual Timer handle (at any EL) at any given time, as + /// they access shared mutable state within the processor and do + /// read-modify-writes on that state. pub unsafe fn new() -> El2VirtualTimer { unsafe { El2VirtualTimer(El1VirtualTimer::new()) } } From b6df0b93fe5e26cb248adaad1d557c62012ec74e Mon Sep 17 00:00:00 2001 From: Jonathan Pallant Date: Mon, 8 Jun 2026 09:18:54 +0100 Subject: [PATCH 2/2] Make Hyp Timer !Send --- aarch32-cpu/src/generic_timer/el2.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/aarch32-cpu/src/generic_timer/el2.rs b/aarch32-cpu/src/generic_timer/el2.rs index ad80e701..3b0aba6f 100644 --- a/aarch32-cpu/src/generic_timer/el2.rs +++ b/aarch32-cpu/src/generic_timer/el2.rs @@ -1,5 +1,7 @@ //! Code and types for Generic Timer support at EL2 on Armv8-R. +use core::marker::PhantomData; + use crate::register; use super::{El1PhysicalTimer, El1VirtualTimer, GenericTimer}; @@ -160,8 +162,16 @@ impl GenericTimer for El2VirtualTimer { } } -/// Represents our Hypervisor-specific Physical Timer when we are running at EL1. -pub struct El2HypPhysicalTimer(); +/// Represents our Hypervisor-specific Physical Timer. +/// +/// This is designed for use by a hypervisor, whilst an EL1 application +/// concurrently uses the Physical Timer and/or the Virtual Timer. +/// +/// This type is not [Send] because it is a per-core type and should not be moved across +/// cores on an SMP system. +pub struct El2HypPhysicalTimer { + _phantom: PhantomData<*const u8>, +} impl El2HypPhysicalTimer { /// Create a Timer handle for the EL2-specific Hyp Physical Timer. @@ -169,9 +179,13 @@ impl El2HypPhysicalTimer { /// # Safety /// /// Only create one of these at any given time, as they access shared - /// mutable state within the processor and do read-modify-writes on that state. + /// mutable state within the processor and do read-modify-writes on that + /// state. This timer is distinct from the Physical Timer and the Virtual + /// Timer, and so can exist concurrently. pub unsafe fn new() -> El2HypPhysicalTimer { - El2HypPhysicalTimer() + El2HypPhysicalTimer { + _phantom: PhantomData, + } } }