| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- use core::{
- alloc::Layout,
- pin::Pin,
- ptr::{addr_of, NonNull},
- };
- use super::{enable_sse, percpu::init_percpu_area_thiscpu, GDTEntry, InterruptControl, GDT};
- #[repr(C)]
- #[derive(Debug, Clone, Copy)]
- #[allow(non_camel_case_types)]
- struct TSS_SP {
- low: u32,
- high: u32,
- }
- #[repr(C)]
- pub struct TSS {
- _reserved1: u32,
- rsp: [TSS_SP; 3],
- _reserved2: u32,
- _reserved3: u32,
- ist: [TSS_SP; 7],
- _reserved4: u32,
- _reserved5: u32,
- _reserved6: u16,
- iomap_base: u16,
- }
- impl TSS {
- pub fn new() -> Self {
- Self {
- _reserved1: 0,
- rsp: [TSS_SP { low: 0, high: 0 }; 3],
- _reserved2: 0,
- _reserved3: 0,
- ist: [TSS_SP { low: 0, high: 0 }; 7],
- _reserved4: 0,
- _reserved5: 0,
- _reserved6: 0,
- iomap_base: 0,
- }
- }
- pub fn set_rsp0(&mut self, rsp: u64) {
- self.rsp[0].low = rsp as u32;
- self.rsp[0].high = (rsp >> 32) as u32;
- }
- }
- /// Architecture-specific per-cpu status.
- #[allow(dead_code)]
- pub struct CPUStatus {
- id: usize,
- gdt: GDT,
- tss: TSS,
- percpu_area: NonNull<u8>,
- pub interrupt: InterruptControl,
- }
- impl CPUStatus {
- pub unsafe fn new_thiscpu<F>(allocate: F) -> Self
- where
- F: FnOnce(Layout) -> NonNull<u8>,
- {
- const PAGE_SIZE: usize = 0x1000;
- extern "C" {
- static PERCPU_PAGES: usize;
- fn _PERCPU_DATA_START();
- }
- let percpu_area = allocate(Layout::from_size_align_unchecked(
- PERCPU_PAGES * PAGE_SIZE,
- PAGE_SIZE,
- ));
- percpu_area.copy_from_nonoverlapping(
- NonNull::new(_PERCPU_DATA_START as *mut u8).unwrap(),
- PERCPU_PAGES * PAGE_SIZE,
- );
- let (interrupt_control, cpuid) = InterruptControl::new();
- init_percpu_area_thiscpu(percpu_area);
- Self {
- id: cpuid,
- gdt: GDT::new(),
- tss: TSS::new(),
- percpu_area,
- interrupt: interrupt_control,
- }
- }
- /// Load GDT and TSS in place.
- ///
- /// # Safety
- /// Make sure preemption and interrupt are disabled before calling this function.
- pub unsafe fn init(self: Pin<&mut Self>) {
- enable_sse();
- // SAFETY: We don't move the object.
- let self_mut = self.get_unchecked_mut();
- let tss_addr = addr_of!(self_mut.tss);
- self_mut.gdt.set_tss(tss_addr as u64);
- self_mut.gdt.load();
- // SAFETY: `self` is pinned, so are its fields.
- Pin::new_unchecked(&mut self_mut.interrupt).setup_idt();
- self_mut.interrupt.setup_timer();
- }
- /// Bootstrap all CPUs.
- /// This should only be called on the BSP.
- pub unsafe fn bootstrap_cpus(&self) {
- self.interrupt.send_sipi();
- }
- pub unsafe fn set_rsp0(&mut self, rsp: u64) {
- self.tss.set_rsp0(rsp);
- }
- pub unsafe fn set_tls32(&mut self, desc: GDTEntry) {
- self.gdt.set_tls32(desc);
- }
- pub fn cpuid(&self) -> usize {
- self.id
- }
- }
- #[macro_export]
- macro_rules! define_smp_bootstrap {
- ($cpu_count:literal, $ap_entry:ident, $alloc_kstack:tt) => {
- #[no_mangle]
- static BOOT_SEMAPHORE: core::sync::atomic::AtomicU64 =
- core::sync::atomic::AtomicU64::new(0);
- #[no_mangle]
- static BOOT_STACK: core::sync::atomic::AtomicU64 =
- core::sync::atomic::AtomicU64::new(0);
- #[no_mangle]
- static CPU_COUNT: core::sync::atomic::AtomicU64 =
- core::sync::atomic::AtomicU64::new(0);
- core::arch::global_asm!(
- r#"
- .pushsection .stage1.smp
- .code16
- .globl ap_bootstrap
- .type ap_bootstrap, @function
- ap_bootstrap:
- ljmp $0x0, $.Lap1
- .Lap1:
- # we use the shared gdt for cpu bootstrapping
- lgdt .Lshared_gdt_desc
- # set msr
- mov $0xc0000080, %ecx
- rdmsr
- or $0x901, %eax # set LME, NXE, SCE
- wrmsr
- # set cr4
- mov %cr4, %eax
- or $0xa0, %eax # set PAE, PGE
- mov %eax, %cr4
- # load new page table
- mov ${KERNEL_PML4}, %eax
- mov %eax, %cr3
- mov %cr0, %eax
- // SET PE, WP, PG
- or $0x80010001, %eax
- mov %eax, %cr0
- ljmp $0x08, $.Lap_bootstrap_end
- .align 16
- .Lshared_gdt_desc:
- .8byte 0x0000000000005f
- .code64
- .Lap_bootstrap_end:
- mov $0x10, %ax
- mov %ax, %ds
- mov %ax, %es
- mov %ax, %ss
- xor %rsp, %rsp
- xor %rax, %rax
- inc %rax
- 1:
- xchg %rax, {BOOT_SEMAPHORE}
- cmp $0, %rax
- je 1f
- pause
- jmp 1b
- 1:
- mov {BOOT_STACK}, %rsp # Acquire
- cmp $0, %rsp
- jne 1f
- pause
- jmp 1b
- 1:
- xor %rax, %rax
- mov %rax, {BOOT_STACK} # Release
- xchg %rax, {BOOT_SEMAPHORE}
- lock incq {CPU_COUNT}
- xor %rbp, %rbp
- push %rbp # NULL return address
- jmp {AP_ENTRY}
- .popsection
- "#,
- KERNEL_PML4 = const 0x2000,
- BOOT_SEMAPHORE = sym BOOT_SEMAPHORE,
- BOOT_STACK = sym BOOT_STACK,
- CPU_COUNT = sym CPU_COUNT,
- AP_ENTRY = sym $ap_entry,
- options(att_syntax),
- );
- pub unsafe fn wait_cpus_online() {
- use core::sync::atomic::Ordering;
- while CPU_COUNT.load(Ordering::Acquire) != $cpu_count - 1 {
- if BOOT_STACK.load(Ordering::Acquire) == 0 {
- let stack_bottom = $alloc_kstack as u64;
- BOOT_STACK.store(stack_bottom, Ordering::Release);
- }
- $crate::pause();
- }
- }
- };
- }
|