diff --git a/blog/content/second-edition/posts/06-cpu-exceptions/index.md b/blog/content/second-edition/posts/06-cpu-exceptions/index.md index 9fd6303a..82a5099f 100644 --- a/blog/content/second-edition/posts/06-cpu-exceptions/index.md +++ b/blog/content/second-edition/posts/06-cpu-exceptions/index.md @@ -434,15 +434,14 @@ Let's create an integration test that ensures that the above continues to work. ```rust // in src/bin/test-exception-breakpoint.rs -use blog_os::exit_qemu; +[…] use core::sync::atomic::{AtomicUsize, Ordering}; static BREAKPOINT_HANDLER_CALLED: AtomicUsize = AtomicUsize::new(0); -#[cfg(not(test))] #[no_mangle] pub extern "C" fn _start() -> ! { - init_idt(); + init_test_idt(); // invoke a breakpoint exception x86_64::instructions::int3(); @@ -463,16 +462,31 @@ pub extern "C" fn _start() -> ! { loop {} } -extern "x86-interrupt" fn breakpoint_handler(_: &mut ExceptionStackFrame) { + +lazy_static! { + static ref TEST_IDT: InterruptDescriptorTable = { + let mut idt = InterruptDescriptorTable::new(); + idt.breakpoint.set_handler_fn(breakpoint_handler); + idt + }; +} + +pub fn init_test_idt() { + TEST_IDT.load(); +} + +extern "x86-interrupt" fn breakpoint_handler( + _stack_frame: &mut ExceptionStackFrame) +{ BREAKPOINT_HANDLER_CALLED.fetch_add(1, Ordering::SeqCst); } -// […] +[…] ``` For space reasons we don't show the full content here. You can find the full file [on Github](https://github.com/phil-opp/blog_os/blob/master/src/bin/test-exception-breakpoint.rs). -It is basically a copy of our `main.rs` with some modifications to `_start` and `breakpoint_handler`. The most interesting part is the `BREAKPOINT_HANDLER_CALLER` static. It is an [`AtomicUsize`], an integer type that can be safely concurrently modifies because all of its operations are atomic. We increment it when the `breakpoint_handler` is called and verify in our `_start` function that the handler was called exactly once. +It is similar to our `main.rs`, but uses a custom IDT called `TEST_IDT` and different `_start` and `breakpoint_handler` functions. The most interesting part is the `BREAKPOINT_HANDLER_CALLER` static. It is an [`AtomicUsize`], an integer type that can be safely concurrently modifies because all of its operations are atomic. We increment it when the `breakpoint_handler` is called and verify in our `_start` function that the handler was called exactly once. [`AtomicUsize`]: https://doc.rust-lang.org/core/sync/atomic/struct.AtomicUsize.html diff --git a/src/bin/test-exception-breakpoint.rs b/src/bin/test-exception-breakpoint.rs index e6150acf..f9de1543 100644 --- a/src/bin/test-exception-breakpoint.rs +++ b/src/bin/test-exception-breakpoint.rs @@ -18,7 +18,7 @@ static BREAKPOINT_HANDLER_CALLED: AtomicUsize = AtomicUsize::new(0); #[cfg(not(test))] #[no_mangle] pub extern "C" fn _start() -> ! { - init_idt(); + init_test_idt(); // invoke a breakpoint exception x86_64::instructions::int3(); @@ -59,15 +59,15 @@ fn panic(info: &PanicInfo) -> ! { use x86_64::structures::idt::{ExceptionStackFrame, InterruptDescriptorTable}; lazy_static! { - static ref IDT: InterruptDescriptorTable = { + static ref TEST_IDT: InterruptDescriptorTable = { let mut idt = InterruptDescriptorTable::new(); idt.breakpoint.set_handler_fn(breakpoint_handler); idt }; } -pub fn init_idt() { - IDT.load(); +pub fn init_test_idt() { + TEST_IDT.load(); } extern "x86-interrupt" fn breakpoint_handler(_stack_frame: &mut ExceptionStackFrame) { diff --git a/src/bin/test-exception-double-fault-stack-overflow.rs b/src/bin/test-exception-double-fault-stack-overflow.rs index 876e4486..30125c86 100644 --- a/src/bin/test-exception-double-fault-stack-overflow.rs +++ b/src/bin/test-exception-double-fault-stack-overflow.rs @@ -17,7 +17,7 @@ use core::panic::PanicInfo; #[allow(unconditional_recursion)] pub extern "C" fn _start() -> ! { blog_os::gdt::init(); - init_idt(); + init_test_idt(); fn stack_overflow() { stack_overflow(); // for each recursion, the return address is pushed @@ -53,7 +53,7 @@ fn panic(info: &PanicInfo) -> ! { use x86_64::structures::idt::{ExceptionStackFrame, InterruptDescriptorTable}; lazy_static! { - static ref IDT: InterruptDescriptorTable = { + static ref TEST_IDT: InterruptDescriptorTable = { let mut idt = InterruptDescriptorTable::new(); unsafe { idt.double_fault @@ -65,8 +65,8 @@ lazy_static! { }; } -pub fn init_idt() { - IDT.load(); +pub fn init_test_idt() { + TEST_IDT.load(); } extern "x86-interrupt" fn double_fault_handler(