⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

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}