123 lines
3.4 KiB
Rust
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
|
|
}
|