Share external interrupt handlers between x86 and x86_64

This commit is contained in:
bjorn3
2025-09-10 18:31:28 +02:00
parent 5e6d681fcb
commit ad6c6c5e41
6 changed files with 28 additions and 323 deletions
+2 -2
View File
@@ -319,9 +319,9 @@ macro_rules! interrupt {
($name:ident, || $code:block) => {
#[naked]
pub unsafe extern "C" fn $name() { unsafe {
unsafe extern "C" fn inner() { unsafe {
unsafe extern "C" fn inner() {
$code
}}
}
core::arch::naked_asm!(concat!(
// Backup all userspace registers to stack
-319
View File
@@ -1,319 +0,0 @@
use core::sync::atomic::{AtomicUsize, Ordering};
use alloc::vec::Vec;
use crate::{
context,
context::timeout,
device::{
ioapic, local_apic, pic, pit,
serial::{COM1, COM2},
},
interrupt, interrupt_stack,
ipi::{ipi, IpiKind, IpiTarget},
percpu::PercpuBlock,
scheme::{
debug::{debug_input, debug_notify},
irq::irq_trigger,
serio::serio_input,
},
time,
};
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum IrqMethod {
Pic = 0,
Apic = 1,
}
static SPURIOUS_COUNT_IRQ7: AtomicUsize = AtomicUsize::new(0);
static SPURIOUS_COUNT_IRQ15: AtomicUsize = AtomicUsize::new(0);
pub fn spurious_count_irq7() -> usize {
SPURIOUS_COUNT_IRQ7.load(Ordering::Relaxed)
}
pub fn spurious_count_irq15() -> usize {
SPURIOUS_COUNT_IRQ15.load(Ordering::Relaxed)
}
pub fn spurious_count() -> usize {
spurious_count_irq7() + spurious_count_irq15()
}
pub fn spurious_irq_resource() -> syscall::Result<Vec<u8>> {
match irq_method() {
IrqMethod::Apic => Ok(Vec::from(&b"(not implemented for APIC yet)"[..])),
IrqMethod::Pic => Ok(format!(
"{}\tIRQ7\n{}\tIRQ15\n{}\ttotal\n",
spurious_count_irq7(),
spurious_count_irq15(),
spurious_count()
)
.into_bytes()),
}
}
static IRQ_METHOD: AtomicUsize = AtomicUsize::new(IrqMethod::Pic as usize);
pub fn set_irq_method(method: IrqMethod) {
IRQ_METHOD.store(method as usize, core::sync::atomic::Ordering::Release);
}
fn irq_method() -> IrqMethod {
let raw = IRQ_METHOD.load(core::sync::atomic::Ordering::Acquire);
match raw {
0 => IrqMethod::Pic,
1 => IrqMethod::Apic,
_ => unreachable!(),
}
}
/// Notify the IRQ scheme that an IRQ has been registered. This should mask the IRQ until the
/// scheme user unmasks it ("acknowledges" it).
unsafe fn trigger(irq: u8) {
unsafe {
match irq_method() {
IrqMethod::Pic => {
if irq < 16 {
pic_mask(irq)
}
}
IrqMethod::Apic => ioapic_mask(irq),
}
irq_trigger(irq);
}
}
/// Unmask the IRQ. This is called from the IRQ scheme, which does this when a user process has
/// processed the IRQ.
pub unsafe fn acknowledge(irq: usize) {
unsafe {
match irq_method() {
IrqMethod::Pic => {
if irq < 16 {
pic_unmask(irq)
}
}
IrqMethod::Apic => ioapic_unmask(irq),
}
}
}
/// Sends an end-of-interrupt, so that the interrupt controller can go on to the next one.
pub unsafe fn eoi(irq: u8) {
unsafe {
PercpuBlock::current().stats.add_irq(irq);
match irq_method() {
IrqMethod::Pic => {
if irq < 16 {
pic_eoi(irq)
}
}
IrqMethod::Apic => lapic_eoi(),
}
}
}
unsafe fn pic_mask(irq: u8) {
unsafe {
debug_assert!(irq < 16);
if irq >= 8 {
pic::slave().mask_set(irq - 8);
} else {
pic::master().mask_set(irq);
}
}
}
unsafe fn ioapic_mask(irq: u8) {
unsafe {
ioapic::mask(irq);
}
}
unsafe fn pic_eoi(irq: u8) {
unsafe {
debug_assert!(irq < 16);
if irq >= 8 {
pic::master().ack();
pic::slave().ack();
} else {
pic::master().ack();
}
}
}
unsafe fn lapic_eoi() {
unsafe { local_apic::the_local_apic().eoi() }
}
unsafe fn pic_unmask(irq: usize) {
unsafe {
debug_assert!(irq < 16);
if irq >= 8 {
pic::slave().mask_clear(irq as u8 - 8);
} else {
pic::master().mask_clear(irq as u8);
}
}
}
unsafe fn ioapic_unmask(irq: usize) {
unsafe {
ioapic::unmask(irq as u8);
}
}
interrupt_stack!(pit_stack, |_stack| {
// Saves CPU time by not sending IRQ event irq_trigger(0);
{
*time::OFFSET.lock() += pit::RATE;
}
eoi(0);
// Wake up other CPUs
ipi(IpiKind::Pit, IpiTarget::Other);
// Any better way of doing this?
timeout::trigger();
// Switch after a sufficient amount of time since the last switch.
context::switch::tick();
});
interrupt!(keyboard, || {
let data: u8;
core::arch::asm!("in al, 0x60", out("al") data);
eoi(1);
serio_input(0, data);
});
interrupt!(cascade, || {
// No need to do any operations on cascade
eoi(2);
});
interrupt!(com2, || {
while let Some(c) = COM2.lock().receive() {
debug_input(c);
}
debug_notify();
eoi(3);
});
interrupt!(com1, || {
while let Some(c) = COM1.lock().receive() {
debug_input(c);
}
debug_notify();
eoi(4);
});
interrupt!(lpt2, || {
trigger(5);
eoi(5);
});
interrupt!(floppy, || {
trigger(6);
eoi(6);
});
interrupt!(lpt1, || {
if irq_method() == IrqMethod::Pic && pic::master().isr() & (1 << 7) == 0 {
// the IRQ was spurious, ignore it but increment a counter.
SPURIOUS_COUNT_IRQ7.fetch_add(1, Ordering::Relaxed);
return;
}
trigger(7);
eoi(7);
});
interrupt!(rtc, || {
trigger(8);
eoi(8);
});
interrupt!(pci1, || {
trigger(9);
eoi(9);
});
interrupt!(pci2, || {
trigger(10);
eoi(10);
});
interrupt!(pci3, || {
trigger(11);
eoi(11);
});
interrupt!(mouse, || {
let data: u8;
core::arch::asm!("in al, 0x60", out("al") data);
eoi(12);
serio_input(1, data);
});
interrupt!(fpu, || {
trigger(13);
eoi(13);
});
interrupt!(ata1, || {
trigger(14);
eoi(14);
});
interrupt!(ata2, || {
if irq_method() == IrqMethod::Pic && pic::slave().isr() & (1 << 7) == 0 {
SPURIOUS_COUNT_IRQ15.fetch_add(1, Ordering::Relaxed);
pic::master().ack();
return;
}
trigger(15);
eoi(15);
});
interrupt!(lapic_timer, || {
println!("Local apic timer interrupt");
lapic_eoi();
});
interrupt!(lapic_error, || {
println!(
"Local apic internal error: ESR={:#0x}",
local_apic::the_local_apic().esr()
);
lapic_eoi();
});
// XXX: This would look way prettier using const generics.
macro_rules! allocatable_irq(
( $idt:expr_2021, $number:literal, $name:ident ) => {
interrupt!($name, || {
allocatable_irq_generic($number);
});
}
);
pub unsafe fn allocatable_irq_generic(number: u8) {
unsafe {
irq_trigger(number - 32);
lapic_eoi();
}
}
define_default_irqs!();
-1
View File
@@ -5,7 +5,6 @@ pub use crate::arch::x86_shared::interrupt::*;
#[macro_use]
pub mod handler;
pub mod irq;
pub mod syscall;
pub use self::handler::InterruptStack;
-1
View File
@@ -5,7 +5,6 @@ pub use crate::arch::x86_shared::interrupt::*;
#[macro_use]
pub mod handler;
pub mod irq;
pub mod syscall;
pub use self::handler::InterruptStack;
@@ -322,6 +322,29 @@ interrupt!(lapic_error, || {
unsafe { lapic_eoi() };
});
// XXX: This would look way prettier using const generics.
#[cfg(target_arch = "x86")]
macro_rules! allocatable_irq(
( $idt:expr_2021, $number:literal, $name:ident ) => {
interrupt!($name, || {
unsafe { allocatable_irq_generic($number) };
});
}
);
#[cfg(target_arch = "x86")]
pub unsafe fn allocatable_irq_generic(number: u8) {
unsafe {
irq_trigger(number - 32);
lapic_eoi();
}
}
#[cfg(target_arch = "x86")]
define_default_irqs!();
#[cfg(target_arch = "x86_64")]
interrupt_error!(generic_irq, |_stack, code| {
// The reason why 128 is subtracted and added from the code, is that PUSH imm8 sign-extends the
// value, and the longer PUSH imm32 would make the generic_interrupts table twice as large
@@ -331,6 +354,7 @@ interrupt_error!(generic_irq, |_stack, code| {
unsafe { lapic_eoi() };
});
#[cfg(target_arch = "x86_64")]
core::arch::global_asm!("
.globl __generic_interrupts_start
.globl __generic_interrupts_end
@@ -346,6 +370,7 @@ __generic_interrupts_start:
__generic_interrupts_end:
", sym generic_irq);
#[cfg(target_arch = "x86_64")]
unsafe extern "C" {
pub fn __generic_interrupts_start();
pub fn __generic_interrupts_end();
+1
View File
@@ -2,6 +2,7 @@
pub mod exception;
pub mod ipi;
pub mod irq;
pub mod trace;
pub use super::idt::{available_irqs_iter, is_reserved, set_reserved};