diff options
Diffstat (limited to 'kernel/src/interrupts/apic.rs')
-rw-r--r-- | kernel/src/interrupts/apic.rs | 121 |
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 + }) + } +} |