strat9_kernel/arch/x86_64/
x2apic.rs1use 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 let base = 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 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 pub fn id(&self) -> u32 {
68 rdmsr(IA32_X2APIC_APICID) as u32
70 }
71
72 pub fn version(&self) -> u32 {
73 rdmsr(IA32_X2APIC_VERSION) as u32
75 }
76
77 pub fn eoi(&self) {
78 wrmsr(IA32_X2APIC_EOI, 0);
80 }
81
82 pub fn send_ipi(&self, target_id: u32, vector: u8) {
83 wrmsr(IA32_X2APIC_ESR, 0);
87 let icr = ((target_id as u64) << 32) | (vector as u64) | (1 << 14);
88 wrmsr(IA32_X2APIC_ICR, icr);
89 }
90
91 pub fn configure_timer(&self, initial_count: u32, vector: u8, periodic: bool) {
92 let mut lvt = vector as u64;
95 if periodic {
96 lvt |= 1 << 17;
97 }
98 wrmsr(IA32_X2APIC_LVT_TIMER, lvt);
99 wrmsr(IA32_X2APIC_TIMER_INIT, initial_count as u64);
100 }
101}