Update x86_64 doc links to version 0.5.0

This commit is contained in:
Philipp Oppermann
2019-03-09 12:45:57 +01:00
parent c14d1db833
commit b95ec88e4f
5 changed files with 35 additions and 35 deletions

View File

@@ -80,7 +80,7 @@ Don't worry about steps 4 and 5 for now, we will learn about the global descript
## An IDT Type ## An IDT Type
Instead of creating our own IDT type, we will use the [`InterruptDescriptorTable` struct] of the `x86_64` crate, which looks like this: Instead of creating our own IDT type, we will use the [`InterruptDescriptorTable` struct] of the `x86_64` crate, which looks like this:
[`InterruptDescriptorTable` struct]: https://docs.rs/x86_64/0.2.8/x86_64/structures/idt/struct.InterruptDescriptorTable.html [`InterruptDescriptorTable` struct]: https://docs.rs/x86_64/0.5.0/x86_64/structures/idt/struct.InterruptDescriptorTable.html
``` rust ``` rust
#[repr(C)] #[repr(C)]
@@ -111,10 +111,10 @@ pub struct InterruptDescriptorTable {
The fields have the type [`idt::Entry<F>`], which is a struct that represents the fields of an IDT entry (see the table above). The type parameter `F` defines the expected handler function type. We see that some entries require a [`HandlerFunc`] and some entries require a [`HandlerFuncWithErrCode`]. The page fault even has its own special type: [`PageFaultHandlerFunc`]. The fields have the type [`idt::Entry<F>`], which is a struct that represents the fields of an IDT entry (see the table above). The type parameter `F` defines the expected handler function type. We see that some entries require a [`HandlerFunc`] and some entries require a [`HandlerFuncWithErrCode`]. The page fault even has its own special type: [`PageFaultHandlerFunc`].
[`idt::Entry<F>`]: https://docs.rs/x86_64/0.2.8/x86_64/structures/idt/struct.Entry.html [`idt::Entry<F>`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/idt/struct.Entry.html
[`HandlerFunc`]: https://docs.rs/x86_64/0.2.8/x86_64/structures/idt/type.HandlerFunc.html [`HandlerFunc`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/idt/type.HandlerFunc.html
[`HandlerFuncWithErrCode`]: https://docs.rs/x86_64/0.2.8/x86_64/structures/idt/type.HandlerFuncWithErrCode.html [`HandlerFuncWithErrCode`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/idt/type.HandlerFuncWithErrCode.html
[`PageFaultHandlerFunc`]: https://docs.rs/x86_64/0.2.8/x86_64/structures/idt/type.PageFaultHandlerFunc.html [`PageFaultHandlerFunc`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/idt/type.PageFaultHandlerFunc.html
Let's look at the `HandlerFunc` type first: Let's look at the `HandlerFunc` type first:
@@ -189,7 +189,7 @@ So the _exception stack frame_ looks like this:
In the `x86_64` crate, the exception stack frame is represented by the [`ExceptionStackFrame`] struct. It is passed to interrupt handlers as `&mut` and can be used to retrieve additional information about the exception's cause. The struct contains no error code field, since only some few exceptions push an error code. These exceptions use the separate [`HandlerFuncWithErrCode`] function type, which has an additional `error_code` argument. In the `x86_64` crate, the exception stack frame is represented by the [`ExceptionStackFrame`] struct. It is passed to interrupt handlers as `&mut` and can be used to retrieve additional information about the exception's cause. The struct contains no error code field, since only some few exceptions push an error code. These exceptions use the separate [`HandlerFuncWithErrCode`] function type, which has an additional `error_code` argument.
[`ExceptionStackFrame`]: https://docs.rs/x86_64/0.2.8/x86_64/structures/idt/struct.ExceptionStackFrame.html [`ExceptionStackFrame`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/idt/struct.ExceptionStackFrame.html
Note that there is currently [a bug in LLVM] that leads to wrong error code arguments. The cause of the issue is already known and a solution is [being worked on]. Note that there is currently [a bug in LLVM] that leads to wrong error code arguments. The cause of the issue is already known and a solution is [being worked on].
@@ -277,7 +277,7 @@ This error occurs because the `x86-interrupt` calling convention is still unstab
In order that the CPU uses our new interrupt descriptor table, we need to load it using the [`lidt`] instruction. The `InterruptDescriptorTable` struct of the `x86_64` provides a [`load`][InterruptDescriptorTable::load] method function for that. Let's try to use it: In order that the CPU uses our new interrupt descriptor table, we need to load it using the [`lidt`] instruction. The `InterruptDescriptorTable` struct of the `x86_64` provides a [`load`][InterruptDescriptorTable::load] method function for that. Let's try to use it:
[`lidt`]: http://x86.renejeschke.de/html/file_module_x86_id_156.html [`lidt`]: http://x86.renejeschke.de/html/file_module_x86_id_156.html
[InterruptDescriptorTable::load]: https://docs.rs/x86_64/0.2.8/x86_64/structures/idt/struct.InterruptDescriptorTable.html#method.load [InterruptDescriptorTable::load]: https://docs.rs/x86_64/0.5.0/x86_64/structures/idt/struct.InterruptDescriptorTable.html#method.load
```rust ```rust
// in src/interrupts.rs // in src/interrupts.rs
@@ -476,7 +476,7 @@ Both variants have the exact same effects, so it comes down to personal preferen
The `x86-interrupt` calling convention and the [`InterruptDescriptorTable`] type made the exception handling process relatively straightforward and painless. If this was too much magic for you and you like to learn all the gory details of exception handling, we got you covered: Our [“Handling Exceptions with Naked Functions”] series shows how to handle exceptions without the `x86-interrupt` calling convention and also creates its own IDT type. Historically, these posts were the main exception handling posts before the `x86-interrupt` calling convention and the `x86_64` crate existed. Note that these posts are based on the [first edition] of this blog and might be out of date. The `x86-interrupt` calling convention and the [`InterruptDescriptorTable`] type made the exception handling process relatively straightforward and painless. If this was too much magic for you and you like to learn all the gory details of exception handling, we got you covered: Our [“Handling Exceptions with Naked Functions”] series shows how to handle exceptions without the `x86-interrupt` calling convention and also creates its own IDT type. Historically, these posts were the main exception handling posts before the `x86-interrupt` calling convention and the `x86_64` crate existed. Note that these posts are based on the [first edition] of this blog and might be out of date.
[“Handling Exceptions with Naked Functions”]: ./first-edition/extra/naked-exceptions/_index.md [“Handling Exceptions with Naked Functions”]: ./first-edition/extra/naked-exceptions/_index.md
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.2.8/x86_64/structures/idt/struct.InterruptDescriptorTable.html [`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/idt/struct.InterruptDescriptorTable.html
[first edition]: ./first-edition/_index.md [first edition]: ./first-edition/_index.md
## What's next? ## What's next?

View File

@@ -223,7 +223,7 @@ The _Privilege Stack Table_ is used by the CPU when the privilege level changes.
### Creating a TSS ### Creating a TSS
Let's create a new TSS that contains a separate double fault stack in its interrupt stack table. For that we need a TSS struct. Fortunately, the `x86_64` crate already contains a [`TaskStateSegment` struct] that we can use. Let's create a new TSS that contains a separate double fault stack in its interrupt stack table. For that we need a TSS struct. Fortunately, the `x86_64` crate already contains a [`TaskStateSegment` struct] that we can use.
[`TaskStateSegment` struct]: https://docs.rs/x86_64/0.2.8/x86_64/structures/tss/struct.TaskStateSegment.html [`TaskStateSegment` struct]: https://docs.rs/x86_64/0.5.0/x86_64/structures/tss/struct.TaskStateSegment.html
We create the TSS in a new `gdt` module (the name will make sense later): We create the TSS in a new `gdt` module (the name will make sense later):
@@ -376,8 +376,8 @@ pub fn init() {
We reload the code segment register using [`set_cs`] and to load the TSS using [`load_tss`]. The functions are marked as `unsafe`, so we need an `unsafe` block to invoke them. The reason is that it might be possible to break memory safety by loading invalid selectors. We reload the code segment register using [`set_cs`] and to load the TSS using [`load_tss`]. The functions are marked as `unsafe`, so we need an `unsafe` block to invoke them. The reason is that it might be possible to break memory safety by loading invalid selectors.
[`set_cs`]: https://docs.rs/x86_64/0.2.8/x86_64/instructions/segmentation/fn.set_cs.html [`set_cs`]: https://docs.rs/x86_64/0.5.0/x86_64/instructions/segmentation/fn.set_cs.html
[`load_tss`]: https://docs.rs/x86_64/0.2.8/x86_64/instructions/tables/fn.load_tss.html [`load_tss`]: https://docs.rs/x86_64/0.5.0/x86_64/instructions/tables/fn.load_tss.html
Now that we loaded a valid TSS and interrupt stack table, we can set the stack index for our double fault handler in the IDT: Now that we loaded a valid TSS and interrupt stack table, we can set the stack index for our double fault handler in the IDT:

View File

@@ -222,7 +222,7 @@ extern "x86-interrupt" fn timer_interrupt_handler(
Our `timer_interrupt_handler` has the same signature as our exception handlers, because the CPU reacts identically to exceptions and external interrupts (the only difference is that some exceptions push an error code). The [`InterruptDescriptorTable`] struct implements the [`IndexMut`] trait, so we can access individual entries through array indexing syntax. Our `timer_interrupt_handler` has the same signature as our exception handlers, because the CPU reacts identically to exceptions and external interrupts (the only difference is that some exceptions push an error code). The [`InterruptDescriptorTable`] struct implements the [`IndexMut`] trait, so we can access individual entries through array indexing syntax.
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.2.11/x86_64/structures/idt/struct.InterruptDescriptorTable.html [`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/idt/struct.InterruptDescriptorTable.html
[`IndexMut`]: https://doc.rust-lang.org/core/ops/trait.IndexMut.html [`IndexMut`]: https://doc.rust-lang.org/core/ops/trait.IndexMut.html
In our timer interrupt handler, we print a dot to the screen. As the timer interrupt happens periodically, we would expect to see a dot appearing on each timer tick. However, when we run it we see that only a single dot is printed: In our timer interrupt handler, we print a dot to the screen. As the timer interrupt happens periodically, we would expect to see a dot appearing on each timer tick. However, when we run it we see that only a single dot is printed:
@@ -348,7 +348,7 @@ pub fn _print(args: fmt::Arguments) {
The [`without_interrupts`] function takes a [closure] and executes it in an interrupt-free environment. We use it to ensure that no interrupt can occur as long as the `Mutex` is locked. When we run our kernel now we see that it keeps running without hanging. (We still don't notice any dots, but this is because they're scrolling by too fast. Try to slow down the printing, e.g. by putting a `for _ in 0..10000 {}` inside the loop.) The [`without_interrupts`] function takes a [closure] and executes it in an interrupt-free environment. We use it to ensure that no interrupt can occur as long as the `Mutex` is locked. When we run our kernel now we see that it keeps running without hanging. (We still don't notice any dots, but this is because they're scrolling by too fast. Try to slow down the printing, e.g. by putting a `for _ in 0..10000 {}` inside the loop.)
[`without_interrupts`]: https://docs.rs/x86_64/0.2.10/x86_64/instructions/interrupts/fn.without_interrupts.html [`without_interrupts`]: https://docs.rs/x86_64/0.5.0/x86_64/instructions/interrupts/fn.without_interrupts.html
[closure]: https://doc.rust-lang.org/book/second-edition/ch13-01-closures.html [closure]: https://doc.rust-lang.org/book/second-edition/ch13-01-closures.html
We can apply the same change to our serial printing function to ensure that no deadlocks occur with it either: We can apply the same change to our serial printing function to ensure that no deadlocks occur with it either:
@@ -518,7 +518,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(
We use the [`Port`] type of the `x86_64` crate to read a byte from the keyboard's data port. This byte is called the [_scancode_] and is a number that represents the key press/release. We don't do anything with the scancode yet, we just print it to the screen: We use the [`Port`] type of the `x86_64` crate to read a byte from the keyboard's data port. This byte is called the [_scancode_] and is a number that represents the key press/release. We don't do anything with the scancode yet, we just print it to the screen:
[`Port`]: https://docs.rs/x86_64/0.2.11/x86_64/instructions/port/struct.Port.html [`Port`]: https://docs.rs/x86_64/0.5.0/x86_64/instructions/port/struct.Port.html
[_scancode_]: https://en.wikipedia.org/wiki/Scancode [_scancode_]: https://en.wikipedia.org/wiki/Scancode
![QEMU printing scancodes to the screen when keys are pressed](qemu-printing-scancodes.gif) ![QEMU printing scancodes to the screen when keys are pressed](qemu-printing-scancodes.gif)

View File

@@ -227,8 +227,8 @@ Let's take a closer look at the available flags:
The `x86_64` crate provides types for [page tables] and their [entries], so we don't need to create these structures ourselves. The `x86_64` crate provides types for [page tables] and their [entries], so we don't need to create these structures ourselves.
[page tables]: https://docs.rs/x86_64/0.3.4/x86_64/structures/paging/struct.PageTable.html [page tables]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/struct.PageTable.html
[entries]: https://docs.rs/x86_64/0.3.4/x86_64/structures/paging/struct.PageTableEntry.html [entries]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/page_table/struct.PageTableEntry.html
### The Translation Lookaside Buffer ### The Translation Lookaside Buffer
@@ -237,7 +237,7 @@ A 4-level page table makes the translation of virtual addresses expensive, becau
Unlike the other CPU caches, the TLB is not fully transparent and does not update or remove translations when the contents of page tables change. This means that the kernel must manually update the TLB whenever it modifies a page table. To do this, there is a special CPU instruction called [`invlpg`] (“invalidate page”) that removes the translation for the specified page from the TLB, so that it is loaded again from the page table on the next access. The TLB can also be flushed completely by reloading the `CR3` register, which simulates an address space switch. The `x86_64` crate provides Rust functions for both variants in the [`tlb` module]. Unlike the other CPU caches, the TLB is not fully transparent and does not update or remove translations when the contents of page tables change. This means that the kernel must manually update the TLB whenever it modifies a page table. To do this, there is a special CPU instruction called [`invlpg`] (“invalidate page”) that removes the translation for the specified page from the TLB, so that it is loaded again from the page table on the next access. The TLB can also be flushed completely by reloading the `CR3` register, which simulates an address space switch. The `x86_64` crate provides Rust functions for both variants in the [`tlb` module].
[`invlpg`]: https://www.felixcloutier.com/x86/INVLPG.html [`invlpg`]: https://www.felixcloutier.com/x86/INVLPG.html
[`tlb` module]: https://docs.rs/x86_64/0.3.4/x86_64/instructions/tlb/index.html [`tlb` module]: https://docs.rs/x86_64/0.5.0/x86_64/instructions/tlb/index.html
It is important to remember flushing the TLB on each page table modification because otherwise the CPU might keep using the old translation, which can lead to non-deterministic bugs that are very hard to debug. It is important to remember flushing the TLB on each page table modification because otherwise the CPU might keep using the old translation, which can lead to non-deterministic bugs that are very hard to debug.
@@ -291,8 +291,8 @@ extern "x86-interrupt" fn page_fault_handler(
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. 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`]: 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 [`Cr2::read`]: https://docs.rs/x86_64/0.5.0/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 [`PageFaultErrorCode`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/idt/struct.PageFaultErrorCode.html
[LLVM bug]: https://github.com/rust-lang/rust/issues/57270 [LLVM bug]: https://github.com/rust-lang/rust/issues/57270
[`hlt_loop`]: ./second-edition/posts/08-hardware-interrupts/index.md#the [`hlt_loop`]: ./second-edition/posts/08-hardware-interrupts/index.md#the
@@ -368,9 +368,9 @@ pub extern "C" fn _start() -> ! {
The [`Cr3::read`] function of the `x86_64` returns the currently active level 4 page table from the `CR3` register. It returns a tuple of a [`PhysFrame`] and a [`Cr3Flags`] type. We are only interested in the frame, so we ignore the second element of the tuple. The [`Cr3::read`] function of the `x86_64` returns the currently active level 4 page table from the `CR3` register. It returns a tuple of a [`PhysFrame`] and a [`Cr3Flags`] type. We are only interested in the frame, so we ignore the second element of the tuple.
[`Cr3::read`]: https://docs.rs/x86_64/0.3.4/x86_64/registers/control/struct.Cr3.html#method.read [`Cr3::read`]: https://docs.rs/x86_64/0.5.0/x86_64/registers/control/struct.Cr3.html#method.read
[`PhysFrame`]: https://docs.rs/x86_64/0.3.4/x86_64/structures/paging/struct.PhysFrame.html [`PhysFrame`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/frame/struct.PhysFrame.html
[`Cr3Flags`]: https://docs.rs/x86_64/0.3.4/x86_64/registers/control/struct.Cr3Flags.html [`Cr3Flags`]: https://docs.rs/x86_64/0.5.0/x86_64/registers/control/struct.Cr3Flags.html
When we run it, we see the following output: When we run it, we see the following output:
@@ -380,7 +380,7 @@ Level 4 page table at: PhysAddr(0x1000)
So the currently active level 4 page table is stored at address `0x1000` in _physical_ memory, as indicated by the [`PhysAddr`] wrapper type. The question now is: how can we access this table from our kernel? So the currently active level 4 page table is stored at address `0x1000` in _physical_ memory, as indicated by the [`PhysAddr`] wrapper type. The question now is: how can we access this table from our kernel?
[`PhysAddr`]: https://docs.rs/x86_64/0.3.4/x86_64/struct.PhysAddr.html [`PhysAddr`]: https://docs.rs/x86_64/0.5.0/x86_64/struct.PhysAddr.html
Accessing physical memory directly is not possible when paging is active, since programs could easily circumvent memory protection and access memory of other programs otherwise. So the only way to access the table is through some virtual page that is mapped to the physical frame at address `0x1000`. This problem of creating mappings for page table frames is a general problem, since the kernel needs to access the page tables regularly, for example when allocating a stack for a new thread. Accessing physical memory directly is not possible when paging is active, since programs could easily circumvent memory protection and access memory of other programs otherwise. So the only way to access the table is through some virtual page that is mapped to the physical frame at address `0x1000`. This problem of creating mappings for page table frames is a general problem, since the kernel needs to access the page tables regularly, for example when allocating a stack for a new thread.
@@ -418,7 +418,7 @@ When we look at the [format of page table entries][page table format], we see th
Instead of working with unsafe raw pointers we can use the [`PageTable`] type of the `x86_64` crate: Instead of working with unsafe raw pointers we can use the [`PageTable`] type of the `x86_64` crate:
[`PageTable`]: https://docs.rs/x86_64/0.3.4/x86_64/structures/paging/struct.PageTable.html [`PageTable`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/page_table/struct.PageTable.html
```rust ```rust
// in src/main.rs // in src/main.rs

View File

@@ -248,7 +248,7 @@ First, we introduce variables for the recursive index (511 = `0o777`) and the si
In the next step we calculate the virtual addresses of the four page tables as descripbed in the [address calculation] section. We transform each of these addresses to [`PageTable`] references later in the function. These transformations are `unsafe` operations since the compiler can't know that these addresses are valid. In the next step we calculate the virtual addresses of the four page tables as descripbed in the [address calculation] section. We transform each of these addresses to [`PageTable`] references later in the function. These transformations are `unsafe` operations since the compiler can't know that these addresses are valid.
[address calculation]: #address-calculation [address calculation]: #address-calculation
[`PageTable`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/struct.PageTable.html [`PageTable`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/page_table/struct.PageTable.html
After the address calculation, we use the indexing operator to look at the entry in the level 4 table. If that entry is null, there is no level 3 table for this level 4 entry, which means that the `addr` is not mapped to any physical memory, so we return `None`. If the entry is not `None`, we know that a level 3 table exists. We then do the same cast and entry-checking as with the level 4 table. After the address calculation, we use the indexing operator to look at the entry in the level 4 table. If that entry is null, there is no level 3 table for this level 4 entry, which means that the `addr` is not mapped to any physical memory, so we return `None`. If the entry is not `None`, we know that a level 3 table exists. We then do the same cast and entry-checking as with the level 4 table.
@@ -293,7 +293,7 @@ As expected, the identity-mapped address `0xb8000` translates to the same physic
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: 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 [`RecursivePageTable`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/struct.RecursivePageTable.html
```rust ```rust
// in src/memory.rs // in src/memory.rs
@@ -377,7 +377,7 @@ pub unsafe fn init(level_4_table_addr: usize) -> RecursivePageTable<'static> {
The problem with this is that we don't immediately see which parts are unsafe. For example, we don't know whether the `RecursivePageTable::new` function is unsafe or not without looking at [its definition][RecursivePageTable::new]. This makes it very easy to accidentally do something unsafe without noticing. The problem with this is that we don't immediately see which parts are unsafe. For example, we don't know whether the `RecursivePageTable::new` function is unsafe or not without looking at [its definition][RecursivePageTable::new]. This makes it very easy to accidentally do something unsafe without noticing.
[RecursivePageTable::new]: https://docs.rs/x86_64/0.3.6/x86_64/structures/paging/struct.RecursivePageTable.html#method.new [RecursivePageTable::new]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/struct.RecursivePageTable.html#method.new
To avoid this problem, we can add a safe inner function: To avoid this problem, we can add a safe inner function:
@@ -435,25 +435,25 @@ pub fn create_example_mapping(
The function takes a mutable reference to the `RecursivePageTable` because it needs to modify it and a `FrameAllocator` that is explained below. It then uses the [`map_to`] function of the [`Mapper`] trait to map the page at address `0x1000` to the physical frame at address `0xb8000`. The function is unsafe because it's possible to break memory safety with invalid arguments. The function takes a mutable reference to the `RecursivePageTable` because it needs to modify it and a `FrameAllocator` that is explained below. It then uses the [`map_to`] function of the [`Mapper`] trait to map the page at address `0x1000` to the physical frame at address `0xb8000`. The function is unsafe because it's possible to break memory safety with invalid arguments.
[`map_to`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/trait.Mapper.html#tymethod.map_to [`map_to`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/mapper/trait.Mapper.html#tymethod.map_to
[`Mapper`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/trait.Mapper.html [`Mapper`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/mapper/trait.Mapper.html
Apart from the `page` and `frame` arguments, the [`map_to`] function takes two more arguments. The third argument is a set of flags for the page table entry. We set the `PRESENT` flag because it is required for all valid entries and the `WRITABLE` flag to make the mapped page writable. Apart from the `page` and `frame` arguments, the [`map_to`] function takes two more arguments. The third argument is a set of flags for the page table entry. We set the `PRESENT` flag because it is required for all valid entries and the `WRITABLE` flag to make the mapped page writable.
The fourth 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. The `Size4KiB` argument in the trait implementation is needed because the [`Page`] and [`PhysFrame`] types are [generic] over the [`PageSize`] trait to work with both standard 4KiB pages and huge 2MiB/1GiB pages. The fourth 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. The `Size4KiB` argument in the trait implementation is needed because the [`Page`] and [`PhysFrame`] types are [generic] over the [`PageSize`] trait 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 [`FrameAllocator`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/trait.FrameAllocator.html
[`Page`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/struct.Page.html [`Page`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/page/struct.Page.html
[`PhysFrame`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/struct.PhysFrame.html [`PhysFrame`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/frame/struct.PhysFrame.html
[generic]: https://doc.rust-lang.org/book/ch10-00-generics.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 [`PageSize`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/page/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 forget to use it. 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 forget to use it.
[`Result`]: https://doc.rust-lang.org/core/result/enum.Result.html [`Result`]: https://doc.rust-lang.org/core/result/enum.Result.html
[`expect`]: https://doc.rust-lang.org/core/result/enum.Result.html#method.expect [`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 [`MapperFlush`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/mapper/struct.MapperFlush.html
[`flush`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/struct.MapperFlush.html#method.flush [`flush`]: https://docs.rs/x86_64/0.5.0/x86_64/structures/paging/mapper/struct.MapperFlush.html#method.flush
[`#[must_use]`]: https://doc.rust-lang.org/std/result/#results-must-be-used [`#[must_use]`]: https://doc.rust-lang.org/std/result/#results-must-be-used
Since we know that no new page tables are required for the address `0x1000`, a frame allocator that always returns `None` suffices. We create such an `EmptyFrameAllocator` for testing our mapping function: Since we know that no new page tables are required for the address `0x1000`, a frame allocator that always returns `None` suffices. We create such an `EmptyFrameAllocator` for testing our mapping function: