From e46f8c5187c7dc0b4427aba938bd567c21026465 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 14 Jan 2019 21:51:52 +0100 Subject: [PATCH] Introduce boot info later; continue post --- .../posts/10-advanced-paging/index.md | 443 +++++++++++++----- .../qemu-bootinfo-print.png | Bin 24012 -> 0 bytes .../10-advanced-paging/qemu-new-mapping.png | Bin 0 -> 7792 bytes 3 files changed, 336 insertions(+), 107 deletions(-) delete mode 100644 blog/content/second-edition/posts/10-advanced-paging/qemu-bootinfo-print.png create mode 100644 blog/content/second-edition/posts/10-advanced-paging/qemu-new-mapping.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 2d764875..3c617c58 100644 --- a/blog/content/second-edition/posts/10-advanced-paging/index.md +++ b/blog/content/second-edition/posts/10-advanced-paging/index.md @@ -144,82 +144,22 @@ Whereas `AAA` is the level 4 index, `BBB` the level 3 index, `CCC` the level 2 i ## Implementation -After all this theory we can finally start our implementation. We already mentioned that the bootloader created page tables for our kernel, but it also created a recursive mapping in the last entry of the level 4 table for us. The bootloader did this, because otherwise there would be a [chicken or egg problem]: We need to access the level 4 table to create a recursive mapping, but we can't access it without some kind of mapping. +After all this theory we can finally start our implementation. Conveniently, the bootloader not only created page tables for our kernel, it also created a recursive mapping in the last entry of the level 4 table. The bootloader did this, because otherwise there would be a [chicken or egg problem]: We need to access the level 4 table to create a recursive mapping, but we can't access it without some kind of mapping. [chicken or egg problem]: https://en.wikipedia.org/wiki/Chicken_or_the_egg -We already used this recursive mapping [at the end of the previous post] to access the level 4 table. We did this through the hardcoded address `0xffff_ffff_ffff_f000`. When we convert this address to [octal] and compare it with the above table, we can see that it exactly follows the structure: with `RRR` = `0o777` = 511, `AAAA` = 0, and the sign extension bits set to `1` each: +We already used this recursive mapping [at the end of the previous post] to access the level 4 table. We did this through the hardcoded address `0xffff_ffff_ffff_f000`. When we convert this address to [octal] and compare it with the above table, we can see that it exactly follows the structure with `RRR` = `0o777`, `AAAA` = 0, and the sign extension bits set to `1` each: ``` structure: 0o_SSSSSS_RRR_RRR_RRR_RRR_AAAA address: 0o_177777_777_777_777_777_0000 ``` -Using hardcoded addresses is rarely a good idea, since they might become outdated. For example, our code would break if a future bootloader version uses a different entry for the recursive mapping. Fortunately the bootloader tells us the recursive entry by passing a _boot information structure_ to our kernel. - -### Boot Information - -To communicate the index of the recursive entry and other information to our kernel, the bootloader passes a reference to a boot information structure as an argument when calling our `_start` function. Right now we don't have this argument declared in our function, so we just ignore it. Let's add the argument to the signature of our `_start` function: - -```rust -// in src/main.rs - -use bootloader::bootinfo::BootInfo; - -#[cfg(not(test))] -#[no_mangle] -pub extern "C" fn _start(boot_info: &'static BootInfo) -> ! { - println!("boot_info: {:x?}", boot_info); - - […] -} -``` - -The [`BootInfo`] struct is still in an early stage, so expect some breakage when updating to future [semver-incompatible] bootloader versions. When we print it, we see that it currently has the three fields `p4_table_addr`, `memory_map`, and `package`: - -[`BootInfo`]: https://docs.rs/bootloader/0.3.11/bootloader/bootinfo/struct.BootInfo.html -[semver-incompatible]: https://doc.rust-lang.org/stable/cargo/reference/specifying-dependencies.html#caret-requirements - -![QEMU printing a `BootInfo` struct: "boot_info: Bootlnfo { p4_table_addr: fffffffffffff000. memory_map: […]. package: […]"](qemu-bootinfo-print.png) - -The most interesting field for us right now is `p4_table_addr`, as it contains a virtual address that is mapped to the physical frame of the level 4 page table. As we see this address is `0xfffffffffffff000`, which is the same as the hardcoded address we used before. - -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 `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. +With our knowlege about recursive page tables we can now create virtual addresess to access all active page tables. This allows us to create a translation function in software. ### 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: +As a first step, let's create a function that translates a virtual address to a physical address by walking the page table hierarchy: ```rust // in src/lib.rs @@ -235,7 +175,7 @@ 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 { +pub fn translate_addr(addr: usize, level_4_table_addr) -> 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; @@ -275,34 +215,43 @@ pub fn translate_addr(addr: usize, level_4_table_addr: usize) -> Option ! { +#[no_mangle] +pub extern "C" fn _start() -> ! { […] // initialize GDT, IDT, PICS use blog_os::memory::translate_addr; - let level_4_table_addr = boot_info.p4_table_addr as usize; + const LEVEL_4_TABLE_ADDR: usize = 0o_177777_777_777_777_777_0000; // the identity-mapped vga buffer page - println!("0xb8000 -> {:?}", translate_addr(0xb8000, level_4_table_addr)); + println!("0xb8000 -> {:?}", translate_addr(0xb8000, LEVEL_4_TABLE_ADDR)); // some code page - println!("0x20010a -> {:?}", translate_addr(0x20010a, level_4_table_addr)); + println!("0x20010a -> {:?}", translate_addr(0x20010a, LEVEL_4_TABLE_ADDR)); // some stack page println!("0x57ac001ffe48 -> {:?}", translate_addr(0x57ac001ffe48, - level_4_table_addr)); + LEVEL_4_TABLE_ADDR)); println!("It did not crash!"); blog_os::hlt_loop(); @@ -327,14 +276,19 @@ The `x86_64` provides a [`RecursivePageTable`] type that implements safe abstrac 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 +/// Create a RecursivePageTable instance from the level 4 address +// TODO call only once +pub fn init(level_4_table_addr: usize) -> RecursivePageTable { 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(); + RecursivePageTable::new(level_4_table).unwrap() +} +/// Returns the physical address for the given virtual address, or +/// `None` if the virtual address is not mapped. +pub fn translate_addr(addr: u64, recursive_page_table: &RecursivePageTable) + -> Option +{ let addr = VirtAddr::new(addr); let page: Page = Page::containing_address(addr); @@ -344,51 +298,325 @@ pub fn translate_addr(addr: u64, level_4_table_addr: usize) -> Option } ``` -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. +The `RecursivePageTable` type encapsulates the unsafety of the page table walk completely. We only need a single instance of `unsafe` in the `init` function to create a `&mut PageTable` from the level 4 page table address. Also, we no longer need to perform any bitwise operations. + +Our `_start` function needs to be updated for the new function signature in the following way: + +```rust +// in src/main.rs + +#[cfg(not(test))] +#[no_mangle] +pub extern "C" fn _start() -> ! { + […] // initialize GDT, IDT, PICS + + use blog_os::memory::{self, translate_addr}; + + const LEVEL_4_TABLE_ADDR: usize = 0o_177777_777_777_777_777_0000; + let recursive_page_table = memory::init(LEVEL_4_TABLE_ADDR); + + // the identity-mapped vga buffer page + println!("0xb8000 -> {:?}", translate_addr(0xb8000, &recursive_page_table)); + // some code page + println!("0x20010a -> {:?}", translate_addr(0x20010a, &recursive_page_table)); + // some stack page + println!("0x57ac001ffe48 -> {:?}", translate_addr(0x57ac001ffe48, + &recursive_page_table)); + + println!("It did not crash!"); + blog_os::hlt_loop(); +} +``` + +Instead of passing the `LEVEL_4_TABLE_ADDR` to `translate_addr` and accessing the page tables through unsafe raw pointers, we now pass references to a `RecursivePageTable` type. By doing this, we now have a safe abstraction and clear ownership semantics. This ensures that we can't accidentally modify the page table concurrently, because an exclusive borrow of the `RecursivePageTable` is needed in order to modify it. + +After reading the page tables and creating a translation function, the next step is to create a new mapping in the page table hierarchy. ### 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: +The difficulty of creating a new mapping depends on on the virtual page that we want to map. In the easiest case, the level 1 page table for the page already exists and we just need to write a single entry. In the most difficult case, the page is in a memory region for that no level 3 exists yet, so that we need to create new level 3, level 2 and level 1 page tables first. -[`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 +For creating a new page table we need to find an unused physical frame where the page table will be stored. We initialize it to zero and create a mapping for that frame in the higher level page table. At this point, we can access the new page table through the recursive page table and continue with the next level. + +Let's start with the simple case and assume that we don't need to create new page tables. The bootloader loads itself in the first megabyte of the virtual address space, so we know that a valid level 1 table exists for this region. We can choose any unused page in this memory region for our example mapping, for example the page at address `0x1000`. As the target frame we use `0xb8000`, the frame of the VGA text buffer. This way we can easily test whether our mapping worked. + +We implement it in a new `create_mapping` function in our `memory` module: ```rust -pub trait Mapper { - fn map_to( - &mut self, - page: Page, - frame: PhysFrame, - flags: PageTableFlags, - allocator: &mut A - ) -> Result, MapToError> - where - A: FrameAllocator; +// in src/memory/mod.rs +use x86_64::structures::paging::{FrameAllocator, PhysFrame, Size4KiB}; + +pub fn create_example_mapping( + recursive_page_table: &mut RecursivePageTable, + frame_allocator: &mut impl FrameAllocator, +) { + use x86_64::structures::paging::PageTableFlags as Flags; + + let page: Page = Page::containing_address(VirtAddr::new(0x1000)); + let frame = PhysFrame::containing_address(PhysAddr::new(0xb8000)); + let flags = Flags::PRESENT | Flags::WRITABLE; + + recursive_page_table.map_to(page, frame, flags, frame_allocator) + .expect("map_to failed").flush(); +} +``` + +The function takes a mutable reference to the `RecursivePageTable` because it needs to modify it. It then uses the [`map_to`] function of the [`Mapper`] trait to map the page at `0x1000` to the physical frame at address `0xb8000`. The `PRESENT` flags is required for all valid entries and the `WRITABLE` flag makes the mapping writable. + +[`map_to`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/trait.Mapper.html#tymethod.map_to +[`Mapper`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/trait.Mapper.html + +The 4th argument needs to be some structure that implements the [`FrameAllocator`] trait. The `map_to` method needs this argument, because it might need unused frames for creating new page tables. Since we know that no new page tables are required for the address `0x1000`, we pass an `EmptyFrameAllocator` type that always returns `None`. The `Size4KiB` argument in the trait implementation is needed because the [`Page`] and [`PhysFrame`] types are [generic] over the [`PageSize`] to work with both standard 4KiB pages and huge 2MiB/1GiB pages. + +[`FrameAllocator`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/trait.FrameAllocator.html +[`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 +[generic]: 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 + +The [`map_to`] function can fail, so it returns a [`Result`]. Since this is just some example code that does not need to be robust, we just use [`expect`] to panic when an error occurs. On success, the function returns a [`MapperFlush`] type that provides an easy way to flush the newly mapped page from the translation lookaside buffer (TLB) with its [`flush`] method. Like `Result`, the type uses the [`#[must_use]`] attribute to emit a warning when we accidentally ignore the return type. + +[`Result`]: https://doc.rust-lang.org/core/result/enum.Result.html +[`expect`]: https://doc.rust-lang.org/core/result/enum.Result.html#method.expect +[`MapperFlush`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/struct.MapperFlush.html +[`flush`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/struct.MapperFlush.html#method.flush +[`#[must_use]`]: https://doc.rust-lang.org/std/result/#results-must-be-used + + + + +TODO + +```rust +// in src/memory/mod.rs + +/// A FrameAllocator that always returns `None`. +pub struct EmptyFrameAllocator; + +impl FrameAllocator for EmptyFrameAllocator { + fn alloc(&mut self) -> Option { + None + } +} +``` + +TODO + + + + +We can test the new mapping function in our `main.rs`: + +```rust +// in src/main.rs + +#[cfg(not(test))] +#[no_mangle] +pub extern "C" fn _start() -> ! { + […] // initialize GDT, IDT, PICS + + use blog_os::memory::{create_example_mapping, EmptyFrameAllocator}; + + let mut recursive_page_table = memory::init(boot_info.p4_table_addr as usize); + + create_example_mapping(&mut recursive_page_table, &mut EmptyFrameAllocator); + unsafe { (0x1c00 as *mut u64).write_volatile(0xffffffffffffffff)}; + + println!("It did not crash!"); + blog_os::hlt_loop(); +} +``` + +We first create the mapping for the page at `0x1000` by calling our `create_example_mapping` function with a mutable reference to the `RecursivePageTable` instance. This maps the page `0x1000` to the VGA text buffer, so we should see any write to it on the screen. We don't write directly to `0x1000` since the top line is directly shifted off the screen by the next `println`. As we learned [in the _“VGA Text Mode”_ post], writes to the VGA buffer should be volatile. + +[in the _“VGA Text Mode”_ post]: ./second-edition/posts/03-vga-text-buffer/index.md#volatile + +When we run it in QEMU, we see the following output: + +![QEMU printing "It did not crash!" with four completely white cells in middle of the screen](qemu-new-mapping.png) + +The white block in the middle of the screen is by our write to `0x1c00`, which means that we successfully created a new mapping in the page tables. + +This only worked because there was already a level 1 table for mapping page `0x1000`. If we try to map a page for that no level 1 table exists yet, the `map_to` function tries to allocate frames from the `EmptyFrameAllocator` to create new page tables, which fails. We can see that happen when we try to map for example page `0xdeadbeaf000` instead of `0x1000`: + +```rust +// in src/memory/mod.rs + +TODO: update create_example_mapping + +// in src/main.rs + +TODO: update accessed address +``` + +When we run it, a panic with the following error message occurs: + +``` +panicked at 'map_to failed: FrameAllocationFailed', /…/result.rs:999:5 +``` + +To map pages that don't have a level 1 page table yet we need to create a proper `FrameAllocator`. But how do we know which frames are unused and how much physical memory is available? + +### Boot Information + +The amount of physical memory and physical memory reserved by devices like the VGA hardware varies between different machines. Only the BIOS or UEFI firmware knows exactly which memory regions can be used by the operating system and which regions are reserved. Both firmaware standards provide functions to retrieve the memory map, but they can only be called very early in the boot process. For this reason, the bootloader already queries this and other information from the firmaware. + +To communicate this information to our kernel, the bootloader passes a reference to a boot information structure as an argument when calling our `_start` function. Right now we don't have this argument declared in our function, so we just ignore it. Let's add it: + +```rust +// in src/main.rs + +use bootloader::bootinfo::BootInfo; + +#[cfg(not(test))] +#[no_mangle] +pub extern "C" fn _start(boot_info: &'static BootInfo) -> ! { […] } ``` -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 +The [`BootInfo`] struct is still in an early stage, so expect some breakage when updating to future [semver-incompatible] bootloader versions. It currently has the three fields `p4_table_addr`, `memory_map`, and `package`: -[`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 +[`BootInfo`]: https://docs.rs/bootloader/0.3.11/bootloader/bootinfo/struct.BootInfo.html +[semver-incompatible]: https://doc.rust-lang.org/stable/cargo/reference/specifying-dependencies.html#caret-requirements -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. +- The `p4_table_addr` field contains the recursive virtual address of the level 4 page table. By using this field we can avoid hardcoding the address `0o_177777_777_777_777_777_0000`. +- The `memory_map` field is most interesting to us, since it contains a list of all memory regions and their type (i.e. unused, reserved, or other). +- 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. -[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 +Before we use the `memory_map` field to create a proper `FrameAllocator`, we want to ensure that we can't use a `boot_info` argument of the wrong type. +#### The `entry_point` Macro -TODO: +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. -- Map adddress 0 to the vga buffer -- We need free physical frames for creating new page tables -> memory map +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: -## A Physical Memory Map +[`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) -> ! { + […] // initialize GDT, IDT, PICS + + let mut recursive_page_table = memory::init(boot_info.p4_table_addr as usize); + + […] // create and test example mapping + + println!("It did not crash!"); + blog_os::hlt_loop(); +} +``` + +We no longer need to use `extern "C"` or `no_mangle` for our entry point, as the macro defines the real lowet level `_start` entry point for us. The `kernel_main` function is now a completely normal Rust function, so we can choose an arbitrary name for it. The important thing is that it is type-checked now, so that a compilation error occurs when we now try to modify the function signature in any way, for example adding an argument or changing the argument type. + +Note that we now pass `boot_info.p4_table_addr` instead of a hardcoded address to our `memory::init`. Thus our code continues to work even if a future version of the bootloader chooses a different entry of the level 4 page table for the recursive mapping. + +### Allocating Frames + +Now that we have access to the memory map through the boot information we can create a proper frame allocator on top. We start with a generic skeleton: + +```rust +// in src/memory/mod.rs + +pub struct BootInfoFrameAllocator where I: Iterator { + frames: I, +} + +impl FrameAllocator for BootInfoFrameAllocator + where I: Iterator +{ + fn alloc(&mut self) -> Option { + self.frames.next() + } +} +``` + +The `frames` field can be initialized with an arbitrary [`Iterator`] of frames. This allows us to just delegate `alloc` calls to the [`Iterator::next`] method. + +[`Iterator`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html +[`Iterator::next`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#tymethod.next + +The initialization of the `BootInfoFrameAllocator` happens in a new `init_frame_allocator` function: + +```rust +// in src/memory/mod.rs + +use bootloader::bootinfo::{MemoryMap, MemoryRegionType}; + +/// Create a FrameAllocator from the passed memory map +pub fn init_frame_allocator(memory_map: &'static MemoryMap) + -> BootInfoFrameAllocator> +{ + // get usable regions from memory map + let regions = memory_map.iter().filter(|r| { + r.region_type == MemoryRegionType::Usable + }); + // map each region to its address range + let addr_ranges = regions.map(|r| r.range.start_addr()..r.range.end_addr()); + // transform to an iterator of frame start addresses + let frame_addresses = addr_ranges.flat_map(|r| r.into_iter().step_by(4096)); + // create `PhysFrame` types from the start addresses + let frames = frame_addresses.map(|addr| { + PhysFrame::containing_address(PhysAddr::new(addr)) + }); + + BootInfoFrameAllocator { frames } +} +``` + +This function uses iterator combinator methods to transform the initial `MemoryMap` into an iterator of usable physical frames: + +- First, we call the `iter` method to convert the memory map to an iterator of [`MemoryRegion`]s. Then we use the [`filter`] method to skip any reserved or otherwise unavailable regions. The bootloader updates the memory map for all the mappings it creates, so frames that are used by our kernel (code, data or stack) or to store the boot information are already marked as `InUse` or similar. Thus we can be sure that `Usable` frames are not in use somewhere else. +- In the second step, we use the [`map`] combinator and Rust's [range syntax] to transform our iterator of memory regions to an iterator of address ranges. +- The third step is the most complicated: We convert each range to an iterator through the `into_iter` method and then choose every 4096th address using [`step_by`]. 4096 bytes (= 4 KiB) is the page size, so by doing this we get the start address of each frame. The bootloader page aligns all usable memory areas, so that we don't need any alignment or rounding code here. By using [`flat_map`] instead of `map`, we get an `Iterator` instead of an `Iterator>`. +- In the final step, we convert the start addresses to `PhysFrame` types to construct the desired `Iterator`. We then use this iterator to create and return a new `BootInfoFrameAllocator`. + +[`MemoryRegion`]: https://docs.rs/bootloader/0.3.12/bootloader/bootinfo/struct.MemoryRegion.html +[`filter`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.filter +[`map`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.map +[range syntax]: https://doc.rust-lang.org/core/ops/struct.Range.html +[`step_by`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.step_by +[`flat_map`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.flat_map + +We can now modify our `kernel_main` function to pass a `BootInfoFrameAllocator` instance instead of an `EmptyFrameAllocator`: + +```rust +// in src/main.rs + +#[cfg(not(test))] +fn kernel_main(boot_info: &'static BootInfo) -> ! { + […] // initialize GDT, IDT, PICS + + use x86_64::structures::paging::{PageTable, RecursivePageTable}; + + let mut recursive_page_table = memory::init(boot_info.p4_table_addr as usize); + let mut frame_allocator = memory::init_frame_allocator(&boot_info.memory_map); + + blog_os::memory::create_mapping(&mut recursive_page_table, &mut frame_allocator); + unsafe { (0xdeadbeafc00 as *mut u64).write_volatile(0xffffffffffffffff)}; + + println!("It did not crash!"); + blog_os::hlt_loop(); +} +``` + +Now the mapping succeeds and we see the white block on the screen again: + +![]() TODO + +The `map_to` method was now able to create the missing page tables on the frames allocated from our `BootInfoFrameAllocator`. You can insert a `println` message into the `BootInfoFrameAllocator::alloc` method to see how it's called. + +We're now able to map arbitrary pages and to allocate new physical frames when we need them. + +# TODO ## Allocating Stacks @@ -397,4 +625,5 @@ TODO: ## What's next? --- -TODO update post date \ No newline at end of file +TODO spellcheck +TODO update post date diff --git a/blog/content/second-edition/posts/10-advanced-paging/qemu-bootinfo-print.png b/blog/content/second-edition/posts/10-advanced-paging/qemu-bootinfo-print.png deleted file mode 100644 index 83efc17ab386df78043c76eccf40617d89cb465d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24012 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 }g?^*})+Xdwqga9OXF<-- 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
eS-_fs?-MFDs*Xir17s!;-Z?h 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