Skip to content

Commit 3030634

Browse files
committed
handle double fault exceptions
1 parent 54bd031 commit 3030634

File tree

6 files changed

+138
-2
lines changed

6 files changed

+138
-2
lines changed

Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,7 @@ test-timeout = 300 # seconds
3333
[[test]]
3434
harness = false
3535
name = "should_panic"
36+
37+
[[test]]
38+
harness = false
39+
name = "stack_overflow"

src/gdt.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use lazy_static::lazy_static;
2+
use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector};
3+
use x86_64::structures::tss::TaskStateSegment;
4+
use x86_64::VirtAddr;
5+
6+
/// IST entry for the double fault stack
7+
pub const DOUBLE_FAULT_IST_INDEX: u16 = 0;
8+
9+
lazy_static! {
10+
static ref TSS: TaskStateSegment = {
11+
let mut tss = TaskStateSegment::new();
12+
// Create a stack for double fault
13+
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = {
14+
const STACK_SIZE: usize = 4096 * 5;
15+
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
16+
17+
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
18+
let stack_end = stack_start + STACK_SIZE;
19+
stack_end
20+
};
21+
tss
22+
};
23+
}
24+
25+
lazy_static! {
26+
static ref GDT: (GlobalDescriptorTable, Selectors) = {
27+
let mut gdt = GlobalDescriptorTable::new();
28+
let code_selector = gdt.add_entry(Descriptor::kernel_code_segment());
29+
let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS));
30+
(
31+
gdt,
32+
Selectors {
33+
code_selector,
34+
tss_selector,
35+
},
36+
)
37+
};
38+
}
39+
40+
struct Selectors {
41+
code_selector: SegmentSelector,
42+
tss_selector: SegmentSelector,
43+
}
44+
45+
pub fn init() {
46+
use x86_64::instructions::segmentation::set_cs;
47+
use x86_64::instructions::tables::load_tss;
48+
49+
GDT.0.load();
50+
unsafe {
51+
set_cs(GDT.1.code_selector);
52+
load_tss(GDT.1.tss_selector);
53+
}
54+
}

src/interrupts.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
use lazy_static::lazy_static;
22
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
33

4+
use crate::gdt;
45
use crate::println;
56

67
lazy_static! {
78
static ref IDT: InterruptDescriptorTable = {
89
let mut idt = InterruptDescriptorTable::new();
910
idt.breakpoint.set_handler_fn(breakpoint_handler);
11+
unsafe {
12+
idt.double_fault
13+
.set_handler_fn(double_fault_handler)
14+
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
15+
}
1016
idt
1117
};
1218
}
@@ -19,6 +25,13 @@ extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut InterruptStackFra
1925
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
2026
}
2127

28+
extern "x86-interrupt" fn double_fault_handler(
29+
stack_frame: &mut InterruptStackFrame,
30+
_error_code: u64,
31+
) -> ! {
32+
panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
33+
}
34+
2235
#[test_case]
2336
fn test_breakpoint_exception() {
2437
x86_64::instructions::interrupts::int3();

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77

88
use core::panic::PanicInfo;
99

10+
pub mod gdt;
1011
pub mod interrupts;
1112
pub mod serial;
1213
pub mod vga_buffer;
1314

1415
pub fn init() {
16+
gdt::init();
1517
interrupts::init_idt();
1618
}
1719

src/main.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ pub extern "C" fn _start() -> ! {
1414

1515
blog_os::init();
1616

17-
// invoke a breakpoint exception
18-
x86_64::instructions::interrupts::int3();
17+
fn stack_overflow() {
18+
stack_overflow();
19+
}
20+
stack_overflow();
1921

2022
#[cfg(test)]
2123
test_main();

tests/stack_overflow.rs

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#![no_std]
2+
#![no_main]
3+
#![feature(abi_x86_interrupt)]
4+
5+
// stack overflow should not cause a triple fault exception
6+
7+
use blog_os::{exit_qemu, serial_print, serial_println};
8+
use core::panic::PanicInfo;
9+
use lazy_static::lazy_static;
10+
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
11+
12+
#[no_mangle]
13+
pub extern "C" fn _start() -> ! {
14+
serial_print!("stack_overflow::stack_overflow...");
15+
16+
blog_os::gdt::init();
17+
init_test_idt();
18+
19+
// trigger a stack overflow
20+
stack_overflow();
21+
22+
panic!("Execution continued after stack overflow");
23+
}
24+
25+
#[panic_handler]
26+
fn panic(info: &PanicInfo) -> ! {
27+
blog_os::test_panic_handler(info)
28+
}
29+
30+
#[allow(unconditional_recursion)]
31+
fn stack_overflow() {
32+
stack_overflow(); // for each recursion, the return address is pushed
33+
volatile::Volatile::new(0).read(); // prevent tail recursion optimizations
34+
}
35+
36+
lazy_static! {
37+
static ref TEST_IDT: InterruptDescriptorTable = {
38+
let mut idt = InterruptDescriptorTable::new();
39+
40+
unsafe {
41+
idt.double_fault
42+
.set_handler_fn(test_double_fault_handler)
43+
.set_stack_index(blog_os::gdt::DOUBLE_FAULT_IST_INDEX);
44+
}
45+
46+
idt
47+
};
48+
}
49+
50+
pub fn init_test_idt() {
51+
TEST_IDT.load();
52+
}
53+
54+
extern "x86-interrupt" fn test_double_fault_handler(
55+
_stack_frame: &mut InterruptStackFrame,
56+
_error_code: u64,
57+
) -> ! {
58+
serial_println!("[ok]");
59+
exit_qemu(blog_os::QemuExitCode::Success);
60+
loop {}
61+
}

0 commit comments

Comments
 (0)