From 1ff102dc27a67525ef6639dbf67e32019cb3e16f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 11 Jan 2019 15:05:51 +0100 Subject: [PATCH] Continue post --- .../posts/10-advanced-paging/index.md | 199 +++++++++++++++++- .../10-advanced-paging/qemu-page-fault.png | Bin 0 -> 11599 bytes .../qemu-print-p4-entries.png | Bin 9480 -> 0 bytes .../qemu-translate-addr.png | Bin 0 -> 9947 bytes 4 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 blog/content/second-edition/posts/10-advanced-paging/qemu-page-fault.png delete mode 100644 blog/content/second-edition/posts/10-advanced-paging/qemu-print-p4-entries.png create mode 100644 blog/content/second-edition/posts/10-advanced-paging/qemu-translate-addr.png diff --git a/blog/content/second-edition/posts/10-advanced-paging/index.md b/blog/content/second-edition/posts/10-advanced-paging/index.md index 06a36bb4..5a8eab14 100644 --- a/blog/content/second-edition/posts/10-advanced-paging/index.md +++ b/blog/content/second-edition/posts/10-advanced-paging/index.md @@ -21,7 +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 -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. +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. ### Page Faults @@ -283,7 +283,202 @@ The most interesting field for us right now is `p4_table_addr`, as it contains a The `memory_map` field will become relevant later in this post. The `package` field is an in-progress feature to bundle additional data with the bootloader. The implementation is not finished, so we can ignore this field for now. -### The `RecursivePageTable` Type +#### The `entry_point` Macro + +Since our `_start` function is called externally from the bootloader, no checking of our function signature occurs. This means that we could let it take arbitrary arguments without any compilation errors, but it would fail or cause undefined behavior at runtime. + +To make sure that the entry point function has always the correct signature that the bootloader expects, the `bootloader` crate provides an [`entry_point`] macro that provides a type-checked way to define a Rust function as entry point. Let's rewrite our entry point function to use this macro: + +[`entry_point`]: https://docs.rs/bootloader/0.3.12/bootloader/macro.entry_point.html + +```rust +// in src/main.rs + +use bootloader::entry_point; + +entry_point!(kernel_main); + +#[cfg(not(test))] +fn kernel_main(boot_info: &'static BootInfo) -> ! { + use blog_os::interrupts::PICS; + + blog_os::gdt::init(); + blog_os::interrupts::init_idt(); + unsafe { PICS.lock().initialize() }; + x86_64::instructions::interrupts::enable(); + + println!("It did not crash!"); + blog_os::hlt_loop(); +} +``` + +Note that we no longer need to use `extern "C"` or `no_mangle` for our entry point, since the macro does this for us. We can also use an arbitrary name for our function. When we now try to modify the function signature in any way, for example adding an argument or changing the argument type, a compilation error occurs. + +### Translating Addresses + +Now we have a clean way to retrieve the virtual address of the recursively mapped level 4 table. which allows us to derive the virtual addreses of all other page tables. As a first step, let's try to create a function that translates a virtual address to a physical address: + +```rust +// in src/lib.rs + +pub mod memory; +``` + +```rust +// in src/memory/mod.rs + +use x86_64::PhysAddr; +use x86_64::structures::paging::PageTable; + +/// Returns the physical address for the given virtual address, or `None` if the +/// virtual address is not mapped. +pub fn translate_addr(addr: usize, level_4_table_addr: usize) -> Option { + // retrieve the page table indices of the address that we want to translate + let level_4_index = (addr >> 39) & 0o777; + let level_3_index = (addr >> 30) & 0o777; + let level_2_index = (addr >> 21) & 0o777; + let level_1_index = (addr >> 12) & 0o777; + let page_offset = addr & 0o7777; + + // check that level 4 entry is mapped + let level_4_table = unsafe {&*(level_4_table_addr as *const PageTable)}; + if level_4_table[level_4_index].addr().is_null() { + return None; + } + let level_3_table_addr = (level_4_table_addr << 9) | (level_4_index << 12); + + // check that level 3 entry is mapped + let level_3_table = unsafe {&*(level_3_table_addr as *const PageTable)}; + if level_3_table[level_3_index].addr().is_null() { + return None; + } + let level_2_table_addr = (level_3_table_addr << 9) | (level_3_index << 12); + + // check that level 2 entry is mapped + let level_2_table = unsafe {&*(level_2_table_addr as *const PageTable)}; + if level_2_table[level_2_index].addr().is_null() { + return None; + } + let level_1_table_addr = (level_2_table_addr << 9) | (level_2_index << 12); + + // check that level 1 entry is mapped and retrieve physical address from it + let level_1_table = unsafe {&*(level_1_table_addr as *const PageTable)}; + let phys_addr = level_1_table[level_1_index].addr(); + if phys_addr.is_null() { + return None; + } + + Some(phys_addr + page_offset) +} +``` + +First, we calculate the page table indices and the page offset from the address: + +![Bits 0–12 are the page offset, bits 12–21 the level 1 index, bits 21–30 the level 2 index, bits 30–39 the level 3 index, and bits 39–48 the level 4 index](../paging-introduction/x86_64-table-indices-from-address.svg) + +Then we check the whether the entries in the four tables are empty and return `None` in that case. It's important that we do this because the address of the next table is only valid if the entry is mapped, and we don't want to risk that our translation function causes a page fault. + +After we checked the three higher level pages, we can finally read the entry of the level 1 table that tells us the physical frame that the address is mapped to. Finally, we add the page offset to that address and return it. + +Now we can use this function to translate some virtual addresses in our `kernel_main` function: + +```rust +// in src/main.rs + +#[cfg(not(test))] +fn kernel_main(boot_info: &'static BootInfo) -> ! { + […] // initialize GDT, IDT, PICS + + use blog_os::memory::translate_addr; + + let level_4_table_addr = boot_info.p4_table_addr as usize; + + // the identity-mapped vga buffer page + println!("0xb8000 -> {:?}", translate_addr(0xb8000, level_4_table_addr)); + // some code page + println!("0x20010a -> {:?}", translate_addr(0x20010a, level_4_table_addr)); + // some stack page + println!("0x57ac001ffe48 -> {:?}", translate_addr(0x57ac001ffe48, + level_4_table_addr)); + + println!("It did not crash!"); + blog_os::hlt_loop(); +} +``` + +When we run it, we see the following output: + +![0xb8000 -> 0xb8000, 0x20010a -> 0x40010a, 0x57ac001ffe48 -> 0x27be48](qemu-translate-addr.png) + +As expected, the identity-mapped address `0xb8000` translates to the same physical address. The code page and the stack page translate to some arbitrary physical addresses, that depend on how the bootloader created the initial mapping for our kernel. + +#### The `RecursivePageTable` Type + +The `x86_64` provides a [`RecursivePageTable`] type that implements safe abstractions for various page table operations. By using this type, we can reimplement our `translate_addr` function in a much cleaner way: + +[`RecursivePageTable`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/struct.RecursivePageTable.html + +```rust +// in src/memory/mod.rs + +use x86_64::{VirtAddr, PhysAddr}; +use x86_64::structures::paging::{Mapper, Page, PageTable, RecursivePageTable}; + +/// Returns the physical address for the given virtual address, or +/// `None` if the virtual address is not mapped. +pub fn translate_addr(addr: u64, level_4_table_addr: usize) -> Option { + // create a RecursivePageTable instance from the level 4 address + let level_4_table_ptr = level_4_table_addr as *mut PageTable; + let level_4_table = unsafe { &mut *level_4_table_ptr }; + let recursive_page_table = RecursivePageTable::new(level_4_table).unwrap(); + + let addr = VirtAddr::new(addr); + let page: Page = Page::containing_address(addr); + + // perform the translation + let frame = recursive_page_table.translate_page(page); + frame.map(|frame| frame.start_address() + addr.page_offset()) +} +``` + +The `RecursivePageTable` type encapsulates the unsafety of the page table walk completely. We only need a single instance of `unsafe` to create a `&mut PageTable` from the level 4 page table address. Also, we no longer need to perform any bitwise operations. + +### Creating a new Mapping + +Let's try to create a new mapping in the page tables. The `RecursivePageTable` type implements the [`Mapper`] trait, which has a [`map_to`] method with the following signature: + +[`Mapper`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/trait.Mapper.html +[`map_to`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/trait.Mapper.html#tymethod.map_to + +```rust +pub trait Mapper { + fn map_to( + &mut self, + page: Page, + frame: PhysFrame, + flags: PageTableFlags, + allocator: &mut A + ) -> Result, MapToError> + where + A: FrameAllocator; + + […] +} +``` + +The method creates a mapping in the page table that maps the given [`Page`] to the given [`PhysFrame`] with the given [`PageTableFlags`]. The last parameter is [generic] and expects some type that implements the [`FrameAllocator`] trait. This parameter is needed because the method might need to create new page tables to create the mapping, so it requires empty frames for this. <-- TODO + +[`Page`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/struct.Page.html +[`PhysFrame`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/struct.PhysFrame.html +[`PageTableFlags`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/struct.PageTableFlags.html +[generic]: https://doc.rust-lang.org/book/ch10-00-generics.html +[`FrameAllocator`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/trait.FrameAllocator.html + +There are two [generic parameters]: `S: PageSize` and `A: FrameAllocator`. The [`PageSize`] trait makes it possible to generate generic code that works with normal 4 KiB pages and huge 2 MiB and 1 GiB pages at the same time. We only use default 4 KiB for now, so we can ignore this parameter for now. + +[generic parameters]: https://doc.rust-lang.org/book/ch10-00-generics.html +[`PageSize`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/trait.PageSize.html + TODO: diff --git a/blog/content/second-edition/posts/10-advanced-paging/qemu-page-fault.png b/blog/content/second-edition/posts/10-advanced-paging/qemu-page-fault.png new file mode 100644 index 0000000000000000000000000000000000000000..5657759750e232705f15b81b77c75fe66f5eb983 GIT binary patch literal 11599 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 V#e3ZkF)%PNc)I$ztaD0e0szDyCwl+@ literal 0 HcmV?d00001 diff --git a/blog/content/second-edition/posts/10-advanced-paging/qemu-print-p4-entries.png b/blog/content/second-edition/posts/10-advanced-paging/qemu-print-p4-entries.png deleted file mode 100644 index 8108b6d7c55bd51f43e376363627f0a04bb2a548..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9480 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 diff --git a/blog/content/second-edition/posts/10-advanced-paging/qemu-translate-addr.png b/blog/content/second-edition/posts/10-advanced-paging/qemu-translate-addr.png new file mode 100644 index 0000000000000000000000000000000000000000..2bf5917fd46d66cd0ec31c4c60234da61b2b1da7 GIT binary patch literal 9947 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 S<0S(F1B0ilpUXO@geCws2JW~3 literal 0 HcmV?d00001