veridian_kernel/arch/x86_64/apic.rs
1//! Local APIC and I/O APIC support for x86_64.
2//!
3//! Provides initialization and control of the Local APIC (interrupt delivery to
4//! the local CPU) and I/O APIC (external interrupt routing). This module is
5//! additive to the existing PIC (8259) setup -- the PIC remains as a fallback
6//! while the APIC handles advanced interrupt routing.
7//!
8//! The Local APIC is memory-mapped at 0xFEE0_0000 (identity-mapped by the
9//! bootloader). The I/O APIC is at 0xFEC0_0000 with indirect register access
10//! via IOREGSEL/IOWIN.
11//!
12//! Register constants and hardware API methods define the complete Local APIC
13//! and I/O APIC register set per the Intel SDM. Unused items are retained for
14//! hardware reference completeness.
15#![allow(dead_code)]
16
17use core::{
18 ptr,
19 sync::atomic::{AtomicBool, Ordering},
20};
21
22use spin::Mutex;
23
24use crate::error::{KernelError, KernelResult};
25
26// ---------------------------------------------------------------------------
27// MSR addresses
28// ---------------------------------------------------------------------------
29
30/// IA32_APIC_BASE MSR address. Contains the APIC base physical address and
31/// enable/BSP flags.
32const IA32_APIC_BASE_MSR: u32 = 0x1B;
33
34/// Bit 11 of IA32_APIC_BASE: global APIC enable.
35const IA32_APIC_BASE_ENABLE: u64 = 1 << 11;
36
37// ---------------------------------------------------------------------------
38// Local APIC register offsets (byte offsets from APIC base)
39// ---------------------------------------------------------------------------
40
41/// Local APIC ID register.
42const LAPIC_ID: u32 = 0x020;
43/// Local APIC Version register.
44const LAPIC_VERSION: u32 = 0x030;
45/// Task Priority Register -- controls interrupt priority filtering.
46const LAPIC_TPR: u32 = 0x080;
47/// End-Of-Interrupt register -- write 0 to signal interrupt completion.
48const LAPIC_EOI: u32 = 0x0B0;
49/// Spurious Interrupt Vector register -- also contains the software enable bit.
50const LAPIC_SVR: u32 = 0x0F0;
51// Hardware register definitions -- retained for completeness per Intel SDM
52const LAPIC_ISR_BASE: u32 = 0x100;
53const LAPIC_TMR_BASE: u32 = 0x180;
54const LAPIC_IRR_BASE: u32 = 0x200;
55const LAPIC_ESR: u32 = 0x280;
56/// Interrupt Command Register (low 32 bits).
57const LAPIC_ICR_LOW: u32 = 0x300;
58/// Interrupt Command Register (high 32 bits -- destination field).
59const LAPIC_ICR_HIGH: u32 = 0x310;
60/// LVT Timer register.
61const LAPIC_LVT_TIMER: u32 = 0x320;
62/// LVT LINT0 register.
63const LAPIC_LVT_LINT0: u32 = 0x350;
64/// LVT LINT1 register.
65const LAPIC_LVT_LINT1: u32 = 0x360;
66/// LVT Error register.
67const LAPIC_LVT_ERROR: u32 = 0x370;
68/// Timer Initial Count register.
69const LAPIC_TIMER_INIT_COUNT: u32 = 0x380;
70/// Timer Current Count register (read-only).
71const LAPIC_TIMER_CUR_COUNT: u32 = 0x390;
72/// Timer Divide Configuration register.
73const LAPIC_TIMER_DIV: u32 = 0x3E0;
74
75/// LVT mask bit (bit 16) -- when set, the interrupt is masked.
76const LVT_MASK: u32 = 1 << 16;
77
78/// Spurious Vector Register software enable bit (bit 8).
79const SVR_ENABLE: u32 = 1 << 8;
80
81/// Default spurious interrupt vector number (0xFF by convention).
82const SPURIOUS_VECTOR: u8 = 0xFF;
83
84/// APIC timer interrupt vector (dedicated, separate from PIC timer at 32).
85pub const APIC_TIMER_VECTOR: u8 = 48;
86
87/// TLB shootdown IPI vector -- sent to remote CPUs to flush stale TLB entries.
88pub const TLB_SHOOTDOWN_VECTOR: u8 = 49;
89
90/// Scheduler wake IPI vector -- sent to wake a remote CPU from idle to run a
91/// task.
92pub const SCHED_WAKE_VECTOR: u8 = 50;
93
94// ---------------------------------------------------------------------------
95// LVT Timer mode bits
96// ---------------------------------------------------------------------------
97
98// Hardware register definitions -- retained for completeness per Intel SDM
99const TIMER_MODE_ONESHOT: u32 = 0b00 << 17;
100/// Periodic timer mode (bits 18:17 = 01).
101const TIMER_MODE_PERIODIC: u32 = 0b01 << 17;
102
103// ---------------------------------------------------------------------------
104// I/O APIC
105// ---------------------------------------------------------------------------
106
107/// Default I/O APIC MMIO base address (QEMU virt machine).
108const IOAPIC_BASE: usize = 0xFEC0_0000;
109
110/// I/O APIC Register Select (write the register index here).
111const IOREGSEL: u32 = 0x00;
112/// I/O APIC Window (read/write the selected register through here).
113const IOWIN: u32 = 0x10;
114
115// Hardware register definition -- retained for completeness per Intel SDM
116const IOAPIC_REG_ID: u32 = 0x00;
117/// I/O APIC Version register.
118const IOAPIC_REG_VER: u32 = 0x01;
119
120/// I/O APIC redirection table entry base (each entry uses two 32-bit
121/// registers).
122const IOAPIC_REDTBL_BASE: u32 = 0x10;
123
124// ---------------------------------------------------------------------------
125// Redirection table entry bitfields
126// ---------------------------------------------------------------------------
127
128/// Redirection table entry -- represents a 64-bit I/O APIC routing entry.
129///
130/// Layout:
131/// - Bits 7:0 -- Interrupt vector
132/// - Bits 10:8 -- Delivery mode (000=Fixed, 001=LowestPri, 010=SMI, 100=NMI,
133/// 101=INIT, 111=ExtINT)
134/// - Bit 11 -- Destination mode (0=Physical, 1=Logical)
135/// - Bit 12 -- Delivery status (read-only: 0=idle, 1=pending)
136/// - Bit 13 -- Pin polarity (0=active high, 1=active low)
137/// - Bit 14 -- Remote IRR (read-only, level-triggered)
138/// - Bit 15 -- Trigger mode (0=edge, 1=level)
139/// - Bit 16 -- Mask (1=masked)
140/// - Bits 63:56 -- Destination APIC ID (physical mode)
141#[derive(Debug, Clone, Copy)]
142pub struct RedirectionEntry {
143 raw: u64,
144}
145
146impl RedirectionEntry {
147 /// Create a new masked redirection entry with the given vector.
148 pub const fn new(vector: u8) -> Self {
149 Self {
150 raw: (vector as u64) | ((1u64) << 16), // masked by default
151 }
152 }
153
154 /// Set the interrupt vector (bits 7:0).
155 pub fn set_vector(&mut self, vector: u8) {
156 self.raw = (self.raw & !0xFF) | (vector as u64);
157 }
158
159 /// Get the interrupt vector.
160 pub fn vector(&self) -> u8 {
161 (self.raw & 0xFF) as u8
162 }
163
164 /// Set delivery mode (bits 10:8).
165 /// 0=Fixed, 1=LowestPriority, 2=SMI, 4=NMI, 5=INIT, 7=ExtINT.
166 pub fn set_delivery_mode(&mut self, mode: u8) {
167 self.raw = (self.raw & !(0b111 << 8)) | (((mode & 0b111) as u64) << 8);
168 }
169
170 /// Set destination mode (bit 11). 0=Physical, 1=Logical.
171 pub fn set_dest_mode_logical(&mut self, logical: bool) {
172 if logical {
173 self.raw |= 1 << 11;
174 } else {
175 self.raw &= !(1 << 11);
176 }
177 }
178
179 /// Set pin polarity (bit 13). false=active high, true=active low.
180 pub fn set_active_low(&mut self, active_low: bool) {
181 if active_low {
182 self.raw |= 1 << 13;
183 } else {
184 self.raw &= !(1 << 13);
185 }
186 }
187
188 /// Set trigger mode (bit 15). false=edge, true=level.
189 pub fn set_level_triggered(&mut self, level: bool) {
190 if level {
191 self.raw |= 1 << 15;
192 } else {
193 self.raw &= !(1 << 15);
194 }
195 }
196
197 /// Set mask bit (bit 16). true=masked.
198 pub fn set_masked(&mut self, masked: bool) {
199 if masked {
200 self.raw |= 1 << 16;
201 } else {
202 self.raw &= !(1 << 16);
203 }
204 }
205
206 /// Check if the entry is masked.
207 pub fn is_masked(&self) -> bool {
208 self.raw & (1 << 16) != 0
209 }
210
211 /// Set destination APIC ID (bits 63:56).
212 pub fn set_destination(&mut self, dest: u8) {
213 self.raw = (self.raw & !(0xFFu64 << 56)) | ((dest as u64) << 56);
214 }
215
216 /// Get the low 32 bits of the entry.
217 pub fn low(&self) -> u32 {
218 self.raw as u32
219 }
220
221 /// Get the high 32 bits of the entry.
222 pub fn high(&self) -> u32 {
223 (self.raw >> 32) as u32
224 }
225
226 /// Construct from low and high 32-bit halves.
227 pub fn from_parts(low: u32, high: u32) -> Self {
228 Self {
229 raw: (low as u64) | ((high as u64) << 32),
230 }
231 }
232}
233
234// ---------------------------------------------------------------------------
235// Local APIC
236// ---------------------------------------------------------------------------
237
238/// Local APIC controller.
239///
240/// Wraps the memory-mapped register file for the per-CPU Local APIC. All
241/// register accesses use volatile reads/writes to prevent compiler reordering.
242pub struct LocalApic {
243 /// Virtual address of the APIC MMIO base (identity-mapped at 0xFEE0_0000).
244 base: usize,
245}
246
247impl LocalApic {
248 /// Create a new `LocalApic` handle with the given MMIO base address.
249 fn new(base: usize) -> Self {
250 Self { base }
251 }
252
253 /// Read a 32-bit Local APIC register at the given byte offset.
254 fn read(&self, offset: u32) -> u32 {
255 let addr = self.base + offset as usize;
256 // SAFETY: The address `self.base + offset` points to a well-known Local
257 // APIC MMIO register. The APIC region at 0xFEE0_0000 is identity-mapped
258 // by the bootloader and reserved in the frame allocator. Volatile read
259 // ensures the compiler does not elide or reorder the access.
260 unsafe { ptr::read_volatile(addr as *const u32) }
261 }
262
263 /// Write a 32-bit value to a Local APIC register at the given byte offset.
264 fn write(&self, offset: u32, value: u32) {
265 let addr = self.base + offset as usize;
266 // SAFETY: Same as `read` -- the address is a valid APIC MMIO register.
267 // Volatile write ensures the hardware sees the store in program order.
268 unsafe { ptr::write_volatile(addr as *mut u32, value) }
269 }
270
271 /// Read the Local APIC ID (bits 31:24 of the ID register).
272 pub fn read_id(&self) -> u8 {
273 ((self.read(LAPIC_ID) >> 24) & 0xFF) as u8
274 }
275
276 /// Read the Local APIC version register.
277 pub fn read_version(&self) -> u32 {
278 self.read(LAPIC_VERSION)
279 }
280
281 /// Enable the Local APIC by setting the software-enable bit in the
282 /// Spurious Interrupt Vector register and configuring the spurious vector.
283 fn enable(&self) {
284 // Set spurious vector to 0xFF and set the software-enable bit (bit 8).
285 self.write(LAPIC_SVR, SVR_ENABLE | SPURIOUS_VECTOR as u32);
286 }
287
288 /// Mask all Local Vector Table entries (Timer, LINT0, LINT1, Error) to
289 /// prevent unexpected interrupts before they are explicitly configured.
290 fn mask_all_lvt(&self) {
291 self.write(LAPIC_LVT_TIMER, LVT_MASK);
292 self.write(LAPIC_LVT_LINT0, LVT_MASK);
293 self.write(LAPIC_LVT_LINT1, LVT_MASK);
294 self.write(LAPIC_LVT_ERROR, LVT_MASK);
295 }
296
297 /// Send an End-Of-Interrupt signal. Must be called at the end of every
298 /// Local APIC interrupt handler.
299 pub fn send_eoi(&self) {
300 self.write(LAPIC_EOI, 0);
301 }
302
303 /// Set the Task Priority Register to allow all interrupts (priority 0).
304 fn set_task_priority(&self, priority: u8) {
305 self.write(LAPIC_TPR, priority as u32);
306 }
307
308 /// Configure the APIC timer for periodic interrupts.
309 ///
310 /// - `vector`: IDT vector number for the timer interrupt.
311 /// - `divide`: Timer divisor encoded as the Divide Configuration Register
312 /// value (e.g., 0x03 = divide by 16, 0x0B = divide by 1).
313 /// - `initial_count`: Initial countdown value. The timer fires when it
314 /// reaches zero and reloads automatically in periodic mode.
315 pub fn setup_timer(&self, vector: u8, divide: u8, initial_count: u32) {
316 // Stop the timer first.
317 self.write(LAPIC_TIMER_INIT_COUNT, 0);
318
319 // Set the divide configuration.
320 self.write(LAPIC_TIMER_DIV, divide as u32);
321
322 // Configure LVT Timer: periodic mode, unmasked, with the given vector.
323 self.write(LAPIC_LVT_TIMER, TIMER_MODE_PERIODIC | vector as u32);
324
325 // Setting the initial count starts the timer.
326 self.write(LAPIC_TIMER_INIT_COUNT, initial_count);
327 }
328
329 /// Stop the APIC timer by zeroing the initial count and masking the LVT
330 /// Timer entry.
331 pub fn stop_timer(&self) {
332 self.write(LAPIC_TIMER_INIT_COUNT, 0);
333 self.write(LAPIC_LVT_TIMER, LVT_MASK);
334 }
335
336 /// Read the current timer count (counts down from the initial value).
337 pub fn read_timer_count(&self) -> u32 {
338 self.read(LAPIC_TIMER_CUR_COUNT)
339 }
340
341 /// Write the Interrupt Command Register to send an IPI.
342 ///
343 /// - `dest`: Destination APIC ID.
344 /// - `vector`: Interrupt vector.
345 pub fn send_ipi(&self, dest: u8, vector: u8) {
346 // Write high dword first (destination in bits 31:24).
347 self.write(LAPIC_ICR_HIGH, (dest as u32) << 24);
348 // Write low dword (vector + delivery mode Fixed). Writing ICR low
349 // triggers the IPI.
350 self.write(LAPIC_ICR_LOW, vector as u32);
351 }
352
353 /// Send an INIT IPI to a target CPU (used in AP startup sequence).
354 pub fn send_init_ipi(&self, dest: u8) {
355 self.write(LAPIC_ICR_HIGH, (dest as u32) << 24);
356 // INIT: delivery mode = 0b101 (INIT), level assert
357 self.write(LAPIC_ICR_LOW, 0x00004500);
358 }
359
360 /// Send a Startup IPI (SIPI) to a target CPU.
361 ///
362 /// `startup_page` is the physical page number (e.g., 0x08 for 0x8000).
363 pub fn send_startup_ipi(&self, dest: u8, startup_page: u8) {
364 self.write(LAPIC_ICR_HIGH, (dest as u32) << 24);
365 // SIPI: delivery mode = 0b110 (StartUp), vector = page number
366 self.write(LAPIC_ICR_LOW, 0x00004600 | startup_page as u32);
367 }
368
369 /// Broadcast IPI to all CPUs except self.
370 pub fn send_ipi_all_excluding_self(&self, vector: u8) {
371 // Shorthand = 11 (all excluding self), no destination field needed.
372 self.write(
373 LAPIC_ICR_LOW,
374 0x000C0000 | vector as u32, // shorthand=11, dest_mode=physical, delivery=fixed
375 );
376 }
377}
378
379// ---------------------------------------------------------------------------
380// I/O APIC
381// ---------------------------------------------------------------------------
382
383/// I/O APIC controller.
384///
385/// The I/O APIC uses indirect register access: write the register index to
386/// IOREGSEL, then read/write the value through IOWIN.
387pub struct IoApic {
388 /// Virtual address of the I/O APIC MMIO base (identity-mapped at
389 /// 0xFEC0_0000).
390 base: usize,
391}
392
393impl IoApic {
394 /// Create a new `IoApic` handle with the given MMIO base address.
395 fn new(base: usize) -> Self {
396 Self { base }
397 }
398
399 /// Read a 32-bit I/O APIC register.
400 pub fn read_register(&self, reg: u32) -> u32 {
401 // SAFETY: IOREGSEL at base+0x00 and IOWIN at base+0x10 are the I/O
402 // APIC's indirect register access ports. The base address 0xFEC0_0000
403 // is identity-mapped by the bootloader. Volatile writes ensure the
404 // register select is visible to hardware before the window read.
405 unsafe {
406 ptr::write_volatile((self.base + IOREGSEL as usize) as *mut u32, reg);
407 ptr::read_volatile((self.base + IOWIN as usize) as *const u32)
408 }
409 }
410
411 /// Write a 32-bit value to an I/O APIC register.
412 pub fn write_register(&self, reg: u32, value: u32) {
413 // SAFETY: Same as `read_register` -- indirect MMIO access through
414 // IOREGSEL/IOWIN. The volatile operations guarantee ordering.
415 unsafe {
416 ptr::write_volatile((self.base + IOREGSEL as usize) as *mut u32, reg);
417 ptr::write_volatile((self.base + IOWIN as usize) as *mut u32, value);
418 }
419 }
420
421 /// Read the maximum number of redirection entries supported by this I/O
422 /// APIC (from bits 23:16 of the version register, plus one).
423 pub fn max_redirection_entries(&self) -> u8 {
424 let ver = self.read_register(IOAPIC_REG_VER);
425 (((ver >> 16) & 0xFF) + 1) as u8
426 }
427
428 /// Read a full 64-bit redirection table entry for the given IRQ.
429 fn read_redirection(&self, irq: u8) -> RedirectionEntry {
430 let reg_base = IOAPIC_REDTBL_BASE + (irq as u32) * 2;
431 let low = self.read_register(reg_base);
432 let high = self.read_register(reg_base + 1);
433 RedirectionEntry::from_parts(low, high)
434 }
435
436 /// Write a full 64-bit redirection table entry for the given IRQ.
437 fn write_redirection(&self, irq: u8, entry: RedirectionEntry) {
438 let reg_base = IOAPIC_REDTBL_BASE + (irq as u32) * 2;
439 // Write high dword first to avoid a transient unmasked state if the
440 // low dword unmasks the entry.
441 self.write_register(reg_base + 1, entry.high());
442 self.write_register(reg_base, entry.low());
443 }
444
445 /// Route an external IRQ to a specific interrupt vector and destination
446 /// APIC ID with edge-triggered, active-high, fixed delivery mode.
447 pub fn set_irq_route(&self, irq: u8, vector: u8, dest: u8) {
448 let mut entry = RedirectionEntry::new(vector);
449 entry.set_destination(dest);
450 entry.set_masked(false);
451 self.write_redirection(irq, entry);
452 }
453
454 /// Mask an IRQ in the I/O APIC redirection table.
455 pub fn mask_irq(&self, irq: u8) {
456 let mut entry = self.read_redirection(irq);
457 entry.set_masked(true);
458 self.write_redirection(irq, entry);
459 }
460
461 /// Unmask an IRQ in the I/O APIC redirection table.
462 pub fn unmask_irq(&self, irq: u8) {
463 let mut entry = self.read_redirection(irq);
464 entry.set_masked(false);
465 self.write_redirection(irq, entry);
466 }
467
468 /// Mask all redirection entries.
469 fn mask_all(&self) {
470 let max = self.max_redirection_entries();
471 for irq in 0..max {
472 self.mask_irq(irq);
473 }
474 }
475}
476
477// ---------------------------------------------------------------------------
478// Global APIC state (no static mut -- uses spin::Mutex)
479// ---------------------------------------------------------------------------
480
481/// Combined Local APIC + I/O APIC state, protected by a spinlock.
482struct ApicState {
483 local_apic: LocalApic,
484 io_apic: IoApic,
485}
486
487// SAFETY: ApicState contains only raw pointer-like fields (usize base
488// addresses) and is always accessed under a spinlock, so there are no data
489// races.
490unsafe impl Send for ApicState {}
491
492/// Global APIC state. Initialized once by `init()`.
493static APIC_STATE: Mutex<Option<ApicState>> = Mutex::new(None);
494
495/// Flag indicating whether the APIC subsystem has been initialized.
496static APIC_INITIALIZED: AtomicBool = AtomicBool::new(false);
497
498// ---------------------------------------------------------------------------
499// MSR helpers (delegated to arch::x86_64::msr module)
500// ---------------------------------------------------------------------------
501
502use super::msr::{phys_to_virt, rdmsr, wrmsr};
503
504/// Initialize the Local APIC and I/O APIC.
505///
506/// This function:
507/// 1. Reads the APIC base address from the IA32_APIC_BASE MSR.
508/// 2. Ensures the global APIC enable bit is set in the MSR.
509/// 3. Translates physical MMIO addresses to virtual using the bootloader's
510/// physical memory offset.
511/// 4. Initializes the Local APIC (software enable, mask all LVTs, set TPR=0).
512/// 5. Initializes the I/O APIC (mask all redirection entries).
513///
514/// Must be called after GDT/IDT initialization but before interrupts are
515/// enabled. Safe to call exactly once; subsequent calls return
516/// `AlreadyExists`.
517pub fn init() -> KernelResult<()> {
518 if APIC_INITIALIZED.load(Ordering::Acquire) {
519 return Err(KernelError::AlreadyExists {
520 resource: "APIC",
521 id: 0,
522 });
523 }
524
525 // Read APIC base from MSR.
526 let apic_base_msr = rdmsr(IA32_APIC_BASE_MSR);
527 let apic_base_phys = (apic_base_msr & 0xFFFF_F000) as usize;
528
529 println!(
530 "[APIC] IA32_APIC_BASE MSR = {:#x}, physical base = {:#x}",
531 apic_base_msr, apic_base_phys
532 );
533
534 // Translate physical APIC addresses to virtual addresses.
535 // The bootloader maps all physical memory at a dynamic offset; MMIO
536 // regions are NOT identity-mapped in a higher-half kernel.
537 let lapic_virt = phys_to_virt(apic_base_phys).ok_or(KernelError::NotInitialized {
538 subsystem: "physical memory mapping (APIC)",
539 })?;
540 let ioapic_virt = phys_to_virt(IOAPIC_BASE).ok_or(KernelError::NotInitialized {
541 subsystem: "physical memory mapping (I/O APIC)",
542 })?;
543
544 println!(
545 "[APIC] Virtual addresses: LAPIC={:#x}, IOAPIC={:#x}",
546 lapic_virt, ioapic_virt
547 );
548
549 // Ensure the global enable bit is set.
550 if apic_base_msr & IA32_APIC_BASE_ENABLE == 0 {
551 println!("[APIC] Global APIC enable bit not set, enabling...");
552 wrmsr(IA32_APIC_BASE_MSR, apic_base_msr | IA32_APIC_BASE_ENABLE);
553 }
554
555 // --- Local APIC initialization ---
556 let lapic = LocalApic::new(lapic_virt);
557
558 // Mask all LVT entries before enabling to prevent spurious interrupts.
559 lapic.mask_all_lvt();
560
561 // Enable the Local APIC via the Spurious Vector Register.
562 lapic.enable();
563
564 // Allow all interrupt priorities.
565 lapic.set_task_priority(0);
566
567 let apic_id = lapic.read_id();
568 println!(
569 "[APIC] Local APIC enabled (ID={}, SVR={:#x})",
570 apic_id,
571 lapic.read(LAPIC_SVR)
572 );
573
574 // --- I/O APIC initialization ---
575 let ioapic = IoApic::new(ioapic_virt);
576
577 // Mask all I/O APIC redirection entries by default.
578 ioapic.mask_all();
579
580 let max_irqs = ioapic.max_redirection_entries();
581 println!(
582 "[APIC] I/O APIC initialized at {:#x} ({} IRQ lines)",
583 ioapic_virt, max_irqs
584 );
585
586 // Store global state.
587 let mut state = APIC_STATE.lock();
588 *state = Some(ApicState {
589 local_apic: lapic,
590 io_apic: ioapic,
591 });
592 APIC_INITIALIZED.store(true, Ordering::Release);
593
594 println!("[APIC] APIC subsystem initialized successfully");
595 Ok(())
596}
597
598/// Check whether the APIC subsystem has been initialized.
599pub fn is_initialized() -> bool {
600 APIC_INITIALIZED.load(Ordering::Acquire)
601}
602
603/// Send an End-Of-Interrupt to the Local APIC.
604///
605/// Must be called at the end of every APIC-sourced interrupt handler.
606pub fn send_eoi() {
607 let state = APIC_STATE.lock();
608 if let Some(ref s) = *state {
609 s.local_apic.send_eoi();
610 }
611}
612
613/// Read the Local APIC ID of the current CPU.
614pub fn read_id() -> Option<u8> {
615 let state = APIC_STATE.lock();
616 state.as_ref().map(|s| s.local_apic.read_id())
617}
618
619/// Configure the Local APIC timer for periodic interrupts.
620///
621/// - `vector`: IDT vector number (e.g., 32 for the timer).
622/// - `divide`: Divide configuration register value:
623/// - `0x00` = divide by 2
624/// - `0x01` = divide by 4
625/// - `0x02` = divide by 8
626/// - `0x03` = divide by 16
627/// - `0x08` = divide by 32
628/// - `0x09` = divide by 64
629/// - `0x0A` = divide by 128
630/// - `0x0B` = divide by 1
631/// - `initial_count`: Initial countdown value.
632pub fn setup_timer(vector: u8, divide: u8, initial_count: u32) -> KernelResult<()> {
633 let state = APIC_STATE.lock();
634 match state.as_ref() {
635 Some(s) => {
636 s.local_apic.setup_timer(vector, divide, initial_count);
637 println!(
638 "[APIC] Timer configured: vector={}, divide={:#x}, count={}",
639 vector, divide, initial_count
640 );
641 Ok(())
642 }
643 None => Err(KernelError::NotInitialized { subsystem: "APIC" }),
644 }
645}
646
647/// Stop the Local APIC timer.
648pub fn stop_timer() -> KernelResult<()> {
649 let state = APIC_STATE.lock();
650 match state.as_ref() {
651 Some(s) => {
652 s.local_apic.stop_timer();
653 Ok(())
654 }
655 None => Err(KernelError::NotInitialized { subsystem: "APIC" }),
656 }
657}
658
659/// Route an external IRQ through the I/O APIC to a specific interrupt vector
660/// and destination CPU.
661///
662/// - `irq`: I/O APIC input pin (0-23 typically).
663/// - `vector`: IDT vector number.
664/// - `dest`: Destination Local APIC ID.
665pub fn set_irq_route(irq: u8, vector: u8, dest: u8) -> KernelResult<()> {
666 let state = APIC_STATE.lock();
667 match state.as_ref() {
668 Some(s) => {
669 s.io_apic.set_irq_route(irq, vector, dest);
670 Ok(())
671 }
672 None => Err(KernelError::NotInitialized { subsystem: "APIC" }),
673 }
674}
675
676/// Mask an IRQ in the I/O APIC.
677pub fn mask_irq(irq: u8) -> KernelResult<()> {
678 let state = APIC_STATE.lock();
679 match state.as_ref() {
680 Some(s) => {
681 s.io_apic.mask_irq(irq);
682 Ok(())
683 }
684 None => Err(KernelError::NotInitialized { subsystem: "APIC" }),
685 }
686}
687
688/// Unmask an IRQ in the I/O APIC.
689pub fn unmask_irq(irq: u8) -> KernelResult<()> {
690 let state = APIC_STATE.lock();
691 match state.as_ref() {
692 Some(s) => {
693 s.io_apic.unmask_irq(irq);
694 Ok(())
695 }
696 None => Err(KernelError::NotInitialized { subsystem: "APIC" }),
697 }
698}
699
700/// Send an Inter-Processor Interrupt via the Local APIC.
701///
702/// - `dest`: Destination APIC ID.
703/// - `vector`: Interrupt vector.
704pub fn send_ipi(dest: u8, vector: u8) -> KernelResult<()> {
705 let state = APIC_STATE.lock();
706 match state.as_ref() {
707 Some(s) => {
708 s.local_apic.send_ipi(dest, vector);
709 Ok(())
710 }
711 None => Err(KernelError::NotInitialized { subsystem: "APIC" }),
712 }
713}
714
715/// Send INIT IPI to a target CPU for AP startup sequence.
716pub fn send_init_ipi(dest: u8) -> KernelResult<()> {
717 let state = APIC_STATE.lock();
718 match state.as_ref() {
719 Some(s) => {
720 s.local_apic.send_init_ipi(dest);
721 Ok(())
722 }
723 None => Err(KernelError::NotInitialized { subsystem: "APIC" }),
724 }
725}
726
727/// Send Startup IPI (SIPI) to a target CPU for AP startup sequence.
728///
729/// `startup_page` is the physical page number where AP trampoline code resides
730/// (e.g., 0x08 for physical address 0x8000).
731pub fn send_startup_ipi(dest: u8, startup_page: u8) -> KernelResult<()> {
732 let state = APIC_STATE.lock();
733 match state.as_ref() {
734 Some(s) => {
735 s.local_apic.send_startup_ipi(dest, startup_page);
736 Ok(())
737 }
738 None => Err(KernelError::NotInitialized { subsystem: "APIC" }),
739 }
740}
741
742/// Broadcast an IPI to all CPUs except self. Used for TLB shootdown.
743pub fn send_ipi_all_excluding_self(vector: u8) -> KernelResult<()> {
744 let state = APIC_STATE.lock();
745 match state.as_ref() {
746 Some(s) => {
747 s.local_apic.send_ipi_all_excluding_self(vector);
748 Ok(())
749 }
750 None => Err(KernelError::NotInitialized { subsystem: "APIC" }),
751 }
752}
753
754// ---------------------------------------------------------------------------
755// APIC Timer calibration and start
756// ---------------------------------------------------------------------------
757
758/// Calibrated APIC timer ticks per millisecond. Set by `calibrate_timer()`.
759static APIC_TICKS_PER_MS: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
760
761/// Whether the APIC timer has been calibrated and started.
762static APIC_TIMER_ACTIVE: AtomicBool = AtomicBool::new(false);
763
764/// Check whether the APIC timer is active.
765pub fn is_timer_active() -> bool {
766 APIC_TIMER_ACTIVE.load(Ordering::Acquire)
767}
768
769/// Get calibrated APIC timer ticks per millisecond.
770pub fn ticks_per_ms() -> u32 {
771 APIC_TICKS_PER_MS.load(core::sync::atomic::Ordering::Relaxed)
772}
773
774/// Calibrate the APIC timer frequency using the PIT (8254) as a reference.
775///
776/// Method: Configure the APIC timer in one-shot mode with a large initial
777/// count, then use the PIT to measure how many APIC ticks elapse in a known
778/// time period (10ms). From this, compute ticks-per-millisecond.
779///
780/// Must be called after `init()` and before `start_timer()`.
781pub fn calibrate_timer() -> KernelResult<u32> {
782 let state = APIC_STATE.lock();
783 let s = state
784 .as_ref()
785 .ok_or(KernelError::NotInitialized { subsystem: "APIC" })?;
786
787 let lapic = &s.local_apic;
788
789 // Use divide-by-16 for calibration (gives good resolution).
790 let divide = 0x03u8; // divide by 16
791 lapic.write(LAPIC_TIMER_DIV, divide as u32);
792
793 // Set timer to one-shot mode with a very large initial count.
794 // Mask the interrupt during calibration.
795 lapic.write(LAPIC_LVT_TIMER, LVT_MASK | APIC_TIMER_VECTOR as u32);
796 lapic.write(LAPIC_TIMER_INIT_COUNT, 0xFFFF_FFFF);
797
798 // Use PIT channel 2 to measure ~10ms.
799 // PIT frequency = 1,193,182 Hz, so 10ms = 11,932 ticks.
800 const PIT_FREQ: u32 = 1_193_182;
801 const CALIBRATION_MS: u32 = 10;
802 let pit_ticks = PIT_FREQ * CALIBRATION_MS / 1000;
803
804 // SAFETY: PIT port I/O for calibration. Port 0x61 controls PIT gate/speaker,
805 // 0x43 is command, 0x42 is channel 2 data. These are standard x86 I/O ports.
806 unsafe {
807 use x86_64::instructions::port::Port;
808
809 // Configure PIT channel 2 for one-shot countdown.
810 let mut port_61 = Port::<u8>::new(0x61);
811 let mut cmd = Port::<u8>::new(0x43);
812 let mut ch2 = Port::<u8>::new(0x42);
813
814 // Disable speaker, enable gate for channel 2.
815 let gate = port_61.read();
816 port_61.write((gate & 0xFD) | 0x01); // gate=1, speaker=0
817
818 // Channel 2, lobyte/hibyte, one-shot mode.
819 cmd.write(0xB0);
820
821 // Load countdown value.
822 ch2.write((pit_ticks & 0xFF) as u8);
823 ch2.write(((pit_ticks >> 8) & 0xFF) as u8);
824
825 // Reset the gate to start countdown (toggle gate: low then high).
826 let g = port_61.read();
827 port_61.write(g & 0xFE); // gate low
828 port_61.write(g | 0x01); // gate high -- starts countdown
829
830 // Wait for PIT channel 2 output to go high (bit 5 of port 0x61).
831 loop {
832 let status = port_61.read();
833 if status & 0x20 != 0 {
834 break;
835 }
836 }
837 }
838
839 // Read how many APIC ticks elapsed during the PIT countdown.
840 let remaining = lapic.read(LAPIC_TIMER_CUR_COUNT);
841 let elapsed = 0xFFFF_FFFFu32.wrapping_sub(remaining);
842
843 // Stop the timer.
844 lapic.write(LAPIC_TIMER_INIT_COUNT, 0);
845
846 // Calculate ticks per millisecond.
847 let ticks_per_ms = elapsed / CALIBRATION_MS;
848
849 if ticks_per_ms == 0 {
850 println!("[APIC] Timer calibration failed: 0 ticks elapsed");
851 return Err(KernelError::HardwareError {
852 device: "APIC timer",
853 code: 0,
854 });
855 }
856
857 APIC_TICKS_PER_MS.store(ticks_per_ms, core::sync::atomic::Ordering::Relaxed);
858
859 // Estimate frequency in MHz.
860 let freq_mhz = (ticks_per_ms as u64 * 1000 * 16) / 1_000_000; // *16 for divisor
861 println!(
862 "[APIC] Timer calibrated: {} ticks/ms, ~{}MHz bus (div16)",
863 ticks_per_ms, freq_mhz
864 );
865
866 Ok(ticks_per_ms)
867}
868
869/// Start the APIC timer in periodic mode at approximately `freq_hz` Hz.
870///
871/// Must be called after `calibrate_timer()`. The timer fires vector
872/// `APIC_TIMER_VECTOR` (48) which must be registered in the IDT.
873pub fn start_timer(freq_hz: u32) -> KernelResult<()> {
874 let tpm = APIC_TICKS_PER_MS.load(core::sync::atomic::Ordering::Relaxed);
875 if tpm == 0 {
876 return Err(KernelError::NotInitialized {
877 subsystem: "APIC timer (not calibrated)",
878 });
879 }
880
881 // Calculate initial count for the desired frequency.
882 // ticks_per_ms * 1000 / freq_hz = ticks per interrupt period.
883 let initial_count = (tpm as u64 * 1000) / (freq_hz as u64);
884 if initial_count == 0 || initial_count > 0xFFFF_FFFF {
885 println!(
886 "[APIC] Invalid timer count for {}Hz: {}",
887 freq_hz, initial_count
888 );
889 return Err(KernelError::HardwareError {
890 device: "APIC timer",
891 code: freq_hz,
892 });
893 }
894
895 let state = APIC_STATE.lock();
896 let s = state
897 .as_ref()
898 .ok_or(KernelError::NotInitialized { subsystem: "APIC" })?;
899
900 // Configure periodic mode at APIC_TIMER_VECTOR with divide-by-16.
901 s.local_apic
902 .setup_timer(APIC_TIMER_VECTOR, 0x03, initial_count as u32);
903
904 APIC_TIMER_ACTIVE.store(true, Ordering::Release);
905
906 println!(
907 "[APIC] Timer started: {}Hz, initial_count={}, vector={}",
908 freq_hz, initial_count, APIC_TIMER_VECTOR
909 );
910
911 Ok(())
912}