mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 14:27:49 +00:00
The page fault section was moved to the paging-introduction post
This commit is contained in:
@@ -21,104 +21,7 @@ In the [previous post] we learned about the principles of paging and how the 4-l
|
|||||||
|
|
||||||
[previous post]: ./second-edition/posts/09-paging-introduction/index.md
|
[previous post]: ./second-edition/posts/09-paging-introduction/index.md
|
||||||
|
|
||||||
This makes our kernel much safer, since every memory access that is out of bounds causes a page fault exception instead of writing to random physical memory. The bootloader even set the correct access permissions for each page, which means that only the pages containing code are executable and only data pages are writable.
|
The problem that page tables use physical addresses internally, which we can't access directly from our kernel. We experienced that problem already [at the end of the previous post] when we tried to inspect the active page tables. The next section discusses the problem in detail and provides different approaches to a solution.
|
||||||
|
|
||||||
### Page Faults
|
|
||||||
|
|
||||||
Let's try to cause a page fault by accessing some memory outside of our kernel! First, we create a page fault handler and register it in our IDT, so that we see a page fault exception instead of a generic [double fault] :
|
|
||||||
|
|
||||||
[double fault]: ./second-edition/posts/07-double-faults/index.md
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// in src/interrupts.rs
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref IDT: InterruptDescriptorTable = {
|
|
||||||
let mut idt = InterruptDescriptorTable::new();
|
|
||||||
|
|
||||||
[…]
|
|
||||||
|
|
||||||
idt.page_fault.set_handler_fn(page_fault_handler); // new
|
|
||||||
|
|
||||||
idt
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
use x86_64::structures::idt::PageFaultErrorCode;
|
|
||||||
|
|
||||||
extern "x86-interrupt" fn page_fault_handler(
|
|
||||||
stack_frame: &mut ExceptionStackFrame,
|
|
||||||
_error_code: PageFaultErrorCode,
|
|
||||||
) {
|
|
||||||
use crate::hlt_loop;
|
|
||||||
use x86_64::registers::control::Cr2;
|
|
||||||
|
|
||||||
println!("EXCEPTION: PAGE FAULT");
|
|
||||||
println!("Accessed Address: {:?}", Cr2::read());
|
|
||||||
println!("{:#?}", stack_frame);
|
|
||||||
hlt_loop();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The [`CR2`] register is automatically set by the CPU on a page fault and contains the accessed virtual address that caused the page fault. We use the [`Cr2::read`] function of the `x86_64` crate to read and print it. Normally the [`PageFaultErrorCode`] type would provide more information about the type of memory access that caused the page fault, but there is currently an [LLVM bug] that passes an invalid error code, so we ignore it for now. We can't continue execution without resolving the page fault, so we enter a [`hlt_loop`] at the end.
|
|
||||||
|
|
||||||
[`CR2`]: https://en.wikipedia.org/wiki/Control_register#CR2
|
|
||||||
[`Cr2::read`]: https://docs.rs/x86_64/0.3.5/x86_64/registers/control/struct.Cr2.html#method.read
|
|
||||||
[`PageFaultErrorCode`]: https://docs.rs/x86_64/0.3.4/x86_64/structures/idt/struct.PageFaultErrorCode.html
|
|
||||||
[LLVM bug]: https://github.com/rust-lang/rust/issues/57270
|
|
||||||
[`hlt_loop`]: ./second-edition/posts/08-hardware-interrupts/index.md#the
|
|
||||||
|
|
||||||
Now we can try to access some memory outside our kernel:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// in src/main.rs
|
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn _start() -> ! {
|
|
||||||
use blog_os::interrupts::PICS;
|
|
||||||
|
|
||||||
println!("Hello World{}", "!");
|
|
||||||
|
|
||||||
// set up the IDT first, otherwise we would enter a boot loop instead of
|
|
||||||
// invoking our page fault handler
|
|
||||||
blog_os::gdt::init();
|
|
||||||
blog_os::interrupts::init_idt();
|
|
||||||
unsafe { PICS.lock().initialize() };
|
|
||||||
x86_64::instructions::interrupts::enable();
|
|
||||||
|
|
||||||
// new
|
|
||||||
let ptr = 0xdeadbeaf as *mut u32;
|
|
||||||
unsafe { *ptr = 42; }
|
|
||||||
|
|
||||||
println!("It did not crash!");
|
|
||||||
blog_os::hlt_loop();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
When we run it, we see that our page fault handler is called:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
The `CR2` register indeed contains `0xdeadbeaf`, the address that we tried to access. This virtual address has no mapping in the page tables, so a page fault occured.
|
|
||||||
|
|
||||||
We see that the current instruction pointer is `0x20430a`, so we know that this address points to a code page. Code pages are mapped read-only by the bootloader, so reading from this address works but writing causes a page fault. You can try this by changing the `0xdeadbeaf` pointer:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// Note: The actual address might be different for you. Use the address that
|
|
||||||
// your page fault handler reports.
|
|
||||||
let ptr = 0x20430a as *mut u32;
|
|
||||||
// read from a code page -> works
|
|
||||||
unsafe { let x = *ptr; }
|
|
||||||
// write to a code page -> page fault
|
|
||||||
unsafe { *ptr = 42; }
|
|
||||||
```
|
|
||||||
|
|
||||||
### The Problem
|
|
||||||
|
|
||||||
We just saw that it is a good thing that our kernel already runs on virtual addresses, as it improves safety. However, it also leads to a problem when we try to access the page tables from our kernel: Page tables use physical addresses internally that we can't access directly.
|
|
||||||
|
|
||||||
We experienced that problem already [at the end of the previous post] when we tried to inspect the page tables from our kernel. The next section discusses the problem in detail and provides different approaches to a solution.
|
|
||||||
|
|
||||||
[at the end of the previous post]: ./second-edition/posts/09-paging-introduction/index.md#try-it-out
|
[at the end of the previous post]: ./second-edition/posts/09-paging-introduction/index.md#try-it-out
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user