1use core::{
2 mem::{offset_of, size_of},
3 pin::Pin,
4 ptr::NonNull,
5 sync::atomic::{AtomicUsize, Ordering},
6};
7
8use crate::{ContextSwitchError, CpuAreaRef, preempt::PreemptionState};
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub(crate) struct CpuBindingEpoch(usize);
12
13#[derive(Clone, Copy, Debug)]
14pub(crate) struct CurrentCpuBinding {
15 pub(crate) area: CpuAreaRef,
16 pub(crate) epoch: CpuBindingEpoch,
17}
18
19const CPU_PHASE_MASK: usize = 0b11;
20const CPU_UNBOUND: usize = 0b00;
21const CPU_BINDING: usize = 0b01;
22const CPU_BOUND: usize = 0b10;
23const CPU_UNBINDING: usize = 0b11;
24
25const fn execution_context_reserved_size() -> usize {
26 64 - 4 * size_of::<usize>() - size_of::<PreemptionState>()
27}
28
29#[repr(C, align(64))]
35pub struct ExecutionContextHeader {
36 cpu_area: AtomicUsize,
37 binding_epoch: AtomicUsize,
38 architecture_state: [AtomicUsize; 2],
39 preemption_state: PreemptionState,
40 reserved: [u8; execution_context_reserved_size()],
41}
42
43impl ExecutionContextHeader {
44 pub const fn new() -> Self {
46 Self {
47 cpu_area: AtomicUsize::new(0),
48 binding_epoch: AtomicUsize::new(CPU_UNBOUND),
49 architecture_state: [const { AtomicUsize::new(0) }; 2],
50 preemption_state: PreemptionState::new(),
51 reserved: [0; execution_context_reserved_size()],
52 }
53 }
54
55 pub(crate) const fn boot(area_base: usize) -> Self {
56 Self {
57 cpu_area: AtomicUsize::new(area_base),
58 binding_epoch: AtomicUsize::new(CPU_BOUND),
59 architecture_state: [const { AtomicUsize::new(0) }; 2],
60 preemption_state: PreemptionState::bootstrap_disabled(),
61 reserved: [0; execution_context_reserved_size()],
62 }
63 }
64
65 #[doc(hidden)]
70 pub const fn new_bootstrap() -> Self {
71 Self {
72 cpu_area: AtomicUsize::new(0),
73 binding_epoch: AtomicUsize::new(CPU_UNBOUND),
74 architecture_state: [const { AtomicUsize::new(0) }; 2],
75 preemption_state: PreemptionState::bootstrap_disabled(),
76 reserved: [0; execution_context_reserved_size()],
77 }
78 }
79
80 pub fn cpu_area(&self) -> Option<CpuAreaRef> {
82 self.cpu_binding().map(|binding| binding.area)
83 }
84
85 pub fn cpu_area_base(&self) -> Option<usize> {
87 self.cpu_binding().map(|binding| binding.area.base())
88 }
89
90 pub(crate) unsafe fn bind_cpu(
91 self: Pin<&Self>,
92 area: CpuAreaRef,
93 ) -> Result<CpuBindingEpoch, ContextSwitchError> {
94 let this = self.get_ref();
95 let unbound = this.binding_epoch.load(Ordering::Acquire);
96 if unbound & CPU_PHASE_MASK != CPU_UNBOUND {
97 return Err(ContextSwitchError::NextContextAlreadyBound);
98 }
99 this.binding_epoch
100 .compare_exchange(
101 unbound,
102 unbound | CPU_BINDING,
103 Ordering::AcqRel,
104 Ordering::Acquire,
105 )
106 .map_err(|_| ContextSwitchError::NextContextAlreadyBound)?;
107 this.cpu_area.store(area.base(), Ordering::Relaxed);
108 let bound = (unbound & !CPU_PHASE_MASK) | CPU_BOUND;
109 this.binding_epoch.store(bound, Ordering::Release);
110 Ok(CpuBindingEpoch(bound))
111 }
112
113 pub(crate) unsafe fn unbind_cpu(
114 self: Pin<&Self>,
115 expected: CpuBindingEpoch,
116 ) -> Result<(), ContextSwitchError> {
117 if expected.0 & CPU_PHASE_MASK != CPU_BOUND {
118 return Err(ContextSwitchError::StalePreviousBinding);
119 }
120 let this = self.get_ref();
121 let unbinding = (expected.0 & !CPU_PHASE_MASK) | CPU_UNBINDING;
122 this.binding_epoch
123 .compare_exchange(expected.0, unbinding, Ordering::AcqRel, Ordering::Acquire)
124 .map_err(|_| ContextSwitchError::StalePreviousBinding)?;
125 this.cpu_area.store(0, Ordering::Relaxed);
126 let next_unbound = (expected.0 & !CPU_PHASE_MASK).wrapping_add(4);
127 this.binding_epoch.store(next_unbound, Ordering::Release);
128 Ok(())
129 }
130
131 pub(crate) fn cpu_binding(&self) -> Option<CurrentCpuBinding> {
132 let (area_base, epoch) = self.raw_cpu_binding()?;
133 let area = unsafe { CpuAreaRef::from_initialized_base(area_base) }.ok()?;
136 Some(CurrentCpuBinding { area, epoch })
137 }
138
139 pub(crate) fn raw_cpu_binding(&self) -> Option<(usize, CpuBindingEpoch)> {
140 loop {
141 let before = self.binding_epoch.load(Ordering::Acquire);
142 if before & CPU_PHASE_MASK != CPU_BOUND {
143 return None;
144 }
145 let area_base = self.cpu_area.load(Ordering::Relaxed);
146 let after = self.binding_epoch.load(Ordering::Acquire);
147 if before == after {
148 return Some((area_base, CpuBindingEpoch(after)));
149 }
150 core::hint::spin_loop();
151 }
152 }
153
154 pub fn as_non_null(self: Pin<&Self>) -> NonNull<Self> {
156 NonNull::from(self.get_ref())
157 }
158
159 #[cfg(any(not(target_arch = "x86_64"), feature = "host-test"))]
160 pub(crate) const fn preemption_state(&self) -> &PreemptionState {
161 &self.preemption_state
162 }
163}
164
165impl Default for ExecutionContextHeader {
166 fn default() -> Self {
167 Self::new()
168 }
169}
170
171pub const EXECUTION_CONTEXT_CPU_BASE_OFFSET: usize = offset_of!(ExecutionContextHeader, cpu_area);
173pub const EXECUTION_CONTEXT_ARCH_STATE_OFFSET: usize =
175 offset_of!(ExecutionContextHeader, architecture_state);
176pub const EXECUTION_CONTEXT_ARCH_STATE_SIZE: usize = 2 * size_of::<usize>();
178
179const _: () = {
180 assert!(EXECUTION_CONTEXT_CPU_BASE_OFFSET == 0);
181 assert!(size_of::<ExecutionContextHeader>() == 64);
182 assert!(core::mem::align_of::<ExecutionContextHeader>() == 64);
183};
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn execution_context_header_starts_with_cpu_binding() {
191 assert_eq!(EXECUTION_CONTEXT_CPU_BASE_OFFSET, 0);
192 }
193}