Interrupt handling working and tested.

- int3
- double fault with separate stack
- page fault
This commit is contained in:
Guillaume DIDIER 2019-11-04 13:54:43 +01:00
parent 510a596849
commit f4cc148d83
8 changed files with 362 additions and 6 deletions

1
Cargo.lock generated
View File

@ -41,6 +41,7 @@ name = "dendrobates_tinctoreus_azureus"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bootloader 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "bootloader 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"polling_serial 0.1.0", "polling_serial 0.1.0",
"vga_buffer 0.1.0", "vga_buffer 0.1.0",
"volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -28,6 +28,10 @@ vga_buffer = { path = "vga_buffer" }
polling_serial = { path = "polling_serial" } polling_serial = { path = "polling_serial" }
volatile = "0.2.6" volatile = "0.2.6"
[dependencies.lazy_static]
version = "1.0"
features = ["spin_no_std"]
[dependencies.bootloader] [dependencies.bootloader]
version = "^0.8.2" version = "^0.8.2"
features = ["sse"] features = ["sse"]
@ -36,9 +40,17 @@ features = ["sse"]
#bootloader = { path = "../bootloader" } #bootloader = { path = "../bootloader" }
[profile.dev] [profile.dev]
opt-level = 0 opt-level = 1
debug = 2
[profile.test]
opt-level = 1
debug = 2 debug = 2
[[test]] [[test]]
name = "panic_test" name = "panic_test"
harness = false harness = false
[[test]]
name = "stack_overflow"
harness = false

52
src/gdt.rs Normal file
View File

@ -0,0 +1,52 @@
use lazy_static::lazy_static;
use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector};
use x86_64::structures::tss::TaskStateSegment;
use x86_64::VirtAddr;
pub const DOUBLE_FAULT_IST_INDEX: u16 = 0;
pub struct Selectors {
code_selector: SegmentSelector,
tss_selector: SegmentSelector,
}
lazy_static! {
static ref TSS: TaskStateSegment = {
let mut tss = TaskStateSegment::new();
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = {
const STACK_SIZE: usize = 4096;
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
let stack_start = VirtAddr::from_ptr(unsafe { &STACK });
// FIXME once we have page allocation available.
let stack_end = stack_start + STACK_SIZE;
stack_end
};
tss
};
}
lazy_static! {
static ref GDT: (GlobalDescriptorTable, Selectors) = {
let mut gdt = GlobalDescriptorTable::new();
let code_selector = gdt.add_entry(Descriptor::kernel_code_segment());
let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS));
(
gdt,
Selectors {
code_selector,
tss_selector,
},
)
};
}
pub fn init() {
use x86_64::instructions::segmentation::set_cs;
use x86_64::instructions::tables::load_tss;
GDT.0.load();
unsafe {
set_cs(GDT.1.code_selector);
load_tss(GDT.1.tss_selector);
}
}

84
src/interrupts.rs Normal file
View File

@ -0,0 +1,84 @@
use lazy_static::lazy_static;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
use crate::gdt;
use crate::hlt_loop;
use polling_serial::serial_println;
use vga_buffer::{print, println, set_colors, Color, ForegroundColor};
lazy_static! {
static ref IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
idt.breakpoint.set_handler_fn(breakpoint_handler);
idt.double_fault.set_handler_fn(double_fault_handler);
unsafe {
idt.double_fault
.set_handler_fn(double_fault_handler)
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
}
idt.page_fault.set_handler_fn(page_fault_handler);
idt
};
}
pub fn init_idt() {
IDT.load();
}
// For now.
extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut InterruptStackFrame) {
serial_println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
x86_64::instructions::bochs_breakpoint();
}
extern "x86-interrupt" fn double_fault_handler(sf: &mut InterruptStackFrame, e: u64) {
// LLVM bug causing misaligned stacks when error codes are present.
// This code realigns the stack and then grabs the correct values by doing some pointer arithmetic
let stack_frame: &mut InterruptStackFrame;
let error_code: u64;
unsafe {
asm!("push rax" :::: "intel");
let s = (sf as *mut InterruptStackFrame);
stack_frame = &mut *((s as *mut u64).offset(1) as *mut InterruptStackFrame);
error_code = *(&e as *const u64).offset(1);
}
// End Hack
println!(
"=====\nUNRECOVERABLE EXCEPTION:\nDouble Fault\nError Code {:x?}\n{:#?}\n=====",
error_code, stack_frame
);
serial_println!(
"=====\nUNRECOVERABLE EXCEPTION:\nDouble Fault\nError Code {:x?}\n{:#?}\n=====",
error_code,
stack_frame
);
panic!("Unrecoverable exception");
}
use x86_64::structures::idt::PageFaultErrorCode;
extern "x86-interrupt" fn page_fault_handler(sf: &mut InterruptStackFrame, e: PageFaultErrorCode) {
// LLVM bug causing misaligned stacks when error codes are present.
// This code realigns the stack and then grabs the correct values by doing some pointer arithmetic
let stack_frame: &mut InterruptStackFrame;
let error_code: PageFaultErrorCode;
use x86_64::registers::control::Cr2;
unsafe {
asm!("push rax" :::: "intel");
let s = (sf as *mut InterruptStackFrame);
stack_frame = &mut *((s as *mut u64).offset(1) as *mut InterruptStackFrame);
error_code = *(&e as *const PageFaultErrorCode).offset(1) as PageFaultErrorCode;
}
println!("EXCEPTION: PAGE FAULT");
println!("Accessed Address: {:?}", Cr2::read());
println!("Error Code: {:?}", error_code);
println!("{:#?}", stack_frame);
hlt_loop();
}

View File

@ -3,11 +3,17 @@
#![feature(custom_test_frameworks)] #![feature(custom_test_frameworks)]
#![test_runner(crate::test_runner)] #![test_runner(crate::test_runner)]
#![reexport_test_harness_main = "test_main"] #![reexport_test_harness_main = "test_main"]
#![feature(abi_x86_interrupt)]
#![feature(asm)]
use core::panic::PanicInfo; use core::panic::PanicInfo;
use polling_serial::{serial_print, serial_println}; use polling_serial::{serial_print, serial_println};
use vga_buffer::{print, println}; use vga_buffer::{print, println};
use x86_64::instructions::bochs_breakpoint;
pub mod gdt;
pub mod interrupts;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)] #[repr(u32)]
@ -45,6 +51,18 @@ pub fn test_runner(tests: &[&dyn Fn()]) {
exit_qemu(QemuExitCode::Success); exit_qemu(QemuExitCode::Success);
} }
pub fn init() {
gdt::init();
interrupts::init_idt();
}
pub fn hlt_loop() -> ! {
loop {
bochs_breakpoint();
x86_64::instructions::hlt();
}
}
#[cfg(test)] #[cfg(test)]
#[no_mangle] #[no_mangle]
pub extern "C" fn _start() -> ! { pub extern "C" fn _start() -> ! {

View File

@ -8,12 +8,13 @@
#![reexport_test_harness_main = "test_main"] #![reexport_test_harness_main = "test_main"]
use polling_serial::{serial_print, serial_println}; use polling_serial::{serial_print, serial_println};
use vga_buffer::{print, println}; use vga_buffer::{print, println, set_colors, Color, ForegroundColor};
use core::fmt::Write; use core::fmt::Write;
use core::panic::PanicInfo; use core::panic::PanicInfo;
use vga_buffer; // required for custom panic handler use vga_buffer; // required for custom panic handler
use dendrobates_tinctoreus_azureus::hlt_loop;
use x86_64; use x86_64;
// Custom panic handler, required for freestanding program // Custom panic handler, required for freestanding program
@ -21,11 +22,10 @@ use x86_64;
#[panic_handler] #[panic_handler]
fn panic(info: &PanicInfo) -> ! { fn panic(info: &PanicInfo) -> ! {
serial_println!("{}", info); serial_println!("{}", info);
set_colors(ForegroundColor::LightRed, Color::Blue);
println!("{}", info); println!("{}", info);
loop {} x86_64::instructions::bochs_breakpoint();
loop { hlt_loop();
println!("{}", info);
}
} }
// Kernel entry point // Kernel entry point
@ -35,9 +35,21 @@ pub extern "C" fn _start() -> ! {
// TODO: We may also need to enable debug registers ? // TODO: We may also need to enable debug registers ?
println!("Hello Blue Frog"); println!("Hello Blue Frog");
dendrobates_tinctoreus_azureus::init();
x86_64::instructions::interrupts::int3(); // new
#[cfg(test)] #[cfg(test)]
test_main(); test_main();
println!("Preparing nasty fault...");
x86_64::instructions::interrupts::int3();
unsafe {
*(0xdeadbeef as *mut u64) = 42;
}
println!("Survived ? oO");
// magic break ? // magic break ?
// x86_64::instructions::bochs_breakpoint(); // x86_64::instructions::bochs_breakpoint();
panic!("Ooops Sorry"); panic!("Ooops Sorry");

104
tests/sse_interrupt_test.rs Normal file
View File

@ -0,0 +1,104 @@
/*
* This test is meant to check that floating point registers do not get clobbered by interrupts.
* Wires int3 to an interrupt handler that clobbers fp registers.
* And then execute a floating point code to detect if registers got clobbered.
*/
// TODO
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(dendrobates_tinctoreus_azureus::test_runner)]
#![reexport_test_harness_main = "test_main"]
#![feature(abi_x86_interrupt)]
use core::panic::PanicInfo;
use lazy_static::lazy_static;
use polling_serial::{serial_print, serial_println};
use vga_buffer::{print, println};
use volatile::Volatile;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
#[no_mangle] // don't mangle the name of this function
pub extern "C" fn _start() -> ! {
test_main();
loop {}
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
dendrobates_tinctoreus_azureus::test_panic_handler(info)
}
lazy_static! {
static ref IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
idt.breakpoint.set_handler_fn(breakpoint_handler);
idt
};
}
// For now.
extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut InterruptStackFrame) {
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
let e: Volatile<f32> = Volatile::new(15.213);
serial_println!("e: {:?}", e.read());
}
#[test_case]
fn test_intr() {
serial_println!("Testing float computations with int3...");
IDT.load();
use volatile::Volatile;
// Make a few floating points test;
let vf: f32 = 84798.0;
let vd: f64 = 0.828494623655914;
let a: Volatile<f32> = Volatile::new(42.0);
let b: Volatile<f32> = Volatile::new(2019.);
let a1 = a.read();
let b1 = b.read();
x86_64::instructions::interrupts::int3(); // new
let rf = a1 * b1;
let c: Volatile<f64> = Volatile::new(15.410);
let d: Volatile<f64> = Volatile::new(18.600);
let c1 = c.read();
let d1 = d.read();
x86_64::instructions::interrupts::int3(); // new
let rd = c1 / d1;
serial_print!(
" {:?} * {:?} = {:?} expected {:?}...",
a.read(),
b.read(),
rf,
vf
);
if (rf == vf) {
serial_println!("[ok]");
} else {
serial_println!("[fail]");
}
serial_print!(
" {:?} / {:?} = {:?} expected {:?}...",
c.read(),
d.read(),
rd,
vd
);
if (rd == vd) {
serial_println!("[ok]");
} else {
serial_println!("[fail]");
}
assert_eq!(rf, vf);
assert_eq!(rd, vd);
serial_println!("Testing float computations... [ok]");
}

73
tests/stack_overflow.rs Normal file
View File

@ -0,0 +1,73 @@
// in tests/stack_overflow.rs
#![no_std]
#![no_main]
#![feature(abi_x86_interrupt)]
#![feature(asm)]
use core::panic::PanicInfo;
use dendrobates_tinctoreus_azureus::{exit_qemu, QemuExitCode};
use polling_serial::{serial_print, serial_println};
#[no_mangle]
pub extern "C" fn _start() -> ! {
serial_print!("stack_overflow... ");
dendrobates_tinctoreus_azureus::gdt::init();
init_test_idt();
// trigger a stack overflow
stack_overflow(0);
panic!("Execution continued after stack overflow");
}
#[allow(unconditional_recursion)]
fn stack_overflow(i: u64) -> u64 {
let a = stack_overflow(i + 1); // for each recursion, the return address is pushed
a + 1
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
dendrobates_tinctoreus_azureus::test_panic_handler(info)
}
use lazy_static::lazy_static;
use x86_64::structures::idt::InterruptDescriptorTable;
lazy_static! {
static ref TEST_IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
unsafe {
idt.double_fault
.set_handler_fn(test_double_fault_handler)
.set_stack_index(dendrobates_tinctoreus_azureus::gdt::DOUBLE_FAULT_IST_INDEX);
}
idt
};
}
pub fn init_test_idt() {
TEST_IDT.load();
}
use x86_64::structures::idt::InterruptStackFrame;
extern "x86-interrupt" fn test_double_fault_handler(sf: &mut InterruptStackFrame, e: u64) {
// LLVM bug causing misaligned stacks when error codes are present.
// This code realigns the stack and then grabs the correct values by doing some pointer arithmetic
let stack_frame: &mut InterruptStackFrame;
let error_code: u64;
unsafe {
asm!("push rax" :::: "intel");
let s = (sf as *mut InterruptStackFrame);
stack_frame = &mut *((s as *mut u64).offset(1) as *mut InterruptStackFrame);
error_code = *(&e as *const u64).offset(1);
}
// End Hack
serial_println!("[ok]");
exit_qemu(QemuExitCode::Success);
loop {}
}