kernel/cpu/interrupts/
apic.rs

1use alloc::vec::Vec;
2use tracing::{error, warn};
3
4use crate::{
5    acpi::tables::{self, BiosTables, InterruptControllerStruct, InterruptSourceOverride},
6    cpu::{self, idt::InterruptStackFrame64, Cpu, CPUS, MAX_CPUS},
7    memory_management::virtual_space::VirtualSpace,
8    sync::{once::OnceLock, spin::mutex::Mutex},
9    utils::vcell::RW,
10};
11
12use super::{
13    allocate_basic_user_interrupt, allocate_user_interrupt, allocate_user_interrupt_all_saved,
14    InterruptHandler,
15};
16
17const APIC_BAR_ENABLED: u64 = 1 << 11;
18const APIC_BASE_MASK: u64 = 0xFFFF_FFFF_FFFF_F000;
19
20static APIC: OnceLock<Mutex<Apic>> = OnceLock::new();
21
22pub fn init(bios_tables: &BiosTables) {
23    if APIC.try_get().is_some() {
24        panic!("APIC already initialized");
25    }
26
27    disable_pic();
28    APIC.get_or_init(|| Mutex::new(Apic::new(bios_tables)));
29}
30
31fn disable_pic() {
32    unsafe {
33        cpu::io_out::<u8>(0x21, 0xFF);
34        cpu::io_out::<u8>(0xA1, 0xFF);
35    }
36}
37
38pub fn return_from_interrupt() {
39    APIC.get().lock().return_from_interrupt();
40}
41
42pub fn is_irq_assigned(irq_num: u8) -> bool {
43    APIC.get().lock().is_irq_assigned(irq_num)
44}
45
46pub fn assign_io_irq<H: InterruptHandler>(handler: H, interrupt_num: u8, cpu: &Cpu) {
47    APIC.get().lock().assign_io_irq(handler, interrupt_num, cpu)
48}
49
50#[allow(dead_code)]
51pub fn assign_io_irq_custom<H: InterruptHandler, F>(
52    handler: H,
53    interrupt_num: u8,
54    cpu: &Cpu,
55    modify_entry: F,
56) where
57    F: FnOnce(IoApicRedirectionBuilder) -> IoApicRedirectionBuilder,
58{
59    APIC.get()
60        .lock()
61        .assign_io_irq_custom(handler, interrupt_num, cpu, modify_entry)
62}
63
64#[repr(C, align(4))]
65struct ApicReg {
66    reg: RW<u32>,
67    pad: [u32; 3],
68}
69
70impl ApicReg {
71    fn write(&mut self, value: u32) {
72        unsafe { self.reg.write(value) };
73    }
74
75    fn read(&self) -> u32 {
76        self.reg.read()
77    }
78}
79
80#[repr(C)]
81struct LocalVectorRegister {
82    reg: ApicReg,
83}
84
85#[allow(dead_code)]
86impl LocalVectorRegister {
87    fn read(&self) -> LocalVectorRegisterBuilder {
88        LocalVectorRegisterBuilder {
89            reg: self.reg.read(),
90        }
91    }
92
93    fn write(&mut self, builder: LocalVectorRegisterBuilder) {
94        self.reg.write(builder.reg)
95    }
96}
97
98const LVT_VECTOR_MASK: u32 = 0xFF;
99const LVT_MESSAGE_TYPE_MASK: u32 = 0x7 << 8;
100const LVT_TRIGGER_MODE_MASK: u32 = 1 << 15;
101const LVT_MASK_MASK: u32 = 1 << 16;
102const LVT_TIMER_MODE_MASK: u32 = 1 << 17;
103
104const SPURIOUS_ENABLE: u32 = 1 << 8;
105
106#[derive(Default, Clone, Copy)]
107struct LocalVectorRegisterBuilder {
108    reg: u32,
109}
110
111#[allow(dead_code)]
112impl LocalVectorRegisterBuilder {
113    fn with_vector(mut self, vector: u8) -> Self {
114        self.reg = (self.reg & !LVT_VECTOR_MASK) | vector as u32;
115        self
116    }
117
118    fn with_message_type(mut self, message_type: u8) -> Self {
119        self.reg = (self.reg & !LVT_MESSAGE_TYPE_MASK) | ((message_type & 0x7) as u32) << 8;
120        self
121    }
122
123    fn with_trigger_mode(mut self, trigger_mode: bool) -> Self {
124        self.reg = (self.reg & !LVT_TRIGGER_MODE_MASK) | (trigger_mode as u32) << 15;
125        self
126    }
127
128    fn with_mask(mut self, mask: bool) -> Self {
129        self.reg = (self.reg & !LVT_MASK_MASK) | (mask as u32) << 16;
130        self
131    }
132
133    fn with_periodic_timer(mut self, timer_mode: bool) -> Self {
134        self.reg = (self.reg & !LVT_TIMER_MODE_MASK) | (timer_mode as u32) << 17;
135        self
136    }
137}
138
139#[repr(C, align(16))]
140struct ApicMmio {
141    _pad1: [ApicReg; 2],
142    id: ApicReg,
143    version: ApicReg,
144    _pad2: [ApicReg; 4],
145    task_priority: ApicReg,
146    arbitration_priority: ApicReg,
147    processor_priority: ApicReg,
148    end_of_interrupt: ApicReg,
149    remote_read: ApicReg,
150    logical_destination: ApicReg,
151    destination_format: ApicReg,
152    spurious_interrupt_vector: ApicReg,
153    in_service: [ApicReg; 8],
154    trigger_mode: [ApicReg; 8],
155    interrupt_request: [ApicReg; 8],
156    error_status: ApicReg,
157    _pad3: [ApicReg; 7],
158    interrupt_command_low: ApicReg,
159    interrupt_command_high: ApicReg,
160    timer_local_vector_table: LocalVectorRegister,
161    thermal_local_vector_table: LocalVectorRegister,
162    performance_local_vector_table: LocalVectorRegister,
163    lint0_local_vector_table: LocalVectorRegister,
164    lint1_local_vector_table: LocalVectorRegister,
165    error_local_vector_table: LocalVectorRegister,
166    timer_initial_count: ApicReg,
167    timer_current_count: ApicReg,
168    _pad4: [ApicReg; 4],
169    timer_divide_configuration: ApicReg,
170    _pad5: ApicReg,
171    extended_apic_features: ApicReg,
172    extended_apic_control: ApicReg,
173    specific_end_of_interrupt: ApicReg,
174    _pad6: [ApicReg; 5],
175    interrupt_enable: [ApicReg; 8],
176    extended_interrupt_local_vector_tables: [ApicReg; 4],
177}
178
179#[allow(dead_code)]
180mod io_apic {
181    pub const IO_APIC_ID: u32 = 0;
182    pub const IO_APIC_VERSION: u32 = 1;
183    pub const IO_APIC_ARBITRATION_ID: u32 = 2;
184    pub const IO_APIC_REDIRECTION_TABLE: u32 = 0x10;
185
186    pub const RDR_VECTOR_MASK: u64 = 0xFF;
187    pub const RDR_DELIVERY_MODE_MASK: u64 = 0x7 << 8;
188    pub const RDR_DESTINATION_MODE_MASK: u64 = 1 << 11;
189    pub const RDR_DELIVERY_STATUS_MASK: u64 = 1 << 12;
190    pub const RDR_PIN_POLARITY_MASK: u64 = 1 << 13;
191    pub const RDR_REMOTE_IRR_MASK: u64 = 1 << 14;
192    pub const RDR_TRIGGER_MODE_MASK: u64 = 1 << 15;
193    pub const RDR_MASK_MASK: u64 = 1 << 16;
194    pub const RDR_DESTINATION_PHYSICAL_MASK: u64 = 0x1F << 59;
195    pub const RDR_DESTINATION_LOGICAL_MASK: u64 = 0xFF << 56;
196}
197
198#[allow(dead_code)]
199enum DestinationType {
200    Physical(u8),
201    Logical(u8),
202}
203
204#[derive(Default, Clone, Copy)]
205pub struct IoApicRedirectionBuilder {
206    reg: u64,
207}
208
209#[allow(dead_code)]
210impl IoApicRedirectionBuilder {
211    fn with_vector(mut self, vector: u8) -> Self {
212        self.reg = (self.reg & !io_apic::RDR_VECTOR_MASK) | vector as u64;
213        self
214    }
215
216    fn with_delivery_mode(mut self, delivery_mode: u8) -> Self {
217        self.reg =
218            (self.reg & !io_apic::RDR_DELIVERY_MODE_MASK) | ((delivery_mode & 0x7) as u64) << 8;
219        self
220    }
221
222    pub fn with_interrupt_polarity_low(mut self, polarity: bool) -> Self {
223        self.reg = (self.reg & !io_apic::RDR_PIN_POLARITY_MASK) | (polarity as u64) << 13;
224        self
225    }
226
227    pub fn with_trigger_mode_level(mut self, trigger_mode: bool) -> Self {
228        self.reg = (self.reg & !io_apic::RDR_TRIGGER_MODE_MASK) | (trigger_mode as u64) << 15;
229        self
230    }
231
232    pub fn with_mask(mut self, mask: bool) -> Self {
233        self.reg = (self.reg & !io_apic::RDR_MASK_MASK) | (mask as u64) << 16;
234        self
235    }
236
237    fn with_destination(mut self, destination: DestinationType) -> Self {
238        match destination {
239            DestinationType::Physical(d) => {
240                assert!(d < 32, "physical destination is out of range");
241                // clear the destination mode bit
242                self.reg &= !io_apic::RDR_DESTINATION_MODE_MASK;
243                self.reg = (self.reg & !io_apic::RDR_DESTINATION_PHYSICAL_MASK)
244                    | (((d & 0x1F) as u64) << 59);
245            }
246            DestinationType::Logical(d) => {
247                // set the destination mode bit
248                self.reg |= io_apic::RDR_DESTINATION_MODE_MASK;
249                self.reg = (self.reg & !io_apic::RDR_DESTINATION_LOGICAL_MASK) | ((d as u64) << 56);
250            }
251        }
252
253        self
254    }
255}
256
257#[repr(C, align(16))]
258struct IoApicMmio {
259    register_select: ApicReg,
260    data: ApicReg,
261}
262
263impl IoApicMmio {
264    pub fn read_register(&mut self, register: u32) -> u32 {
265        self.register_select.write(register);
266        self.data.read()
267    }
268
269    pub fn write_register(&mut self, register: u32, value: u32) {
270        self.register_select.write(register);
271        self.data.write(value);
272    }
273}
274
275#[allow(dead_code)]
276struct IoApic {
277    id: u8,
278    global_irq_base: u32,
279    n_entries: u8,
280    mmio: VirtualSpace<IoApicMmio>,
281}
282
283impl IoApic {
284    fn reset_all_interrupts(&mut self) {
285        for i in 0..self.n_entries {
286            let b = IoApicRedirectionBuilder::default()
287                .with_vector(0)
288                .with_mask(true);
289            self.write_redirect_entry(i, b);
290        }
291    }
292
293    fn read_register(&mut self, register: u32) -> u32 {
294        self.mmio.read_register(register)
295    }
296
297    fn write_register(&mut self, register: u32, value: u32) {
298        self.mmio.write_register(register, value)
299    }
300
301    fn write_redirect_entry(&mut self, entry: u8, builder: IoApicRedirectionBuilder) {
302        let lo = builder.reg as u32;
303        let hi = (builder.reg >> 32) as u32;
304        self.write_register(io_apic::IO_APIC_REDIRECTION_TABLE + entry as u32 * 2, lo);
305        self.write_register(
306            io_apic::IO_APIC_REDIRECTION_TABLE + entry as u32 * 2 + 1,
307            hi,
308        );
309    }
310
311    fn is_entry_taken(&mut self, entry: u8) -> bool {
312        let lo = self.read_register(io_apic::IO_APIC_REDIRECTION_TABLE + entry as u32 * 2);
313        lo as u64 & io_apic::RDR_VECTOR_MASK != 0
314    }
315}
316
317impl From<tables::IoApic> for IoApic {
318    fn from(table: tables::IoApic) -> Self {
319        assert!(table.io_apic_address != 0, "IO APIC address is 0");
320        assert_eq!(
321            table.io_apic_address & 0xF,
322            0,
323            "IO APIC address is not aligned"
324        );
325        let mut s = Self {
326            id: table.io_apic_id,
327            global_irq_base: table.global_system_interrupt_base,
328            n_entries: 0, // to be filled next
329            // Safety: we are sure that the address is valid
330            mmio: unsafe { VirtualSpace::new(table.io_apic_address as u64).unwrap() },
331        };
332        s.n_entries = (s.read_register(io_apic::IO_APIC_VERSION) >> 16) as u8 + 1;
333        s
334    }
335}
336
337struct Apic {
338    mmio: VirtualSpace<ApicMmio>,
339    n_cpus: usize,
340    io_apics: Vec<IoApic>,
341    source_overrides: Vec<InterruptSourceOverride>,
342}
343
344impl Apic {
345    fn new(bios_tables: &BiosTables) -> Self {
346        // do we have APIC in this cpu?
347        let cpuid = unsafe { cpu::cpuid::cpuid!(cpu::cpuid::FN_FEAT) };
348        if cpuid.edx & cpu::cpuid::FEAT_EDX_APIC == 0 {
349            panic!("APIC is not supported");
350        }
351        let apic_bar = unsafe { cpu::msr::read(cpu::msr::APIC_BASE) };
352        if apic_bar & APIC_BAR_ENABLED == 0 {
353            // enable APIC
354            unsafe {
355                cpu::msr::write(cpu::msr::APIC_BASE, apic_bar | APIC_BAR_ENABLED);
356            }
357            // recheck
358            let apic_bar = unsafe { cpu::msr::read(cpu::msr::APIC_BASE) };
359            if apic_bar & APIC_BAR_ENABLED == 0 {
360                panic!("APIC is not enabled");
361            }
362        }
363        let mut apic_address = apic_bar & APIC_BASE_MASK;
364
365        // process the MADT table
366        let madt_table = bios_tables
367            .rsdt
368            .get_table::<tables::Apic>()
369            .expect("MADT table not found");
370
371        if madt_table.local_apic_address as u64 != apic_address {
372            warn!(
373                "MADT table has a different APIC address (CPU:{:X}, MADT:{:X}), using MADT...",
374                apic_address, madt_table.local_apic_address
375            );
376            apic_address = madt_table.local_apic_address as u64;
377        }
378
379        let mut n_cpus = 0;
380        let mut io_apics = Vec::new();
381        let mut source_overrides = Vec::new();
382
383        for strct in &madt_table.interrupt_controller_structs {
384            match strct {
385                InterruptControllerStruct::ProcessorLocalApic(s) => {
386                    if s.flags & 1 == 0 {
387                        // this is a disabled processor
388                        continue;
389                    }
390                    if n_cpus >= MAX_CPUS {
391                        warn!("too many CPUs, have {MAX_CPUS} already, ignoring the rest");
392                    } else {
393                        // initialize the CPUs
394                        // SAFETY: this is safe
395                        unsafe {
396                            CPUS[n_cpus].init(n_cpus, s.apic_id);
397                        }
398                        n_cpus += 1;
399                    }
400                }
401                InterruptControllerStruct::IoApic(s) => {
402                    io_apics.push(s.clone().into());
403                }
404                InterruptControllerStruct::InterruptSourceOverride(s) => {
405                    source_overrides.push(s.clone());
406                }
407                InterruptControllerStruct::NonMaskableInterrupt(_) => todo!(),
408                InterruptControllerStruct::LocalApicNmi(_s) => {
409                    // for now, we are not using nmi, so we just ignore it
410                    // assert!(s.acpi_processor_uid == 0xFF);
411                    // assert!(s.flags == 0);
412                    // assert!(s.local_apic_lint == 1);
413                }
414                InterruptControllerStruct::LocalApicAddressOverride(s) => {
415                    apic_address = s.local_apic_address;
416                }
417                InterruptControllerStruct::Unknown { struct_type, bytes } => {
418                    warn!(
419                        "unknown interrupt controller struct type {:#X} with {:#X?} bytes",
420                        struct_type, bytes
421                    );
422                }
423            }
424        }
425
426        assert!(
427            n_cpus > 0,
428            "no CPUs found in the MADT table, cannot continue"
429        );
430        assert!(
431            !io_apics.is_empty(),
432            "no IO APICs found in the MADT table, cannot continue"
433        );
434        assert_ne!(apic_address, 0, "APIC address is 0, cannot continue");
435        assert_eq!(apic_address & 0xF, 0, "APIC address is not aligned");
436
437        // reset all interrupts
438        io_apics.iter_mut().for_each(|io_apic: &mut IoApic| {
439            io_apic.reset_all_interrupts();
440        });
441
442        let mut s = Self {
443            // Safety: we are sure that the address is valid
444            mmio: unsafe {
445                VirtualSpace::new(apic_address).expect("Could not map APIC virtual space")
446            },
447            n_cpus,
448            io_apics,
449            source_overrides,
450        };
451
452        s.initialize_spurious_interrupt();
453        s.disable_local_interrupts();
454        s.initialize_timer();
455        s.setup_error_interrupt();
456        // ack any pending interrupts
457        s.return_from_interrupt();
458
459        s
460    }
461
462    fn return_from_interrupt(&mut self) {
463        self.mmio.end_of_interrupt.write(0);
464    }
465
466    fn initialize_spurious_interrupt(&mut self) {
467        let interrupt_num = allocate_basic_user_interrupt(spurious_handler);
468        // 1 << 8, to enable spurious interrupts
469        self.mmio
470            .spurious_interrupt_vector
471            .write(SPURIOUS_ENABLE | interrupt_num as u32);
472    }
473
474    /// disable the Local interrupts 0 and 1
475    fn disable_local_interrupts(&mut self) {
476        let vector_table = LocalVectorRegisterBuilder::default().with_mask(true);
477        self.mmio.lint0_local_vector_table.write(vector_table);
478        self.mmio.lint1_local_vector_table.write(vector_table);
479    }
480
481    fn initialize_timer(&mut self) {
482        let interrupt_num = allocate_user_interrupt_all_saved(super::handlers::apic_timer_handler);
483
484        // divide by 1
485        self.mmio.timer_divide_configuration.write(0b1011);
486        // just random value, this is based on the CPU clock speed
487        // so its not accurate timing.
488        self.mmio.timer_initial_count.write(0x1000000);
489        // periodic mode, not masked, and with the allocated vector number
490        let vector_table = LocalVectorRegisterBuilder::default()
491            .with_periodic_timer(true)
492            .with_mask(false)
493            .with_vector(interrupt_num);
494        self.mmio.timer_local_vector_table.write(vector_table);
495    }
496
497    fn setup_error_interrupt(&mut self) {
498        // clear the error status and write 0 to it
499        // 1- clear the error status
500        self.mmio.error_status.write(0);
501        // 2- write 0 to it (yes, we have to do this twice)
502        self.mmio.error_status.write(0);
503
504        let interrupt_num = allocate_basic_user_interrupt(error_interrupt_handler);
505        // not masked, and with the allocated vector number
506        let vector_table = LocalVectorRegisterBuilder::default()
507            .with_mask(false)
508            .with_vector(interrupt_num);
509        self.mmio.error_local_vector_table.write(vector_table);
510    }
511
512    fn get_irq_ioapic_entry(
513        &mut self,
514        irq_num: u8,
515    ) -> Option<(u8, &mut IoApic, Option<&InterruptSourceOverride>)> {
516        // if we have override mapping for this interrupt, use it.
517        let int_override = self
518            .source_overrides
519            .iter()
520            .find(|int_override| int_override.source == irq_num);
521        let mut interrupt_num = irq_num as u32;
522        if let Some(int_override) = int_override {
523            interrupt_num = int_override.global_system_interrupt;
524        }
525        self.io_apics
526            .iter_mut()
527            .find(|io_apic| {
528                io_apic.global_irq_base <= interrupt_num
529                    && interrupt_num < io_apic.global_irq_base + io_apic.n_entries as u32
530            })
531            .map(|io_apic| {
532                let entry_in_ioapic = interrupt_num - io_apic.global_irq_base;
533                (entry_in_ioapic as u8, io_apic, int_override)
534            })
535    }
536
537    fn is_irq_assigned(&mut self, irq_num: u8) -> bool {
538        let (entry_in_ioapic, io_apic, _) = self
539            .get_irq_ioapic_entry(irq_num)
540            .expect("Could not find IO APIC for the interrupt");
541        io_apic.is_entry_taken(entry_in_ioapic)
542    }
543
544    fn assign_io_irq<H: InterruptHandler>(&mut self, handler: H, irq_num: u8, cpu: &Cpu) {
545        self.assign_io_irq_custom(handler, irq_num, cpu, |b| b)
546    }
547
548    fn assign_io_irq_custom<H: InterruptHandler, F>(
549        &mut self,
550        handler: H,
551        irq_num: u8,
552        cpu: &Cpu,
553        modify_entry: F,
554    ) where
555        F: FnOnce(IoApicRedirectionBuilder) -> IoApicRedirectionBuilder,
556    {
557        assert!(cpu.id < self.n_cpus, "CPU ID is out of range");
558        assert!(irq_num < 24, "interrupt number is out of range");
559
560        let (entry_in_ioapic, io_apic, int_override) = self
561            .get_irq_ioapic_entry(irq_num)
562            .expect("Could not find IO APIC for the interrupt");
563        // TODO: this is added for catching bugs early, probably we should return `Result`
564        // using `is_irq_assigned`.
565        assert!(
566            !io_apic.is_entry_taken(entry_in_ioapic),
567            "entry is already taken"
568        );
569
570        // override config
571        let trigger_mode_level = if let Some(int_override) = int_override {
572            match (int_override.flags >> 2) & 0b11 {
573                0b00 => {
574                    // Conforms to the specifications of the bus
575                    // TODO: ok to keep without changing?
576                    false
577                }
578                0b01 => {
579                    // Edge
580                    false
581                }
582                0b10 => {
583                    panic!("Reserved value 0b10 used for trigger mode in interrupt override");
584                }
585                0b11 => {
586                    // Level
587                    true
588                }
589                _ => unreachable!(),
590            }
591        } else {
592            // default
593            false
594        };
595        let polarity_low = if let Some(int_override) = int_override {
596            match int_override.flags & 0b11 {
597                0b00 => {
598                    // Conforms to the specifications of the bus
599                    true
600                }
601                0b01 => {
602                    // High
603                    false
604                }
605                0b10 => {
606                    panic!("Reserved value 0b10 used for polarity in interrupt override");
607                }
608                0b11 => {
609                    // Low
610                    true
611                }
612                _ => unreachable!(),
613            }
614        } else {
615            // default
616            false
617        };
618
619        let vector_num = allocate_user_interrupt(handler);
620        let b = IoApicRedirectionBuilder::default()
621            .with_vector(vector_num)
622            .with_delivery_mode(0) // fixed
623            .with_interrupt_polarity_low(polarity_low) // active high
624            .with_trigger_mode_level(trigger_mode_level) // edge
625            .with_mask(false) // not masked
626            .with_destination(DestinationType::Physical(cpu.apic_id));
627
628        let b = modify_entry(b);
629
630        io_apic.write_redirect_entry(entry_in_ioapic, b);
631    }
632}
633
634extern "x86-interrupt" fn spurious_handler(_frame: InterruptStackFrame64) {
635    warn!("Spurious interrupt");
636    return_from_interrupt();
637}
638
639extern "x86-interrupt" fn error_interrupt_handler(_frame: InterruptStackFrame64) {
640    let error_status = APIC.get().lock().mmio.error_status.read();
641    error!("APIC error: {:#X}", error_status);
642    // clear the error
643    APIC.get().lock().mmio.error_status.write(0);
644    return_from_interrupt();
645}