Skip to main content

proka_kernel/interrupts/
apic.rs

1use crate::get_hhdm_offset;
2use crate::interrupts::idt::SPURIOUS_APIC_VECTOR;
3use crate::interrupts::pic;
4use crate::libs::msr;
5use log::{debug, error, info};
6use raw_cpuid::CpuId;
7use x86_64::registers::model_specific::Msr;
8
9const APIC_SIVR_ENABLE: u64 = 0x100;
10const APIC_BASE_ENABLE: u64 = 1 << 11;
11const APIC_BASE_X2APIC_ENABLE: u64 = 1 << 10;
12
13// MMIO Offsets for xAPIC
14const XAPIC_EOI_OFFSET: u64 = 0x0B0;
15const XAPIC_SIVR_OFFSET: u64 = 0xF0;
16pub fn apic_is_available() -> bool {
17    let cpuid = CpuId::new();
18    cpuid.get_feature_info().is_some_and(|info| info.has_apic())
19}
20
21pub fn x2apic_is_available() -> bool {
22    let cpuid = CpuId::new();
23    cpuid
24        .get_feature_info()
25        .is_some_and(|info| info.has_x2apic())
26}
27
28/// Enables x2APIC mode
29pub fn enable_x2apic() {
30    unsafe {
31        // Step 1: Enable x2APIC mode in IA32_APIC_BASE
32        let mut apic_base = Msr::new(msr::IA32_APIC_BASE);
33        let mut base_val = apic_base.read();
34        base_val |= APIC_BASE_ENABLE | APIC_BASE_X2APIC_ENABLE;
35        apic_base.write(base_val);
36
37        // Step 2: Set SIVR
38        let mut sivr = Msr::new(msr::IA32_X2APIC_SIVR);
39        let sivr_val = APIC_SIVR_ENABLE | SPURIOUS_APIC_VECTOR as u64;
40        sivr.write(sivr_val);
41    }
42}
43
44/// Helper to get the virtual base address of the Local APIC (xAPIC mode)
45unsafe fn get_xapic_base() -> u64 {
46    let apic_base = Msr::new(msr::IA32_APIC_BASE).read();
47    let phys_base = apic_base & 0xFFFFF000; // Frame aligned
48    let hhdm_offset = get_hhdm_offset().as_u64();
49    phys_base + hhdm_offset
50}
51
52/// Enables legacy xAPIC mode
53pub fn enable_apic() {
54    unsafe {
55        // Step 1: Ensure Global Enable in IA32_APIC_BASE
56        let mut apic_base = Msr::new(msr::IA32_APIC_BASE);
57        let mut base_val = apic_base.read();
58        base_val |= APIC_BASE_ENABLE;
59        base_val &= !APIC_BASE_X2APIC_ENABLE;
60        apic_base.write(base_val);
61
62        // Step 2: Set SIVR via MMIO
63        let base_addr = get_xapic_base();
64        let sivr_ptr = (base_addr + XAPIC_SIVR_OFFSET) as *mut u32;
65        let sivr_val = (APIC_SIVR_ENABLE | SPURIOUS_APIC_VECTOR as u64) as u32;
66        core::ptr::write_volatile(sivr_ptr, sivr_val);
67    }
68}
69
70/// Signal End of Interrupt (EOI) to Local APIC
71pub fn end_of_interrupt() {
72    unsafe {
73        if x2apic_is_available() {
74            let mut eoi = Msr::new(msr::IA32_X2APIC_EOI);
75            eoi.write(0);
76        } else {
77            let base_addr = get_xapic_base();
78            let eoi_ptr = (base_addr + XAPIC_EOI_OFFSET) as *mut u32;
79            core::ptr::write_volatile(eoi_ptr, 0);
80        }
81    }
82}
83
84pub fn init() -> bool {
85    // Always disable 8259 PIC when using APIC
86    pic::disable();
87
88    let success = if x2apic_is_available() {
89        debug!("x2APIC is available, enabling...");
90        enable_x2apic();
91        info!("x2APIC enabled");
92        true
93    } else if apic_is_available() {
94        debug!("x2APIC not available, falling back to xAPIC...");
95        enable_apic();
96        info!("xAPIC enabled");
97        true
98    } else {
99        error!("APIC not supported!");
100        false
101    };
102
103    success
104}