Share external interrupt handlers between x86 and x86_64
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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!();
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
@@ -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};
|
||||
|
||||
Reference in New Issue
Block a user