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 { 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 }) } }