1use core::{
4 marker::PhantomData,
5 mem::{ManuallyDrop, MaybeUninit},
6 sync::atomic::{AtomicBool, AtomicPtr, Ordering},
7};
8
9#[instability::unstable]
10pub use crate::soc::cpu_control::is_running;
11use crate::{peripherals::CPU_CTRL, soc::cpu_control::internal_park_core, system::Cpu};
12
13#[repr(C, align(16))]
27#[instability::unstable]
28pub struct Stack<const SIZE: usize> {
29 pub mem: MaybeUninit<[u8; SIZE]>,
31}
32
33impl<const SIZE: usize> Default for Stack<SIZE> {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39#[allow(clippy::len_without_is_empty)]
40impl<const SIZE: usize> Stack<SIZE> {
41 #[instability::unstable]
43 pub const fn new() -> Stack<SIZE> {
44 const {
45 ::core::assert!(SIZE.is_multiple_of(16));
47 }
48
49 Stack {
50 mem: MaybeUninit::uninit(),
51 }
52 }
53
54 #[instability::unstable]
56 pub const fn len(&self) -> usize {
57 SIZE
58 }
59
60 #[instability::unstable]
62 pub fn bottom(&mut self) -> *mut u32 {
63 self.mem.as_mut_ptr() as *mut u32
64 }
65
66 #[instability::unstable]
68 pub fn top(&mut self) -> *mut u32 {
69 unsafe { self.bottom().add(SIZE / 4) }
70 }
71}
72
73pub(crate) static START_CORE1_FUNCTION: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut());
76pub(crate) static APP_CORE_STACK_TOP: AtomicPtr<u32> = AtomicPtr::new(core::ptr::null_mut());
77pub(crate) static APP_CORE_STACK_GUARD: AtomicPtr<u32> = AtomicPtr::new(core::ptr::null_mut());
78
79#[must_use = "Dropping this guard will park the APP core"]
81#[instability::unstable]
82pub struct AppCoreGuard<'a> {
83 phantom: PhantomData<&'a ()>,
84}
85
86impl Drop for AppCoreGuard<'_> {
87 fn drop(&mut self) {
88 unsafe { internal_park_core(Cpu::AppCpu, true) };
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq)]
94#[cfg_attr(feature = "defmt", derive(defmt::Format))]
95#[instability::unstable]
96pub enum Error {
97 CoreAlreadyRunning,
99}
100
101#[procmacros::doc_replace]
102#[instability::unstable]
146pub struct CpuControl<'d> {
147 _cpu_control: CPU_CTRL<'d>,
148}
149
150impl<'d> CpuControl<'d> {
151 #[instability::unstable]
153 pub fn new(cpu_control: CPU_CTRL<'d>) -> CpuControl<'d> {
154 CpuControl {
155 _cpu_control: cpu_control,
156 }
157 }
158
159 #[instability::unstable]
166 pub unsafe fn park_core(&mut self, core: Cpu) {
167 unsafe { internal_park_core(core, true) };
168 }
169
170 #[instability::unstable]
172 pub fn unpark_core(&mut self, core: Cpu) {
173 unsafe { internal_park_core(core, false) };
174 }
175
176 #[inline(never)]
178 pub(crate) unsafe fn start_core1_run<F>() -> !
179 where
180 F: FnOnce(),
181 {
182 let entry = START_CORE1_FUNCTION.load(Ordering::Acquire);
183 debug_assert!(!entry.is_null());
184
185 unsafe {
186 let entry = ManuallyDrop::take(&mut *entry.cast::<ManuallyDrop<F>>());
187 entry();
188 loop {
189 internal_park_core(Cpu::current(), true);
190 }
191 }
192 }
193
194 #[instability::unstable]
201 pub fn start_app_core<'a, const SIZE: usize, F>(
202 &mut self,
203 stack: &'static mut Stack<SIZE>,
204 entry: F,
205 ) -> Result<AppCoreGuard<'a>, Error>
206 where
207 F: FnOnce(),
208 F: Send + 'a,
209 {
210 cfg_select! {
211 all(stack_guard_monitoring) => {
212 let stack_guard_offset = Some(esp_config::esp_config_int!(
213 usize,
214 "ESP_HAL_CONFIG_STACK_GUARD_OFFSET"
215 ));
216 }
217 _ => {
218 let stack_guard_offset = None;
219 }
220 };
221
222 self.start_app_core_with_stack_guard_offset(stack, stack_guard_offset, entry)
223 }
224
225 #[instability::unstable]
232 pub fn start_app_core_with_stack_guard_offset<'a, const SIZE: usize, F>(
233 &mut self,
234 stack: &'static mut Stack<SIZE>,
235 stack_guard_offset: Option<usize>,
236 entry: F,
237 ) -> Result<AppCoreGuard<'a>, Error>
238 where
239 F: FnOnce(),
240 F: Send + 'a,
241 {
242 if !crate::debugger::debugger_connected() && is_running(Cpu::AppCpu) {
243 return Err(Error::CoreAlreadyRunning);
244 }
245
246 setup_second_core_stack(stack, stack_guard_offset, entry);
247
248 APP_CORE_TAKES_IPC.store(false, Ordering::Release);
249 crate::soc::cpu_control::start_core1(start_core1_init::<F> as *const u32);
250
251 self.unpark_core(Cpu::AppCpu);
252
253 Ok(AppCoreGuard {
254 phantom: PhantomData,
255 })
256 }
257}
258
259fn setup_second_core_stack<'a, F, const SIZE: usize>(
260 stack: &'static mut Stack<SIZE>,
261 stack_guard_offset: Option<usize>,
262 entry: F,
263) where
264 F: FnOnce(),
265 F: Send + 'a,
266{
267 let entry = ManuallyDrop::new(entry);
269
270 unsafe {
271 let stack_bottom = stack.bottom().cast::<u8>();
272 let (stack_guard, stack_bottom_above_guard) =
273 if let Some(stack_guard_offset) = stack_guard_offset {
274 assert!(stack_guard_offset.is_multiple_of(4));
275 assert!(stack_guard_offset <= stack.len() - 4);
276 (
277 stack_bottom.byte_add(stack_guard_offset),
278 stack_bottom.byte_add(stack_guard_offset).byte_add(4),
279 )
280 } else {
281 (core::ptr::null_mut(), stack_bottom)
282 };
283
284 let align_offset = stack_bottom_above_guard.align_offset(core::mem::align_of::<F>());
288 let entry_dst = stack_bottom_above_guard
289 .add(align_offset)
290 .cast::<ManuallyDrop<F>>();
291
292 entry_dst.write(entry);
293
294 let entry_fn = entry_dst.cast::<()>();
295 #[cfg(esp32s31)]
296 crate::soc::cpu_control::save_core0_pma(&mut *core0_pma_slot::<F>(entry_fn));
297 START_CORE1_FUNCTION.store(entry_fn, Ordering::Release);
298 APP_CORE_STACK_TOP.store(stack.top(), Ordering::Release);
299 APP_CORE_STACK_GUARD.store(stack_guard.cast(), Ordering::Release);
300 }
301}
302
303#[cfg(esp32s31)]
304fn core0_pma_slot<F>(entry: *mut ()) -> *mut [u32; 32] {
305 let end = entry.cast::<u8>().wrapping_add(core::mem::size_of::<F>());
306 end.wrapping_add(end.align_offset(core::mem::align_of::<u32>()))
307 .cast()
308}
309
310#[cfg(xtensa)]
312pub(crate) fn start_core1_init<F>() -> !
313where
314 F: FnOnce(),
315{
316 unsafe {
318 xtensa_lx::interrupt::set_mask(0);
319 }
320
321 xtensa_lx::timer::set_ccompare0(0);
323 xtensa_lx::timer::set_ccompare1(0);
324 xtensa_lx::timer::set_ccompare2(0);
325
326 unsafe extern "C" {
327 static mut _init_start: u32;
328 }
329
330 unsafe {
332 xtensa_lx::set_vecbase(&raw const _init_start);
333 xtensa_lx::set_stack_pointer(APP_CORE_STACK_TOP.load(Ordering::Acquire));
334
335 #[cfg(all(feature = "rt", stack_guard_monitoring))]
336 {
337 let stack_guard = APP_CORE_STACK_GUARD.load(Ordering::Acquire);
338 stack_guard.write_volatile(esp_config::esp_config_int!(
339 u32,
340 "ESP_HAL_CONFIG_STACK_GUARD_VALUE"
341 ));
342 crate::debugger::set_stack_watchpoint(stack_guard as usize);
344 }
345 }
346
347 cfg_select! {
351 esp32 => {
352 crate::peripherals::DPORT::regs()
353 .appcpu_ctrl_d()
354 .write(|w| unsafe { w.appcpu_boot_addr().bits(0) });
355 }
356 _ => crate::rom::ets_set_appcpu_boot_addr(0),
357 }
358
359 unsafe { crate::interrupt::init_vectoring() };
361
362 enable_app_core_interrupts();
363
364 unsafe { CpuControl::start_core1_run::<F>() }
369}
370
371#[cfg(riscv)]
376#[unsafe(naked)]
377pub(crate) extern "C" fn start_core1_init<F>() -> !
378where
379 F: FnOnce(),
380{
381 core::arch::naked_asm!(
382 ".option push",
383 ".option norelax",
384 "la gp, __global_pointer$",
385 "li ra, 0", ".option pop",
387 "li t0, 0x2000",
392 "csrs mstatus, t0",
393 "li t0, 1",
394 "csrw fcsr, t0",
395 "li t0, 0x2000",
396 "csrc mstatus, t0",
397 "la t0, {stack_top}",
399 "lw sp, 0(t0)",
400 "j {init}",
401 stack_top = sym APP_CORE_STACK_TOP,
402 init = sym start_core1_init_impl::<F>,
403 )
404}
405
406#[cfg(riscv)]
407fn start_core1_init_impl<F>() -> !
408where
409 F: FnOnce(),
410{
411 #[cfg(esp32s31)]
412 crate::soc::cpu_control::set_core1_pma(unsafe {
413 &*core0_pma_slot::<F>(START_CORE1_FUNCTION.load(Ordering::Acquire))
414 });
415
416 crate::soc::branch_predictor::enable();
417
418 crate::rom::ets_set_appcpu_boot_addr(0);
421
422 unsafe {
423 #[cfg(all(feature = "rt", stack_guard_monitoring))]
424 {
425 let guard = APP_CORE_STACK_GUARD.load(Ordering::Acquire);
426 guard.write_volatile(esp_config::esp_config_int!(
427 u32,
428 "ESP_HAL_CONFIG_STACK_GUARD_VALUE"
429 ));
430 crate::debugger::set_stack_watchpoint(guard as usize);
431 }
432 crate::interrupt::init_vectoring();
433 #[cfg(feature = "rt")]
434 crate::interrupt::ipc::install_app();
435 }
436
437 enable_app_core_interrupts();
438
439 unsafe { CpuControl::start_core1_run::<F>() }
440}
441
442fn enable_app_core_interrupts() {
443 unsafe {
444 crate::interrupt::RunLevel::change(crate::interrupt::RunLevel::ThreadMode);
445 #[cfg(riscv)]
446 core::arch::asm!("csrsi mstatus, 8", options(nomem, nostack));
447 }
448 APP_CORE_TAKES_IPC.store(true, Ordering::Release);
449}
450
451static APP_CORE_TAKES_IPC: AtomicBool = AtomicBool::new(false);
452
453pub(crate) fn app_core_takes_ipc() -> bool {
456 APP_CORE_TAKES_IPC.load(Ordering::Acquire)
457}