Interrupt handling working and tested.
- int3 - double fault with separate stack - page fault
This commit is contained in:
parent
510a596849
commit
f4cc148d83
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -41,6 +41,7 @@ name = "dendrobates_tinctoreus_azureus"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"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",
|
||||
"vga_buffer 0.1.0",
|
||||
"volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
14
Cargo.toml
14
Cargo.toml
@ -28,6 +28,10 @@ vga_buffer = { path = "vga_buffer" }
|
||||
polling_serial = { path = "polling_serial" }
|
||||
volatile = "0.2.6"
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "1.0"
|
||||
features = ["spin_no_std"]
|
||||
|
||||
[dependencies.bootloader]
|
||||
version = "^0.8.2"
|
||||
features = ["sse"]
|
||||
@ -36,9 +40,17 @@ features = ["sse"]
|
||||
#bootloader = { path = "../bootloader" }
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 0
|
||||
opt-level = 1
|
||||
debug = 2
|
||||
|
||||
[profile.test]
|
||||
opt-level = 1
|
||||
debug = 2
|
||||
|
||||
[[test]]
|
||||
name = "panic_test"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "stack_overflow"
|
||||
harness = false
|
||||
|
52
src/gdt.rs
Normal file
52
src/gdt.rs
Normal 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
84
src/interrupts.rs
Normal 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();
|
||||
}
|
18
src/lib.rs
18
src/lib.rs
@ -3,11 +3,17 @@
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(crate::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
#![feature(abi_x86_interrupt)]
|
||||
#![feature(asm)]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
use polling_serial::{serial_print, serial_println};
|
||||
use vga_buffer::{print, println};
|
||||
use x86_64::instructions::bochs_breakpoint;
|
||||
|
||||
pub mod gdt;
|
||||
pub mod interrupts;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
@ -45,6 +51,18 @@ pub fn test_runner(tests: &[&dyn Fn()]) {
|
||||
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)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
|
22
src/main.rs
22
src/main.rs
@ -8,12 +8,13 @@
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
|
||||
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::panic::PanicInfo;
|
||||
use vga_buffer; // required for custom panic handler
|
||||
|
||||
use dendrobates_tinctoreus_azureus::hlt_loop;
|
||||
use x86_64;
|
||||
|
||||
// Custom panic handler, required for freestanding program
|
||||
@ -21,11 +22,10 @@ use x86_64;
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
serial_println!("{}", info);
|
||||
set_colors(ForegroundColor::LightRed, Color::Blue);
|
||||
println!("{}", info);
|
||||
loop {}
|
||||
loop {
|
||||
println!("{}", info);
|
||||
}
|
||||
x86_64::instructions::bochs_breakpoint();
|
||||
hlt_loop();
|
||||
}
|
||||
|
||||
// Kernel entry point
|
||||
@ -35,9 +35,21 @@ pub extern "C" fn _start() -> ! {
|
||||
// TODO: We may also need to enable debug registers ?
|
||||
|
||||
println!("Hello Blue Frog");
|
||||
dendrobates_tinctoreus_azureus::init();
|
||||
x86_64::instructions::interrupts::int3(); // new
|
||||
#[cfg(test)]
|
||||
test_main();
|
||||
|
||||
println!("Preparing nasty fault...");
|
||||
|
||||
x86_64::instructions::interrupts::int3();
|
||||
|
||||
unsafe {
|
||||
*(0xdeadbeef as *mut u64) = 42;
|
||||
}
|
||||
|
||||
println!("Survived ? oO");
|
||||
|
||||
// magic break ?
|
||||
// x86_64::instructions::bochs_breakpoint();
|
||||
panic!("Ooops Sorry");
|
||||
|
104
tests/sse_interrupt_test.rs
Normal file
104
tests/sse_interrupt_test.rs
Normal 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
73
tests/stack_overflow.rs
Normal 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 {}
|
||||
}
|
Loading…
Reference in New Issue
Block a user