Skip to content

Commit 11f1cc6

Browse files
committed
add interrupts and cpu exceptiopn handlers
1 parent 3d26133 commit 11f1cc6

File tree

7 files changed

+183
-5
lines changed

7 files changed

+183
-5
lines changed

.vscode/settings.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"editor.defaultFormatter": null,
3+
"[rust]": {
4+
"editor.defaultFormatter": "rust-lang.rust-analyzer",
5+
"editor.formatOnSave": true
6+
},
7+
}

Cargo.toml

+6-1
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,9 @@ test-timeout = 300 # (in seconds)
3636

3737
[[test]]
3838
name = "should_panic"
39-
harness = false
39+
harness = false
40+
41+
[[test]]
42+
name = "stack_overflow"
43+
harness = false
44+

src/gdt.rs

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use lazy_static::lazy_static;
2+
use x86_64::instructions::tables::load_tss;
3+
use x86_64::registers::segmentation::{Segment, CS};
4+
use x86_64::structures::gdt::{self, Descriptor, GlobalDescriptorTable, SegmentSelector};
5+
use x86_64::structures::tss::TaskStateSegment;
6+
use x86_64::VirtAddr;
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+
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = {
13+
const STACK_SIZE: usize = 4096 * 5;
14+
static mut STACK: [usize; STACK_SIZE] = [0; STACK_SIZE];
15+
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
16+
let stack_end = stack_start + STACK_SIZE;
17+
stack_end
18+
};
19+
tss
20+
};
21+
}
22+
23+
lazy_static! {
24+
static ref GDT: (GlobalDescriptorTable, Selectors) = {
25+
let mut gdt = GlobalDescriptorTable::new();
26+
let code_selector = gdt.add_entry(Descriptor::kernel_code_segment());
27+
let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS));
28+
(
29+
gdt,
30+
Selectors {
31+
code_selector,
32+
tss_selector,
33+
},
34+
)
35+
};
36+
}
37+
38+
pub fn init() {
39+
GDT.0.load();
40+
unsafe {
41+
CS::set_reg(GDT.1.code_selector);
42+
load_tss(GDT.1.tss_selector);
43+
}
44+
}
45+
46+
struct Selectors {
47+
code_selector: SegmentSelector,
48+
tss_selector: SegmentSelector,
49+
}

src/interrupts.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
lazy_static! {
2+
static ref IDT: InterruptDescriptorTable = {
3+
let mut idt = InterruptDescriptorTable::new();
4+
idt.breakpoint.set_handler_fn(breakpoint_handler);
5+
unsafe {
6+
idt.double_fault
7+
.set_handler_fn(double_fault_handler)
8+
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
9+
}
10+
idt
11+
};
12+
}
13+
14+
use lazy_static::lazy_static;
15+
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
16+
17+
use crate::{gdt, println};
18+
pub fn init_idt() {
19+
IDT.load();
20+
}
21+
22+
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
23+
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
24+
}
25+
26+
extern "x86-interrupt" fn double_fault_handler(
27+
stack_frame: InterruptStackFrame,
28+
_error_code: u64,
29+
) -> ! {
30+
panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
31+
}
32+
33+
#[test_case]
34+
fn test_breakpoint_exception() {
35+
// invoke a breakpoint exception
36+
x86_64::instructions::interrupts::int3();
37+
}

src/lib.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
#![no_std]
2-
32
#![cfg_attr(test, no_main)]
43
#![feature(custom_test_frameworks)]
54
#![test_runner(crate::test_runner)]
65
#![reexport_test_harness_main = "test_main"]
6+
#![feature(abi_x86_interrupt)]
77

88
use core::panic::PanicInfo;
9+
pub mod gdt;
10+
pub mod interrupts;
911
pub mod serial;
1012
pub mod vga_buffer;
13+
pub fn init() {
14+
gdt::init();
15+
interrupts::init_idt();
16+
}
17+
1118
pub trait Testable {
1219
fn run(&self) -> ();
1320
}
@@ -22,7 +29,7 @@ where
2229
serial_println!("[ok]");
2330
}
2431
}
25-
32+
2633
pub fn test_runner(tests: &[&dyn Testable]) {
2734
serial_println!("Running {} tests", tests.len());
2835
for test in tests {
@@ -42,6 +49,7 @@ pub fn test_panic_handler(info: &PanicInfo) -> ! {
4249
#[cfg(test)]
4350
#[no_mangle]
4451
pub extern "C" fn _start() -> ! {
52+
init();
4553
test_main();
4654
loop {}
4755
}
@@ -66,4 +74,4 @@ pub fn exit_qemu(exit_code: QemuExitCode) {
6674
let mut port = Port::new(0xf4);
6775
port.write(exit_code as u32);
6876
}
69-
}
77+
}

src/main.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,19 @@ use rustos::println;
1111
pub extern "C" fn _start() -> ! {
1212
println!("Hello World{}", "!");
1313

14+
rustos::init();
15+
16+
fn stack_overflow() {
17+
stack_overflow(); // for each recursion, the return address is pushed
18+
}
19+
20+
stack_overflow();
21+
1422
#[cfg(test)]
1523
test_main();
1624

25+
println!("It did not crash!");
26+
1727
loop {}
1828
}
1929

@@ -29,4 +39,4 @@ fn panic(info: &PanicInfo) -> ! {
2939
#[panic_handler]
3040
fn panic(info: &PanicInfo) -> ! {
3141
rustos::test_panic_handler(info)
32-
}
42+
}

tests/stack_overflow.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#![no_main]
2+
#![no_std]
3+
#![feature(abi_x86_interrupt)]
4+
5+
use core::panic::PanicInfo;
6+
7+
use lazy_static::lazy_static;
8+
use rustos::{exit_qemu, QemuExitCode};
9+
use rustos::{gdt, serial_println};
10+
use x86_64::structures::idt::InterruptDescriptorTable;
11+
12+
#[no_mangle]
13+
pub extern "C" fn _start() -> ! {
14+
serial_println!("stack_overflow::stack_overflow...\t");
15+
gdt::init();
16+
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+
rustos::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+
unsafe {
40+
idt.double_fault
41+
.set_handler_fn(test_double_fault_handler)
42+
.set_stack_index(rustos::gdt::DOUBLE_FAULT_IST_INDEX);
43+
}
44+
45+
idt
46+
};
47+
}
48+
49+
pub fn init_test_idt() {
50+
TEST_IDT.load();
51+
}
52+
53+
use x86_64::structures::idt::InterruptStackFrame;
54+
55+
extern "x86-interrupt" fn test_double_fault_handler(
56+
_stack_frame: InterruptStackFrame,
57+
_error_code: u64,
58+
) -> ! {
59+
serial_println!("[ok]");
60+
exit_qemu(QemuExitCode::Success);
61+
loop {}
62+
}

0 commit comments

Comments
 (0)