summaryrefslogtreecommitdiff
path: root/kernel/src/interrupts/apic.rs
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/src/interrupts/apic.rs')
-rw-r--r--kernel/src/interrupts/apic.rs67
1 files changed, 48 insertions, 19 deletions
diff --git a/kernel/src/interrupts/apic.rs b/kernel/src/interrupts/apic.rs
index 0e89943..836af20 100644
--- a/kernel/src/interrupts/apic.rs
+++ b/kernel/src/interrupts/apic.rs
@@ -1,8 +1,11 @@
+use core::sync::atomic::{AtomicU64, Ordering};
use x86_64::registers::model_specific::Msr;
#[cfg(target_arch = "x86_64")]
const APIC_FLAG: u64 = 0x0800;
const APIC_BASE_MSR: u32 = 0x001b;
+const CPU_COUNT: usize = 255;
+const APIC_BASE_ADDR: u64 = 0xfee0_0000;
// APIC constant register values
const APIC_DISABLE: u32 = 0x0001_0000;
@@ -20,8 +23,13 @@ pub fn is_apic() -> bool {
info & (1 << 9) != 0
}
-pub fn set_apic_base(v: u64) {
- unsafe { Msr::new(APIC_BASE_MSR).write(v) }
+pub fn cpu_id() -> u32 {
+ let mut info: u32;
+ unsafe { asm!("cpuid", inout("eax") 1 => _, out("ebx") info) }
+ if !is_x2apic() {
+ info >>= 24;
+ }
+ info
}
#[repr(C)]
@@ -75,27 +83,48 @@ pub enum TimerDivideConfig {
Div128 = 10,
NoDivide = 11,
}
+const NONE_APIC: Option<Apic> = None;
-static mut LOCAL_APIC: Option<Apic> = None;
+static mut APIC_INIT: [Option<Apic>; CPU_COUNT] = [NONE_APIC; CPU_COUNT];
-pub unsafe fn set_local_apic(apic: Apic) -> &'static mut Apic {
- LOCAL_APIC = Some(apic);
- LOCAL_APIC.as_mut().unwrap()
+pub fn move_local_apic(v: u64) -> bool {
+ match get_local_apic() {
+ None => return false,
+ Some(apic) => apic.regs.store(v, Ordering::Release),
+ }
+ unsafe { Msr::new(APIC_BASE_MSR).write(v) };
+ true
}
-pub unsafe fn get_local_apic() -> Option<&'static mut Apic> {
- LOCAL_APIC.as_mut()
+/// Tries to initialize the local apic at it's default location.
+/// returns false if no apic is available
+///
+/// # Safety
+/// Calling this function in a running system might cause the loss of interrupts or other weird
+/// behaviour
+pub unsafe fn try_initialize_local_apic() -> &'static mut Option<Apic> {
+ if is_apic() {
+ let cpu_id = cpu_id() as usize;
+ if APIC_INIT[cpu_id].is_none() {
+ APIC_INIT[cpu_id] = Some(Apic::from_base_apic(APIC_BASE_ADDR));
+ Apic::disable_pic();
+ }
+ APIC_INIT[cpu_id].as_mut().map(|x| x.initialize());
+ }
+ get_local_apic()
+}
+
+pub fn get_local_apic() -> &'static mut Option<Apic> {
+ let cpu_id = cpu_id() as usize;
+ unsafe { &mut APIC_INIT[cpu_id] }
}
pub struct Apic {
- regs: *mut [u32; 1024],
+ regs: AtomicU64,
}
impl Apic {
- pub fn new() -> Option<Self> {
- if !is_apic() {
- return None;
- }
+ fn initialize(&mut self) -> Self {
Self::disable_pic();
let base_apic = unsafe { Msr::new(APIC_BASE_MSR).read() };
let mut apic = Self::from_base_apic(base_apic);
@@ -118,28 +147,28 @@ impl Apic {
apic.set(ApicRegister::SpuriousInterruptVec, 0x1ff);
- Some(apic)
+ apic
}
- pub fn disable_pic() {
+ fn disable_pic() {
unsafe {
x86_64::instructions::port::PortWriteOnly::new(0xa1).write(0xffu8);
x86_64::instructions::port::PortWriteOnly::new(0x21).write(0xffu8);
}
}
- fn from_base_apic(base_apic: u64) -> Self {
+ const fn from_base_apic(base_apic: u64) -> Self {
Self {
- regs: ((base_apic) & 0xfffffffff << 12) as *mut [u32; 1024],
+ regs: AtomicU64::new((base_apic) & 0xfffffffff << 12),
}
}
pub fn ptr(&self) -> *const u32 {
- self.regs as *const u32
+ self.regs.load(Ordering::Acquire) as *const u32
}
fn ptr_mut(&mut self) -> *mut u32 {
- self.regs as *mut u32
+ self.regs.load(Ordering::SeqCst) as *mut u32
}
pub fn get(&self, register: ApicRegister) -> u32 {