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 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 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, 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 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 unsafe {
355 cpu::msr::write(cpu::msr::APIC_BASE, apic_bar | APIC_BAR_ENABLED);
356 }
357 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 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 continue;
389 }
390 if n_cpus >= MAX_CPUS {
391 warn!("too many CPUs, have {MAX_CPUS} already, ignoring the rest");
392 } else {
393 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 }
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 io_apics.iter_mut().for_each(|io_apic: &mut IoApic| {
439 io_apic.reset_all_interrupts();
440 });
441
442 let mut s = Self {
443 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 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 self.mmio
470 .spurious_interrupt_vector
471 .write(SPURIOUS_ENABLE | interrupt_num as u32);
472 }
473
474 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 self.mmio.timer_divide_configuration.write(0b1011);
486 self.mmio.timer_initial_count.write(0x1000000);
489 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 self.mmio.error_status.write(0);
501 self.mmio.error_status.write(0);
503
504 let interrupt_num = allocate_basic_user_interrupt(error_interrupt_handler);
505 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 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 assert!(
566 !io_apic.is_entry_taken(entry_in_ioapic),
567 "entry is already taken"
568 );
569
570 let trigger_mode_level = if let Some(int_override) = int_override {
572 match (int_override.flags >> 2) & 0b11 {
573 0b00 => {
574 false
577 }
578 0b01 => {
579 false
581 }
582 0b10 => {
583 panic!("Reserved value 0b10 used for trigger mode in interrupt override");
584 }
585 0b11 => {
586 true
588 }
589 _ => unreachable!(),
590 }
591 } else {
592 false
594 };
595 let polarity_low = if let Some(int_override) = int_override {
596 match int_override.flags & 0b11 {
597 0b00 => {
598 true
600 }
601 0b01 => {
602 false
604 }
605 0b10 => {
606 panic!("Reserved value 0b10 used for polarity in interrupt override");
607 }
608 0b11 => {
609 true
611 }
612 _ => unreachable!(),
613 }
614 } else {
615 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) .with_interrupt_polarity_low(polarity_low) .with_trigger_mode_level(trigger_mode_level) .with_mask(false) .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 APIC.get().lock().mmio.error_status.write(0);
644 return_from_interrupt();
645}