1#[cfg(not(all(target_arch = "aarch64", not(feature = "host-test"))))]
2use core::sync::atomic::Ordering;
3use core::{
4 mem::{MaybeUninit, align_of, offset_of, size_of},
5 ptr::NonNull,
6 sync::atomic::AtomicUsize,
7};
8
9use crate::{CpuIndex, CpuLocalError, ExecutionContextHeader, preempt::PreemptionState};
10
11const fn runtime_anchor_reserved_size() -> usize {
12 64 - 6 * size_of::<usize>() - size_of::<PreemptionState>()
13}
14
15#[repr(C, align(64))]
17pub struct CpuRuntimeAnchor {
18 current_context: AtomicUsize,
19 architecture_state: [AtomicUsize; 5],
20 preemption_state: PreemptionState,
21 reserved: [u8; runtime_anchor_reserved_size()],
22}
23
24impl CpuRuntimeAnchor {
25 const fn for_boot_context(boot_context: usize) -> Self {
26 let current_context = if cfg!(all(target_arch = "x86_64", not(feature = "host-test")))
27 || cfg!(all(
28 kernel_tls,
29 not(all(target_arch = "aarch64", not(feature = "host-test")))
30 )) {
31 boot_context
32 } else {
33 0
34 };
35 Self {
36 current_context: AtomicUsize::new(current_context),
37 architecture_state: [const { AtomicUsize::new(0) }; 5],
38 preemption_state: PreemptionState::bootstrap_disabled(),
39 reserved: [0; runtime_anchor_reserved_size()],
40 }
41 }
42
43 #[cfg(not(all(target_arch = "aarch64", not(feature = "host-test"))))]
45 pub(crate) fn current_context_raw(&self) -> usize {
46 self.current_context.load(Ordering::Acquire)
47 }
48
49 #[cfg(not(all(target_arch = "aarch64", not(feature = "host-test"))))]
50 pub(crate) const fn current_context_slot(&self) -> &AtomicUsize {
51 &self.current_context
52 }
53
54 #[cfg(all(target_arch = "x86_64", not(feature = "host-test")))]
55 pub(crate) const fn preemption_state(&self) -> &PreemptionState {
56 &self.preemption_state
57 }
58}
59
60#[repr(transparent)]
62pub struct BootContextHeader(ExecutionContextHeader);
63
64impl BootContextHeader {
65 const fn for_area(area_base: usize) -> Self {
66 Self(ExecutionContextHeader::boot(area_base))
67 }
68
69 pub const fn header(&self) -> &ExecutionContextHeader {
71 &self.0
72 }
73}
74
75const fn area_header_reserved_size() -> usize {
76 64 - size_of::<u32>() * 2 - size_of::<usize>()
77}
78
79#[repr(C, align(64))]
81pub struct CpuAreaHeader {
82 cpu_index: u32,
83 reserved_word: u32,
84 self_base: usize,
85 reserved: [u8; area_header_reserved_size()],
86}
87
88impl CpuAreaHeader {
89 const fn new(cpu_index: CpuIndex, self_base: usize) -> Self {
90 Self {
91 cpu_index: cpu_index.as_u32(),
92 reserved_word: 0,
93 self_base,
94 reserved: [0; area_header_reserved_size()],
95 }
96 }
97
98 #[inline(always)]
100 pub const fn cpu_index(&self) -> CpuIndex {
101 match CpuIndex::from_u32(self.cpu_index) {
102 Some(index) => index,
103 None => panic!("initialized CPU area contains the reserved CPU index"),
104 }
105 }
106
107 pub const fn self_base(&self) -> usize {
109 self.self_base
110 }
111}
112
113#[repr(C, align(64))]
115pub struct CpuAreaPrefix {
116 header: CpuAreaHeader,
117 runtime: CpuRuntimeAnchor,
118 boot_context: BootContextHeader,
119}
120
121impl CpuAreaPrefix {
122 pub fn initialize(cpu_index: CpuIndex, area_base: usize) -> Result<Self, CpuLocalError> {
129 validate_area_base(area_base)?;
130 area_base
131 .checked_add(CPU_AREA_BOOT_CONTEXT_OFFSET)
132 .ok_or(CpuLocalError::AddressOverflow)?;
133 Ok(Self {
134 header: CpuAreaHeader::new(cpu_index, area_base),
135 runtime: CpuRuntimeAnchor::for_boot_context(area_base + CPU_AREA_BOOT_CONTEXT_OFFSET),
136 boot_context: BootContextHeader::for_area(area_base),
137 })
138 }
139
140 pub const fn header(&self) -> &CpuAreaHeader {
142 &self.header
143 }
144
145 pub const fn runtime_anchor(&self) -> &CpuRuntimeAnchor {
147 &self.runtime
148 }
149
150 pub const fn boot_context(&self) -> &BootContextHeader {
152 &self.boot_context
153 }
154}
155
156#[derive(Clone, Copy, Debug, Eq, PartialEq)]
158pub struct CpuAreaRef {
159 prefix: NonNull<CpuAreaPrefix>,
160}
161
162unsafe impl Send for CpuAreaRef {}
166unsafe impl Sync for CpuAreaRef {}
169
170impl CpuAreaRef {
171 #[doc(hidden)]
178 pub unsafe fn from_initialized_base(area_base: usize) -> Result<Self, CpuLocalError> {
179 #[cfg(feature = "host-test")]
180 crate::register::host_test::record_initialized_area_validation();
181 validate_area_base(area_base)?;
182 let prefix = NonNull::new(area_base as *mut CpuAreaPrefix)
183 .ok_or(CpuLocalError::InvalidAreaBase { base: area_base })?;
184 let header = unsafe { prefix.as_ref() }.header();
186 CpuIndex::from_u32(header.cpu_index).ok_or(CpuLocalError::AreaIdentityMismatch)?;
187 if header.self_base != area_base {
188 return Err(CpuLocalError::AreaIdentityMismatch);
189 }
190 let expected_boot = area_base
191 .checked_add(CPU_AREA_BOOT_CONTEXT_OFFSET)
192 .ok_or(CpuLocalError::AddressOverflow)?;
193 if unsafe { prefix.as_ref() }
194 .boot_context()
195 .header()
196 .raw_cpu_binding()
197 .map(|(boot_area, _)| boot_area)
198 != Some(area_base)
199 || expected_boot
200 != core::ptr::addr_of!(unsafe { prefix.as_ref() }.boot_context.0) as usize
201 {
202 return Err(CpuLocalError::AreaIdentityMismatch);
203 }
204 Ok(Self { prefix })
205 }
206
207 pub(crate) unsafe fn from_installed_base(area_base: usize) -> Self {
216 debug_assert!(area_base != 0);
217 debug_assert!(area_base.is_multiple_of(align_of::<CpuAreaPrefix>()));
218 let prefix = unsafe { NonNull::new_unchecked(area_base as *mut CpuAreaPrefix) };
221 Self { prefix }
222 }
223
224 #[inline(always)]
226 pub const fn cpu_index(self) -> CpuIndex {
227 self.prefix().header().cpu_index()
228 }
229
230 #[inline(always)]
232 pub fn base(self) -> usize {
233 self.prefix.as_ptr() as usize
234 }
235
236 #[inline(always)]
238 pub const fn prefix(self) -> &'static CpuAreaPrefix {
239 unsafe { self.prefix.as_ref() }
241 }
242
243 #[inline(always)]
245 pub fn runtime_anchor(self) -> &'static CpuRuntimeAnchor {
246 self.prefix().runtime_anchor()
247 }
248}
249
250fn validate_area_base(area_base: usize) -> Result<(), CpuLocalError> {
251 if area_base == 0 || !area_base.is_multiple_of(align_of::<CpuAreaPrefix>()) {
252 Err(CpuLocalError::InvalidAreaBase { base: area_base })
253 } else {
254 Ok(())
255 }
256}
257
258pub const CPU_AREA_HEADER_SIZE: usize = size_of::<CpuAreaHeader>();
260pub const CPU_AREA_RUNTIME_ANCHOR_OFFSET: usize = offset_of!(CpuAreaPrefix, runtime);
262pub const CPU_AREA_BOOT_CONTEXT_OFFSET: usize = offset_of!(CpuAreaPrefix, boot_context);
264pub const CPU_AREA_SELF_BASE_OFFSET: usize = offset_of!(CpuAreaHeader, self_base);
266pub const CPU_AREA_CPU_INDEX_OFFSET: usize = offset_of!(CpuAreaHeader, cpu_index);
268pub const CPU_AREA_CURRENT_CONTEXT_OFFSET: usize =
270 CPU_AREA_RUNTIME_ANCHOR_OFFSET + offset_of!(CpuRuntimeAnchor, current_context);
271pub const CPU_AREA_ARCH_STATE_OFFSET: usize =
273 CPU_AREA_RUNTIME_ANCHOR_OFFSET + offset_of!(CpuRuntimeAnchor, architecture_state);
274pub const CPU_AREA_ARCH_STATE_SIZE: usize = 5 * size_of::<usize>();
276pub const CPU_AREA_PREEMPTION_STATE_OFFSET: usize =
278 CPU_AREA_RUNTIME_ANCHOR_OFFSET + offset_of!(CpuRuntimeAnchor, preemption_state);
279
280const _: () = {
281 assert!(size_of::<CpuAreaHeader>() == 64);
282 assert!(align_of::<CpuAreaHeader>() == 64);
283 assert!(size_of::<CpuRuntimeAnchor>() == 64);
284 assert!(align_of::<CpuRuntimeAnchor>() == 64);
285 assert!(size_of::<BootContextHeader>() == 64);
286 assert!(align_of::<BootContextHeader>() == 64);
287 assert!(size_of::<CpuAreaPrefix>() == 192);
288 assert!(align_of::<CpuAreaPrefix>() == 64);
289 assert!(CPU_AREA_RUNTIME_ANCHOR_OFFSET == 64);
290 assert!(CPU_AREA_BOOT_CONTEXT_OFFSET == 128);
291};
292
293#[doc(hidden)]
294#[used]
295#[unsafe(no_mangle)]
296#[unsafe(link_section = ".percpu.template.header")]
297pub static mut __CPU_LOCAL_AREA_PREFIX: MaybeUninit<CpuAreaPrefix> = MaybeUninit::uninit();
298
299#[doc(hidden)]
300#[used]
301#[unsafe(no_mangle)]
302#[unsafe(link_section = ".percpu.template.end")]
303pub static __CPU_LOCAL_TEMPLATE_END: u8 = 0;
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308
309 #[test]
310 fn cpu_area_ref_keeps_one_area_identity() {
311 assert_eq!(size_of::<CpuAreaRef>(), size_of::<usize>());
312 assert_eq!(size_of::<Option<CpuAreaRef>>(), size_of::<usize>());
313 }
314}