strat9_kernel/arch/x86_64/
apic.rs1use crate::memory;
8use core::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
9
10static APIC_INITIALIZED: AtomicBool = AtomicBool::new(false);
12static APIC_X2_MODE: AtomicBool = AtomicBool::new(false);
14
15static APIC_BASE_VIRT: AtomicU64 = AtomicU64::new(0);
17
18static APIC_BASE_PHYS: AtomicU64 = AtomicU64::new(0);
20static ICR_HIGH_SHADOW: [AtomicU32; crate::arch::x86_64::percpu::MAX_CPUS] =
22 [const { AtomicU32::new(0) }; crate::arch::x86_64::percpu::MAX_CPUS];
23
24pub fn lapic_phys() -> u64 {
26 APIC_BASE_PHYS.load(Ordering::Relaxed)
27}
28
29const REG_ID: u32 = 0x020;
33#[allow(dead_code)]
35const REG_VERSION: u32 = 0x030;
36const REG_TPR: u32 = 0x080;
38const REG_EOI: u32 = 0x0B0;
40const REG_SPURIOUS: u32 = 0x0F0;
42pub const REG_ESR: u32 = 0x280;
44pub const REG_ICR_LOW: u32 = 0x300;
46pub const REG_ICR_HIGH: u32 = 0x310;
48pub const REG_LVT_TIMER: u32 = 0x320;
50pub const REG_TIMER_INIT: u32 = 0x380;
52pub const REG_TIMER_CURRENT: u32 = 0x390;
54pub const REG_TIMER_DIVIDE: u32 = 0x3E0;
56
57pub const LVT_TIMER_PERIODIC: u32 = 1 << 17;
61#[allow(dead_code)]
63pub const LVT_TIMER_MASKED: u32 = 1 << 16;
64pub const LVT_TIMER_VECTOR: u8 = 0xD2;
66
67const IA32_APIC_BASE_MSR: u32 = 0x1B;
69const APIC_BASE_ADDR_MASK: u64 = 0x000F_FFFF_FFFF_F000;
71const APIC_BASE_ENABLE: u64 = 1 << 11;
73const APIC_BASE_EXTD: u64 = 1 << 10;
75
76const SPURIOUS_VECTOR: u8 = 0xFF;
78
79pub const IPI_RESCHED_VECTOR: u8 = 0xE0;
81
82pub const IPI_TLB_SHOOTDOWN_VECTOR: u8 = 0xF0;
84
85pub const IPI_N3_MIGRATE_VECTOR: u8 = 0xF1;
87
88pub fn is_present() -> bool {
90 let (_eax, _ebx, _ecx, edx) = super::cpuid(1, 0);
91 edx & (1 << 9) != 0
93}
94
95pub fn is_x2apic_supported() -> bool {
97 let (_eax, _ebx, ecx, _edx) = super::cpuid(1, 0);
98 ecx & (1 << 21) != 0
100}
101
102pub fn lapic_id() -> u32 {
107 if APIC_INITIALIZED.load(Ordering::Relaxed) {
108 let raw = unsafe { read_reg(REG_ID) };
110 if APIC_X2_MODE.load(Ordering::Relaxed) {
111 return raw;
112 }
113 return raw >> 24;
114 }
115
116 let (_eax, ebx, _ecx, _edx) = super::cpuid(1, 0);
117 (ebx >> 24) & 0xFF
118}
119
120pub fn is_initialized() -> bool {
122 APIC_INITIALIZED.load(Ordering::Relaxed)
123}
124
125pub fn is_x2apic_enabled() -> bool {
127 APIC_X2_MODE.load(Ordering::Relaxed)
128}
129
130#[inline]
131fn x2apic_msr_for_reg(offset: u32) -> Option<u32> {
132 match offset {
133 REG_ID => Some(0x802),
134 REG_VERSION => Some(0x803),
135 REG_TPR => Some(0x808),
136 REG_EOI => Some(0x80B),
137 REG_SPURIOUS => Some(0x80F),
138 REG_ESR => Some(0x828),
139 REG_LVT_TIMER => Some(0x832),
140 REG_TIMER_INIT => Some(0x838),
141 REG_TIMER_CURRENT => Some(0x839),
142 REG_TIMER_DIVIDE => Some(0x83E),
143 _ => None,
144 }
145}
146
147#[inline]
148fn current_cpu_slot() -> usize {
149 let idx = crate::arch::x86_64::percpu::current_cpu_index();
150 if idx < crate::arch::x86_64::percpu::MAX_CPUS {
151 idx
152 } else {
153 0
154 }
155}
156
157pub unsafe fn read_reg(offset: u32) -> u32 {
162 if APIC_X2_MODE.load(Ordering::Relaxed) {
163 return match offset {
164 REG_ICR_LOW => super::rdmsr(0x830) as u32,
165 REG_ICR_HIGH => ((super::rdmsr(0x830) >> 32) as u32) << 24,
166 _ => {
167 let Some(msr) = x2apic_msr_for_reg(offset) else {
168 return 0;
169 };
170 super::rdmsr(msr) as u32
171 }
172 };
173 }
174
175 let addr = APIC_BASE_VIRT.load(Ordering::Relaxed) + offset as u64;
176 unsafe { core::ptr::read_volatile(addr as *const u32) }
178}
179
180pub unsafe fn write_reg(offset: u32, value: u32) {
185 if APIC_X2_MODE.load(Ordering::Relaxed) {
186 match offset {
187 REG_ICR_HIGH => {
188 let cpu = current_cpu_slot();
189 let dest = if value & 0x00FF_FFFF == 0 {
192 value >> 24
193 } else {
194 value
195 };
196 ICR_HIGH_SHADOW[cpu].store(dest, Ordering::Relaxed);
197 }
198 REG_ICR_LOW => {
199 let cpu = current_cpu_slot();
200 let dest = ICR_HIGH_SHADOW[cpu].load(Ordering::Relaxed) as u64;
201 super::wrmsr(0x830, (dest << 32) | value as u64);
202 }
203 _ => {
204 if let Some(msr) = x2apic_msr_for_reg(offset) {
205 super::wrmsr(msr, value as u64);
206 }
207 }
208 }
209 return;
210 }
211
212 let addr = APIC_BASE_VIRT.load(Ordering::Relaxed) + offset as u64;
213 unsafe { core::ptr::write_volatile(addr as *mut u32, value) }
215}
216
217pub fn init(madt_lapic_addr: u64) {
222 let apic_base_msr = super::rdmsr(IA32_APIC_BASE_MSR);
224 let apic_phys = apic_base_msr & APIC_BASE_ADDR_MASK;
225
226 if apic_phys != madt_lapic_addr as u64 {
227 log::warn!(
228 "LAPIC: MSR base 0x{:X} differs from MADT 0x{:X}, using MSR",
229 apic_phys,
230 madt_lapic_addr
231 );
232 }
233
234 let use_x2apic = if is_x2apic_supported() {
235 if apic_base_msr & APIC_BASE_ENABLE == 0 {
236 super::wrmsr(IA32_APIC_BASE_MSR, apic_base_msr | APIC_BASE_ENABLE);
237 }
238 super::wrmsr(
239 IA32_APIC_BASE_MSR,
240 apic_base_msr | APIC_BASE_ENABLE | APIC_BASE_EXTD,
241 );
242 let verify = super::rdmsr(IA32_APIC_BASE_MSR);
243 verify & (APIC_BASE_ENABLE | APIC_BASE_EXTD) == (APIC_BASE_ENABLE | APIC_BASE_EXTD)
244 } else {
245 if apic_base_msr & APIC_BASE_ENABLE == 0 {
246 super::wrmsr(IA32_APIC_BASE_MSR, apic_base_msr | APIC_BASE_ENABLE);
247 }
248 false
249 };
250 APIC_X2_MODE.store(use_x2apic, Ordering::Release);
251
252 let apic_virt = memory::phys_to_virt(apic_phys);
254 APIC_BASE_VIRT.store(apic_virt, Ordering::Relaxed);
255 APIC_BASE_PHYS.store(apic_phys, Ordering::Relaxed);
256
257 crate::serial_println!(
259 "[apic] init: hhdm={:#x} lapic_phys={:#x} lapic_virt={:#x}",
260 memory::hhdm_offset(),
261 apic_phys,
262 apic_virt
263 );
264 if apic_virt == apic_phys {
265 crate::serial_println!(
266 "[apic] WARN: lapic_virt == lapic_phys (HHDM offset is 0!) \
267 The LAPIC MMIO is identity-mapped at a low address. \
268 Kernel MMIO entries will be propagated to user page tables."
269 );
270 }
271
272 unsafe {
274 write_reg(REG_ESR, 0);
276 write_reg(REG_ESR, 0);
277
278 write_reg(REG_TPR, 0);
280
281 write_reg(REG_SPURIOUS, 0x100 | SPURIOUS_VECTOR as u32);
283 }
284
285 APIC_INITIALIZED.store(true, Ordering::Relaxed);
286
287 let id = lapic_id();
288 log::info!(
289 "LAPIC: initialized at phys=0x{:X} virt=0x{:X} (ID={}, mode={})",
290 apic_phys,
291 apic_virt,
292 id,
293 if use_x2apic { "x2APIC" } else { "xAPIC" }
294 );
295}
296
297pub fn init_ap() {
302 if !APIC_INITIALIZED.load(Ordering::Relaxed) {
303 log::warn!("LAPIC: init_ap called before init");
304 return;
305 }
306
307 if APIC_X2_MODE.load(Ordering::Acquire) {
308 let base = super::rdmsr(IA32_APIC_BASE_MSR);
309 if base & (APIC_BASE_ENABLE | APIC_BASE_EXTD) != (APIC_BASE_ENABLE | APIC_BASE_EXTD) {
310 if base & APIC_BASE_ENABLE == 0 {
311 super::wrmsr(IA32_APIC_BASE_MSR, base | APIC_BASE_ENABLE);
312 }
313 super::wrmsr(IA32_APIC_BASE_MSR, base | APIC_BASE_ENABLE | APIC_BASE_EXTD);
314 }
315 }
316
317 unsafe {
319 write_reg(REG_ESR, 0);
320 write_reg(REG_ESR, 0);
321 write_reg(REG_TPR, 0);
322 write_reg(REG_SPURIOUS, 0x100 | SPURIOUS_VECTOR as u32);
323 }
324}
325
326#[inline]
328pub fn eoi() {
329 unsafe {
331 write_reg(REG_EOI, 0);
332 }
333}
334
335pub fn send_ipi_raw(target_apic_id: u32, icr_low: u32) {
339 unsafe {
340 if APIC_X2_MODE.load(Ordering::Relaxed) {
341 super::wrmsr(0x830, ((target_apic_id as u64) << 32) | icr_low as u64);
343 } else {
344 write_reg(REG_ICR_HIGH, target_apic_id << 24);
346 write_reg(REG_ICR_LOW, icr_low);
347 }
348 }
349}
350
351pub fn send_resched_ipi(target_apic_id: u32) {
360 send_ipi_raw(target_apic_id, IPI_RESCHED_VECTOR as u32);
363}
364
365pub fn self_ipi_resched() {
372 if !is_initialized() {
373 return;
374 }
375 send_resched_ipi(lapic_id());
376}