The page fault section was moved to the paging-introduction post

This commit is contained in:
Philipp Oppermann
2019-01-14 21:50:39 +01:00
parent 1ff102dc27
commit 41b21914be
2 changed files with 1 additions and 98 deletions

View File

@@ -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:
![EXCEPTION: Page Fault, Accessed Address: VirtAddr(0xdeadbeaf), ExceptionStackFrame: {…}](qemu-page-fault.png)
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