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.rs121
1 files changed, 121 insertions, 0 deletions
diff --git a/kernel/src/interrupts/apic.rs b/kernel/src/interrupts/apic.rs
new file mode 100644
index 0000000..519caf2
--- /dev/null
+++ b/kernel/src/interrupts/apic.rs
@@ -0,0 +1,121 @@
+use x86_64::registers::model_specific::Msr;
+
+#[cfg(target_arch = "x86_64")]
+const APIC_FLAG: u64 = 0x0800;
+const APIC_BASE_MSR: u32 = 0x1b;
+
+pub fn is_x2apic() -> bool {
+ let info: u32;
+ unsafe {
+ asm!("cpuid" : "={ecx}" (info) : "{eax}" (1) : "memory");
+ }
+ info & (1 << 21) != 0
+}
+
+pub fn is_apic() -> bool {
+ let info: u32;
+ unsafe {
+ asm!("cpuid" : "={edx}" (info) : "{eax}" (1) : "memory");
+ }
+ info & (1 << 9) != 0
+}
+
+pub fn set_apic_base(v: u64) {
+ unsafe { Msr::new(APIC_BASE_MSR).write(v) }
+}
+
+#[repr(C)]
+pub struct InterruptCommand {
+ low: u32,
+ _rsv: [u32; 3],
+ high: u32
+}
+
+#[repr(isize)]
+pub enum ApicRegister {
+ ApicId=8,
+ ApicVer=12,
+ TaskPriority=32,
+ ArbitrationPriority=36,
+ ProcessorPriority=40,
+ EndOfInterrupt=44,
+ LogicalDst=52,
+ DstFmt=56,
+ SpuriousInterruptVec=60,
+ InService=64,
+ TriggerMode=96,
+ InterruptRequest=128,
+ ErrorStatus=160,
+ InterruptCommand=192,
+ InterruptCommandHigh=196,
+ LvtTimer=200,
+ LvtThermalSensor=204,
+ LvtPerformanceMonitor=208,
+ LvtLint1=212,
+ LvtError=220,
+ TimerInitialCount=224,
+ TimerCurrentCount=228,
+ TimerDivideConfig=248,
+ ExtApicFeature=256,
+ ExtApicControl=260,
+ SpecificEndOfInterrupt=264,
+ InterruptEnable=288,
+ ExtInterruptLocalVectorTable=320,
+}
+
+#[repr(C)]
+pub struct ApicRegisters {
+ regs32: *mut [u32; 1024],
+}
+
+impl ApicRegisters {
+ fn from_base_apic(base_apic: u64) -> Self {
+ Self { regs32:
+ ((base_apic >> 12) & 0xfffffffff) as *mut [u32; 1024]
+ }
+ }
+
+ fn ptr(&self) -> *const u32 { self.regs32 as *const u32 }
+ fn ptr_mut(&mut self) -> *mut u32 { self.regs32 as *mut u32 }
+
+ pub fn get(&self, register: ApicRegister) -> u32 {
+ unsafe { self.ptr().offset(register as isize).read_volatile() }
+ }
+
+ pub fn set(&mut self, register: ApicRegister, val: u32) {
+ unsafe { self.ptr_mut().offset(register as isize).write_volatile(val) }
+ }
+
+ pub fn set_interrupt_command(&mut self, dst: u8, v: u8) {
+ let (dst, v): (u32, u32) = (dst.into(), v.into());
+ self.set(ApicRegister::InterruptCommandHigh, dst << 24);
+ self.set(ApicRegister::InterruptCommand, v)
+ }
+}
+
+pub struct Apic {
+ base_apic: u64,
+ regs: ApicRegisters,
+}
+
+impl Apic {
+ pub fn new() -> Option<Self> {
+ use core::fmt::Write;
+ let mut stdout = crate::io::vga_text::OStream::new();
+ stdout.clear();
+
+ if !is_apic() { return None; }
+ let mut base_apic = unsafe { Msr::new(APIC_BASE_MSR).read() };
+ let mut regs = ApicRegisters::from_base_apic(base_apic);
+
+ let spurious = regs.get(ApicRegister::SpuriousInterruptVec) | 0x1ff;
+
+ unsafe { Msr::new(APIC_BASE_MSR).write(base_apic | APIC_FLAG) };
+
+ regs.set(ApicRegister::SpuriousInterruptVec, spurious);
+
+ Some(Self {
+ base_apic, regs
+ })
+ }
+}