Skip to content

Commit a41d7db

Browse files
committed
Added more sophisticated IRQ handling
The details are described in more detail in the doc comment at the top of the interrupts module, but IRQs are now handled more differently from CPU exceptions than they were previously. Signed-off-by: SlyMarbo <[email protected]>
1 parent f5d17d5 commit a41d7db

File tree

1 file changed

+143
-21
lines changed

1 file changed

+143
-21
lines changed

kernel/src/interrupts.rs

Lines changed: 143 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@
1616
// message.
1717
// 4. Timer: the timer handler increments the counter in
1818
// the time module and acknowledges the interrupt.
19+
//
20+
// The functionality for the PIC is quite different from
21+
// the functionality for CPU exceptions. Exceptions are
22+
// handled directly through the IDT. The PIC's IRQs are
23+
// instead registered using the register_irq function,
24+
// making it easier to handle IRQs, without needing to
25+
// know the details of the PIC.
26+
//
27+
// The other big difference with the IRQ handling is that
28+
// IRQ handlers don't need to acknowledge the PIC, and
29+
// are passed the IRQ number.
1930

2031
use crate::{gdt, halt_loop, println, time};
2132
use lazy_static::lazy_static;
@@ -29,6 +40,7 @@ use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, Pag
2940
pub fn init() {
3041
IDT.load();
3142
unsafe { PICS.lock().initialize() };
43+
register_irq(0, timer_interrupt_handler);
3244
}
3345

3446
lazy_static! {
@@ -41,7 +53,23 @@ lazy_static! {
4153
.set_handler_fn(double_fault_handler)
4254
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
4355
}
44-
idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer_interrupt_handler);
56+
57+
idt[PIC_1_OFFSET + 0].set_handler_fn(irq_handler_0);
58+
idt[PIC_1_OFFSET + 1].set_handler_fn(irq_handler_1);
59+
idt[PIC_1_OFFSET + 2].set_handler_fn(irq_handler_2);
60+
idt[PIC_1_OFFSET + 3].set_handler_fn(irq_handler_3);
61+
idt[PIC_1_OFFSET + 4].set_handler_fn(irq_handler_4);
62+
idt[PIC_1_OFFSET + 5].set_handler_fn(irq_handler_5);
63+
idt[PIC_1_OFFSET + 6].set_handler_fn(irq_handler_6);
64+
idt[PIC_1_OFFSET + 7].set_handler_fn(irq_handler_7);
65+
idt[PIC_1_OFFSET + 8].set_handler_fn(irq_handler_8);
66+
idt[PIC_1_OFFSET + 9].set_handler_fn(irq_handler_9);
67+
idt[PIC_1_OFFSET + 10].set_handler_fn(irq_handler_10);
68+
idt[PIC_1_OFFSET + 11].set_handler_fn(irq_handler_11);
69+
idt[PIC_1_OFFSET + 12].set_handler_fn(irq_handler_12);
70+
idt[PIC_1_OFFSET + 13].set_handler_fn(irq_handler_13);
71+
idt[PIC_1_OFFSET + 14].set_handler_fn(irq_handler_14);
72+
idt[PIC_1_OFFSET + 15].set_handler_fn(irq_handler_15);
4573

4674
idt
4775
};
@@ -71,41 +99,135 @@ extern "x86-interrupt" fn double_fault_handler(
7199
panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
72100
}
73101

74-
extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) {
102+
fn timer_interrupt_handler(_stack_frame: InterruptStackFrame, _irq: u8) {
75103
time::tick();
104+
}
105+
106+
// PIC code.
107+
108+
const PIC_1_OFFSET: usize = 32;
109+
const PIC_2_OFFSET: usize = PIC_1_OFFSET + 8;
110+
111+
/// PICS is the set of programmable interrupt controllers.
112+
///
113+
/// PICS can be used to acknowledge an interrupt.
114+
///
115+
static PICS: spin::Mutex<ChainedPics> =
116+
spin::Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET as u8, PIC_2_OFFSET as u8) });
117+
118+
/// IrqHandler represents an IRQ handler function.
119+
///
120+
/// The irq argument is an integer between 0 and 15.
121+
///
122+
pub type IrqHandler = fn(frame: InterruptStackFrame, irq: u8);
123+
124+
/// irq_handler_none is a dummy IRQ handler, which
125+
/// does nothing.
126+
///
127+
fn irq_handler_none(_frame: InterruptStackFrame, _irq: u8) {}
128+
129+
// IRQ handlers.
130+
131+
#[inline]
132+
fn irq_handler_generic(frame: InterruptStackFrame, irq: u8) {
133+
let irqs = IRQS.try_lock();
134+
if let Some(irqs) = irqs {
135+
let handler = irqs[irq as usize];
136+
handler(frame, irq);
137+
}
76138

77139
unsafe {
78140
PICS.lock()
79-
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
141+
.notify_end_of_interrupt(irq + PIC_1_OFFSET as u8);
80142
}
81143
}
82144

83-
// PIC code.
145+
extern "x86-interrupt" fn irq_handler_0(frame: InterruptStackFrame) {
146+
irq_handler_generic(frame, 0u8);
147+
}
84148

85-
#[doc(hidden)]
86-
pub const PIC_1_OFFSET: u8 = 32;
87-
#[doc(hidden)]
88-
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
149+
extern "x86-interrupt" fn irq_handler_1(frame: InterruptStackFrame) {
150+
irq_handler_generic(frame, 1u8);
151+
}
89152

90-
static PICS: spin::Mutex<ChainedPics> =
91-
spin::Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });
153+
extern "x86-interrupt" fn irq_handler_2(frame: InterruptStackFrame) {
154+
irq_handler_generic(frame, 2u8);
155+
}
156+
157+
extern "x86-interrupt" fn irq_handler_3(frame: InterruptStackFrame) {
158+
irq_handler_generic(frame, 3u8);
159+
}
160+
161+
extern "x86-interrupt" fn irq_handler_4(frame: InterruptStackFrame) {
162+
irq_handler_generic(frame, 4u8);
163+
}
164+
165+
extern "x86-interrupt" fn irq_handler_5(frame: InterruptStackFrame) {
166+
irq_handler_generic(frame, 5u8);
167+
}
168+
169+
extern "x86-interrupt" fn irq_handler_6(frame: InterruptStackFrame) {
170+
irq_handler_generic(frame, 6u8);
171+
}
172+
173+
extern "x86-interrupt" fn irq_handler_7(frame: InterruptStackFrame) {
174+
irq_handler_generic(frame, 7u8);
175+
}
92176

93-
#[derive(Debug, Clone, Copy)]
94-
#[repr(u8)]
95-
#[doc(hidden)]
96-
pub enum InterruptIndex {
97-
Timer = PIC_1_OFFSET,
98-
Keyboard,
177+
extern "x86-interrupt" fn irq_handler_8(frame: InterruptStackFrame) {
178+
irq_handler_generic(frame, 8u8);
99179
}
100180

101-
impl InterruptIndex {
102-
fn as_u8(self) -> u8 {
103-
self as u8
181+
extern "x86-interrupt" fn irq_handler_9(frame: InterruptStackFrame) {
182+
irq_handler_generic(frame, 9u8);
183+
}
184+
185+
extern "x86-interrupt" fn irq_handler_10(frame: InterruptStackFrame) {
186+
irq_handler_generic(frame, 10u8);
187+
}
188+
189+
extern "x86-interrupt" fn irq_handler_11(frame: InterruptStackFrame) {
190+
irq_handler_generic(frame, 11u8);
191+
}
192+
193+
extern "x86-interrupt" fn irq_handler_12(frame: InterruptStackFrame) {
194+
irq_handler_generic(frame, 12u8);
195+
}
196+
197+
extern "x86-interrupt" fn irq_handler_13(frame: InterruptStackFrame) {
198+
irq_handler_generic(frame, 13u8);
199+
}
200+
201+
extern "x86-interrupt" fn irq_handler_14(frame: InterruptStackFrame) {
202+
irq_handler_generic(frame, 14u8);
203+
}
204+
205+
extern "x86-interrupt" fn irq_handler_15(frame: InterruptStackFrame) {
206+
irq_handler_generic(frame, 15u8);
207+
}
208+
209+
/// IRQS helps us to track which IRQs have been allocated.
210+
///
211+
static IRQS: spin::Mutex<[IrqHandler; 16]> = spin::Mutex::new([irq_handler_none; 16]);
212+
213+
/// register_irq sets the handler for the given IRQ.
214+
///
215+
/// The irq parameter must be an integer between 0 and 15.
216+
///
217+
/// If the given IRQ has already been assigned, register_irq
218+
/// panics.
219+
///
220+
pub fn register_irq(irq: u8, handler: IrqHandler) {
221+
let mut irqs = IRQS.lock();
222+
if irq > 15 {
223+
panic!("invalid IRQ {} passed to register_irq", irq);
104224
}
105225

106-
fn as_usize(self) -> usize {
107-
usize::from(self.as_u8())
226+
if irqs[irq as usize] != irq_handler_none {
227+
panic!("IRQ {} has already been registered", irq);
108228
}
229+
230+
irqs[irq as usize] = handler;
109231
}
110232

111233
// Tests

0 commit comments

Comments
 (0)