esp_hal/system/
multi_core.rs1use core::{
4 marker::PhantomData,
5 mem::{ManuallyDrop, MaybeUninit},
6 sync::atomic::{AtomicPtr, Ordering},
7};
8
9#[instability::unstable]
10pub use crate::soc::cpu_control::is_running;
11use crate::{
12 peripherals::CPU_CTRL,
13 soc::cpu_control::{internal_park_core, start_core1_init},
14 system::Cpu,
15};
16
17#[repr(C, align(16))]
31#[instability::unstable]
32pub struct Stack<const SIZE: usize> {
33 pub mem: MaybeUninit<[u8; SIZE]>,
35}
36
37impl<const SIZE: usize> Default for Stack<SIZE> {
38 fn default() -> Self {
39 Self::new()
40 }
41}
42
43#[allow(clippy::len_without_is_empty)]
44impl<const SIZE: usize> Stack<SIZE> {
45 #[instability::unstable]
47 pub const fn new() -> Stack<SIZE> {
48 const {
49 ::core::assert!(SIZE.is_multiple_of(16));
51 }
52
53 Stack {
54 mem: MaybeUninit::uninit(),
55 }
56 }
57
58 #[instability::unstable]
60 pub const fn len(&self) -> usize {
61 SIZE
62 }
63
64 #[instability::unstable]
66 pub fn bottom(&mut self) -> *mut u32 {
67 self.mem.as_mut_ptr() as *mut u32
68 }
69
70 #[instability::unstable]
72 pub fn top(&mut self) -> *mut u32 {
73 unsafe { self.bottom().add(SIZE / 4) }
74 }
75}
76
77pub(crate) static START_CORE1_FUNCTION: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut());
80pub(crate) static APP_CORE_STACK_TOP: AtomicPtr<u32> = AtomicPtr::new(core::ptr::null_mut());
81pub(crate) static APP_CORE_STACK_GUARD: AtomicPtr<u32> = AtomicPtr::new(core::ptr::null_mut());
82
83#[must_use = "Dropping this guard will park the APP core"]
85#[instability::unstable]
86pub struct AppCoreGuard<'a> {
87 phantom: PhantomData<&'a ()>,
88}
89
90impl Drop for AppCoreGuard<'_> {
91 fn drop(&mut self) {
92 unsafe { internal_park_core(Cpu::AppCpu, true) };
93 }
94}
95
96#[derive(Debug, Clone, Copy, PartialEq)]
98#[cfg_attr(feature = "defmt", derive(defmt::Format))]
99#[instability::unstable]
100pub enum Error {
101 CoreAlreadyRunning,
103}
104
105#[procmacros::doc_replace]
106#[instability::unstable]
150pub struct CpuControl<'d> {
151 _cpu_control: CPU_CTRL<'d>,
152}
153
154impl<'d> CpuControl<'d> {
155 #[instability::unstable]
157 pub fn new(cpu_control: CPU_CTRL<'d>) -> CpuControl<'d> {
158 CpuControl {
159 _cpu_control: cpu_control,
160 }
161 }
162
163 #[instability::unstable]
170 pub unsafe fn park_core(&mut self, core: Cpu) {
171 unsafe { internal_park_core(core, true) };
172 }
173
174 #[instability::unstable]
176 pub fn unpark_core(&mut self, core: Cpu) {
177 unsafe { internal_park_core(core, false) };
178 }
179
180 #[inline(never)]
182 pub(crate) unsafe fn start_core1_run<F>() -> !
183 where
184 F: FnOnce(),
185 {
186 let entry = START_CORE1_FUNCTION.load(Ordering::Acquire);
187 debug_assert!(!entry.is_null());
188
189 unsafe {
190 let entry = ManuallyDrop::take(&mut *entry.cast::<ManuallyDrop<F>>());
191 entry();
192 loop {
193 internal_park_core(Cpu::current(), true);
194 }
195 }
196 }
197
198 #[instability::unstable]
205 pub fn start_app_core<'a, const SIZE: usize, F>(
206 &mut self,
207 stack: &'static mut Stack<SIZE>,
208 entry: F,
209 ) -> Result<AppCoreGuard<'a>, Error>
210 where
211 F: FnOnce(),
212 F: Send + 'a,
213 {
214 cfg_select! {
215 all(stack_guard_monitoring) => {
216 let stack_guard_offset = Some(esp_config::esp_config_int!(
217 usize,
218 "ESP_HAL_CONFIG_STACK_GUARD_OFFSET"
219 ));
220 }
221 _ => {
222 let stack_guard_offset = None;
223 }
224 };
225
226 self.start_app_core_with_stack_guard_offset(stack, stack_guard_offset, entry)
227 }
228
229 #[instability::unstable]
236 pub fn start_app_core_with_stack_guard_offset<'a, const SIZE: usize, F>(
237 &mut self,
238 stack: &'static mut Stack<SIZE>,
239 stack_guard_offset: Option<usize>,
240 entry: F,
241 ) -> Result<AppCoreGuard<'a>, Error>
242 where
243 F: FnOnce(),
244 F: Send + 'a,
245 {
246 if !crate::debugger::debugger_connected() && is_running(Cpu::AppCpu) {
247 return Err(Error::CoreAlreadyRunning);
248 }
249
250 setup_second_core_stack(stack, stack_guard_offset, entry);
251
252 crate::soc::cpu_control::start_core1(start_core1_init::<F> as *const u32);
253
254 self.unpark_core(Cpu::AppCpu);
255
256 Ok(AppCoreGuard {
257 phantom: PhantomData,
258 })
259 }
260}
261
262fn setup_second_core_stack<'a, F, const SIZE: usize>(
263 stack: &'static mut Stack<SIZE>,
264 stack_guard_offset: Option<usize>,
265 entry: F,
266) where
267 F: FnOnce(),
268 F: Send + 'a,
269{
270 let entry = ManuallyDrop::new(entry);
272
273 unsafe {
274 let stack_bottom = stack.bottom().cast::<u8>();
275 let (stack_guard, stack_bottom_above_guard) =
276 if let Some(stack_guard_offset) = stack_guard_offset {
277 assert!(stack_guard_offset.is_multiple_of(4));
278 assert!(stack_guard_offset <= stack.len() - 4);
279 (
280 stack_bottom.byte_add(stack_guard_offset),
281 stack_bottom.byte_add(stack_guard_offset).byte_add(4),
282 )
283 } else {
284 (core::ptr::null_mut(), stack_bottom)
285 };
286
287 let align_offset = stack_bottom_above_guard.align_offset(core::mem::align_of::<F>());
291 let entry_dst = stack_bottom_above_guard
292 .add(align_offset)
293 .cast::<ManuallyDrop<F>>();
294
295 entry_dst.write(entry);
296
297 let entry_fn = entry_dst.cast::<()>();
298 START_CORE1_FUNCTION.store(entry_fn, Ordering::Release);
299 APP_CORE_STACK_TOP.store(stack.top(), Ordering::Release);
300 APP_CORE_STACK_GUARD.store(stack_guard.cast(), Ordering::Release);
301 }
302}