Skip to main content

strat9_kernel/arch/x86_64/
x2apic.rs

1//! x2APIC (Extended xAPIC) driver
2//!
3//! Provides MSR-based access to x2APIC registers instead of MMIO.
4//! x2APIC extends the APIC ID to 32 bits and uses MSRs for all register access.
5
6use crate::arch::x86_64::{rdmsr, wrmsr};
7
8const IA32_APIC_BASE_MSR: u32 = 0x1B;
9const APIC_BASE_EN: u64 = 1 << 11;
10const APIC_BASE_EXTD: u64 = 1 << 10;
11
12const IA32_X2APIC_APICID: u32 = 0x802;
13const IA32_X2APIC_VERSION: u32 = 0x803;
14const IA32_X2APIC_EOI: u32 = 0x80B;
15const IA32_X2APIC_SIVR: u32 = 0x80F;
16const IA32_X2APIC_ESR: u32 = 0x828;
17const IA32_X2APIC_ICR: u32 = 0x830;
18const IA32_X2APIC_LVT_TIMER: u32 = 0x832;
19const IA32_X2APIC_TIMER_INIT: u32 = 0x838;
20const IA32_X2APIC_TIMER_DIV: u32 = 0x83E;
21
22pub struct X2Apic {
23    _private: (),
24}
25
26impl X2Apic {
27    pub fn new() -> Option<Self> {
28        if !Self::is_supported() {
29            return None;
30        }
31        // SAFETY: rdmsr is a privileged Ring-0 instruction, valid here.
32        let base = unsafe { rdmsr(IA32_APIC_BASE_MSR) };
33        if base & (APIC_BASE_EN | APIC_BASE_EXTD) == (APIC_BASE_EN | APIC_BASE_EXTD) {
34            Some(Self { _private: () })
35        } else {
36            None
37        }
38    }
39
40    pub fn is_supported() -> bool {
41        let (_eax, _ebx, ecx, _edx) = super::cpuid(1, 0);
42        ecx & (1 << 21) != 0
43    }
44
45    pub fn enable(&self) {
46        // SAFETY: wrmsr/rdmsr are Ring-0 privileged instructions, valid here.
47        unsafe {
48            let base = rdmsr(IA32_APIC_BASE_MSR);
49            let already_extd = base & APIC_BASE_EXTD != 0;
50
51            if !already_extd {
52                if base & APIC_BASE_EN == 0 {
53                    wrmsr(IA32_APIC_BASE_MSR, base | APIC_BASE_EN);
54                }
55                wrmsr(IA32_APIC_BASE_MSR, base | APIC_BASE_EN | APIC_BASE_EXTD);
56            }
57
58            let base_after = rdmsr(IA32_APIC_BASE_MSR);
59            if base_after & (APIC_BASE_EN | APIC_BASE_EXTD) != (APIC_BASE_EN | APIC_BASE_EXTD) {
60                return;
61            }
62
63            let svr: u64 = (1 << 8) | 0xFF;
64            wrmsr(IA32_X2APIC_SIVR, svr);
65        }
66    }
67
68    pub fn id(&self) -> u32 {
69        // SAFETY: x2APIC MSR reads are valid after enable().
70        unsafe { rdmsr(IA32_X2APIC_APICID) as u32 }
71    }
72
73    pub fn version(&self) -> u32 {
74        // SAFETY: x2APIC MSR reads are valid after enable().
75        unsafe { rdmsr(IA32_X2APIC_VERSION) as u32 }
76    }
77
78    pub fn eoi(&self) {
79        // SAFETY: x2APIC MSR writes are valid after enable().
80        unsafe { wrmsr(IA32_X2APIC_EOI, 0) };
81    }
82
83    pub fn send_ipi(&self, target_id: u32, vector: u8) {
84        // SAFETY: x2APIC ICR write is valid after enable(). ICR is a single
85        // 64-bit MSR write in x2APIC: bits[63:32]=destination, bits[31:0]=command.
86        unsafe {
87            wrmsr(IA32_X2APIC_ESR, 0);
88            let icr = ((target_id as u64) << 32)
89                | (vector as u64)
90                | (1 << 14);
91            wrmsr(IA32_X2APIC_ICR, icr);
92        }
93    }
94
95    pub fn configure_timer(&self, initial_count: u32, vector: u8, periodic: bool) {
96        // SAFETY: x2APIC timer MSR writes are valid after enable().
97        unsafe {
98            let mut lvt = vector as u64;
99            if periodic {
100                lvt |= 1 << 17;
101            }
102            wrmsr(IA32_X2APIC_LVT_TIMER, lvt);
103            wrmsr(IA32_X2APIC_TIMER_INIT, initial_count as u64);
104        }
105    }
106}