123 lines
3.4 KiB
Rust

//! x86-64 multiprocessing implementation
use core::sync::atomic::{AtomicUsize, Ordering};
use acpi_lib::platform::{ProcessorInfo, ProcessorState};
use crate::{
arch::{
x86_64::{
boot::__x86_64_ap_entry,
intrinsics::flush_tlb_entry,
mem::{
table::{PageAttributes, PageEntry, PageTable, L1, L2},
KERNEL_TABLES,
},
},
Architecture, ArchitectureImpl,
},
mem::{
address::{AsPhysicalAddress, FromRaw, IntoRaw},
phys,
pointer::PhysicalRefMut,
PhysicalAddress,
},
task::Cpu,
};
use super::acpi::AcpiAllocator;
/// The number of CPUs present in the system
pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(1);
static AP_BOOTSTRAP_BIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/__x86_64_ap_boot.bin"));
const AP_STACK_PAGES: usize = 8;
const AP_BOOTSTRAP_DATA: PhysicalAddress = PhysicalAddress::from_raw(0x6000usize);
const AP_BOOTSTRAP_CODE: PhysicalAddress = PhysicalAddress::from_raw(0x7000usize);
#[repr(C)]
#[allow(dead_code)]
struct ApBootstrapData {
cr3: PhysicalAddress,
stack_base: usize,
stack_size: usize,
entry: usize,
}
unsafe fn start_ap_core(apic_id: u32) {
assert!(ArchitectureImpl::interrupt_mask());
let bsp_cpu = Cpu::local();
let bsp_apic = bsp_cpu.local_apic();
let cr3 = KERNEL_TABLES.as_physical_address();
let stack_base = phys::alloc_pages_contiguous(AP_STACK_PAGES)
.unwrap()
.virtualize_raw();
let stack_size = AP_STACK_PAGES * 0x1000;
let data = ApBootstrapData {
cr3,
stack_base,
stack_size,
entry: __x86_64_ap_entry as usize,
};
let mut data_ref = PhysicalRefMut::<ApBootstrapData>::map(AP_BOOTSTRAP_DATA);
*data_ref = data;
let cpu_count = CPU_COUNT.load(Ordering::Acquire);
// Send an IPI to wake up the AP
core::arch::asm!("wbinvd");
bsp_apic.wakeup_cpu(apic_id, AP_BOOTSTRAP_CODE);
while cpu_count == CPU_COUNT.load(Ordering::Acquire) {
core::hint::spin_loop();
}
infoln!("cpu{} up", cpu_count);
}
/// Starts up application processors specified by ACPI MADT.
///
/// # Safety
///
/// Only meant to be called once by the BSP.
pub unsafe fn start_ap_cores(info: &ProcessorInfo<AcpiAllocator>) {
let aps = &info.application_processors;
if aps.is_empty() {
return;
}
// Temporarily identity-map the lowest 2MiB
let mut identity_l1 = PageTable::<L1>::new_zeroed().unwrap();
let mut identity_l2 = PageTable::<L2>::new_zeroed().unwrap();
identity_l1[0] =
PageEntry::<L1>::table(identity_l2.as_physical_address(), PageAttributes::WRITABLE);
identity_l2[0] = PageEntry::<L2>::block(PhysicalAddress::ZERO, PageAttributes::WRITABLE);
assert_eq!(KERNEL_TABLES.l0.data[0], 0);
KERNEL_TABLES.l0.data[0] = IntoRaw::<u64>::into_raw(identity_l1.as_physical_address())
| (PageAttributes::WRITABLE | PageAttributes::PRESENT).bits();
// Load AP_BOOTSTRAP_CODE
let mut code_ref = PhysicalRefMut::map_slice(AP_BOOTSTRAP_CODE, AP_BOOTSTRAP_BIN.len());
code_ref.copy_from_slice(AP_BOOTSTRAP_BIN);
for ap in aps.iter() {
if ap.is_ap && ap.state == ProcessorState::WaitingForSipi {
start_ap_core(ap.local_apic_id);
}
}
// Remove the identity-map
identity_l2[0] = PageEntry::INVALID;
flush_tlb_entry(0);
KERNEL_TABLES.l0.data[0] = 0;
// TODO drop the tables
}