135 lines
3.9 KiB
Rust

//! x86-64 APIC interface (Local + I/O)
use core::arch::global_asm;
use static_assertions::{const_assert, const_assert_eq};
use crate::{
arch::{x86_64::cpu::Cpu, Architecture, CpuAccess},
task::thread::Thread,
};
use super::{
exception::{self, IrqFrame},
ARCHITECTURE,
};
pub mod ioapic;
pub mod local;
// I/O APIC 0..MAX_EXTERNAL_VECTORS range is mapped to BSP Local APIC 2..
/// Fixed IRQ vector for Local APIC timer
pub const APIC_TIMER_VECTOR: u32 = 0x00;
/// Fixed IRQ vector for LINT0 line
pub const APIC_LINT0_VECTOR: u32 = 0x01;
/// Fixed IRQ vector for LINT1 line
pub const APIC_LINT1_VECTOR: u32 = 0x02;
/// Fixed vector for inter-processor interrupt
pub const APIC_IPI_VECTOR: u32 = 0x03;
/// Fixed vector for spurious interrupt
pub const APIC_SPURIOUS_VECTOR: u32 = 0xDF;
/// Start of the I/O APIC IRQ range
pub const APIC_EXTERNAL_OFFSET: u32 = 4;
/// Start of the MSI range
pub const APIC_MSI_OFFSET: u32 = APIC_EXTERNAL_OFFSET + MAX_EXTERNAL_VECTORS;
/// Maximum number of APIC vectors allocated for handling IRQs from I/O APIC
pub const MAX_EXTERNAL_VECTORS: u32 = APIC_SPURIOUS_VECTOR - APIC_EXTERNAL_OFFSET - MAX_MSI_VECTORS;
/// Number of I/O APIC IRQ vectors that are actually populated
pub const POPULATED_EXTERNAL_VECTORS: u32 = 16;
/// Maximum number of APIC vectors allocated for handling MSIs
pub const MAX_MSI_VECTORS: u32 = 16;
const_assert!(POPULATED_EXTERNAL_VECTORS <= MAX_EXTERNAL_VECTORS);
const_assert_eq!(APIC_MSI_OFFSET + MAX_MSI_VECTORS, APIC_SPURIOUS_VECTOR);
/// Fills the IDT with interrupt vectors for this APIC
pub fn setup_vectors(idt: &mut [exception::Entry]) {
extern "C" {
// IRQ vectors
static __x86_64_apic_vectors: [usize; 224];
}
for (i, &entry) in unsafe { __x86_64_apic_vectors.iter() }.enumerate() {
idt[i] = exception::Entry::new(
entry,
0x08,
exception::Entry::PRESENT | exception::Entry::INT32,
);
}
}
unsafe extern "C" fn irq_handler(vector: usize, frame: *mut IrqFrame) {
if vector >= POPULATED_EXTERNAL_VECTORS as _ {
todo!("Got a weird IRQ with vector {}", vector);
}
let cpu = Cpu::local();
let frame = &mut *frame;
ARCHITECTURE
.external_interrupt_controller()
.handle_specific_irq(vector);
cpu.local_apic().clear_interrupt();
if let Some(thread) = Thread::get_current() {
thread.handle_pending_signals(frame);
}
}
unsafe extern "C" fn msi_handler(vector: usize, frame: *mut IrqFrame) {
if vector >= MAX_MSI_VECTORS as _ {
todo!("Got a weird MSI with vector {}", vector);
}
let cpu = Cpu::local();
let frame = &mut *frame;
ARCHITECTURE
.message_interrupt_controller()
.handle_msi(vector);
cpu.local_apic().clear_interrupt();
if let Some(thread) = Thread::get_current() {
thread.handle_pending_signals(frame);
}
}
unsafe extern "C" fn local_timer_irq_handler(frame: *mut IrqFrame) {
let frame = &mut *frame;
let cpu = Cpu::local();
// Clear interrupt before switching, because otherwise we won't receive the next one
cpu.local_apic().clear_interrupt();
if let Some(queue) = cpu.get_queue() {
queue.yield_cpu();
}
if let Some(thread) = Thread::get_current() {
thread.handle_pending_signals(frame);
}
}
unsafe extern "C" fn dummy_irq_handler() {
todo!()
}
unsafe extern "C" fn ipi_handler() {
let cpu = Cpu::local();
todo!("Processor {} received an IPI", cpu.id());
}
global_asm!(
include_str!("vectors.S"),
local_timer_irq_handler = sym local_timer_irq_handler,
irq_handler = sym irq_handler,
msi_handler = sym msi_handler,
ipi_handler = sym ipi_handler,
dummy_irq_handler = sym dummy_irq_handler,
irq_vector_offset = const APIC_EXTERNAL_OFFSET,
irq_vector_count = const MAX_EXTERNAL_VECTORS,
msi_vector_offset = const APIC_MSI_OFFSET,
msi_vector_count = const MAX_MSI_VECTORS,
options(att_syntax)
);