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 = 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 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 unsafe { rdmsr(IA32_X2APIC_APICID) as u32 }
71 }
72
73 pub fn version(&self) -> u32 {
74 unsafe { rdmsr(IA32_X2APIC_VERSION) as u32 }
76 }
77
78 pub fn eoi(&self) {
79 unsafe { wrmsr(IA32_X2APIC_EOI, 0) };
81 }
82
83 pub fn send_ipi(&self, target_id: u32, vector: u8) {
84 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 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}