use crate::IRQ_APIC_LVT_ERROR; use x86_64::registers::model_specific::Msr; #[allow(unused)] struct LocalVectorTable { cmci: Msr, timer: Msr, thermal_sensor: Msr, perfmon: Msr, lint0: Msr, lint1: Msr, error: Msr, } #[allow(unused)] pub struct LocalApic { id: Msr, version: Msr, task_priority: Msr, processor_priority: Msr, eoi: Msr, logical_destination: Msr, spurious_interrupt_vector: Msr, in_service: [Msr; 8], trigger_mode: [Msr; 8], interrupt_request: [Msr; 8], error_status: Msr, local_vector_table: LocalVectorTable, interrupt_command: Msr, initial_count: Msr, current_count: Msr, divide_config: Msr, self_ipi: Msr, } #[allow(unused)] #[derive(Debug, Copy, Clone)] #[repr(u32)] pub enum IpiDestination { JustMe = 0b01 << 18, All = 0b10 << 18, AllButMe = 0b11 << 18, } #[allow(unused)] #[derive(Debug, Copy, Clone)] #[repr(u32)] pub enum IpiTriggerMode { Edge = 0 << 15, Level = 1 << 15, } #[allow(unused)] #[derive(Debug, Copy, Clone)] #[repr(u32)] pub enum IpiLevel { Deassert = 0 << 14, Assert = 1 << 14, } #[allow(unused)] #[derive(Debug, Copy, Clone)] #[repr(u32)] pub enum IpiDeliveryMode { Fixed = 0b000 << 8, LowestPriority = 0b001 << 8, Smi = 0b010 << 8, Nmi = 0b100 << 8, Init = 0b101 << 8, StartUp = 0b110 << 8, } #[allow(unused)] impl LocalApic { pub fn init() -> LocalApic { const MSR_BASE: u32 = 0x800; /* Set bits 10 and 11 of IA32_APIC_BASE MSR to enable x2APIC */ let mut apic_base_msr = Msr::new(0x1b); let mut apic_base = unsafe { apic_base_msr.read() }; debug!("APIC BASE: 0x{:x}", apic_base); apic_base |= 0b11 << 10; unsafe { apic_base_msr.write(apic_base) }; /* * See Intel SDM Table 10-6: * Local APIC Register Address Map Supported by x2APIC */ LocalApic { id: Msr::new(MSR_BASE + 0x002), version: Msr::new(MSR_BASE + 0x003), task_priority: Msr::new(MSR_BASE + 0x008), processor_priority: Msr::new(MSR_BASE + 0x00a), eoi: Msr::new(MSR_BASE + 0x00b), logical_destination: Msr::new(MSR_BASE + 0x00d), spurious_interrupt_vector: Msr::new(MSR_BASE + 0x00f), in_service: [Msr::new(MSR_BASE + 0x010), Msr::new(MSR_BASE + 0x011), Msr::new(MSR_BASE + 0x012), Msr::new(MSR_BASE + 0x013), Msr::new(MSR_BASE + 0x014), Msr::new(MSR_BASE + 0x015), Msr::new(MSR_BASE + 0x016), Msr::new(MSR_BASE + 0x017)], trigger_mode: [Msr::new(MSR_BASE + 0x018), Msr::new(MSR_BASE + 0x019), Msr::new(MSR_BASE + 0x01a), Msr::new(MSR_BASE + 0x01b), Msr::new(MSR_BASE + 0x01c), Msr::new(MSR_BASE + 0x01d), Msr::new(MSR_BASE + 0x01e), Msr::new(MSR_BASE + 0x01f)], interrupt_request: [Msr::new(MSR_BASE + 0x020), Msr::new(MSR_BASE + 0x021), Msr::new(MSR_BASE + 0x022), Msr::new(MSR_BASE + 0x023), Msr::new(MSR_BASE + 0x024), Msr::new(MSR_BASE + 0x025), Msr::new(MSR_BASE + 0x026), Msr::new(MSR_BASE + 0x027)], error_status: Msr::new(MSR_BASE + 0x028), local_vector_table: LocalVectorTable { cmci: Msr::new(MSR_BASE + 0x02f), timer: Msr::new(MSR_BASE + 0x032), thermal_sensor: Msr::new(MSR_BASE + 0x033), perfmon: Msr::new(MSR_BASE + 0x034), lint0: Msr::new(MSR_BASE + 0x035), lint1: Msr::new(MSR_BASE + 0x036), error: Msr::new(MSR_BASE + 0x037), }, interrupt_command: Msr::new(MSR_BASE + 0x030), initial_count: Msr::new(MSR_BASE + 0x038), current_count: Msr::new(MSR_BASE + 0x039), divide_config: Msr::new(MSR_BASE + 0x03e), self_ipi: Msr::new(MSR_BASE + 0x03f), } } pub fn id(&self) -> u32 { unsafe { self.id.read() as u32 } } pub fn version(&self) -> u32 { unsafe { self.version.read() as u32 } } pub fn enable(&mut self) { /* Set bit in SVR 8 to enable local APIC */ let mut svr = unsafe { self.spurious_interrupt_vector.read() }; svr |= 1 << 8; unsafe { self.spurious_interrupt_vector.write(svr); } /* * Set Local Vector Table error register: * Directs APIC errors to interrupt #IRQ_APIC_LVT_ERROR */ let mut lvte = unsafe { self.local_vector_table.error.read() as u32 }; lvte = (lvte & 0xffffff00) | (IRQ_APIC_LVT_ERROR as u32); unsafe { self.local_vector_table.error.write(lvte as u64); } } pub fn send_ipi(&mut self, dest: IpiDestination, trigger_mode: IpiTriggerMode, level: IpiLevel, delivery_mode: IpiDeliveryMode, vector: u8) { unsafe { self.interrupt_command.write((dest as u64) | (trigger_mode as u64) | (level as u64) | (delivery_mode as u64) | (vector as u64)); } } }