cpu_local/pin.rs
1use core::marker::PhantomData;
2
3use crate::{CpuAreaRef, CpuLocalError, register};
4
5/// Scoped proof that execution cannot migrate away from one validated CPU.
6///
7/// The token can only be created by [`with_cpu_pin`]. Its invariant lifetime
8/// and higher-ranked callback prevent it from escaping the caller's migration
9/// guard or offline-CPU critical section.
10#[must_use = "CPU-local access is valid only while this pin remains in scope"]
11#[derive(Debug)]
12pub struct CpuPin<'scope> {
13 area: CpuAreaRef,
14 _scope: PhantomData<&'scope mut &'scope ()>,
15 _not_send_or_sync: PhantomData<*mut ()>,
16}
17
18impl CpuPin<'_> {
19 /// Returns the initialized CPU area validated when this pin was created.
20 pub const fn area(&self) -> CpuAreaRef {
21 self.area
22 }
23}
24
25/// Scoped proof of exclusive local access to CPU-owned mutable state.
26///
27/// In addition to migration exclusion, the caller that creates this token has
28/// excluded local IRQ/re-entry and every conflicting remote access.
29#[must_use = "mutable CPU-local access is valid only while this token remains in scope"]
30#[derive(Debug)]
31pub struct ExclusiveCpu<'pin> {
32 area: CpuAreaRef,
33 _scope: PhantomData<&'pin mut &'pin ()>,
34 _not_send_or_sync: PhantomData<*mut ()>,
35}
36
37impl ExclusiveCpu<'_> {
38 /// Returns the initialized area covered by this stronger capability.
39 pub const fn area(&self) -> CpuAreaRef {
40 self.area
41 }
42}
43
44/// Runs `operation` with a validated, non-escaping CPU pin.
45///
46/// The higher-ranked callback prevents retaining the token:
47///
48/// ```compile_fail
49/// let retained = unsafe { cpu_local::with_cpu_pin(|pin| pin) }.unwrap();
50/// # let _ = retained;
51/// ```
52///
53/// It also cannot be sent to another execution context:
54///
55/// ```compile_fail
56/// unsafe {
57/// cpu_local::with_cpu_pin(|pin| {
58/// std::thread::scope(|scope| scope.spawn(|| drop(pin)));
59/// })
60/// .unwrap();
61/// }
62/// ```
63///
64/// # Errors
65///
66/// Returns [`CpuLocalError::AreaNotInstalled`] before this CPU has installed
67/// its runtime area, or an identity error if the live register and area header
68/// disagree.
69///
70/// # Safety
71///
72/// The caller must prevent migration for the complete callback. Offline boot
73/// code may call this while the CPU cannot be scheduled; runtime code must
74/// hold an appropriate preemption or IRQ guard.
75pub unsafe fn with_cpu_pin<R>(
76 operation: impl for<'scope> FnOnce(&CpuPin<'scope>) -> R,
77) -> Result<R, CpuLocalError> {
78 let area = register::current_area()?;
79 let pin = CpuPin {
80 area,
81 _scope: PhantomData,
82 _not_send_or_sync: PhantomData,
83 };
84 // Validate the second architecture-owned source before exposing any
85 // typed access. This catches a restored CPU base paired with a stale task
86 // register (notably after a vCPU exit) at the pin boundary.
87 register::current_thread(&pin)?;
88 Ok(operation(&pin))
89}
90
91/// Runs `operation` with exclusive access to mutable state on the pinned CPU.
92///
93/// # Safety
94///
95/// The caller must prevent migration, local IRQ/re-entry, and conflicting
96/// remote access for the complete callback. `pin` must be covered by the same
97/// guard that establishes those conditions.
98pub unsafe fn with_exclusive_cpu<R>(
99 pin: &CpuPin<'_>,
100 operation: impl for<'exclusive> FnOnce(&ExclusiveCpu<'exclusive>) -> R,
101) -> R {
102 let exclusive = ExclusiveCpu {
103 area: pin.area,
104 _scope: PhantomData,
105 _not_send_or_sync: PhantomData,
106 };
107 operation(&exclusive)
108}