use acpi::platform::PlatformInfo;
use crate::{
arch::x86::kernel::{
acpi::ACPI_TABLES,
apic::{
self, ApicId, DeliveryMode, DeliveryStatus, DestinationMode, DestinationShorthand, Icr,
Level, TriggerMode,
},
},
mm::{paddr_to_vaddr, PAGE_SIZE},
};
pub(crate) fn get_num_processors() -> Option<u32> {
if !ACPI_TABLES.is_completed() {
return None;
}
let processor_info = PlatformInfo::new(&*ACPI_TABLES.get().unwrap().lock())
.unwrap()
.processor_info
.unwrap();
Some(processor_info.application_processors.len() as u32 + 1)
}
pub(crate) fn bringup_all_aps() {
copy_ap_boot_code();
init_boot_stack_array();
send_boot_ipis();
}
pub(super) const AP_BOOT_START_PA: usize = 0x8000;
pub(super) fn ap_boot_code_size() -> usize {
__ap_boot_end as usize - __ap_boot_start as usize
}
fn copy_ap_boot_code() {
let ap_boot_start = __ap_boot_start as usize as *const u8;
let len = __ap_boot_end as usize - __ap_boot_start as usize;
unsafe {
core::ptr::copy_nonoverlapping(
ap_boot_start,
crate::mm::paddr_to_vaddr(AP_BOOT_START_PA) as *mut u8,
len,
);
}
}
fn init_boot_stack_array() {
let pages = &crate::boot::smp::AP_BOOT_INFO
.get()
.unwrap()
.boot_stack_array;
extern "C" {
fn __ap_boot_stack_array_pointer();
}
let ap_boot_stack_arr_ptr: *mut u64 = __ap_boot_stack_array_pointer as usize as *mut u64;
log::debug!(
"__ap_boot_stack_array_pointer: {:#x?}",
ap_boot_stack_arr_ptr
);
unsafe {
ap_boot_stack_arr_ptr.write_volatile(paddr_to_vaddr(pages.start_paddr()) as u64);
}
}
extern "C" {
fn __ap_boot_start();
fn __ap_boot_end();
}
fn send_boot_ipis() {
send_init_to_all_aps();
spin_wait_cycles(100_000_000);
send_init_deassert();
spin_wait_cycles(20_000_000);
send_startup_to_all_aps();
spin_wait_cycles(20_000_000);
send_startup_to_all_aps();
spin_wait_cycles(20_000_000);
}
fn send_startup_to_all_aps() {
let icr = Icr::new(
ApicId::from(0),
DestinationShorthand::AllExcludingSelf,
TriggerMode::Edge,
Level::Assert,
DeliveryStatus::Idle,
DestinationMode::Physical,
DeliveryMode::StartUp,
(AP_BOOT_START_PA / PAGE_SIZE) as u8,
);
apic::with_borrow(|apic| unsafe { apic.send_ipi(icr) });
}
fn send_init_to_all_aps() {
let icr = Icr::new(
ApicId::from(0),
DestinationShorthand::AllExcludingSelf,
TriggerMode::Level,
Level::Assert,
DeliveryStatus::Idle,
DestinationMode::Physical,
DeliveryMode::Init,
0,
);
apic::with_borrow(|apic| unsafe { apic.send_ipi(icr) });
}
fn send_init_deassert() {
let icr = Icr::new(
ApicId::from(0),
DestinationShorthand::AllIncludingSelf,
TriggerMode::Level,
Level::Deassert,
DeliveryStatus::Idle,
DestinationMode::Physical,
DeliveryMode::Init,
0,
);
apic::with_borrow(|apic| unsafe { apic.send_ipi(icr) });
}
fn spin_wait_cycles(c: u64) {
fn duration(from: u64, to: u64) -> u64 {
if to >= from {
to - from
} else {
u64::MAX - from + to
}
}
use core::arch::x86_64::_rdtsc;
let start = unsafe { _rdtsc() };
while duration(start, unsafe { _rdtsc() }) < c {
core::hint::spin_loop();
}
}