axhal/platform/x86_pc/
apic.rs

1#![allow(dead_code)]
2
3use core::{cell::SyncUnsafeCell, mem::MaybeUninit};
4
5use kspin::SpinNoIrq;
6use lazyinit::LazyInit;
7use memory_addr::PhysAddr;
8use x2apic::ioapic::IoApic;
9use x2apic::lapic::{LocalApic, LocalApicBuilder, xapic_base};
10use x86_64::instructions::port::Port;
11
12use self::vectors::*;
13use crate::mem::phys_to_virt;
14
15pub(super) mod vectors {
16    pub const APIC_TIMER_VECTOR: u8 = 0xf0;
17    pub const APIC_SPURIOUS_VECTOR: u8 = 0xf1;
18    pub const APIC_ERROR_VECTOR: u8 = 0xf2;
19}
20
21/// The maximum number of IRQs.
22pub const MAX_IRQ_COUNT: usize = 256;
23
24/// The timer IRQ number.
25pub const TIMER_IRQ_NUM: usize = APIC_TIMER_VECTOR as usize;
26
27const IO_APIC_BASE: PhysAddr = pa!(0xFEC0_0000);
28
29static LOCAL_APIC: SyncUnsafeCell<MaybeUninit<LocalApic>> =
30    SyncUnsafeCell::new(MaybeUninit::uninit());
31static mut IS_X2APIC: bool = false;
32static IO_APIC: LazyInit<SpinNoIrq<IoApic>> = LazyInit::new();
33
34/// Enables or disables the given IRQ.
35#[cfg(feature = "irq")]
36pub fn set_enable(vector: usize, enabled: bool) {
37    // should not affect LAPIC interrupts
38    if vector < APIC_TIMER_VECTOR as _ {
39        unsafe {
40            if enabled {
41                IO_APIC.lock().enable_irq(vector as u8);
42            } else {
43                IO_APIC.lock().disable_irq(vector as u8);
44            }
45        }
46    }
47}
48
49/// Registers an IRQ handler for the given IRQ.
50///
51/// It also enables the IRQ if the registration succeeds. It returns `false` if
52/// the registration failed.
53#[cfg(feature = "irq")]
54pub fn register_handler(vector: usize, handler: crate::irq::IrqHandler) -> bool {
55    crate::irq::register_handler_common(vector, handler)
56}
57
58/// Dispatches the IRQ.
59///
60/// This function is called by the common interrupt handler. It looks
61/// up in the IRQ handler table and calls the corresponding handler. If
62/// necessary, it also acknowledges the interrupt controller after handling.
63#[cfg(feature = "irq")]
64pub fn dispatch_irq(vector: usize) {
65    crate::irq::dispatch_irq_common(vector);
66    unsafe { local_apic().end_of_interrupt() };
67}
68
69pub(super) fn local_apic<'a>() -> &'a mut LocalApic {
70    // It's safe as `LOCAL_APIC` is initialized in `init_primary`.
71    unsafe { LOCAL_APIC.get().as_mut().unwrap().assume_init_mut() }
72}
73
74pub(super) fn raw_apic_id(id_u8: u8) -> u32 {
75    if unsafe { IS_X2APIC } {
76        id_u8 as u32
77    } else {
78        (id_u8 as u32) << 24
79    }
80}
81
82fn cpu_has_x2apic() -> bool {
83    match raw_cpuid::CpuId::new().get_feature_info() {
84        Some(finfo) => finfo.has_x2apic(),
85        None => false,
86    }
87}
88
89pub(super) fn init_primary() {
90    info!("Initialize Local APIC...");
91
92    unsafe {
93        // Disable 8259A interrupt controllers
94        Port::<u8>::new(0x21).write(0xff);
95        Port::<u8>::new(0xA1).write(0xff);
96    }
97
98    let mut builder = LocalApicBuilder::new();
99    builder
100        .timer_vector(APIC_TIMER_VECTOR as _)
101        .error_vector(APIC_ERROR_VECTOR as _)
102        .spurious_vector(APIC_SPURIOUS_VECTOR as _);
103
104    if cpu_has_x2apic() {
105        info!("Using x2APIC.");
106        unsafe { IS_X2APIC = true };
107    } else {
108        info!("Using xAPIC.");
109        let base_vaddr = phys_to_virt(pa!(unsafe { xapic_base() } as usize));
110        builder.set_xapic_base(base_vaddr.as_usize() as u64);
111    }
112
113    let mut lapic = builder.build().unwrap();
114    unsafe {
115        lapic.enable();
116        LOCAL_APIC.get().as_mut().unwrap().write(lapic);
117    }
118
119    info!("Initialize IO APIC...");
120    let io_apic = unsafe { IoApic::new(phys_to_virt(IO_APIC_BASE).as_usize() as u64) };
121    IO_APIC.init_once(SpinNoIrq::new(io_apic));
122}
123
124#[cfg(feature = "smp")]
125pub(super) fn init_secondary() {
126    unsafe { local_apic().enable() };
127}