Skip to content
This repository was archived by the owner on Jan 1, 2024. It is now read-only.

Commit 93e55fe

Browse files
committed
Migrate to exception handling in asm
We avoid using the "x86-interrupt" abi and instead implement interrupt service routines directly in assembly. This avoids the bug rust-lang/rust#109918, allowing a correct stack frame to be obtained by the handler.
1 parent e28370f commit 93e55fe

File tree

1 file changed

+176
-97
lines changed

1 file changed

+176
-97
lines changed

src/interrupt.rs

Lines changed: 176 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
//! Module for interrupt support.
22
3+
use core::arch::global_asm;
34
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
45
use doors_alloc::boxed::Box;
56
use x86_64::instructions::interrupts;
67
use x86_64::registers::control::Cr2;
78
use x86_64::structures::idt::{self, InterruptDescriptorTable, InterruptStackFrame};
9+
use x86_64::VirtAddr;
810

911
// We initialize the counter with 1 since the bootloader disables interrupts when
1012
// control is passed to the kernel.
@@ -73,130 +75,207 @@ pub fn end_interrupt_disable() {
7375
interrupts::enable();
7476
}
7577

76-
macro_rules! exception {
77-
($id:ident, $name:literal) => {
78-
extern "x86-interrupt" fn $id(frame: InterruptStackFrame) {
79-
cpu_exception(frame, $name);
80-
}
81-
};
82-
}
83-
84-
macro_rules! exception_errcode {
85-
($id:ident, $name:literal) => {
86-
extern "x86-interrupt" fn $id(frame: InterruptStackFrame, errcode: u64) {
87-
cpu_exception_errcode(frame, $name, errcode);
88-
}
89-
};
78+
fn exception_id_to_name(id: u64) -> &'static str {
79+
match id {
80+
0 => "Divide error",
81+
1 => "Debug exception",
82+
2 => "Non-maskable interrupt",
83+
3 => "Breakpoint exception",
84+
4 => "Overflow exception",
85+
5 => "BOUND range exceeded",
86+
6 => "Invalid opcode",
87+
7 => "Device not available",
88+
8 => "Double fault",
89+
10 => "Invalid TSS",
90+
11 => "Segment not present",
91+
12 => "Stack segment exception",
92+
13 => "General protection fault",
93+
14 => "Page fault",
94+
16 => "x87 floating-point exception",
95+
17 => "Alignment check exception",
96+
18 => "Machine check exception",
97+
19 => "SIMD floating-point exception",
98+
20 => "Virtualization exception",
99+
29 => "VMM communication exception",
100+
30 => "Security exception",
101+
_ => "Unknown exception",
102+
}
90103
}
91104

92-
macro_rules! exception_diverging {
93-
($id:ident, $name:literal) => {
94-
extern "x86-interrupt" fn $id(frame: InterruptStackFrame) -> ! {
95-
cpu_exception(frame, $name);
105+
macro_rules! exception {
106+
($id:ident, $number:literal) => {
107+
global_asm!(
108+
concat!(stringify!($id), ":"),
109+
"push rax",
110+
"push rcx",
111+
"push rdx",
112+
"push rsi",
113+
"push rdi",
114+
"push r8",
115+
"push r9",
116+
"push r10",
117+
"push r11",
118+
"lea rdi, [rsp + 72]", // stack frame
119+
concat!("mov rsi, ", $number), // exception id
120+
"call {}",
121+
"pop r11",
122+
"pop r10",
123+
"pop r9",
124+
"pop r8",
125+
"pop rdi",
126+
"pop rsi",
127+
"pop rdx",
128+
"pop rcx",
129+
"pop rax",
130+
"iretq",
131+
sym cpu_exception
132+
);
133+
extern "C" {
134+
fn $id();
96135
}
97-
};
136+
}
98137
}
99138

100-
macro_rules! exception_diverging_errcode {
101-
($id:ident, $name:literal) => {
102-
extern "x86-interrupt" fn $id(frame: InterruptStackFrame, errcode: u64) -> ! {
103-
cpu_exception_errcode(frame, $name, errcode);
139+
macro_rules! exception_errcode {
140+
($id:ident, $number:literal) => {
141+
global_asm!(
142+
concat!(stringify!($id), ":"),
143+
"push rax",
144+
"push rcx",
145+
"push rdx",
146+
"push rsi",
147+
"push rdi",
148+
"push r8",
149+
"push r9",
150+
"push r10",
151+
"push r11",
152+
"lea rdi, [rsp + 80]", // stack frame
153+
concat!("mov rsi, ", $number), // exception id
154+
"mov rdx, [rsp + 72]", // error code
155+
"push 0", // 16 byte alignment
156+
"call {}",
157+
"pop rax",
158+
"pop r11",
159+
"pop r10",
160+
"pop r9",
161+
"pop r8",
162+
"pop rdi",
163+
"pop rsi",
164+
"pop rdx",
165+
"pop rcx",
166+
"pop rax",
167+
"add rsp, 8", // error code
168+
"iretq",
169+
sym cpu_exception_errcode
170+
);
171+
extern "C" {
172+
fn $id();
104173
}
105-
};
174+
}
106175
}
107176

108-
exception!(handler_divide_error, "Divide error");
109-
exception!(handler_debug, "Debug exception");
110-
exception!(handler_non_maskable_interrupt, "Non-maskable interrupt");
111-
exception!(handler_breakpoint, "Breakpoint exception");
112-
exception!(handler_overflow, "Overflow exception");
113-
exception!(handler_bound_range_exceeded, "Bound range exceeded");
114-
exception!(handler_invalid_opcode, "Invalid opcode");
115-
exception!(handler_device_not_available, "Device not available");
116-
exception_diverging_errcode!(handler_double_fault, "Double fault");
117-
exception_errcode!(handler_invalid_tss, "Invalid TSS");
118-
exception_errcode!(handler_segment_not_present, "Segment not present");
119-
exception_errcode!(handler_stack_segment_fault, "Stack segment exception");
120-
exception_errcode!(handler_general_protection_fault, "General protection fault");
121-
// See below for page fault handler
122-
exception!(handler_x87_floating_point, "x87 floating-point exception");
123-
exception_errcode!(handler_alignment_check, "Alignment check exception");
124-
exception_diverging!(handler_machine_check, "Machine check exception");
125-
exception!(handler_simd_floating_point, "SIMD floating-point exception");
126-
exception!(handler_virtualization, "Virtualization exception");
127-
exception_errcode!(
128-
handler_vmm_communication_exception,
129-
"VMM communication exception"
130-
);
131-
exception_errcode!(handler_security_exception, "Security exception");
177+
exception!(handler_divide_error, 0);
178+
exception!(handler_debug, 1);
179+
exception!(handler_non_maskable_interrupt, 2);
180+
exception!(handler_breakpoint, 3);
181+
exception!(handler_overflow, 4);
182+
exception!(handler_bound_range_exceeded, 5);
183+
exception!(handler_invalid_opcode, 6);
184+
exception!(handler_device_not_available, 7);
185+
exception_errcode!(handler_double_fault, 8);
186+
exception_errcode!(handler_invalid_tss, 10);
187+
exception_errcode!(handler_segment_not_present, 11);
188+
exception_errcode!(handler_stack_segment_fault, 12);
189+
exception_errcode!(handler_general_protection_fault, 13);
190+
exception_errcode!(handler_page_fault, 14);
191+
exception!(handler_x87_floating_point, 16);
192+
exception_errcode!(handler_alignment_check, 17);
193+
exception!(handler_machine_check, 18);
194+
exception!(handler_simd_floating_point, 19);
195+
exception!(handler_virtualization, 20);
196+
exception_errcode!(handler_vmm_communication_exception, 29);
197+
exception_errcode!(handler_security_exception, 30);
132198

133-
fn cpu_exception(frame: InterruptStackFrame, name: &'static str) -> ! {
134-
IN_KERNEL_INTERRUPT_HANDLER.store(true, Ordering::Relaxed);
135-
IN_KERNEL_EXCEPTION_HANDLER.store(true, Ordering::Relaxed);
136-
panic!("CPU exception: {}, stack frame: {:?}", name, frame);
137-
}
138-
139-
fn cpu_exception_errcode(frame: InterruptStackFrame, name: &'static str, errcode: u64) -> ! {
199+
extern "C" fn cpu_exception(frame: &InterruptStackFrame, id: u64) {
140200
IN_KERNEL_INTERRUPT_HANDLER.store(true, Ordering::Relaxed);
141201
IN_KERNEL_EXCEPTION_HANDLER.store(true, Ordering::Relaxed);
142202
panic!(
143-
"CPU exception: {}, error code: {}, stack frame: {:?}",
144-
name, errcode, frame
203+
"CPU exception: {}, stack frame: {:?}",
204+
exception_id_to_name(id),
205+
frame
145206
);
146207
}
147208

148-
extern "x86-interrupt" fn handler_page_fault(
149-
frame: InterruptStackFrame,
150-
errcode: idt::PageFaultErrorCode,
151-
) {
209+
extern "C" fn cpu_exception_errcode(frame: &InterruptStackFrame, id: u64, errcode: u64) {
152210
IN_KERNEL_INTERRUPT_HANDLER.store(true, Ordering::Relaxed);
153211
IN_KERNEL_EXCEPTION_HANDLER.store(true, Ordering::Relaxed);
212+
if id == 14 {
213+
panic!(
214+
"CPU exception: Page fault, stack frame: {:?}, error code: {:?}, address: {:?}",
215+
frame,
216+
idt::PageFaultErrorCode::from_bits_truncate(errcode),
217+
Cr2::read()
218+
);
219+
}
154220
panic!(
155-
"CPU exception: Page fault, error code: {:?}, address: {:?}, stack frame: {:?}",
156-
errcode,
157-
Cr2::read(),
158-
frame
221+
"CPU exception: {}, stack frame: {:?}, error code: {}",
222+
exception_id_to_name(id),
223+
frame,
224+
errcode
159225
);
160226
}
161227

162228
/// Initializes interrupt handling.
163229
///
164230
/// This requires heap allocation to be initialized first.
231+
#[allow(clippy::fn_to_numeric_cast)]
165232
pub fn init() {
166233
let idt = Box::leak(
167234
Box::try_new(InterruptDescriptorTable::new()).expect("Allocation should succeed"),
168235
);
169-
idt.divide_error.set_handler_fn(handler_divide_error);
170-
idt.debug.set_handler_fn(handler_debug);
171-
idt.non_maskable_interrupt
172-
.set_handler_fn(handler_non_maskable_interrupt);
173-
idt.breakpoint.set_handler_fn(handler_breakpoint);
174-
idt.overflow.set_handler_fn(handler_overflow);
175-
idt.bound_range_exceeded
176-
.set_handler_fn(handler_bound_range_exceeded);
177-
idt.invalid_opcode.set_handler_fn(handler_invalid_opcode);
178-
idt.device_not_available
179-
.set_handler_fn(handler_device_not_available);
180-
idt.double_fault.set_handler_fn(handler_double_fault);
181-
idt.invalid_tss.set_handler_fn(handler_invalid_tss);
182-
idt.segment_not_present
183-
.set_handler_fn(handler_segment_not_present);
184-
idt.stack_segment_fault
185-
.set_handler_fn(handler_stack_segment_fault);
186-
idt.general_protection_fault
187-
.set_handler_fn(handler_general_protection_fault);
188-
idt.page_fault.set_handler_fn(handler_page_fault);
189-
idt.x87_floating_point
190-
.set_handler_fn(handler_x87_floating_point);
191-
idt.alignment_check.set_handler_fn(handler_alignment_check);
192-
idt.machine_check.set_handler_fn(handler_machine_check);
193-
idt.simd_floating_point
194-
.set_handler_fn(handler_simd_floating_point);
195-
idt.virtualization.set_handler_fn(handler_virtualization);
196-
idt.vmm_communication_exception
197-
.set_handler_fn(handler_vmm_communication_exception);
198-
idt.security_exception
199-
.set_handler_fn(handler_security_exception);
200-
236+
unsafe {
237+
idt.divide_error
238+
.set_handler_addr(VirtAddr::new(handler_divide_error as u64));
239+
idt.debug
240+
.set_handler_addr(VirtAddr::new(handler_debug as u64));
241+
idt.non_maskable_interrupt
242+
.set_handler_addr(VirtAddr::new(handler_non_maskable_interrupt as u64));
243+
idt.breakpoint
244+
.set_handler_addr(VirtAddr::new(handler_breakpoint as u64));
245+
idt.overflow
246+
.set_handler_addr(VirtAddr::new(handler_overflow as u64));
247+
idt.bound_range_exceeded
248+
.set_handler_addr(VirtAddr::new(handler_bound_range_exceeded as u64));
249+
idt.invalid_opcode
250+
.set_handler_addr(VirtAddr::new(handler_invalid_opcode as u64));
251+
idt.device_not_available
252+
.set_handler_addr(VirtAddr::new(handler_device_not_available as u64));
253+
idt.double_fault
254+
.set_handler_addr(VirtAddr::new(handler_double_fault as u64));
255+
idt.invalid_tss
256+
.set_handler_addr(VirtAddr::new(handler_invalid_tss as u64));
257+
idt.segment_not_present
258+
.set_handler_addr(VirtAddr::new(handler_segment_not_present as u64));
259+
idt.stack_segment_fault
260+
.set_handler_addr(VirtAddr::new(handler_stack_segment_fault as u64));
261+
idt.general_protection_fault
262+
.set_handler_addr(VirtAddr::new(handler_general_protection_fault as u64));
263+
idt.page_fault
264+
.set_handler_addr(VirtAddr::new(handler_page_fault as u64));
265+
idt.x87_floating_point
266+
.set_handler_addr(VirtAddr::new(handler_x87_floating_point as u64));
267+
idt.alignment_check
268+
.set_handler_addr(VirtAddr::new(handler_alignment_check as u64));
269+
idt.machine_check
270+
.set_handler_addr(VirtAddr::new(handler_machine_check as u64));
271+
idt.simd_floating_point
272+
.set_handler_addr(VirtAddr::new(handler_simd_floating_point as u64));
273+
idt.virtualization
274+
.set_handler_addr(VirtAddr::new(handler_virtualization as u64));
275+
idt.vmm_communication_exception
276+
.set_handler_addr(VirtAddr::new(handler_vmm_communication_exception as u64));
277+
idt.security_exception
278+
.set_handler_addr(VirtAddr::new(handler_security_exception as u64));
279+
}
201280
idt.load();
202281
}

0 commit comments

Comments
 (0)