Эх сурвалжийг харах

feat: initialize ap cpus to a usable state

greatbridf 3 долоо хоног өмнө
parent
commit
4d1ab3b719

+ 1 - 1
arch/x86_64/src/gdt.rs

@@ -72,7 +72,7 @@ impl GDT {
     }
 
     pub fn set_tls32(&mut self, desc: GDTEntry) {
-        self.0[7] = desc;
+        self.0[Self::TLS32_INDEX] = desc;
     }
 
     pub unsafe fn load(&self) {

+ 0 - 1
src/driver.rs

@@ -1,7 +1,6 @@
 pub mod ahci;
 pub mod e1000e;
 pub mod serial;
-pub mod timer;
 
 // TODO!!!: Put it somewhere else.
 pub struct Port8 {

+ 0 - 16
src/driver/timer.rs

@@ -1,16 +0,0 @@
-use super::Port8;
-
-const COUNT: Port8 = Port8::new(0x40);
-const CONTROL: Port8 = Port8::new(0x43);
-
-pub fn init() {
-    arch::interrupt::disable();
-    // Set interval
-    CONTROL.write(0x34);
-
-    // Send interval number
-    // 0x2e9a = 11930 = 100Hz
-    COUNT.write(0x9a);
-    COUNT.write(0x2e);
-    arch::interrupt::enable();
-}

+ 1 - 0
src/kernel/arch/x86_64.rs

@@ -1,4 +1,5 @@
 pub mod init;
+pub mod interrupt;
 
 use arch::x86_64::{gdt::GDT, task::TSS};
 

+ 31 - 34
src/kernel/arch/x86_64/init.rs

@@ -1,23 +1,18 @@
-use super::{GDT_OBJECT, TSS_OBJECT};
+use super::{interrupt::setup_idt, GDT_OBJECT, TSS_OBJECT};
 use crate::{
     kernel::{
-        mem::{
-            paging::Page,
-            phys::{CachedPP, PhysPtr as _},
-        },
+        arch::interrupt::APIC_BASE,
+        mem::{paging::Page, phys::PhysPtr as _},
         smp,
     },
     println_debug, println_info,
     sync::preempt,
 };
 use arch::{
-    task::{pause, rdmsr},
+    task::pause,
     x86_64::{gdt::GDT, task::TSS},
 };
-use core::{
-    arch::asm,
-    sync::atomic::{AtomicU32, AtomicUsize, Ordering},
-};
+use core::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
 
 unsafe fn init_gdt_tss_thiscpu() {
     preempt::disable();
@@ -41,12 +36,31 @@ unsafe fn init_gdt_tss_thiscpu() {
     preempt::enable();
 }
 
+/// Initialization routine for all CPUs.
 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();
+
+    setup_idt();
+
+    APIC_BASE.spurious().write(0x1ff);
+    APIC_BASE.task_priority().write(0);
+    APIC_BASE.timer_divide().write(0x3); // Divide by 16
+    APIC_BASE.timer_register().write(0x20040);
+
+    // TODO: Get the bus frequency from...?
+    let freq = 800;
+    let count = freq * 1_000_000 / 16 / 100;
+    APIC_BASE.timer_initial_count().write(count as u32);
+
+    let cpu = CPU_COUNT.fetch_add(1, Ordering::Relaxed);
+    if cpu != 0 {
+        // Application processor
+        println_debug!("AP{} started", cpu);
+    }
 }
 
 #[no_mangle]
@@ -54,15 +68,11 @@ 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);
+pub static CPU_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.
 
@@ -72,32 +82,19 @@ pub unsafe extern "C" fn ap_entry(_stack_start: u64) {
 }
 
 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>();
+    let icr = APIC_BASE.interrupt_command();
 
-    println_debug!("SPURIOUS: {:#x}", unsafe { spurious.read() });
-
-    unsafe { icr.write_volatile(0xc4500) };
-
-    while unsafe { icr.read_volatile() } & 0x1000 != 0 {
+    icr.write(0xc4500);
+    while icr.read() & 0x1000 != 0 {
         pause();
     }
 
-    unsafe { icr.write_volatile(0xc4601) };
-
-    while unsafe { icr.read_volatile() } & 0x1000 != 0 {
+    icr.write(0xc4601);
+    while icr.read() & 0x1000 != 0 {
         pause();
     }
 
-    while AP_COUNT.load(Ordering::Acquire) != 3 {
+    while CPU_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;

+ 129 - 0
src/kernel/arch/x86_64/interrupt.rs

@@ -0,0 +1,129 @@
+use crate::kernel::mem::phys::{CachedPP, PhysPtr as _};
+use arch::task::rdmsr;
+use lazy_static::lazy_static;
+
+extern "C" {
+    static ISR_START_ADDR: usize;
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+struct IDTEntry {
+    offset_low: u16,
+    selector: u16,
+
+    interrupt_stack: u8,
+    attributes: u8,
+
+    offset_mid: u16,
+    offset_high: u32,
+    reserved: u32,
+}
+
+impl IDTEntry {
+    const fn new(offset: usize, selector: u16, attributes: u8) -> Self {
+        Self {
+            offset_low: offset as u16,
+            selector,
+            interrupt_stack: 0,
+            attributes,
+            offset_mid: (offset >> 16) as u16,
+            offset_high: (offset >> 32) as u32,
+            reserved: 0,
+        }
+    }
+
+    const fn null() -> Self {
+        Self {
+            offset_low: 0,
+            selector: 0,
+            interrupt_stack: 0,
+            attributes: 0,
+            offset_mid: 0,
+            offset_high: 0,
+            reserved: 0,
+        }
+    }
+}
+
+pub struct APICReg(*mut u32);
+pub struct APICRegs {
+    base: CachedPP,
+}
+
+impl APICReg {
+    fn new(pointer: *mut u32) -> Self {
+        Self(pointer)
+    }
+
+    pub fn read(&self) -> u32 {
+        unsafe { self.0.read_volatile() }
+    }
+
+    pub fn write(&self, value: u32) {
+        unsafe { self.0.write_volatile(value) }
+    }
+}
+
+impl APICRegs {
+    pub fn spurious(&self) -> APICReg {
+        APICReg::new(self.base.offset(0xf0).as_ptr())
+    }
+
+    pub fn task_priority(&self) -> APICReg {
+        APICReg::new(self.base.offset(0x80).as_ptr())
+    }
+
+    pub fn end_of_interrupt(&self) {
+        APICReg::new(self.base.offset(0xb0).as_ptr()).write(0)
+    }
+
+    pub fn interrupt_command(&self) -> APICReg {
+        APICReg::new(self.base.offset(0x300).as_ptr())
+    }
+
+    pub fn timer_register(&self) -> APICReg {
+        APICReg::new(self.base.offset(0x320).as_ptr())
+    }
+
+    pub fn timer_initial_count(&self) -> APICReg {
+        APICReg::new(self.base.offset(0x380).as_ptr())
+    }
+
+    pub fn timer_current_count(&self) -> APICReg {
+        APICReg::new(self.base.offset(0x390).as_ptr())
+    }
+
+    pub fn timer_divide(&self) -> APICReg {
+        APICReg::new(self.base.offset(0x3e0).as_ptr())
+    }
+}
+
+lazy_static! {
+    static ref IDT: [IDTEntry; 256] = core::array::from_fn(|idx| match idx {
+        0..0x80 => IDTEntry::new(unsafe { ISR_START_ADDR } + 8 * idx, 0x08, 0x8e),
+        0x80 => IDTEntry::new(unsafe { ISR_START_ADDR } + 8 * idx, 0x08, 0xee),
+        _ => IDTEntry::null(),
+    });
+    pub static ref APIC_BASE: APICRegs = {
+        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;
+        APICRegs {
+            base: CachedPP::new(apic_base as usize),
+        }
+    };
+}
+
+pub fn setup_idt() {
+    arch::x86_64::interrupt::lidt(
+        IDT.as_ptr() as usize,
+        (size_of::<IDTEntry>() * 256 - 1) as u16,
+    );
+}
+
+pub fn end_of_interrupt() {
+    APIC_BASE.end_of_interrupt()
+}

+ 4 - 56
src/kernel/interrupt.rs

@@ -8,66 +8,16 @@ use crate::{driver::Port8, prelude::*};
 use super::mem::handle_page_fault;
 use super::syscall::handle_syscall32;
 use super::task::{ProcessList, Signal};
+use super::timer::timer_interrupt;
 
 const PIC1_COMMAND: Port8 = Port8::new(0x20);
 const PIC1_DATA: Port8 = Port8::new(0x21);
 const PIC2_COMMAND: Port8 = Port8::new(0xA0);
 const PIC2_DATA: Port8 = Port8::new(0xA1);
 
-#[repr(C)]
-#[derive(Clone, Copy)]
-struct IDTEntry {
-    offset_low: u16,
-    selector: u16,
-
-    interrupt_stack: u8,
-    attributes: u8,
-
-    offset_mid: u16,
-    offset_high: u32,
-    reserved: u32,
-}
-
-extern "C" {
-    static ISR_START_ADDR: usize;
-}
-
 lazy_static! {
     static ref IRQ_HANDLERS: Spin<[Option<Arc<dyn Fn() + Send + Sync>>; 16]> =
         Spin::new([const { None }; 16]);
-    static ref IDT: [IDTEntry; 256] = core::array::from_fn(|idx| {
-        match idx {
-            0..0x80 => IDTEntry::new(unsafe { ISR_START_ADDR } + 8 * idx, 0x08, 0x8e),
-            0x80 => IDTEntry::new(unsafe { ISR_START_ADDR } + 8 * idx, 0x08, 0xee),
-            _ => IDTEntry::null(),
-        }
-    });
-}
-
-impl IDTEntry {
-    const fn new(offset: usize, selector: u16, attributes: u8) -> Self {
-        Self {
-            offset_low: offset as u16,
-            selector,
-            interrupt_stack: 0,
-            attributes,
-            offset_mid: (offset >> 16) as u16,
-            offset_high: (offset >> 32) as u32,
-            reserved: 0,
-        }
-    }
-
-    const fn null() -> Self {
-        Self {
-            offset_low: 0,
-            selector: 0,
-            interrupt_stack: 0,
-            attributes: 0,
-            offset_mid: 0,
-            offset_high: 0,
-            reserved: 0,
-        }
-    }
 }
 
 fn irq_handler(irqno: usize) {
@@ -104,6 +54,8 @@ pub extern "C" fn interrupt_handler(int_stack: *mut interrupt_stack, mmxregs: *m
         0..0x20 => fault_handler(int_stack),
         // Syscall
         0x80 => handle_syscall32(int_stack.regs.rax as usize, int_stack, mmxregs),
+        // Timer
+        0x40 => timer_interrupt(),
         // IRQ
         no => irq_handler(no as usize - 0x20),
     }
@@ -123,11 +75,7 @@ where
 }
 
 pub fn init() -> KResult<()> {
-    arch::x86_64::interrupt::lidt(
-        IDT.as_ptr() as usize,
-        (size_of::<IDTEntry>() * 256 - 1) as u16,
-    );
-
+    // TODO: Move this to `arch`
     // Initialize PIC
     PIC1_COMMAND.write(0x11); // edge trigger mode
     PIC1_DATA.write(0x20); // IRQ 0-7 offset

+ 6 - 7
src/kernel/timer.rs

@@ -1,8 +1,8 @@
 use core::sync::atomic::{AtomicUsize, Ordering};
 
-use crate::{prelude::*, sync::preempt};
+use crate::{println_debug, sync::preempt};
 
-use super::{interrupt::register_irq_handler, task::Scheduler};
+use super::{arch::interrupt::end_of_interrupt, task::Scheduler};
 
 static TICKS: AtomicUsize = AtomicUsize::new(0);
 
@@ -26,19 +26,18 @@ impl Ticks {
     }
 }
 
-fn timer_interrupt() {
+pub fn timer_interrupt() {
     TICKS.fetch_add(1, Ordering::Relaxed);
     if preempt::count() == 0 {
         // To make scheduler satisfied.
         preempt::disable();
+        end_of_interrupt();
         Scheduler::schedule();
+    } else {
+        end_of_interrupt();
     }
 }
 
 pub fn ticks() -> Ticks {
     Ticks(TICKS.load(Ordering::Relaxed))
 }
-
-pub fn init() -> KResult<()> {
-    register_irq_handler(0, timer_interrupt)
-}

+ 0 - 5
src/lib.rs

@@ -150,11 +150,6 @@ extern "C" fn init_process(early_kstack_pfn: usize) {
     unsafe { Page::take_pfn(early_kstack_pfn, 9) };
     preempt::enable();
 
-    kernel::timer::init().unwrap();
-
-    // Use the PIT timer for now.
-    driver::timer::init();
-
     kernel::syscall::register_syscalls();
     CharDevice::init().unwrap();