From ad6c6c5e4178a0f787ad4d9bf181a9bd4e2ae910 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 10 Sep 2025 18:31:28 +0200 Subject: [PATCH] Share external interrupt handlers between x86 and x86_64 --- src/arch/x86/interrupt/handler.rs | 4 +- src/arch/x86/interrupt/irq.rs | 319 ------------------ src/arch/x86/interrupt/mod.rs | 1 - src/arch/x86_64/interrupt/mod.rs | 1 - .../{x86_64 => x86_shared}/interrupt/irq.rs | 25 ++ src/arch/x86_shared/interrupt/mod.rs | 1 + 6 files changed, 28 insertions(+), 323 deletions(-) delete mode 100644 src/arch/x86/interrupt/irq.rs rename src/arch/{x86_64 => x86_shared}/interrupt/irq.rs (92%) diff --git a/src/arch/x86/interrupt/handler.rs b/src/arch/x86/interrupt/handler.rs index 01aee638eb..3f2faff586 100644 --- a/src/arch/x86/interrupt/handler.rs +++ b/src/arch/x86/interrupt/handler.rs @@ -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 diff --git a/src/arch/x86/interrupt/irq.rs b/src/arch/x86/interrupt/irq.rs deleted file mode 100644 index 0b6da58c8c..0000000000 --- a/src/arch/x86/interrupt/irq.rs +++ /dev/null @@ -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> { - 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!(); diff --git a/src/arch/x86/interrupt/mod.rs b/src/arch/x86/interrupt/mod.rs index 5ba2f7c67c..efc4999262 100644 --- a/src/arch/x86/interrupt/mod.rs +++ b/src/arch/x86/interrupt/mod.rs @@ -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; diff --git a/src/arch/x86_64/interrupt/mod.rs b/src/arch/x86_64/interrupt/mod.rs index 5ba2f7c67c..efc4999262 100644 --- a/src/arch/x86_64/interrupt/mod.rs +++ b/src/arch/x86_64/interrupt/mod.rs @@ -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; diff --git a/src/arch/x86_64/interrupt/irq.rs b/src/arch/x86_shared/interrupt/irq.rs similarity index 92% rename from src/arch/x86_64/interrupt/irq.rs rename to src/arch/x86_shared/interrupt/irq.rs index 52e7232b88..11c8902dda 100644 --- a/src/arch/x86_64/interrupt/irq.rs +++ b/src/arch/x86_shared/interrupt/irq.rs @@ -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(); diff --git a/src/arch/x86_shared/interrupt/mod.rs b/src/arch/x86_shared/interrupt/mod.rs index 7a49ffb7fe..0eb966c5bb 100644 --- a/src/arch/x86_shared/interrupt/mod.rs +++ b/src/arch/x86_shared/interrupt/mod.rs @@ -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};