cpu_local/register/
mod.rs1use core::{pin::Pin, ptr::NonNull, sync::atomic::Ordering};
4
5use crate::{CpuAreaRef, CpuLocalError, CpuPin, CurrentThreadHeader, ThreadSwitchError};
6
7#[cfg(all(not(feature = "host-test"), target_arch = "aarch64"))]
8mod aarch64;
9#[cfg(feature = "host-test")]
10mod host;
11#[cfg(all(not(feature = "host-test"), target_arch = "loongarch64"))]
12mod loongarch64;
13#[cfg(all(
14 not(feature = "host-test"),
15 any(target_arch = "riscv32", target_arch = "riscv64")
16))]
17mod riscv;
18#[cfg(all(not(feature = "host-test"), target_arch = "x86_64"))]
19mod x86_64;
20
21#[cfg(all(not(feature = "host-test"), target_arch = "aarch64"))]
22use aarch64 as imp;
23#[cfg(feature = "host-test")]
24use host as imp;
25#[cfg(all(not(feature = "host-test"), target_arch = "loongarch64"))]
26use loongarch64 as imp;
27#[cfg(all(
28 not(feature = "host-test"),
29 any(target_arch = "riscv32", target_arch = "riscv64")
30))]
31use riscv as imp;
32#[cfg(all(not(feature = "host-test"), target_arch = "x86_64"))]
33use x86_64 as imp;
34
35#[cfg(all(
36 not(feature = "host-test"),
37 not(any(
38 target_arch = "x86_64",
39 target_arch = "aarch64",
40 target_arch = "riscv32",
41 target_arch = "riscv64",
42 target_arch = "loongarch64"
43 ))
44))]
45compile_error!("cpu-local supports x86_64, AArch64, RISC-V, and LoongArch64 only");
46
47#[doc(hidden)]
54pub unsafe fn install_cpu_area(area: CpuAreaRef) -> Result<(), CpuLocalError> {
55 imp::validate_environment()?;
56 let boot_thread = area.prefix().boot_thread().header();
57 let boot_pointer = boot_thread as *const CurrentThreadHeader as usize;
58 unsafe { imp::install_cpu_base(area.base(), boot_pointer) };
60 if unsafe { imp::read_cpu_base()? } != area.base() {
61 fatal_register_invariant();
62 }
63 Ok(())
64}
65
66pub(crate) fn current_area() -> Result<CpuAreaRef, CpuLocalError> {
67 let area_base = unsafe { imp::read_cpu_base()? };
68 if area_base == 0 {
69 return Err(CpuLocalError::AreaNotInstalled);
70 }
71 unsafe { CpuAreaRef::from_initialized_base(area_base) }
74}
75
76pub(crate) unsafe fn commit_current_thread(area: CpuAreaRef, value: usize) {
83 area.runtime_anchor()
84 .current_thread_slot()
85 .store(value, Ordering::Release);
86}
87
88pub fn current_thread(pin: &CpuPin<'_>) -> Result<NonNull<CurrentThreadHeader>, CpuLocalError> {
90 let area = pin.area();
91 let slot = area.runtime_anchor().current_thread_raw();
92 let register = unsafe { imp::read_current_thread(area.base()) };
93 if slot == 0
94 || slot != register
95 || !slot.is_multiple_of(core::mem::align_of::<CurrentThreadHeader>())
96 {
97 return Err(CpuLocalError::CurrentThreadMismatch);
98 }
99 let pointer = NonNull::new(slot as *mut CurrentThreadHeader)
100 .ok_or(CpuLocalError::CurrentThreadMismatch)?;
101 let thread_area = unsafe { pointer.as_ref() }
104 .cpu_area()
105 .ok_or(CpuLocalError::CurrentThreadMismatch)?;
106 if thread_area != area {
107 return Err(CpuLocalError::CurrentThreadMismatch);
108 }
109 Ok(pointer)
110}
111
112#[doc(hidden)]
119pub unsafe fn scheduler_current_thread() -> Result<NonNull<CurrentThreadHeader>, CpuLocalError> {
120 #[cfg(not(feature = "tls"))]
121 {
122 let register = unsafe { imp::read_current_thread(0) };
126 NonNull::new(register as *mut CurrentThreadHeader)
127 .ok_or(CpuLocalError::CurrentThreadMismatch)
128 }
129
130 #[cfg(feature = "tls")]
131 loop {
132 let area = current_area()?;
136 let register = unsafe { imp::read_current_thread(area.base()) };
137 if unsafe { imp::read_cpu_base()? } != area.base() {
138 continue;
139 }
140 return NonNull::new(register as *mut CurrentThreadHeader)
141 .ok_or(CpuLocalError::CurrentThreadMismatch);
142 }
143}
144
145#[cfg(all(test, feature = "host-test"))]
146mod tests {
147 use core::mem::MaybeUninit;
148
149 use super::*;
150 use crate::{CpuAreaPrefix, CpuIndex};
151
152 fn modeled_area(cpu_index: usize) -> CpuAreaRef {
153 let storage = Box::leak(Box::new(MaybeUninit::<CpuAreaPrefix>::uninit()));
154 let base = storage.as_mut_ptr() as usize;
155 storage.write(
156 CpuAreaPrefix::initialize(CpuIndex::try_from(cpu_index).unwrap(), base).unwrap(),
157 );
158 unsafe { CpuAreaRef::from_initialized_base(base) }.unwrap()
160 }
161
162 #[test]
163 fn scheduler_current_thread_survives_migration_during_bootstrap_read() {
164 let first = modeled_area(0);
165 let second = modeled_area(1);
166 let first_boot = first.prefix().boot_thread().header();
167 let second_boot = second.prefix().boot_thread().header();
168
169 unsafe { imp::install_cpu_base(first.base(), first_boot as *const _ as usize) };
171 imp::migrate_on_next_current_read(second.base());
172
173 assert_eq!(
174 unsafe { scheduler_current_thread() },
176 Ok(NonNull::from(second_boot)),
177 );
178 }
179
180 #[test]
181 fn scheduler_current_thread_rejects_an_uninstalled_host_area() {
182 #[cfg(feature = "tls")]
183 let expected_error = CpuLocalError::AreaNotInstalled;
184 #[cfg(not(feature = "tls"))]
185 let expected_error = CpuLocalError::CurrentThreadMismatch;
186
187 let rejected = std::thread::spawn(move || {
188 matches!(
191 unsafe { scheduler_current_thread() },
192 Err(error) if error == expected_error
193 )
194 })
195 .join()
196 .expect("host current-thread probe panicked");
197
198 assert!(rejected);
199 }
200}
201
202#[doc(hidden)]
209pub unsafe fn install_bootstrap_thread(
210 pin: &CpuPin<'_>,
211 header: Pin<&CurrentThreadHeader>,
212) -> Result<(), ThreadSwitchError> {
213 let epoch = unsafe { header.bind_cpu(pin.area()) }?;
214 let pointer = header.as_non_null().as_ptr() as usize;
215 unsafe { commit_current_thread(pin.area(), pointer) };
216 unsafe { imp::write_current_thread(pointer) };
219 if current_thread(pin) != Ok(header.as_non_null()) {
220 let _ = epoch;
223 fatal_register_invariant();
224 }
225 Ok(())
226}
227
228#[cfg(feature = "tls")]
230pub fn kernel_tls(_pin: &CpuPin<'_>) -> usize {
231 unsafe { imp::read_kernel_tls() }
232}
233
234#[cfg(feature = "tls")]
241#[doc(hidden)]
242pub unsafe fn install_kernel_tls(_pin: &CpuPin<'_>, value: usize) {
243 unsafe { imp::write_kernel_tls(value) };
244}
245
246#[cold]
247#[inline(never)]
248fn fatal_register_invariant() -> ! {
249 panic!("CPU-local register commit did not retain the validated state")
250}