1use alloc::{boxed::Box, collections::btree_map::BTreeMap, vec::Vec};
6
7use spin::Once;
8
9use crate::{
10 arch::irq::HwCpuId,
11 mm::{
12 FrameAllocOptions, HasPaddrRange, PAGE_SIZE,
13 frame::{Segment, meta::KernelMeta},
14 paddr_to_vaddr,
15 },
16 sync::SpinLock,
17 task::Task,
18 util::id_set::Id,
19};
20
21static AP_BOOT_INFO: Once<ApBootInfo> = Once::new();
22
23const AP_BOOT_STACK_SIZE: usize = PAGE_SIZE * 64;
24
25struct ApBootInfo {
26 per_ap_raw_info: Box<[PerApRawInfo]>,
28 #[expect(dead_code)]
30 per_ap_info: Box<[PerApInfo]>,
31}
32
33struct PerApInfo {
34 #[expect(dead_code)]
39 boot_stack_pages: Segment<KernelMeta>,
40}
41
42#[repr(C)]
48#[derive(Clone, Copy)]
49pub(crate) struct PerApRawInfo {
50 stack_top: *mut u8,
51 cpu_local: *mut u8,
52}
53
54unsafe impl Send for PerApRawInfo {}
58unsafe impl Sync for PerApRawInfo {}
59
60static HW_CPU_ID_MAP: SpinLock<BTreeMap<u32, HwCpuId>> = SpinLock::new(BTreeMap::new());
61
62pub(crate) unsafe fn boot_all_aps() {
73 report_online_and_hw_cpu_id(crate::cpu::CpuId::bsp().as_usize().try_into().unwrap());
75
76 let num_cpus = crate::cpu::num_cpus();
77
78 if num_cpus == 1 {
79 return;
80 }
81 log::info!("Booting {} processors", num_cpus - 1);
82
83 let mut per_ap_raw_info = Vec::with_capacity(num_cpus);
84 let mut per_ap_info = Vec::with_capacity(num_cpus);
85
86 for ap in 1..num_cpus {
87 let boot_stack_pages = FrameAllocOptions::new()
88 .zeroed(false)
89 .alloc_segment_with(AP_BOOT_STACK_SIZE / PAGE_SIZE, |_| KernelMeta)
90 .unwrap();
91
92 per_ap_raw_info.push(PerApRawInfo {
93 stack_top: paddr_to_vaddr(boot_stack_pages.end_paddr()) as *mut u8,
94 cpu_local: paddr_to_vaddr(crate::cpu::local::get_ap(ap.try_into().unwrap())) as *mut u8,
95 });
96 per_ap_info.push(PerApInfo { boot_stack_pages });
97 }
98
99 assert!(!AP_BOOT_INFO.is_completed());
100 AP_BOOT_INFO.call_once(move || ApBootInfo {
101 per_ap_raw_info: per_ap_raw_info.into_boxed_slice(),
102 per_ap_info: per_ap_info.into_boxed_slice(),
103 });
104
105 log::info!("Booting all application processors...");
106
107 let info_ptr = AP_BOOT_INFO.get().unwrap().per_ap_raw_info.as_ptr();
108 let pt_ptr = crate::mm::page_table::boot_pt::with_borrow(|pt| pt.root_address()).unwrap();
109 unsafe { crate::arch::boot::smp::bringup_all_aps(info_ptr, pt_ptr, num_cpus as u32) };
112
113 wait_for_all_aps_started(num_cpus);
114
115 log::info!("All application processors started. The BSP continues to run.");
116}
117
118static AP_LATE_ENTRY: Once<fn()> = Once::new();
119
120pub fn register_ap_entry(entry: fn()) {
125 AP_LATE_ENTRY.call_once(|| entry);
126}
127
128#[unsafe(no_mangle)]
137pub(crate) unsafe extern "C" fn ap_early_entry(cpu_id: u32) -> ! {
138 unsafe { crate::cpu::init_on_ap(cpu_id) };
142
143 crate::arch::enable_cpu_features();
144
145 unsafe { crate::arch::trap::init_on_cpu() };
147
148 unsafe { crate::mm::kspace::activate_kernel_page_table() };
150
151 unsafe { crate::arch::init_on_ap() };
154
155 crate::arch::irq::enable_local();
156
157 unsafe { crate::mm::page_table::boot_pt::dismiss() };
162
163 report_online_and_hw_cpu_id(cpu_id);
165
166 log::info!("Processor {} started. Spinning for tasks.", cpu_id);
167
168 let ap_late_entry = AP_LATE_ENTRY.wait();
169 ap_late_entry();
170
171 Task::yield_now();
172 unreachable!("`yield_now` in the boot context should not return");
173}
174
175fn report_online_and_hw_cpu_id(cpu_id: u32) {
176 let hw_cpu_id = HwCpuId::read_current(&crate::task::disable_preempt());
179
180 let old_val = HW_CPU_ID_MAP.lock().insert(cpu_id, hw_cpu_id);
181 assert!(old_val.is_none());
182}
183
184fn wait_for_all_aps_started(num_cpus: usize) {
185 fn is_all_aps_started(num_cpus: usize) -> bool {
186 HW_CPU_ID_MAP.lock().len() == num_cpus
187 }
188
189 while !is_all_aps_started(num_cpus) {
190 core::hint::spin_loop();
191 }
192}
193
194pub(crate) fn construct_hw_cpu_id_mapping() -> Box<[HwCpuId]> {
202 let mut hw_cpu_id_map = HW_CPU_ID_MAP.lock();
203 assert_eq!(hw_cpu_id_map.len(), crate::cpu::num_cpus());
204
205 let result = hw_cpu_id_map
206 .values()
207 .cloned()
208 .collect::<Vec<_>>()
209 .into_boxed_slice();
210 hw_cpu_id_map.clear();
211
212 result
213}