Browse Source

feat: bootstrap cpus, assign a stack to each of them

greatbridf 3 weeks ago
parent
commit
fab09e80c7
7 changed files with 174 additions and 97 deletions
  1. 17 0
      arch/x86_64/src/io.rs
  2. 28 9
      src/boot.s
  3. 8 35
      src/kernel/arch/x86_64.rs
  4. 112 0
      src/kernel/arch/x86_64/init.rs
  5. 7 0
      src/kernel/smp.rs
  6. 0 16
      src/kinit.cpp
  7. 2 37
      src/lib.rs

+ 17 - 0
arch/x86_64/src/io.rs

@@ -1,5 +1,22 @@
 use core::arch::asm;
 
+pub fn enable_sse() {
+    unsafe {
+        asm!(
+            "mov %cr0, %rax",
+            "and $(~0xc), %rax",
+            "or $0x22, %rax",
+            "mov %rax, %cr0",
+            "mov %cr4, %rax",
+            "or $0x600, %rax",
+            "mov %rax, %cr4",
+            "fninit",
+            out("rax") _,
+            options(att_syntax, nomem, nostack)
+        )
+    }
+}
+
 pub fn inb(no: u16) -> u8 {
     let data;
     unsafe {

+ 28 - 9
src/boot.s

@@ -367,12 +367,31 @@ shared_gdt_desc:
 	mov %ax, %es
 	mov %ax, %ss
 
-	lock incl boot_semaphore
-	jmp .
-
-.section .bss
-.align 4
-.globl boot_semaphore
-.type boot_semaphore, @object
-boot_semaphore:
-	.long 0
+	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
+
+	xor %rbp, %rbp
+	mov %rsp, %rdi # stack area start address as the first argument
+
+	add $0x200000, %rsp # kernel stack order 9
+	push %rbp # NULL return address
+	jmp ap_entry

+ 8 - 35
src/kernel/arch/x86_64.rs

@@ -1,3 +1,5 @@
+pub mod init;
+
 use arch::x86_64::{gdt::GDT, task::TSS};
 
 // TODO!!!: This can be stored in the percpu area.
@@ -9,40 +11,6 @@ static GDT_OBJECT: Option<GDT> = None;
 #[arch::define_percpu]
 static TSS_OBJECT: Option<TSS> = None;
 
-pub mod init {
-    use super::{GDT_OBJECT, TSS_OBJECT};
-    use crate::{kernel::smp, sync::preempt};
-    use arch::x86_64::{gdt::GDT, task::TSS};
-
-    unsafe fn init_gdt_tss_thiscpu() {
-        preempt::disable();
-        let gdt_ref = unsafe { GDT_OBJECT.as_mut() };
-        let tss_ref = unsafe { TSS_OBJECT.as_mut() };
-        *gdt_ref = Some(GDT::new());
-        *tss_ref = Some(TSS::new());
-
-        if let Some(gdt) = gdt_ref.as_mut() {
-            if let Some(tss) = tss_ref.as_mut() {
-                gdt.set_tss(tss as *mut _ as u64);
-            } else {
-                panic!("TSS is not initialized");
-            }
-
-            unsafe { gdt.load() };
-        } else {
-            panic!("GDT is not initialized");
-        }
-
-        preempt::enable();
-    }
-
-    pub unsafe fn init_bscpu() {
-        let area = smp::alloc_percpu_area();
-        smp::set_percpu_area(area);
-        init_gdt_tss_thiscpu();
-    }
-}
-
 pub mod user {
     use crate::sync::preempt;
     use arch::x86_64::gdt::GDTEntry;
@@ -51,8 +19,13 @@ pub mod user {
 
     #[derive(Debug, Clone)]
     pub enum TLS {
+        /// TODO: This is not used yet.
+        #[allow(dead_code)]
         TLS64(u64),
-        TLS32 { base: u64, desc: GDTEntry },
+        TLS32 {
+            base: u64,
+            desc: GDTEntry,
+        },
     }
 
     impl TLS {

+ 112 - 0
src/kernel/arch/x86_64/init.rs

@@ -0,0 +1,112 @@
+use super::{GDT_OBJECT, TSS_OBJECT};
+use crate::{
+    kernel::{
+        mem::{
+            paging::Page,
+            phys::{CachedPP, PhysPtr as _},
+        },
+        smp,
+    },
+    println_debug, println_info,
+    sync::preempt,
+};
+use arch::{
+    task::{pause, rdmsr},
+    x86_64::{gdt::GDT, task::TSS},
+};
+use core::{
+    arch::asm,
+    sync::atomic::{AtomicU32, AtomicUsize, Ordering},
+};
+
+unsafe fn init_gdt_tss_thiscpu() {
+    preempt::disable();
+    let gdt_ref = unsafe { GDT_OBJECT.as_mut() };
+    let tss_ref = unsafe { TSS_OBJECT.as_mut() };
+    *gdt_ref = Some(GDT::new());
+    *tss_ref = Some(TSS::new());
+
+    if let Some(gdt) = gdt_ref.as_mut() {
+        if let Some(tss) = tss_ref.as_mut() {
+            gdt.set_tss(tss as *mut _ as u64);
+        } else {
+            panic!("TSS is not initialized");
+        }
+
+        unsafe { gdt.load() };
+    } else {
+        panic!("GDT is not initialized");
+    }
+
+    preempt::enable();
+}
+
+pub unsafe fn init_cpu() {
+    arch::x86_64::io::enable_sse();
+
+    let area = smp::alloc_percpu_area();
+    smp::set_percpu_area(area);
+    init_gdt_tss_thiscpu();
+}
+
+#[no_mangle]
+pub static BOOT_SEMAPHORE: AtomicU32 = AtomicU32::new(0);
+#[no_mangle]
+pub static BOOT_STACK: AtomicUsize = AtomicUsize::new(0);
+
+pub static AP_COUNT: AtomicUsize = AtomicUsize::new(0);
+
+#[no_mangle]
+pub unsafe extern "C" fn ap_entry(_stack_start: u64) {
+    init_cpu();
+    let cpuid = AP_COUNT.fetch_add(1, Ordering::Release);
+    println_debug!("AP{} started", cpuid);
+
+    // TODO!!!!!: Set up LAPIC and timer.
+
+    // TODO!!!!!: Set up idle task.
+
+    // TODO!!!!!: Free the stack before switching to idle task.
+
+    loop {}
+}
+
+pub unsafe fn bootstrap_cpus() {
+    let apic_base = rdmsr(0x1b);
+    assert_eq!(apic_base & 0x800, 0x800, "LAPIC not enabled");
+    assert_eq!(apic_base & 0x100, 0x100, "Is not bootstrap processor");
+
+    let apic_base = apic_base & !0xfff;
+    println_debug!("IA32_APIC_BASE: {apic_base:#x}");
+
+    let apic_base = CachedPP::new(apic_base as usize);
+    let spurious = apic_base.offset(0xf0).as_ptr::<u32>();
+    let icr = apic_base.offset(0x300).as_ptr::<u32>();
+
+    println_debug!("SPURIOUS: {:#x}", unsafe { spurious.read() });
+
+    unsafe { icr.write_volatile(0xc4500) };
+
+    while unsafe { icr.read_volatile() } & 0x1000 != 0 {
+        pause();
+    }
+
+    unsafe { icr.write_volatile(0xc4601) };
+
+    while unsafe { icr.read_volatile() } & 0x1000 != 0 {
+        pause();
+    }
+
+    while AP_COUNT.load(Ordering::Acquire) != 3 {
+        if BOOT_STACK.load(Ordering::Acquire) == 0 {
+            let page = Page::alloc_many(9);
+            let stack_start = page.as_cached().as_ptr::<()>() as usize;
+            core::mem::forget(page);
+
+            BOOT_STACK.store(stack_start, Ordering::Release);
+        }
+        pause();
+    }
+
+    println_info!("Processors startup finished");
+}

+ 7 - 0
src/kernel/smp.rs

@@ -1,3 +1,10 @@
 mod percpu;
 
 pub use percpu::{alloc_percpu_area, set_percpu_area};
+
+pub unsafe fn bootstrap_smp() {
+    #[cfg(feature = "smp")]
+    {
+        super::arch::init::bootstrap_cpus();
+    }
+}

+ 0 - 16
src/kinit.cpp

@@ -32,20 +32,6 @@ struct PACKED bootloader_data {
 
 namespace kernel::kinit {
 
-static inline void enable_sse() {
-    asm volatile(
-        "mov %%cr0, %%rax\n\t"
-        "and $(~0xc), %%rax\n\t"
-        "or $0x22, %%rax\n\t"
-        "mov %%rax, %%cr0\n\t"
-        "\n\t"
-        "mov %%cr4, %%rax\n\t"
-        "or $0x600, %%rax\n\t"
-        "mov %%rax, %%cr4\n\t"
-        "fninit\n\t" ::
-            : "rax");
-}
-
 static inline void setup_early_kernel_page_table() {
     using namespace kernel::mem::paging;
 
@@ -132,8 +118,6 @@ static inline void save_memory_info(bootloader_data* data) {
 extern "C" void rust_kinit(uintptr_t early_kstack_vaddr);
 
 extern "C" void NORETURN kernel_init(bootloader_data* data) {
-    enable_sse();
-
     setup_early_kernel_page_table();
     save_memory_info(data);
 

+ 2 - 37
src/lib.rs

@@ -23,11 +23,9 @@ mod rcu;
 mod sync;
 
 use alloc::ffi::CString;
-use arch::task::rdmsr;
 use core::{
     alloc::{GlobalAlloc, Layout},
     arch::{asm, global_asm},
-    sync::atomic::AtomicU32,
 };
 use elf::ParsedElf32;
 use kernel::{
@@ -108,46 +106,13 @@ global_asm!(
 
 extern "C" {
     fn to_init_process();
-    fn boot_semaphore();
-}
-
-fn bootstrap_cpus() {
-    let apic_base = rdmsr(0x1b);
-    assert_eq!(apic_base & 0x800, 0x800, "LAPIC not enabled");
-    assert_eq!(apic_base & 0x100, 0x100, "Is not bootstrap processor");
-
-    let apic_base = apic_base & !0xfff;
-    println_debug!("IA32_APIC_BASE: {apic_base:#x}");
-
-    let apic_base = CachedPP::new(apic_base as usize);
-    let spurious = apic_base.offset(0xf0).as_ptr::<u32>();
-    let icr = apic_base.offset(0x300).as_ptr::<u32>();
-
-    println_debug!("SPURIOUS: {:#x}", unsafe { spurious.read() });
-
-    unsafe { icr.write_volatile(0xc4500) };
-
-    while unsafe { icr.read_volatile() } & 0x1000 != 0 {
-        unsafe { asm!("pause") };
-    }
-
-    unsafe { icr.write_volatile(0xc4601) };
-
-    while unsafe { icr.read_volatile() } & 0x1000 != 0 {
-        unsafe { asm!("pause") };
-    }
-
-    let sem = unsafe { AtomicU32::from_ptr(boot_semaphore as *mut _) };
-    while sem.load(core::sync::atomic::Ordering::Acquire) != 3 {}
-
-    println_info!("Processors startup finished");
 }
 
 #[no_mangle]
 pub extern "C" fn rust_kinit(early_kstack_pfn: usize) -> ! {
     // We don't call global constructors.
     // Rust doesn't need that, and we're not going to use global variables in C++.
-    unsafe { kernel::arch::init::init_bscpu() };
+    unsafe { kernel::arch::init::init_cpu() };
 
     kernel::interrupt::init().unwrap();
 
@@ -202,7 +167,7 @@ extern "C" fn init_process(early_kstack_pfn: usize) {
     fs::procfs::init();
     fs::fat32::init();
 
-    bootstrap_cpus();
+    unsafe { kernel::smp::bootstrap_smp() };
 
     let (ip, sp) = {
         // mount fat32 /mnt directory