From 2ad8de51de5e2e343e26a31b1132c8ef28efe704 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 21 Dec 2018 00:49:22 +0100 Subject: [PATCH 01/29] Begin second paging post --- .../identity-mapped-page-tables.svg | 2 + .../second-edition/posts/10-paging-2/index.md | 134 ++++++++++++++++++ .../10-paging-2/recursive-page-table.svg | 2 + .../temporarily-mapped-page-tables.svg | 2 + 4 files changed, 140 insertions(+) create mode 100644 blog/content/second-edition/posts/10-paging-2/identity-mapped-page-tables.svg create mode 100644 blog/content/second-edition/posts/10-paging-2/index.md create mode 100644 blog/content/second-edition/posts/10-paging-2/recursive-page-table.svg create mode 100644 blog/content/second-edition/posts/10-paging-2/temporarily-mapped-page-tables.svg diff --git a/blog/content/second-edition/posts/10-paging-2/identity-mapped-page-tables.svg b/blog/content/second-edition/posts/10-paging-2/identity-mapped-page-tables.svg new file mode 100644 index 00000000..ad9b0aa9 --- /dev/null +++ b/blog/content/second-edition/posts/10-paging-2/identity-mapped-page-tables.svg @@ -0,0 +1,2 @@ + +
Physical Memory
Physical Memory
Virtual
Memory
[Not supported by viewer]
0KiB
[Not supported by viewer]
4KiB
[Not supported by viewer]
16KiB
<div>16KiB</div>
24KiB
<div>24KiB</div>
8KiB
[Not supported by viewer]
1000KiB
<div>1000KiB</div>
0KiB
[Not supported by viewer]
4KiB
[Not supported by viewer]
16KiB
<div>16KiB</div>
24KiB
<div>24KiB</div>
8KiB
[Not supported by viewer]
1000KiB
<div>1000KiB</div>
\ No newline at end of file diff --git a/blog/content/second-edition/posts/10-paging-2/index.md b/blog/content/second-edition/posts/10-paging-2/index.md new file mode 100644 index 00000000..62295d35 --- /dev/null +++ b/blog/content/second-edition/posts/10-paging-2/index.md @@ -0,0 +1,134 @@ ++++ +title = "Paging 2" +order = 10 +path = "paging-2" +date = 0000-01-01 +template = "second-edition/page.html" ++++ + +This post TODO + + + +This blog is openly developed on [Github]. If you have any problems or questions, please open an issue there. You can also leave comments [at the bottom]. + +[Github]: https://github.com/phil-opp/blog_os +[at the bottom]: #comments + +## Introduction + +In the [previous post] we learned about the principles of paging and how the 4-level page tables on the x86_64 architecture work. One thing that the post did not mention: **Our kernel already runs on paging**. The bootloader that we added in the ["A minimal Rust Kernel"] post already set up a 4-level paging hierarchy that maps every page of our kernel to a physical frame. The reason why the bootloader does this is that paging is manditory in 64-bit mode on x86_64. + +[previous post]: ./second-edition/posts/09-paging/index.md +["A minimal Rust kernel"]: ./second-edition/posts/02-minimal-rust-kernel/index.md#creating-a-bootimage + +The bootloader also sets the correct access permissions for each page, which means that only the pages containing code are executable and only data pages are writable. You can try this by accessing some memory outside our kernel: + +```rust +let ptr = 0xdeadbeaf as *mut u32; +unsafe { *ptr = 42; } +``` + +You will see that this results in an page fault exception. (We don't have page fault handler, so you will see that the double fault handler is invoked.) + +In case you are wondering how we could access the physical address `0xb8000` in order to print to the [VGA text buffer]: The bootloader identity mapped this frame, which means that it set up a page at the virtual address `0xb8000` that points to the physical frame with the same address. + +[VGA text buffer]: ./second-edition/posts/03-vga-text-buffer/index.md + +The question is: How do we access the page tables that our kernel runs to create new page mappings? + +## Accessing Page Tables + +Accessing the page tables from our kernel is not as easy as it may seem. To understand the problem let's take a look at the example 4-level page table hierarchy of the previous post again: + +![An example 4-level page hierarchy with each page table shown in physical memory](../paging/x86_64-page-table-translation.svg) + +The important thing here is that each page entry stores the _physical_ address of the next table. This avoids the need to run a translation for these addresses too, which would be bad for performance and could easily cause endless translation loops. + +The problem for us is that we can't directly access physical addresses from our kernel, since our kernel also runs on top of virtual addresses. For example when we access address 4KiB, we access the _virtual_ address 4KiB, not the _physical_ address 4KiB where the level 4 page table lives. When we want to acccess the physical address 4KiB, we can only do so through some virtual address that maps to it. + +So in order access page table frames, we need to map some virtual pages to them. There are different ways to create these mappings that all allow us to access arbitrary page table frames: + + +- A simple solution is to **identity map all page tables** like the VGA text buffer: + + ![A virtual and a physical address space with various virtual pages mapped to the physical frame with the same address](identity-mapped-page-tables.svg) + + In this example we see various identity-mapped page table frames. This way the physical addresses in the page tables are also valid virtual addresses so that we can easily access the page tables of all levels starting from the CR3 register. + + However, it clutters the virtual address space and makes it more difficult to find continuous memory regions of larger sizes. For example, imagine that we want to create a virtual memory region of size 1000 KiB in the above graphic, e.g. for [memory-mapping a file]. We can't start the region at 26 KiB because it would collide with the already mapped page at 1004 MiB. So we have to look further until we find a large enough unmapped area, for example at 1008 KiB. This is a similar fragmentation problem as with [segmentation]. + + [memory-mapping a file]: https://en.wikipedia.org/wiki/Memory-mapped_file + [segmentation]: ./second-edition/posts/09-paging/index.md#fragmentation + + Equally, it makes it much more difficult to create new page tables, because we need to find physical frames whose corresponding pages aren't already in use. For example, let's assume that we reserved the 1000 KiB memory region starting at 1008 KiB for our memory-mapped file. Now we can't use any frame with a _physical_ address between 1000 KiB and 2008 KiB anymore, because we can't identity map it. + +- Alternatively, we could **map the page tables frames only temporarily** when we need to access them. To be able to create the temporary mappings, we could identity map some level 1 table: + + ![A virtual and a physical address space with an identity mapped level 1 table, which maps its 0th entry to the level 2 table frame, therey mapping that frame to page with address 0](temporarily-mapped-page-tables.svg) + + The level 1 table in this graphic controls the first 2 MiB of the virtual address space. This is because it is reachable by starting at the CR3 register and following the 0th entry in the level 4, level 3, and level 2 page tables. The entry with index 8 maps the virtual page at address 32 KiB to the physical frame at address 32 KiB, thereby identity mapping the level 1 table it is contained in. The graphic shows this identity-mapping by the horizontal arrow at 32 KiB. + + By writing the identity-mapped level 1 table our kernel can up to 511 temporary mappings. In the above example, the kernel temporarily mapped the physical frame at 32 KiB to the virtual page at 0 KiB, indicated by the dashed arrow. Now the kernel can access the level 2 page table by writing to the page starting at 0 KiB. + + The process for accessing an arbitrary page table frame would be: + + - Search for a free entry in the identity mapped level 1 table. + - Store the physical address of the target frame in that entry. + - Access the target frame through the virtual page that maps to the entry. + - Set the entry back to unused. + + This approach keeps the virtual address space clean, since it reuses the same 512 virtual pages for creating the mappings. The drawback is that it is a bit cumbersome, especially since we would need to temporarily map up to three frames in order to create a single new mapping in the 4-level page table. + +- While both of the above approaches work, there is a third technique called **recursive page tables** that combines their advantages: It keeps all page table frames mapped like with the identity-mapping, so that no temporary mappings are needed, and also keeps the mapped pages together to avoid fragmentation of the virtual address space. Recursive page tables are described in detail in the following section, because this is the technique that we will use for our implementation. + +### Recursive Page Tables + +The idea behind this approach sounds simple: _Map some entry of the level 4 page table to the frame of the very same table_, similar to how the level 1 table in the previous example mapped itself. By doing this in the level 4 table, we effectively reserve a part of the virtual address space and map all current and future page table frames to that space. Thus, the single entry makes every table of every level accessible through a calculatable address. + +Let's go through an example to understand how this all works: + +![An example 4-level page hierarchy with each page table shown in physical memory. Entry 511 of the level 4 page is mapped to frame 4KiB, the frame of the level 4 table itself.](recursive-page-table.svg) + +The only difference to the [example at the beginning of this post] is the additional entry at index 511 in the level 4 table, which is mapped to physical frame 4 KiB, the frame of the level 4 table itself. + +[example at the beginning of this post]: #accessing-page-tables + +By letting the CPU follow this entry on a translation, it doesn't reach a level 3 table, but the same level 4 table again. This is similar to a recursive function that calls itself, therefore this table is called a _recursive page table_. The important thing is that the CPU assumes that every entry in the level 4 table points to a level 3 table, so it now treats the level 4 table as a level 3 table. This works because tables of all levels have the exact same layout on x86_64. + +By following the recursive entry one or multiple times before we start the actual translation, we can effectively shorten the number of levels that the CPU traverses. For example, if we follow the recursive entry once and then proceed to the level 3 table, the CPU thinks that the level 3 table is a level 2 table. Going further, it treats the level 2 table as a level 1 table, and the level 1 table as the mapped frame. This means that we can now read and write the level 1 page table because the CPU thinks that it is the mapped frame. + +TODO graphic + +Similarly, we can follow the recursive entry twice before starting the translation to reduce the number of traversed levels to two. Let's go through it step by step: First the CPU follows the recursive entry on the level 4 table and thinks that it reaches a level 3 table. Then it follows the recursive entry again and thinks that it reaches a level 2 table. But in reality, it is still on the level 4 table. When the CPU now follows another entry, it lands on a level 3 table, but thinks it is already on a level 1 table. So while the next entry points at a level 2 table, the CPU thinks that it points to the mapped frame, which allows us to read and write the level 2 table. + +TODO graphic + +Accessing the level 3 tables works in the same way. For accessing the level 3 table, we follow the recursive entry entry three times, tricking the CPU into thinking it is already on a level 1 table. Then we follow another entry and reach a level 3 table, which the CPU treats as a mapped frame. For accessing the level 4 table itself, we just follow the recursive entry four times until the CPU treats the level 4 table as mapped frame. + +TODO graphic + +#### Address Calculation + +We saw that we can access tables of all levels by following the recursive entry once or multiple times before the actual translation. But how do we do this? + +Remember, the indexes into the various table levels are derived directly from the virtual address: + +TODO graphic + +To follow the recursive entry once before doing the translation we move each of the address one entry to the right: + +TODO graphic + + + +## A Physical Memory Map + +## Allocating Stacks + +## Summary + +## What's next? + +--- +TODO update post date \ No newline at end of file diff --git a/blog/content/second-edition/posts/10-paging-2/recursive-page-table.svg b/blog/content/second-edition/posts/10-paging-2/recursive-page-table.svg new file mode 100644 index 00000000..1325ae5f --- /dev/null +++ b/blog/content/second-edition/posts/10-paging-2/recursive-page-table.svg @@ -0,0 +1,2 @@ + +
Physical Memory
Physical Memory
0KiB
[Not supported by viewer]
Level 4 Page Table
Level 4 Page Table
4KiB
[Not supported by viewer]
IndexFrameFlags
024KiB
r/w



[Not supported by viewer]
Level 3 Page Table
Level 3 Page Table
16KiB
<div>16KiB</div>
24KiB
<div>24KiB</div>
8KiB
[Not supported by viewer]
IndexFrameFlags



511
24KiB
r/w
[Not supported by viewer]
Level 2 Page Table
Level 2 Page Table
Level 1 Page Table
Level 1 Page Table
32KiB
<div>32KiB</div>
IndexFrameFlags
036KiB
r/w



127
12KiBr


[Not supported by viewer]
4KiB
4KiB
CR3 Register
CR3 Register
IndexFrameFlags
0-
-
1
16KiB
r/w



511
4KiB
r/w
[Not supported by viewer]
\ No newline at end of file diff --git a/blog/content/second-edition/posts/10-paging-2/temporarily-mapped-page-tables.svg b/blog/content/second-edition/posts/10-paging-2/temporarily-mapped-page-tables.svg new file mode 100644 index 00000000..8b1e67e5 --- /dev/null +++ b/blog/content/second-edition/posts/10-paging-2/temporarily-mapped-page-tables.svg @@ -0,0 +1,2 @@ + +
Physical Memory
Physical Memory
0KiB
[Not supported by viewer]
Level 4 Page Table
Level 4 Page Table
4KiB
[Not supported by viewer]
IndexFrameFlags
024KiB
r/w



[Not supported by viewer]
Level 3 Page Table
Level 3 Page Table
16KiB
<div>16KiB</div>
24KiB
<div>24KiB</div>
8KiB
[Not supported by viewer]
IndexFrameFlags
0
14KiB
r/w



[Not supported by viewer]
Level 2 Page Table
Level 2 Page Table
Level 1 Page Table
Level 1 Page Table
32KiB
<div>32KiB</div>
IndexFrameFlags
024KiB
r/w



8
32KiBr/w


[Not supported by viewer]
4KiB
4KiB
CR3 Register
CR3 Register
IndexFrameFlags
016KiB
r/w



[Not supported by viewer]
Virtual Memory
Virtual Memory
0KiB
[Not supported by viewer]
4KiB
[Not supported by viewer]
16KiB
<div>16KiB</div>
24KiB
<div>24KiB</div>
8KiB
[Not supported by viewer]
32KiB
<div>32KiB</div>
\ No newline at end of file From d87c41fa6c7ffbc09e28e59e9571986add7b2590 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 6 Jan 2019 23:21:14 +0100 Subject: [PATCH 02/29] Continue second paging post --- .../second-edition/posts/10-paging-2/index.md | 156 +++++++++++++++--- .../posts/10-paging-2/qemu-bootinfo-print.png | Bin 0 -> 24012 bytes .../10-paging-2/qemu-print-p4-entries.png | Bin 0 -> 9480 bytes .../recursive-page-table-access-level-1.svg | 2 + .../recursive-page-table-access-level-2.svg | 2 + .../recursive-page-table-access-level-3.svg | 2 + ...indices-from-address-recursive-level-1.svg | 2 + ...indices-from-address-recursive-level-2.svg | 2 + ...indices-from-address-recursive-level-3.svg | 2 + ...indices-from-address-recursive-level-4.svg | 2 + 10 files changed, 149 insertions(+), 21 deletions(-) create mode 100644 blog/content/second-edition/posts/10-paging-2/qemu-bootinfo-print.png create mode 100644 blog/content/second-edition/posts/10-paging-2/qemu-print-p4-entries.png create mode 100644 blog/content/second-edition/posts/10-paging-2/recursive-page-table-access-level-1.svg create mode 100644 blog/content/second-edition/posts/10-paging-2/recursive-page-table-access-level-2.svg create mode 100644 blog/content/second-edition/posts/10-paging-2/recursive-page-table-access-level-3.svg create mode 100644 blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-1.svg create mode 100644 blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-2.svg create mode 100644 blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-3.svg create mode 100644 blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-4.svg diff --git a/blog/content/second-edition/posts/10-paging-2/index.md b/blog/content/second-edition/posts/10-paging-2/index.md index 62295d35..17e19350 100644 --- a/blog/content/second-edition/posts/10-paging-2/index.md +++ b/blog/content/second-edition/posts/10-paging-2/index.md @@ -67,24 +67,24 @@ So in order access page table frames, we need to map some virtual pages to them. ![A virtual and a physical address space with an identity mapped level 1 table, which maps its 0th entry to the level 2 table frame, therey mapping that frame to page with address 0](temporarily-mapped-page-tables.svg) - The level 1 table in this graphic controls the first 2 MiB of the virtual address space. This is because it is reachable by starting at the CR3 register and following the 0th entry in the level 4, level 3, and level 2 page tables. The entry with index 8 maps the virtual page at address 32 KiB to the physical frame at address 32 KiB, thereby identity mapping the level 1 table it is contained in. The graphic shows this identity-mapping by the horizontal arrow at 32 KiB. + The level 1 table in this graphic controls the first 2 MiB of the virtual address space. This is because it is reachable by starting at the CR3 register and following the 0th entry in the level 4, level 3, and level 2 page tables. The entry with index 8 maps the virtual page at address 32 KiB to the physical frame at address 32 KiB, thereby identity mapping the level 1 table itself. The graphic shows this identity-mapping by the horizontal arrow at 32 KiB. - By writing the identity-mapped level 1 table our kernel can up to 511 temporary mappings. In the above example, the kernel temporarily mapped the physical frame at 32 KiB to the virtual page at 0 KiB, indicated by the dashed arrow. Now the kernel can access the level 2 page table by writing to the page starting at 0 KiB. + By writing to the identity-mapped level 1 table, our kernel can create up to 511 temporary mappings (512 minus the entry required for the identity mapping). In the above example, the kernel mapped the 0th entry of the level 1 table to the frame with address 24KiB. This created a temporary mapping of the virtual page at 0 KiB to the physical frame of the level 2 page table, indicated by the dashed arrow. Now the kernel can access the level 2 page table by writing to the page starting at 0 KiB. - The process for accessing an arbitrary page table frame would be: + The process for accessing an arbitrary page table frame with temporary mappings would be: - Search for a free entry in the identity mapped level 1 table. - - Store the physical address of the target frame in that entry. + - Map that entry to the physical frame of the page table that we want to access. - Access the target frame through the virtual page that maps to the entry. - - Set the entry back to unused. + - Set the entry back to unused thereby removing the temporary mapping again. - This approach keeps the virtual address space clean, since it reuses the same 512 virtual pages for creating the mappings. The drawback is that it is a bit cumbersome, especially since we would need to temporarily map up to three frames in order to create a single new mapping in the 4-level page table. + This approach keeps the virtual address space clean, since it reuses the same 512 virtual pages for creating the mappings. The drawback is that it is a bit cumbersome, especially since a new mapping might require modifications of multiple table levels, which means that we would need to repeat the above process multiple times. -- While both of the above approaches work, there is a third technique called **recursive page tables** that combines their advantages: It keeps all page table frames mapped like with the identity-mapping, so that no temporary mappings are needed, and also keeps the mapped pages together to avoid fragmentation of the virtual address space. Recursive page tables are described in detail in the following section, because this is the technique that we will use for our implementation. +- While both of the above approaches work, there is a third technique called **recursive page tables** that combines their advantages: It keeps all page table frames mapped like with the identity-mapping, so that no temporary mappings are needed, and also keeps the mapped pages together to avoid fragmentation of the virtual address space. This is the technique that we will use for our implementation, therefore it is described in detail in the following section. -### Recursive Page Tables +## Recursive Page Tables -The idea behind this approach sounds simple: _Map some entry of the level 4 page table to the frame of the very same table_, similar to how the level 1 table in the previous example mapped itself. By doing this in the level 4 table, we effectively reserve a part of the virtual address space and map all current and future page table frames to that space. Thus, the single entry makes every table of every level accessible through a calculatable address. +The idea behind this approach sounds simple: _Map some entry of the level 4 page table to the frame of level 4 table itself_, similar to how the level 1 table in the previous example mapped itself. By doing this in the level 4 table, we effectively reserve a part of the virtual address space and map all current and future page table frames to that space. Thus, the single entry makes every table of every level accessible through a calculatable address. Let's go through an example to understand how this all works: @@ -96,29 +96,143 @@ The only difference to the [example at the beginning of this post] is the additi By letting the CPU follow this entry on a translation, it doesn't reach a level 3 table, but the same level 4 table again. This is similar to a recursive function that calls itself, therefore this table is called a _recursive page table_. The important thing is that the CPU assumes that every entry in the level 4 table points to a level 3 table, so it now treats the level 4 table as a level 3 table. This works because tables of all levels have the exact same layout on x86_64. -By following the recursive entry one or multiple times before we start the actual translation, we can effectively shorten the number of levels that the CPU traverses. For example, if we follow the recursive entry once and then proceed to the level 3 table, the CPU thinks that the level 3 table is a level 2 table. Going further, it treats the level 2 table as a level 1 table, and the level 1 table as the mapped frame. This means that we can now read and write the level 1 page table because the CPU thinks that it is the mapped frame. +By following the recursive entry one or multiple times before we start the actual translation, we can effectively shorten the number of levels that the CPU traverses. For example, if we follow the recursive entry once and then proceed to the level 3 table, the CPU thinks that the level 3 table is a level 2 table. Going further, it treats the level 2 table as a level 1 table, and the level 1 table as the mapped frame. This means that we can now read and write the level 1 page table because the CPU thinks that it is the mapped frame. The graphic below illustrates the 5 translation steps: -TODO graphic +![The above example 4-level page hierarchy with 5 arrows: "Step 0" from CR4 to level 4 table, "Step 1" from level 4 table to level 4 table, "Step 2" from level 4 table to level 3 table, "Step 3" from level 3 table to level 2 table, and "Step 4" from level 2 table to level 1 table.](recursive-page-table-access-level-1.svg) -Similarly, we can follow the recursive entry twice before starting the translation to reduce the number of traversed levels to two. Let's go through it step by step: First the CPU follows the recursive entry on the level 4 table and thinks that it reaches a level 3 table. Then it follows the recursive entry again and thinks that it reaches a level 2 table. But in reality, it is still on the level 4 table. When the CPU now follows another entry, it lands on a level 3 table, but thinks it is already on a level 1 table. So while the next entry points at a level 2 table, the CPU thinks that it points to the mapped frame, which allows us to read and write the level 2 table. +Similarly, we can follow the recursive entry twice before starting the translation to reduce the number of traversed levels to two: -TODO graphic +![The same 4-level page hierarchy with the following 4 arrows: "Step 0" from CR4 to level 4 table, "Steps 1&2" from level 4 table to level 4 table, "Step 3" from level 4 table to level 3 table, and "Step 4" from level 3 table to level 2 table.](recursive-page-table-access-level-2.svg) -Accessing the level 3 tables works in the same way. For accessing the level 3 table, we follow the recursive entry entry three times, tricking the CPU into thinking it is already on a level 1 table. Then we follow another entry and reach a level 3 table, which the CPU treats as a mapped frame. For accessing the level 4 table itself, we just follow the recursive entry four times until the CPU treats the level 4 table as mapped frame. +Let's go through it step by step: First the CPU follows the recursive entry on the level 4 table and thinks that it reaches a level 3 table. Then it follows the recursive entry again and thinks that it reaches a level 2 table. But in reality, it is still on the level 4 table. When the CPU now follows a different entry, it lands on a level 3 table, but thinks it is already on a level 1 table. So while the next entry points at a level 2 table, the CPU thinks that it points to the mapped frame, which allows us to read and write the level 2 table. -TODO graphic +Accessing the tables of levels 3 and 4 works in the same way. For accessing the level 3 table, we follow the recursive entry entry three times, tricking the CPU into thinking it is already on a level 1 table. Then we follow another entry and reach a level 3 table, which the CPU treats as a mapped frame. For accessing the level 4 table itself, we just follow the recursive entry four times until the CPU treats the level 4 table itself as mapped frame (in blue in the graphic below). -#### Address Calculation +![The same 4-level page hierarchy with the following 3 arrows: "Step 0" from CR4 to level 4 table, "Steps 1,2,3" from level 4 table to level 4 table, and "Step 4" from level 4 table to level 3 table. In blue the alternative "Steps 1,2,3,4" arrow from level 4 table to level 4 table.](recursive-page-table-access-level-3.svg) -We saw that we can access tables of all levels by following the recursive entry once or multiple times before the actual translation. But how do we do this? +It might take some time to wrap your head around the concept, but it works quite well in practice. -Remember, the indexes into the various table levels are derived directly from the virtual address: +### Address Calculation -TODO graphic +We saw that we can access tables of all levels by following the recursive entry once or multiple times before the actual translation. Since the indexes into the tables of the four levels are derived directly from the virtual address, we need to construct special virtual addresses for this technique. Remember, the page table indexes are derived from the address in the following way: -To follow the recursive entry once before doing the translation we move each of the address one entry to the right: +![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/x86_64-table-indices-from-address.svg) -TODO graphic +Let's assume that we want to access the level 1 page table that maps a specific page. As we learned above, this means that we have to follow the recursive entry one time before continuing with the level 4, level 3, and level 2 indexes. To do that we move each block of the address one block to the right and use the set the original level 4 index to the index of the recursive entry: + +![Bits 0–12 are the offset into the level 1 table frame, bits 12–21 the level 2 index, bits 21–30 the level 3 index, bits 30–39 the level 4 index, and bits 39–48 the index of the recursive entry](table-indices-from-address-recursive-level-1.svg) + +For accessing the level 2 table of that page, we move each index block two blocks to the right and set both the blocks of the level 4 index and the level 3 index to the index of the recursive entry: + +![Bits 0–12 are the offset into the level 2 table frame, bits 12–21 the level 3 index, bits 21–30 the level 4 index, and bits 30–39 and bits 39–48 are the index of the recursive entry](table-indices-from-address-recursive-level-2.svg) + +Accessing the level 3 table works by moving each block three blocks to the right and using the recursive index for the level 4, level 3, and level 2 address blocks: + +![Bits 0–12 are the offset into the level 3 table frame, bits 12–21 the level 4 index, and bits 21–30, bits 30–39 and bits 39–48 are the index of the recursive entry](table-indices-from-address-recursive-level-3.svg) + +Finally, we can access the level 4 table by moving each block four blocks to the right and using the recursive index for all address blocks except for the offset: + +![Bits 0–12 are the offset into the level l table frame and bits 12–21, bits 21–30, bits 30–39 and bits 39–48 are the index of the recursive entry](table-indices-from-address-recursive-level-4.svg) + +The page table index blocks are 9 bits, so moving each block one block to the right means a bitshift by 9 bits: `address >> 9`. To derive the 12-bit offset field from the shifted index, we need to multiply it by 8, the size of a page table entry. Through this operation, we can calculate addresses for accessing all four page tables in the mapping of each page. + +The table below summarizes the address structure for accessing the different kinds of frames: + +Mapped Frame for | Address Structure ([octal]) +---------------- | ------------------------------- +Page | `0o_SSSSSS_AAA_BBB_CCC_DDD_EEEE` +Level 1 Table | `0o_SSSSSS_RRR_AAA_BBB_CCC_DDDD` +Level 2 Table | `0o_SSSSSS_RRR_RRR_AAA_BBB_CCCC` +Level 3 Table | `0o_SSSSSS_RRR_RRR_RRR_AAA_BBBB` +Level 4 Table | `0o_SSSSSS_RRR_RRR_RRR_RRR_AAAA` + +[octal]: https://en.wikipedia.org/wiki/Octal + +Whereas `AAA` is the level 4 index, `BBB` the level 3 index, `CCC` the level 2 index, `DDD` the level 1 index, and `EEEE` the offset into the mapped frame. `RRR` is the index of the recursive entry. When an index (three digits) is transformed to an offset (four digits), it is done by multiplying it by 8 (the size of a page table entry). With this offset, the resulting address directly points to the respective page table entry. + +`SSSSSS` are sign extension bits, which means that they are all copies of bit 47. This is a special requirement for valid addresses on the x86_64 architecture. We explained it in the [previous post][sign extension]. + +[sign extension]: ./second-edition/posts/09-paging/index.md#paging-on-x86 + +## Implementation + +After all this theory we can finally start our implementation. As already mentioned, our kernel already runs on a page tables created by the bootloader. The bootloader also set up a recursive mapping for us, so we already can use addresses with the above structure to access the page tables. The only missing thing that we don't know is which entry is mapped recursively. + +### 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 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) -> ! { + println!("Hello World{}", "!"); + println!("boot_info: {:x?}", boot_info); + + […] +} +``` + +The [`BootInfo`] struct is still in an early stage, so expect some breakage in newer 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 + +![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 indicates a recursive address with the recursive index 511. + +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. + +### Accessing the Level 4 Page Table + +We can now try to access the level 4 page table: + +```rust +// inside our `_start` function + +[…] + +let level_4_table_pointer = boot_info.p4_table_addr as *const u64; + +let entry_0 = unsafe { *level_4_table_pointer }; +println!("Entry 0: {:#x}", entry_0); + +let entry_1 = unsafe { *level_4_table_pointer.offset(1) }; +println!("Entry 1: {:#x}", entry_1); + +let entry_2 = unsafe { *level_4_table_pointer.offset(2) }; +println!("Entry 2: {:#x}", entry_2); + +let entry_511 = unsafe { *level_4_table_pointer.offset(511) }; +println!("Entry 511: {:#x}", entry_511); + +[…] +``` + +This code casts the `p4_table_addr` to a pointer to an `u64`. As we saw in the [previous post][page table format], each page table entry is 8 bytes (64 bits), so an `u64` represents exactly one entry. We use unsafe blocks to read from the raw pointers and the [`offset` method] to perform pointer arithmetic. When we run it, we see the following output: + +[page table format]: ./second-edition/posts/09-paging/index.md##page-table-format +[`offset` method]: https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + +![QEMU printing "Hello world! Entry 0: 0x2023 Entry 1: 0x6d8063 Entry 2: 0x0 Entry 511: 0x1063 It did not crash!](qemu-print-p4-entries.png) + +When we look at the [format of page table entries][page table format], we see that the value `0x2023` of entry 0 means that the entry is `present`, `writable`, was `accessed` by the CPU, and is mapped to frame `0x2000`. Entry 1 is mapped to frame `0x6d8000` has the same flags as entry 0, with the addition of the `dirty` flag that indicates that the page was written. + +Entry 2 is not `present`, so this virtual address range is not mapped to any physical addresses. Entry 511 is mapped to frame `0x1000` with the same flags as entry 1. This is the recursive entry, which means that `0x1000` is the physical frame that contains the level 4 page table. + +### Page Table Types + +While accessing the page tables through raw pointers is possible, it is cumbersome and requires many uses of `unsafe`. Like always we want to avoid that by creating safe abstractions. + +TODO x86_64 PageTable type + +TODO directly pass &PageTable in boot_info? + +TODO introduce boot_info earlier? diff --git a/blog/content/second-edition/posts/10-paging-2/qemu-bootinfo-print.png b/blog/content/second-edition/posts/10-paging-2/qemu-bootinfo-print.png new file mode 100644 index 0000000000000000000000000000000000000000..83efc17ab386df78043c76eccf40617d89cb465d GIT binary patch literal 24012 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 }g?^*})+Xdwqga9OXF<-- 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
eS-_fs?-MFDs*Xir17s!;-Z?h zH#gkL3>Cf1mvZ@a>?aP@u*%hDx9xWB;Mgeme)a`Jo##9AHg0)Yb=jU}<_->{>t58PCuJ(4f)%~r9Qw73u6P)UH zm92DNv*(rF%IwU~7p4em`D$!Tl2~Pj=yz;Pj`+Ut!d5%qSNqRBUcP!;yT*01wre~6 zV{e`d6Zzj4*)Fjqb(`$`Dc=^=+%(v*Dp;^!^E*SIheCoaqMQ7a_XU3EEoQgquDrO; zP5Px>S>MWY+sv2mdOc&6T=wU)e!=qtGP_Lvz7@J(+Pm=OPQ9;%df7*E!m|uxo^LyD za%CI;)>!AQ=l)(Ty}cPUtPp;$wDM>B!(Ae0)i(dx^YTMw=&oJ7Q`MmDiS4;ft3Da- z^{f81;9cpqU9&!M%sjT$ue35`s;E)ht-5|m=eKIRPNv=7s-K(beDuOno!d3#fm_9H zz6$oce(ctx)9JBmSM|TQ`5s#xduq$ZuS+#^Cl}RRlfP^w_UiAJRdLHoo_<+)tlzA6 z*}K>^oKK5lKYe`JedU|kzFRk(*Ic@hbaDFL7x&(sD^jj~`fBSbx9^^-+;Yh?W4=D7n>o)guwGHZ{NO~v$ubXXV%;k4-N?|HJTSZJH9kKNv{`Q zxi#(i;h&kMZ*wOGg_yI)uA82_+rKv7_*z-qtfHDc*I7nyb;ECKnJ?XORLa{q>Gz`Y zRfavb&!VlB*4}hUkNhlum*e8eS-)DmguOa8ovO^Zm8zv)#&f$*e_ET^{Sfgw-srnq zi*J63$WVMNlWeQ2HO;W)WBi-$8ykH*uU2!vnt9CsR8s%XFtbaye7|^I?oAMen002; zshZzgKqUn4LjCU0@J}_h^#{2N4UWx9mi2aj@zH1hy_a7u@lB7f(Cn(*f9lg?J?WJq z{>EEZp1nNfwS@C+O~>oet7bk6548%O>yw)KI%fI1H7(yZPB6@AUM9cl)#A+XGuzgm z(ums>7~Z$-t*^fFO6~ReTap+5*m3^%%GVN`ZI>AyDb*3VzcZ)$|J7%gzt*kE*?O!c z;zC)$_AKno zRGOWXHA{Tf&rrS1UCY)yO7?x0S-J3w?XD#&`(NJ@dgU?w+2Lhs=R)hJ%X42XoBgR~ zT~*xW<)x(`R~Db(|B$<$_o;;F&TV^Ru3z2n+-n$q^w*Ak`@a{;M(cPUj0e``f9DcuV+29F8BF4C0St6eixg4ITG2$o248S?R&*O^BArOTHd+v zW$&Un?xD9zgRXyDXa3iDLKM%|ubH2DPlZ-)ei^)7ZFWU;o$q6jVp%T3`=_(hC)@E| zeYUG=i5a^yhoSnaPpgHj>o%Ug-1AuA)~x+_T*SGKil zt=5~ruqI*Uq+1cZsVm+}ttx%De%$xtY$(FUFmgcoY?wYag3tH;ad{hqQ^_p)l+Ej!a#sb@m-mp#4^79M;{?epE|wa2%7oAvMQB>$_$pE#Gy0*O|EywHHIAf6oh*iq>&DE1Nr~_IGxE`SWjXyH;&n zDf^N+W{Fi_FsSR&%8|kRI7c%~rlVZuh-Gf|ZtK(f(w71y7KI!u&icIiok#?vP2 zc>SGBO5JhCIlqrrWU|lYXfZzW^J(#~LXNj=L4uItq!?73%#oWYws3KS8E;`-<}H&W zWy~u(Z1>MNUnuar&hyu|a?n8MDroOzPW{abt?%EykNLd*w0rUC**CWD=T_XhDQM5% zSox*bw%O$W+q`S#g*)r=Wq;gb)4B3G_Vmi#rcw7xO!hz!boTYQxHRaF#WjFoGIaO`Sl~Y}^hqvACS-SA(_8oFZXIQVy`t?lI zy6mV?+lBC+!%J6vl>@gnYUMv~v(43(&U~v@r5ff?`&wAbS7YVWtD!u<%>=jp+GJ3s zdj9%%@jcfh|K5|y_O^2BPye;mwluxS>=b8sXKj71dW)I=^R=fFE+3n4`^}8dzndVHLdDf>nzlbO3 zt-kbged%Mj?yQl@|9vF!H%ri&#aeHzE*#nL`fOJGm0tNrH)7sMh+S{j+V;Gwc=ex$ z_f+Q>&GXH?C0wN2vvXhDjOM4`Thx;Ep0o?zFT4@{ls~~YdF9Uu0^V7#Z)o*)p8geL zUJe-}5qN5uzr8G8YLCuj$JnF zQ?HDAzwPpb_FuA|_r0F6ZK>g0z2eZViCe$}9$Sx2&;5McZSLz!JJtj)+x6?(@{>!8 zw;3jS-A>{AHD{99+V*Ajppp5Q(7!&FN9EdQ?JD26ZOZA=lgF3q`y|I*UKeuRFj#Wo zqszzcTs&sE_1`M3bVyO6XL!LJ);_s-_}7B>)02)iR)9)~rDy-$x_vjY?&bu?Mn$hw zo8@NvU-@k}wJnAEHz@`Nh7SeD^`+Z(EpsWho_!^6xtQ09O|$O&)B9@T zyl0=zR`3w%J>S-6X4S6`d=E=p823KP@9x{nzm}~ooRM@VsduU3#@dV4S0(ro-n3%d5*aQ#ge^W5tvLIOGxGW$KIo$i{cHf2KU&YVTQsY|CV zTahmuxLTh(IsDU}`u!!X&t80g77-2Uue`ko>g0S^lm6=YMs$_+(<2HyeWedC%ri;W zJa)P4*Tt{ja*zIbg*gmrxBbay&&8enW&5&|EslJ30F@!zK1NUP&#wO&7i5T3ip+R) z=c?7T=lt`3P7txOJNo=q&ToO=-n-l8|J=0j<>j!ei}I_TFX;T5` zmcF-ry4y70ExT;>)MVSfO<(7On;?G1Q^iy7D@V@ym2oR|_3ZVJB#iemny%cOT_5`Q z?ImdW5#cxc^VU}x=eu72(7ayy_5F;$sjK`i7v5;!n)-FtvgE+q;-!xdrT2b%yM9%b zY3k|yG3!@-x%F$^yw7v3m(`py^w_%JWY)WzSKOC`>+6U|#A!I||8Nc#cWwz4zYS`3 z2w1m0RhCNK_PpzJzNwi{vdistX7@w7&$q^?C#}1z_$=Y)-lZpJaQ{p^QZDl?^e1z~ z<%ldhl_QgGS#9%5ezeK%>RE8(1Jom$WqW&OZ^_*w96KYg?XgRn6>!b6ZDpN?0czP1 zC*j=Nt5z8CyzoZoZRaVsQ)C{Q-M-oN=)U1=f#z*zwmp7)>Dwy9```a%?Mg4+*I#l~ zF>i-r+t*umPkVj@y}BH*oaOZ?jpz558<%N*6`#QM?C7tRw>S5?eBrM9c}paCnHea# zXX@fLNnXzOBd=*FuXlAif8pY-ZfUP&#m9Tp5ossF3%l%O@L?XTj$*C z0c(rvzixc1_uJ@p@xJP9Ir-)4`-{FteVyEP<`i#)ZC-WlOtsuICPzzVRb{VVUDBHv zDz~g^xtQ~GlUuLVYRzu1`fX^?L3-C!<>&7+=&khK9Ta=qC$*)^ zU%J>``uUY#r&Iry-rfB6?YlcWC$a@i_#~J;@yjdGrZltyga7Z=?YkeBUFTD13sLjU zn!Mv(Ni?M8aW`3Ki?#lYVo(F(Wp;g`i`0e!+t&N+cFWJTUC2A21|BCpv$0IzdELus zko1a)zf`_0%)BgcoA<^MxzA@dT67fe|FUuaoP2QSrUA0x;pv&Bvc7rmr)A9waGLV8 zYWA`VPnQ;C@3!+=n_GGB&-2RQg?sk~b-l`Cd{%Tf_jc{8>fM{ReZDj$xZHaCzKi!B zZCP6s+y!ofEd0)CSYrCj+34*2z)&S$wPn=;%ciMK5lEf-dByJjWJBNRQa;J&+kP{6 zJ9p>L7rSKicCYvAXMR0)ypqo)vhxBr_9*uJPl`X%m+H0ZUNmUv6jYSt{}!;)QZG8W zM5g1)r52%8I%gaBO*MCQ72j8D4~rLDz3xfn|2kgozlS8U`$~0W7alkMdu!e+P0^z>Zu)WVy7y@9jQCh9Xn*E-*|Gyq3G`=T^k#03wG*l8 zxhZ#9;*phD*ZY`ld)>1?8y?+x+H~_DMa`0$uJc=OezW^F{rYrX)A>t+yoI|~u35#e zq-b`rw0qMt5l{KYIZ3llEZZ^rQLta~YyN2Y%u`x+*PcuXgZE-2vOoKDZL^!T@uJ)9 z+^+SeYo%w|tzF&iKi4^D(d%2{UY2gIzSj@K`!DN0*Tx(VTc2t5Z%b{Uxn9k+7J>Jt z7hXTJ3OuL#eplL$bF#s9PbHSziZ{FCZnYq^{c3Gg{-(}tQ}^a=`PjVwO45q8{?V0 zj~36gwyXU4Owg<2(;l-+3(Y{~WKek6-M~gOvx}v_B+|vd`6Ny7y&#-Xwbc-?pW^qR!Jd`7GZM6Iz?G>FV-1 zdZ1FGmZ49v<`Bnf7{%XmB|sy=k;=Iy(S ztCe|=AW92GrdtOrqPs-dAIXB61!kb$O#ALKg>@lf^R9>;+7+DO+aI(1lHVHfW3ju7 zC;SAfy7Bj@VCEX#w=37%eYBiw+5+l|lvdpjk^8(;_Q=loq_5)cOEz9>+aZvJ)=%lY z8Gq*=AE=3O0M^o2Dyz6ovv6;1z>80}_VvbbXdc`7PRvE~p4)ox*r@sc%6lOh`44Zp z?VJ^@X1ccjeeR(k{pIh(vTH9twde`#>yAjy^l*AVG3iCtnf8>~_NCcBR?IOt{l#7_ zX|+eJ-BS}glTyB^YEwcUui0Ck)MEi_a3otCi@EkJc3t@7qNFXFVQ<1f!=yfChJ3*x zTkkENm-%POn)k_Zn>)8{%}zOWL|xz(Z*)iF!j;7qrzmzQ{Hj@pHqU))pGrieUx^QkI&?ro3DW|tQwcJ0oT znRfZcqs;D&pq9t1%EX|!DYgA6nXevOO?RH>`7t(nORb=F+tYgSoyTr&&C5M^ebr)H zZQuLcpk-3=q4mExEct$K*tRC_@$ZL=R@sHti(kI^>h)%~)*F*sVvhU#oc=m=>b+Fk z45M<;n8$>y`pg^G9GCz0Sg#fPy>si!XXiq`?dlMHzO4IhK*nSC`i;pE_gKDF_FT_h zbW3RS8G+&rGRpG|{XykTIFbqAdbaBbr*Bj+RT#?bCc z#M8MqJ+AZq+U2^p*SBPDfb_>1`9Vi4Z6PIxVq3nhWRySVBX!Wj@o3(4#m7RYg-dId8l}v~CNR9~j@|d1OYe0Z0**EFJcnOzpSoVm{X(l*_x7Ps}(J0+h>Okca>+jiaD6&qg# zO?z1~bM-3z<=cKux!l_0A3W<<*6YVX>Crr^KNY>bJRv!B@jBbk%-K^O7kk}8X-A}5 zKe&ADj%Q)6=JeN-`u*c)+}m&R)~iyMdt-d$(v3%zu0VPomt=0NJ_YK0oa;5YAg(FD zQOt9FoMMKU?Hc}Hb1fBcKhx!hLs+v6JJ*eO}{!fFT3iA)zq>@)jh3c z&smH0_#WNS*Zd~#zNE2j%krDQjy%6|Q{WcwjU!fj^R@aduYsjO#X}=Q+m08I;=yd= zahqZtkj%=j(?7p`4_dad0lZ*qk45aQhgH87f_JU@qIu_*Udx5lpZ6xIel=M+cNV82 zs3)?&UTU|-Gg;@Fy#LqSYh-)8{w^pb=9*)K&^~nr~T?E**M{9 zeJE?lQp1b)E}stxTVb-WMep>tTQfd#LK+iCuFpT+B=g!=^VqM@5YXi7${YS0lV?tX zO`g=sf1cHwt5vQ0)h%kbA)n!aRZJ0i4q#)vZ*N?-Dabqc%687RSEsXX)2_U?KmKr3 z`j_zHHJmRwZ0Eha{w>4&qIT@NtuB6gZ$DVhz5Zp&RV%#>RCE3sMI>~8&9cj?;F%qV$r z&=hNWsCz@y4BxJ4_AHM<}ahy+J3`@e`5BW?)vrFXJ%dK3;yA)+IDp3)>q|c?|eFW-Sxze zBQf^QXXbvIoz=lF=zI05^3;zqLGVt9&~BTv7JP#8XYYJGdD^@`AZe<2@%qx^vDUBb z63y?r{J!u6G>cN2dh^knUk8=bYJ$2qcdLDDj`cFs(VtiPS@YPUc|kQ!uU5q_ycg~> z`_#g3;nLSi>oOtBz(6Ay@7ITXiz=_Vx#5+;j%@-jZa(T+y3IdUd0#}W)T=Ft@rLCM zn`el>R=*baxwM*^!dOF~?X~^q4@wGX-PuHFL7BjtCS8yVz$oN0CJl5{B%TLyATs!2H^!MMq zI=9Vqs<2V-vU{^kwr$$7eD>C_eUkGpWZv3ib7JfM+)UFPQmx)TJ5`oDBhxOt zH~jMFTP6!zR`sX-?Y#M3YTG`OrMW?U=QT4gN2UMT?rj%(KJ@DODbwFFheapb9W`AT zv(DSjt~Stof6`ai;M_OHVVOA_IZVs96#A{*Z^Hg$)u+y#*Fggjc}v#E>`lwz5QskI zzQ*;^>Tr%(M}=Q5K0ggMV39iaS9X5+@we)S8YO~e)~wWJD_y>HmfTLirE@>H%Wt!u zH&Ywb|A_ql?@@*{vPAGBS_;|V@Q>z;pE zeWr?#{)XIJwW`$EZ&6BzVy$o4@y?ZB5DR(VIm-ul7z~HF>2@?%dZm>>lp2 z5i@Gbz3|03wASRU*WS097mn@xu_O1+)u)T!>fcV5a*h#p`Z!m7+jr|+yJZ&TJO53w zS846@|G3law%w6`QlH{pp8@qS1d^9=9t{b*x|}<@UHrecefZb>>%U5-F3VkZ^ted* z<;*EXQnyvM`0Dva9z8y5&N?mqcl+Frn0&ps<#d>N{4CcX;peONpYCtXs@6DKbW3OT zs}(1@vTvU*U2Da3dCC45(bvhhICwLCBla$PspbS3p{V=$Ej)DZtWA(57}HG`pS)xE zG`P=k)!(HX`>NM&wO7(D`aOBp+sUy%H-`SYnq10lk+-_rAGCOj+i$H~81?+$tK{3U--5S2zm~dQ6+T#LJ$c8R4RbtuOqO}=Ubb;+YSr3j zU%W!?LeKBKvi!8kt=IZ!qYatn;oGtg`*3&V&dL0o`ItEr!uB ziG3bEb(^Qq*pNUl&w*E3&c7E-%m`~pYchA{%+wH4w zc(UnH$mD5sbWm?%*6Ypu_ix|-IVJJ5^3vM7pgzQ{y@6#XHm&-!ap_0h;~`;|>jN^K zu83PXcjp(%9hq@D>S>i}W$lSAzDN4zZ zy!onCAst*=-0QWRW83ez3BQ>oA>wEC*BPCYWnwmdPeeM z?ltyEO@-H+)(cNGd4KETvz2Ge{N2C!8qV8xS!Lb!H0@{M(_wQT8*5*cRO+PgBp2Dv zO?`EFSJ};58K;kb+qLYqLsqR|SJ^ue`R^}-&(64hPQ6!go&CZe>+H9E56iXNm+cfC zHvPrVFVoFnBbV7He(y4lH|wc9ayTUHYTx~9oJaqkIv@IX_bQvWFFAZ`oR((J&U7?5 zdRJs|k<{%Y7xE_fq>9RC7jdoJ^Wc}<(o0v|FY86$tr0yQVYz?Tu8dz-w#J(2Rhnq3 zr_5db>hd$t%FV*u@SW>I-4jfTH@wQ1U$)We%GdO-vzE=~$c?|!%RRqT=g2I9qP520 zlHr$HHN*6s$)}E&*1lV{-{e|h{PZV+nGrb(!q_#X zrKxQl{b}!hJb60XU1Q@Z=~w&SopG}|HoI|~PVRwO1-7det#n^u5(^!*)H39$RM|Mg zU+e2dkEQxfuUEw?p4wV#A(d6X^_VUSs^0ihrUo@d*M7+6B;qJ23uXEmiRk^k4)b)2$;)N$KJGbzX$WfcrH%TkH zQ0s=u@_V!GND_@d1<`!ckyROedG&Gl=UV|~kF z`L+02&Gn%_c}nBAZ{J<~+)e85B>0e|qx7QEjO6H!yFqI#1l~gmfVVep-}U^yPIkrg zQ!O63P1ojXWr2GV+cM|gx@DGh<6TA4nq%OypsQW^5{FauuBHApe%-ta?`k{eN#7C$ zPmCVRvhFC}exdaKV&Xy_}=DGarX|hR~J()OSf0Ql0K`ecv}a$w&KyJdvm+i9jo!!w|#B#$=#={ z-W+#(+7fx~U+*U8XXP;_+``WHI^O3MI zXcnV?WxkK0&hm|4mqU6CGnV=LLe^UO*2Wx9yzTyc%h!w7%BODA^S%6QhsLvu&ums( z&DwaWsL+gKQTsyu+Q0mgG4OPCEeOtEan|#jyS~1z8eCxj(khK%1t1hIx zoAF8WxWd(cmlEus%~w0Ad`W4$ed|rTt!r=w?+KSq}YO!bF*&p0f*tU4((fzmAT^1<4x=!HfEjtU7NilmtD?Vmvg;srr4BKg;`Z`v(=^qeAPUD)XY8iXUNiQZM#{o|D1viBOIHF zSP79@9h{wgX88^^=g3u?Dr+zl7fc?e*H5&3?!u_0!UA5?AIlb`I#P9E`qcZyH^82=`BDw^sNA9Uy5&!CZZ`HN(^J0J3$vWFkz3F~*#%;gJJJ4n! zzOB3rnuX{&`FvZgU8c#$i|aV|O|Rx{G&^_nc<$rvdh*x*eTv=RzGGXRq1ZkG2|!G6)}AA{FrU%fbW+wapM^Orr|z@L5km8tLXMcedW9cw)=_4Iap=+~@M z)4=N>wtkJZeGXYf1sYH^xp_U*HhgOR36p)<%d%(ry;I)%s^ao;-OCFicy(IB#jiuB zGmo-v>w3HMk&JHrfzp%9=cEVE`qmPoeCxzzl_N4b`&XJt7AxBK{OCKpPph6GTgv-v zdrL%6^zpDvpO|aQckO%(-AcATFFyRcqeY6;<=3|^-E!Nt?$W-4A2e65&)k(Dy(eQ? zw?tTO$JVc!`aKJexlOiM^V#Cfs<(=#a*g;3XTQyH{rG+cXw`Pw?T>|bcT7EAbN{#Y zvweEYmQ`;%_fC1OrA>boY(2z^;HJ{lDWBh8PMQ-`&$u!nZq5ACyVvL5^f2EU=JLBO zVqe|TKT^EDk+nX-Q(mPdt>8{MQ?^v>@TzR))qWREclMoGR-?7&N5qRwZ3!!K<&lbv z-8biDXUAVxJ6k>L{XE^!E<^?cQ<^i$g3EUsMH+r4!uWIQ2J>hr9!rMvoOp5hRlZ}zOu?A*$) z(@+0;y=#5<&iYke*TZI3@3u}izx1MiN`FN6lJNBcCf#z^*lYJAl^x~Z%F1``1eYC` zmY8+E_EX1P{UH8JTUTbHo4~r|AATReG6MM}T*^YJH=)g<(-btCz&QIx@;pm1_QH4K zzqDoN-UZK4yf8Mq1UfyT-CFN0N9F5@0#4JeKABd#MDp8o>7eZBdFKB;ABJS)$?xE+ z$!~o2>(|0q^P47lE4wW9+XN0r25T;YaTcmv~c0- zUcHdp?X%Lp?s;k1V!Y*tnD}~$*KVt3={>r2L-aE5!3np5M2h3S*tUhf56qo5xhdlJ z{36TH)XYmex=huQry2{b{I?~DZn=Z3YMLjohb-v5OoBLTXYTozv;(Hpl z8R{ooO368L=l_R$;%@oxZzWoV2G-TAn7v<7>)YJ7Q+i&nT#-~)`qHP=XzJ3n#j>|& zGEG13bj)B`z;bctFVo9%FCHt%$nJkH@!585j`scSI+tfn__ar&EidL-_0{Z{Pn|2* z{_TG&B=9!7J1CytdGCJEdWUC|L(cd=TDIwxpJR(ow@G10E?al~^!b~pXR)E zsXzM;uQHl#be7vU_LrQqbac!XljP9N-*&B&&h6oiu3h!&S#a3Z*Qc}nCfD)rl`|9k z^mp%e_a&}*^Mzytih{QB~05sGm6zPE)S8m!3MXgB?>DUp`87pinXa33 z@|*ggyAO^DExNmRe}BoHJ)xq7qbO8>1%IU2D7&NGfk zN&9}c*Ib`nJ1bsU@UC?G>)G9E9KP%SYsNngs_S)Iv5nu zvCCgn?+uHMTy{OnKX7JTdeuCWUuRp?=Dyv!ttw>cYOmGcMGb2{&B(4=%w2r0G;>SxnmKXRzAM+-`T8Bcb*yErpYOHw+*+U1%vmlrp!+#>vU zL*L`potI<`U36-tlVv6zQz$LeT9<3L{CeAkyd_K5C$Cxm)z9;O(v9uE7S&rQ^&XUZ z)U*=Zkl6C2wCC&Adzvq!d47k*e+hmMS=~?qJa#ax4umM%)`LYU{)ZX2|B5oSsFKW zShoDF#(#nCmwn=u{|YATPRz*jUA~jQG;8|~ZqfN7JC05J-Nww&P+E9g@a3at%kz?@ z4+-QKns143E$Pg?%Ks7MmQd~Q6Ztfk`Wi7XFl<33x?z3~(M=yQO-NeAaP?fV};jT4@)%6(|8Wgj) zOnj!XEOy0@Euznh_9>cO?%`x$*r50BhDY{0=A2)PW{3R@V!Hk8Y?%D?nps-YQ?sjI z{0Mpa_?G{&A3LV8GB7mUh8?C5dmWSnUi9w0E4vOnWmN;2xw4@uHRq( zcagd@q0MB8;#sTZj0}Q(;KO?2u($>yV+S1)hf-!P)2>&5hRQGe{?91G`tz_^x!je&u|`rBk>1_lO}VkgfK4h{~E8jh3> z1_lO+64!{5;QX|b^2DN4hTO!GRNdm_qSVy9;*9)~6VpC;F)%1Fc)B=-RLpsMw|YX% z^;-EK@84eE!yvFkZiz!=noE`LYfsNaTWn_uq; z+O3)X_-xp=n3YA3N^_6y?hKVazESyhr|9uT9=R#6GNa4BbljWd8)0-N7Trr<=}u z^JlfUP2F2Yak-73A7B4pblf)Pg8RjrIdzt5?^oY?S-t;7@AN&Nxqsby)_?8W-kgum zvmR}}@oW90xBd4&P2jX&c*y?9)o1#?SFSIW-GA_A%%kTsRWif=Pv)+_l_{THe7Jl+1GumWMC-Z&7P$e8oG4d>npx^iQQ0?!(Lo#RezW*Irxi53p)w=WM_jb*HSYUE-=eDTOsLZ8T*ImlmdMWE| zP2SHdD=)uXGf#nmK_p|An(yV5kn1bm7xSg zR|EE3@}H`eIm^U%?!g2TNBhU$LPM8s4O|v$w?5Io8v)TOqoWH-nJ$-X? zvt{L{5V`6%8*6^wzQ1q9{59N%7GKox_V)hiT`qfScK&|Va+y2xqrabhGIKNc&u1r% ze;!uvd;YinPfAtr-X+#{Y3hl)c71>Q-Q@T8_q&&HTS)b8+HvFjpFd8kR-HO?rexW& zWtU!V(O-F=+oW`wn3(Z>`80!7lUGgk+dg~d&6hsqVf#*AnQ32XmCi0MA6J_Gsi`?Q zQ#JF~^G|nPIgNns^9&r`SW=G ziA%HUUj6#h^CiRLO!xBJKX1OT4&{40+bg(#(k{Q?>no$yF1hM;HS77w&42&Q?XT55 zufrGK{8Q!J97}7yOTWLJ6^%b|^l05Y`#L4x%S$fjsb95-*dG1%PR-|oKE7YOe#eFK z7p2wd-T!>9{OR5Lf2Kdz-Tt%U%#t4aKK>Ho*eKlS7P-2C(S`=9$i58cUGwo2{q<-dF0<^OZ4d&GPG z+5LZC{?Ga@KVNtmV}tamf}K+q-+uXQ?)w^rw|-L(UQ+O#Ws=>Ldv#UNQm^gT&t}|s zGSS^`%EOEae#>Wvt&ZZmczDYyt@kz0nP2*rhX3{~-X~I-Yk$~rZo7P)N~y8e^2@QZ z*(Ng2zO5{)`}Z$AZJFefxTrf{{zUJ6sS|SdPw)M|N%JJlUY1s8Zrv9Dt{Xu_x^&HJo?XS139Cw`~CmE?Rh@=zQ^vneydF*UG(S`-j~DElfBW9o`_J0rET;$0{~P}E ze%-^1HkMgSrBr{%e?R>5<^PZWYW`Qtoq1mOY41-TV`ICFFDZ7v*4L@Zzuta1?x)km zcWHUM=3cCs+PwYFl(5T7^6t)(|IwX)?){%X%Z*=^7|ob}YIgmHe|PR>T{2lKv*)LS zkyY76KXc#9J^NGF+TUEZ^NH+Q28MN_ivuSg%$RUH-+s!=moLwp*H2mMvUuI=rwlf6 zp^KyT1#Vciiihp+nKNgeU3lC6>5cL6OWEIzq>Z@aLP? zbDv+>x3Wj<+>>p!?4RfFPl^Ax|M|3Ip<==je3z{E7j$=fubi!R`LglPZ2NDEE1zob z(|&$#uJ5Eh&qD7{mj63Z{^b52)AvtWdQgFzyIWIRUNUXtzjZbr`lc`2`{nANXVvzT z(yMz_~k_t^bE{Wt#vbNg`#1Hk#^Q5B zH}7eEbCmJ-rR{C33_HTs+)4Q{$yxv8p+in?i!Xlq{PmU2r>@VNPd;wCwQF7RiwoYL zu1xk{db`Klg->>uZg=h*Uwf){b0J7`_G;J_VecN`K?;{RCn{wUng7tEIw~H^Z!3y{nNRZADbO6 zUS9maJMGiKbg@gb4bJPHkA83SxA#?G)m^_`cm3Ygy&nY5w;pp}j*mI>I{x#k zKT*qP$8Dc|F(bmn&28}&^ZN96J63xAU911Rd%4xjTlM?q-@9TE|NBvLEkbQKFKf~__}h{ zq-Aoc_wU3^oA%37Z(T`*=1GhHe>6fvQ%$7O^2*9;KF1&Xd*ZSxdK;8NS)d9HxEP{AJ0l*LwH( zOni+@r@mA&uMod*9Bxdh^xFmu0V>=7j6B zKRuYR;g)|ra^~hw z6K(yTd8gFxe_#Lk;?b#6FSYKjjN7#~P|RrZ&YN0i|J0mIopD_5U&g-D`~1)J|F6Bh zdHv_Je{LzqpWpE7ywUy%A;rZrSI?T0*Cxp`Tjlc1xsub{P1Y{SOEa<#R+UW2obc4z z%ln=>!-1{Kqwb{^B-R=F8tc7Yu`2A{{{O$jUM~t?bH!Uo z{(I%BkMCNwwbR$k?~Dt){`FH)x$m#P|90dox#|_VwJ2@j{FQ-om#qxHUH|p`yfgd% zzFPU`Lv;M?@BfeMKks|LPxF59<)2rdt_ssm7Z(c-UEBHj&L^Q$XZz$gZ#I5eW}Ufx z^`Fn*_nFWC{b#z7we|FyH+R;&`&+~75T6}K7{waxsTY2*Ls^{-sJvZ%NXU*%KOYUk_^vglP^$&}$W<5R9Ia#vz6;J!&9=F41 z&Yw?DOjP9N=8lYsiTPVu_bi+Y*R^L5mVX?jF)s>f5>yF*7UB5T; z;~(E1&&dx9w%EkY4PNf2pZ_&>>D|2N{pWR-dCsje@vxP8@>*T|Q*pfI%>6%^^H1&0 zfBkHE-yYNdPgbjcek%NVb?&TX=aSQZXO`a*-uK;G{rU&Yo|Dh$pILtW_0n~-?^M0cefeFy_}7=0vi%c7UY5$8Z+734gz?>RRT?qr3m@{_{dQ{j$yP47s0goNh(NZCzZe@%`^r`LnUv=4mVY zpYqqewtwP1^W2$|Nt3-LO+2e##r*ql|8H5`-TKUbUz+Eid=#R6^-|C;Z_lmuYQ~|L z-tm5#Z}(&O&uicRiC2E@|GW3kw_=yWaXmWyW4P zUy=Lr3ID$b{bn-N7BWlL&AwV^H8*e7vZ-;~r}x{}tF5mJyreUI_L*~Mp4I>0UVq}d zj_m0NH=h?j|JVHUcKiKFE0gPIrCHd&+i>^v;`6+hU%%U(pjNGYbgArezq#kmoH1Bm z`}S^RM8t^#6QA|g^W@^Cbe7)DT7LWOv)lLUY@VN+tIanfbo=aJ`JYSwT%P_v<=?Sd z`w8>^?5r++_Twc_dj7eyo8Ou&-#@MXFZX}7c<=XrT1s9|+w*DP@6V^U%JTlV@c(m8 z`DaqOmGNAyvk98szEf9S^?d!2|GD`8^=fb9^n>53{Y}fXQNBC-oX={{VBe*mf8;-} z|KGm+vvd8^e|tWj3x8gH`|zB)XWBpc<*VxJ-d0c?>v*d=XYXkfBMqr;$oLpt(r4?{`t@6ZRgLtd2@-^vSrIZO`QDMvMOQ1 z^su#84lk9NC8_$^f88~gem}eE*6xd!gfE{bzwGMUrd_&yY=8aL z?`nVB`mVozci&UBwDk1i*K4;wJ-7SaDem|mi?*x^n^*blrOme+^Pk+N<)3#aXUANypwcd8AG0d#n0D2gm+B{rRvxctKWq}Z?_+Pi%F0WxOE1rs%4fO}|K|CDM<@TB zS^Kq0cnVFi4A6DqT zxOwxY@AZlE=FdML>~A}_Z1>caFJE%!sjPXe=KZ(MZLw85-`#05XU=?TckSQi`mK9v z?rGfr_f2@^YpctbgI7ka{ZjQjUvAF)`Tpe}W9}6{D7j~G)&JGgP`hd69V=I9Wp4fa z@?Xfmn=hFqecpSozkc@7p=Y-{*PBZ4T&lZyI_}r^=(WAiQ@_tzb=7P6Y@6>#^`ETV zY5nQf_C31a_db@j{4{H6%_Hadvpf4{CNeZ=ugsivWpUuLs=Z6R{ABDW*41mSoVDfF zS`*3j7j5kA=RcosKkxbcdb_>vLaKs;S1ayXbu3#wJalW#=fug6kNfvbzp{E#*y^df z{9Y|PmsD@4?my?uWqN^*Kb$jh>7$!%6YZ_F%{d#@fGI(g@+ zlOfuc!P`GQb<}@vZ9Khxb3t&`Z@wkhU#He37CxHweAl|;#+R1fE|0hMW&4{rEz10< zb=<#AccSbk>i@abrEh9hmet{dLDPQyHJSNF?XZd@pZjBlyYmX{Kki-bX`2-k z9%`klD8=v~E3>Fh^-b=ge0d_kjmrE=@v z6z-cpRqAE#`K{}M%}d*w1Alq{y(%kEo@635Rlk0Fc*t6vN8-$O(_F!f>(EDbV{#yoyhSdv;6aF8x`SL+I{I?AQ!-H9c zUrs#qHT%ZQz_3llgPFmB(NT(lfs5l314Dqr1U7~SrX~{x1`&ZJj0_7J6nGgJSXnX| z7&H_{m5zqMXb6mkz-R~zbO>DVoV2Xcz%wZHeROT&;^%5xi{6AC_rLTkDEFJ3-qXFl zR?o`*zJDSWyY4kJgTv0#M#{FHA9lwbez@d)_U^Lm=5M#=Gcw$eo>IoZ;Lv!^Pb%NU zxN7HVqi=^Ivb}Z67ONcPF=jZh^?j4%%Vm`XUpL;nWw7&8#P2_Ox3ZJh*1ronmwsv2 z^R4US*QXjVFkIinowme$R%`70sJXs(*Ghlads<=6&%_X8Ulwv}B1fZk3_C-E^zFXp zFI!(k#WFfauru7)t(SSV@9e9U-@Cq+9=jfO_ha1teGCldn>w4n+>(BCjcrda(@PcO z1JPS=o{X5DHECm+(c-<6zn5C(FJfVM@M%?_oaG(V+AL9e69a<-C)R50g-W#k{;*w7 z?%aBMcipzL(-z)dot*onGH3gl2X47)FVin=WMJ44pnbFI@g$+@%-APeUpvLWE1g}Q zyyk0d&b9Mp$-bA)FG*%(DA?%hZN08N@?@`B_)RUHxKn;j*0R~glWQit?UM zF9U=5s&j7s>!zgUY}p+jy7zrk!j7AK4X^t(U#9PVzRq;^-geKj&O2-CRd-BVFVAow zuIOUVm$c2xv(s(NwV5O2_nFW7cqijo$?C%L(<+ea&}xUH2YQgcKuZsW`;Mpv)NwW<(d+%H774+x#`L9 zq&1UNKV6GIeChkvee+lUyve{2^Lo`{cRq9Z9}fMpzDxYJe@uMO#1uQs9^@nRIfx*X_<_l?DD&Gh$Xs ziHI`h7yZz*`*IfUTY1)&&ZJv4e z4rAfw;(PCEuI;bWd@sxJAZlY~=JBsavv*83DA%<9n``noR!C$0lc4&^b3Us)-z*)! zkAY#Gsr0&A8-u28yt%Gs^1G-Vcg1er{%U-3UBm60-kTRsev=b%>Aqi_cm*Rv*>P9n zOSf~+?zKx<&wu##)Xto3dw;}n?AmR*dDqP|-`w7pt}fX7ZR14=Y1!=;Ur(MAontP& z|L)H<`J1oqWMqip*PAsl&OW%t@@|J?_S0W^<)82Uy7^Yr=+h#4BzIZ+(!;Z2X zsU_vw4`NpqRBiQ*`oAaI`^nTBYnG%;n)P<>7jA|E{a;p0USnf)vUmDy+0CDBg)OXIUAN8p`J~S`vve35ULU-u+(p+F7O1cLR&{0ayBrRN1NX`=_IxRe&RWlZ^Y!5mWvNQDbM@-s$JK85-u^ z+_2@b7yEAQja|p$el=~_wkCDAWDx_yw`IE5C+@p;nyXA|(fSb0qi#!O85m-s&3G^E z7X9!oYVP}|R`Wey&)*(>D{pz}6$XYF*{c_o+h_CFe0cDTcUy#|jL&991_qXX&{}}x z-O^9Dy-Hqu-ORdf(l_gu$;t;G_U5) z*{AQfSwHtYb}Ps7@Y!ECvTPU{UOTGatT_G3`{tGu{oDhSe8gU;Sw7ePy3g#Fj@SMf zIpyaj_n2l1#MhsR`(eb&P_Q#6>Qw>#{7pHHke&n`r%a_w<{hYtJCHopN?=PF~ z#?T;rv~6|SnlmbSX4;l7*R|fxowzsF-g>6!UXjTxSMSbPd0%((y9qm{pOa*GP*of_ z;a*nuyRZLa_W!#+IY)Niw)tm&-OUZJ+Wq$KtNP8acic=b)J%R~Ci`{i9BzgKTOTY^ z*ju;kN6{{+#&1#4nFWh;C01n#_hcKXt6E_K;#>+6q7 z*%)?&phjcz>IALNa!xEGr{Jg0*|U%)MQ1RqF*7F*4l9HjCN%I4f9hcJ<~h7kC&B#FhJk zs_p}E%Aj^gBe$1z*(Q^_c~jzjHtqeYVlEZ8GIx`DscmlW-pkpqj|DJ1a4S66^X1lu z&6mH<^?nm}sE{3=|jShf4{ z_Uc-5r@Ob_Zn?g1dG9Hau(H;ZBB5efiu7d**$0+bUN-4!gD6QGg+# zsL4clYUz|OtPBOVyO${BiWO(?F0IwOUK)4*eO^oEesxgdVs)&VRN7#^`(kd;Yj4hN zT=~&+&-?w_8S?u0tyg>BfBSlK#r(N-ezRle&+TJnI8X_W!*;p4Ct35^85m-W`{XRw zxJfZEcv0FXKH%!OD^t$rvdxaCj<+%v_nJY8;>Ftyl|emgaMcQ_Cixi{cBIMJ7TxGE zVPKd-Y45wSyQ$=|&5UK01*e<0Tx4T-;Fb>Z5m$(Nzf7K7)sKm*YvmXi3eH+oJ=q`$ t>fS4Y5)))3pkz{EFFON+!Os8k$7Js=vJ2@{VPIfj@O1TaS?83{1OSNpvhDx? literal 0 HcmV?d00001 diff --git a/blog/content/second-edition/posts/10-paging-2/recursive-page-table-access-level-1.svg b/blog/content/second-edition/posts/10-paging-2/recursive-page-table-access-level-1.svg new file mode 100644 index 00000000..a151a767 --- /dev/null +++ b/blog/content/second-edition/posts/10-paging-2/recursive-page-table-access-level-1.svg @@ -0,0 +1,2 @@ + +
Physical Memory
Physical Memory
0KiB
[Not supported by viewer]
Level 4 Page Table
Level 4 Page Table
4KiB
[Not supported by viewer]
IndexFrameFlags
024KiB
r/w



[Not supported by viewer]
Level 3 Page Table
Level 3 Page Table
16KiB
<div>16KiB</div>
24KiB
<div>24KiB</div>
8KiB
[Not supported by viewer]
IndexFrameFlags



511
24KiB
r/w
[Not supported by viewer]
Level 2 Page Table
Level 2 Page Table
Level 1 Page Table
Level 1 Page Table
32KiB
<div>32KiB</div>
IndexFrameFlags
036KiB
r/w



127
12KiBr


[Not supported by viewer]
4KiB
4KiB
CR3 Register
CR3 Register
IndexFrameFlags
0-
-
1
16KiB
r/w



511
4KiB
r/w
[Not supported by viewer]
Step 0
Step 0
Step 1
Step 1
Step 2
Step 2
Step 3
Step 3
Step 4
Step 4
\ No newline at end of file diff --git a/blog/content/second-edition/posts/10-paging-2/recursive-page-table-access-level-2.svg b/blog/content/second-edition/posts/10-paging-2/recursive-page-table-access-level-2.svg new file mode 100644 index 00000000..e28ab42d --- /dev/null +++ b/blog/content/second-edition/posts/10-paging-2/recursive-page-table-access-level-2.svg @@ -0,0 +1,2 @@ + +
Physical Memory
Physical Memory
0KiB
[Not supported by viewer]
Level 4 Page Table
Level 4 Page Table
4KiB
[Not supported by viewer]
IndexFrameFlags
024KiB
r/w



[Not supported by viewer]
Level 3 Page Table
Level 3 Page Table
16KiB
<div>16KiB</div>
24KiB
<div>24KiB</div>
8KiB
[Not supported by viewer]
IndexFrameFlags



511
24KiB
r/w
[Not supported by viewer]
Level 2 Page Table
Level 2 Page Table
Level 1 Page Table
Level 1 Page Table
32KiB
<div>32KiB</div>
IndexFrameFlags
036KiB
r/w



127
12KiBr


[Not supported by viewer]
4KiB
4KiB
CR3 Register
CR3 Register
IndexFrameFlags
0-
-
1
16KiB
r/w



511
4KiB
r/w
[Not supported by viewer]
Step 0
Step 0
Steps 1&2
Steps 1&2
Step 3
Step 3
Step 4
Step 4
\ No newline at end of file diff --git a/blog/content/second-edition/posts/10-paging-2/recursive-page-table-access-level-3.svg b/blog/content/second-edition/posts/10-paging-2/recursive-page-table-access-level-3.svg new file mode 100644 index 00000000..50a32a4e --- /dev/null +++ b/blog/content/second-edition/posts/10-paging-2/recursive-page-table-access-level-3.svg @@ -0,0 +1,2 @@ + +
Physical Memory
Physical Memory
0KiB
[Not supported by viewer]
Level 4 Page Table
Level 4 Page Table
4KiB
[Not supported by viewer]
IndexFrameFlags
024KiB
r/w



[Not supported by viewer]
Level 3 Page Table
Level 3 Page Table
16KiB
<div>16KiB</div>
24KiB
<div>24KiB</div>
8KiB
[Not supported by viewer]
IndexFrameFlags



511
24KiB
r/w
[Not supported by viewer]
Level 2 Page Table
Level 2 Page Table
Level 1 Page Table
Level 1 Page Table
32KiB
<div>32KiB</div>
IndexFrameFlags
036KiB
r/w



127
12KiBr


[Not supported by viewer]
4KiB
4KiB
CR3 Register
CR3 Register
IndexFrameFlags
0-
-
1
16KiB
r/w



511
4KiB
r/w
[Not supported by viewer]
Step 0
Step 0
Steps 1,2,3
Steps 1,2,3
Step 4
Step 4
Steps 1,2,3,4
Steps 1,2,3,4
\ No newline at end of file diff --git a/blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-1.svg b/blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-1.svg new file mode 100644 index 00000000..95f36c3b --- /dev/null +++ b/blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-1.svg @@ -0,0 +1,2 @@ + +
64
[Not supported by viewer]
0
[Not supported by viewer]
12
[Not supported by viewer]
21
21
30
[Not supported by viewer]
39
39
48
48
Index of Recursive Level 4 Entry
[Not supported by viewer]
Level 4
Index
[Not supported by viewer]
Level 3
Index
[Not supported by viewer]
Level 2
Index
[Not supported by viewer]
Offset into Level 1 Page Table Frame
Offset into Level 1 Page Table Frame
\ No newline at end of file diff --git a/blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-2.svg b/blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-2.svg new file mode 100644 index 00000000..9443213f --- /dev/null +++ b/blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-2.svg @@ -0,0 +1,2 @@ + +
64
[Not supported by viewer]
0
[Not supported by viewer]
12
[Not supported by viewer]
21
21
30
[Not supported by viewer]
39
39
48
48
Index of Recursive Level 4 Entry
[Not supported by viewer]
Index of Recursive Level 4 Entry
<div>Index of Recursive Level 4 Entry</div>
Level 4
Index
[Not supported by viewer]
Level 3
Index
[Not supported by viewer]
Offset into Level 2 Page Table Frame
Offset into Level 2 Page Table Frame
\ No newline at end of file diff --git a/blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-3.svg b/blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-3.svg new file mode 100644 index 00000000..28b55b8b --- /dev/null +++ b/blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-3.svg @@ -0,0 +1,2 @@ + +
64
[Not supported by viewer]
0
[Not supported by viewer]
12
[Not supported by viewer]
21
21
30
[Not supported by viewer]
39
39
48
48
Index of Recursive Level 4 Entry
[Not supported by viewer]
Index of Recursive Level 4 Entry
<div>Index of Recursive Level 4 Entry</div>
Index of Recursive Level 4 Entry
<div>Index of Recursive Level 4 Entry</div>
Level 4
Index
[Not supported by viewer]
Offset into Level 3 Page Table Frame
Offset into Level 3 Page Table Frame
\ No newline at end of file diff --git a/blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-4.svg b/blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-4.svg new file mode 100644 index 00000000..8b06a77d --- /dev/null +++ b/blog/content/second-edition/posts/10-paging-2/table-indices-from-address-recursive-level-4.svg @@ -0,0 +1,2 @@ + +
64
[Not supported by viewer]
0
[Not supported by viewer]
12
[Not supported by viewer]
21
21
30
[Not supported by viewer]
39
39
48
48
Index of Recursive Level 4 Entry
[Not supported by viewer]
Index of Recursive Level 4 Entry
<div>Index of Recursive Level 4 Entry</div>
Index of Recursive Level 4 Entry
<div>Index of Recursive Level 4 Entry</div>
Index of Recursive Level 4 Entry
<div>Index of Recursive Level 4 Entry</div>
Offset into Level 4 Page Table Frame
Offset into Level 4 Page Table Frame
\ No newline at end of file From abd5082c569f6a65950beb9f0dd840875d894d52 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 6 Jan 2019 23:21:37 +0100 Subject: [PATCH 03/29] Add second paging post to index page --- blog/templates/second-edition/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/blog/templates/second-edition/index.html b/blog/templates/second-edition/index.html index adafd4fa..ee1640f4 100644 --- a/blog/templates/second-edition/index.html +++ b/blog/templates/second-edition/index.html @@ -44,6 +44,7 @@
{{ macros::post_link(page=posts.8) }} + {{ macros::post_link(page=posts.9) }}
eS-_fs?-MFDs*Xir17s!;-Z?h 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 Date: Fri, 18 Jan 2019 13:54:59 +0100 Subject: [PATCH 12/29] Various minor improvements to post --- .../posts/10-advanced-paging/index.md | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) 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 3c617c58..94b54ef6 100644 --- a/blog/content/second-edition/posts/10-advanced-paging/index.md +++ b/blog/content/second-edition/posts/10-advanced-paging/index.md @@ -17,13 +17,13 @@ This blog is openly developed on [Github]. If you have any problems or questions ## Introduction -In the [previous post] we learned about the principles of paging and how the 4-level page tables on the x86_64 architecture work. We also found out that the bootloader already set up a 4-level page table hierarchy for our kernel, since paging is mandatory on x86_64 in 64-bit mode. This means that our kernel already runs on virtual addresses. +In the [previous post] we learned about the principles of paging and how the 4-level page tables on the x86_64 architecture work. We also found out that the bootloader already set up a 4-level page table hierarchy for our kernel, which means that our kernel already runs on virtual addresses. This improves safety since illegal memory accesses cause page fault exceptions instead of modifying arbitrary physical memory. [previous post]: ./second-edition/posts/09-paging-introduction/index.md -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. +However, it also causes a problem when we try to access the page tables from our kernel, because we can't directly access the physical addresses that are stored in page table entries or the `CR3` register. 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. -[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#accessing-the-page-tables ## Accessing Page Tables @@ -44,7 +44,7 @@ So in order access page table frames, we need to map some virtual pages to them. In this example we see various identity-mapped page table frames. This way the physical addresses in the page tables are also valid virtual addresses, so that we can easily access the page tables of all levels starting from the CR3 register. - However, it clutters the virtual address space and makes it more difficult to find continuous memory regions of larger sizes. For example, imagine that we want to create a virtual memory region of size 1000 KiB in the above graphic, e.g. for [memory-mapping a file]. We can't start the region at `26 KiB` because it would collide with the already mapped page at `1004 MiB`. So we have to look further until we find a large enough unmapped area, for example at `1008 KiB`. This is a similar fragmentation problem as with [segmentation]. + However, it clutters the virtual address space and makes it more difficult to find continuous memory regions of larger sizes. For example, imagine that we want to create a virtual memory region of size 1000 KiB in the above graphic, e.g. for [memory-mapping a file]. We can't start the region at `28 KiB` because it would collide with the already mapped page at `1004 MiB`. So we have to look further until we find a large enough unmapped area, for example at `1008 KiB`. This is a similar fragmentation problem as with [segmentation]. [memory-mapping a file]: https://en.wikipedia.org/wiki/Memory-mapped_file [segmentation]: ./second-edition/posts/09-paging-introduction/index.md#fragmentation @@ -122,17 +122,17 @@ Finally, we can access the level 4 table by moving each block four blocks to the ![Bits 0–12 are the offset into the level l table frame and bits 12–21, bits 21–30, bits 30–39 and bits 39–48 are the index of the recursive entry](table-indices-from-address-recursive-level-4.svg) -The page table index blocks are 9 bits, so moving each block one block to the right means a bitshift by 9 bits: `address >> 9`. To derive the 12-bit offset field from the shifted index, we need to multiply it by 8, the size of a page table entry. Through this operation, we can calculate virtual addresses for the page tables of all four levels. +We can now calculate virtual addresses for the page tables of all four levels. We can even calculate an address that points exactly to a specific page table entry by multiplying the its index by 8, the size of a page table entry. The table below summarizes the address structure for accessing the different kinds of frames: -Mapped Frame for | Address Structure ([octal]) ----------------- | ------------------------------- -Page | `0o_SSSSSS_AAA_BBB_CCC_DDD_EEEE` -Level 1 Table | `0o_SSSSSS_RRR_AAA_BBB_CCC_DDDD` -Level 2 Table | `0o_SSSSSS_RRR_RRR_AAA_BBB_CCCC` -Level 3 Table | `0o_SSSSSS_RRR_RRR_RRR_AAA_BBBB` -Level 4 Table | `0o_SSSSSS_RRR_RRR_RRR_RRR_AAAA` +Virtual Address for | Address Structure ([octal]) +------------------- | ------------------------------- +Page | `0o_SSSSSS_AAA_BBB_CCC_DDD_EEEE` +Level 1 Table Entry | `0o_SSSSSS_RRR_AAA_BBB_CCC_DDDD` +Level 2 Table Entry | `0o_SSSSSS_RRR_RRR_AAA_BBB_CCCC` +Level 3 Table Entry | `0o_SSSSSS_RRR_RRR_RRR_AAA_BBBB` +Level 4 Table Entry | `0o_SSSSSS_RRR_RRR_RRR_RRR_AAAA` [octal]: https://en.wikipedia.org/wiki/Octal @@ -142,6 +142,8 @@ Whereas `AAA` is the level 4 index, `BBB` the level 3 index, `CCC` the level 2 i [sign extension]: ./second-edition/posts/09-paging-introduction/index.md#paging-on-x86 +We use [octal] numbers for representing the addresses since each octal character represents 3 bits, which allows us to clearly spearate the the 9-bit indexes of the different page table levels. This isn't possible with the hexadecimal system where each character represents four bits. + ## Implementation 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. @@ -219,7 +221,9 @@ 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 transform the `level_4_table_addr` to a `&PageTable`, which is an `unsafe` operation since the compiler can't know that the address is valid. We use the indexing operator to look at the entry with `level_4_index`. 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`. +Then we transform the `level_4_table_addr` to a [`PageTable`] reference, which is an `unsafe` operation since the compiler can't know that the address is valid. We use the indexing operator to look at the entry with `level_4_index`. 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`. + +[`PageTable`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/struct.PageTable.html If the entry is not `None`, we know that a level 3 table exist. We calculate the virtual address of it by shifting the level 4 address 9 bits to the left and setting the address bits 12–21 to the level 4 index (see the section about [address calculation]). We can do that because the recursive index is `0o777`, so that it is also a valid sign extension. We then do the same cast and entry-checking as with the level 4 table. @@ -227,7 +231,7 @@ If the entry is not `None`, we know that a level 3 table exist. We calculate the 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. As a last step, we add the page offset to that address and return it. -If we knew that the address is mapped, we could directly access the level 1 table without looking at the higher level pages first. But since we don't know this, so we have to check whether the level 1 table exists first, otherwise we would cause a page fault for unmapped addresses. +If we knew that the address is mapped, we could directly access the level 1 table without looking at the higher level pages first. But since we don't know this, we have to check whether the level 1 table exists first, otherwise we would cause a page fault for unmapped addresses. #### Try it out @@ -336,8 +340,6 @@ After reading the page tables and creating a translation function, the next step 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. -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: @@ -614,6 +616,9 @@ Now the mapping succeeds and we see the white block on the screen again: 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. +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. + + We're now able to map arbitrary pages and to allocate new physical frames when we need them. # TODO @@ -626,4 +631,6 @@ We're now able to map arbitrary pages and to allocate new physical frames when w --- TODO spellcheck +TODO improve transition between sections TODO update post date +TODO check level 2 table diagram 24KiB -> 32KiB \ No newline at end of file From 4cec9642ab823ee3a7cbb53e8207f1ffed1de831 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 25 Jan 2019 18:10:01 +0100 Subject: [PATCH 13/29] Update from gutenberg to zola --- blog/content/second-edition/posts/10-advanced-paging/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 94b54ef6..8ceb444e 100644 --- a/blog/content/second-edition/posts/10-advanced-paging/index.md +++ b/blog/content/second-edition/posts/10-advanced-paging/index.md @@ -1,6 +1,6 @@ +++ title = "Advanced Paging" -order = 10 +weight = 10 path = "advanced-paging" date = 0000-01-01 template = "second-edition/page.html" From 660528bb52911f2c193befcc55f1792186170105 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 25 Jan 2019 19:20:54 +0100 Subject: [PATCH 14/29] Minor rewordings --- .../second-edition/posts/10-advanced-paging/index.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 8ceb444e..5a1d5d78 100644 --- a/blog/content/second-edition/posts/10-advanced-paging/index.md +++ b/blog/content/second-edition/posts/10-advanced-paging/index.md @@ -17,14 +17,16 @@ This blog is openly developed on [Github]. If you have any problems or questions ## Introduction -In the [previous post] we learned about the principles of paging and how the 4-level page tables on the x86_64 architecture work. We also found out that the bootloader already set up a 4-level page table hierarchy for our kernel, which means that our kernel already runs on virtual addresses. This improves safety since illegal memory accesses cause page fault exceptions instead of modifying arbitrary physical memory. +In the [previous post] we learned about the principles of paging and how the 4-level page tables on x86_64 work. We also found out that the bootloader already set up a page table hierarchy for our kernel, which means that our kernel already runs on virtual addresses. This improves safety since illegal memory accesses cause page fault exceptions instead of modifying arbitrary physical memory. [previous post]: ./second-edition/posts/09-paging-introduction/index.md -However, it also causes a problem when we try to access the page tables from our kernel, because we can't directly access the physical addresses that are stored in page table entries or the `CR3` register. 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. +However, it also causes a problem when we try to access the page tables from our kernel, because we can't directly access the physical addresses that are stored in page table entries or the `CR3` register. We experienced that problem already [at the end of the previous post] when we tried to inspect the active page tables. [at the end of the previous post]: ./second-edition/posts/09-paging-introduction/index.md#accessing-the-page-tables +The next section discusses the problem in detail and provides different approaches to a solution. Afterwards, we will implement a function to translate virtual to physical addresses and learn how to create new mappings in the page tables. + ## Accessing Page Tables Accessing the page tables from our kernel is not as easy as it may seem. To understand the problem let's take a look at the example 4-level page table hierarchy of the previous post again: @@ -35,14 +37,14 @@ The important thing here is that each page entry stores the _physical_ address o The problem for us is that we can't directly access physical addresses from our kernel, since our kernel also runs on top of virtual addresses. For example when we access address `4 KiB`, we access the _virtual_ address `4 KiB`, not the _physical_ address `4 KiB` where the level 4 page table lives. When we want to acccess the physical address `4 KiB`, we can only do so through some virtual address that maps to it. -So in order access page table frames, we need to map some virtual pages to them. There are different ways to create these mappings that all allow us to access arbitrary page table frames: +So in order to access page table frames, we need to map some virtual pages to them. There are different ways to create these mappings that all allow us to access arbitrary page table frames: - A simple solution is to **identity map all page tables**: ![A virtual and a physical address space with various virtual pages mapped to the physical frame with the same address](identity-mapped-page-tables.svg) - In this example we see various identity-mapped page table frames. This way the physical addresses in the page tables are also valid virtual addresses, so that we can easily access the page tables of all levels starting from the CR3 register. + In this example we see various identity-mapped page table frames. This way the physical addresses of page tables are also valid virtual addresses, so that we can easily access the page tables of all levels starting from the CR3 register. However, it clutters the virtual address space and makes it more difficult to find continuous memory regions of larger sizes. For example, imagine that we want to create a virtual memory region of size 1000 KiB in the above graphic, e.g. for [memory-mapping a file]. We can't start the region at `28 KiB` because it would collide with the already mapped page at `1004 MiB`. So we have to look further until we find a large enough unmapped area, for example at `1008 KiB`. This is a similar fragmentation problem as with [segmentation]. From 7cb62ee7fe18c8680529a855c52c5535bb139b6f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 25 Jan 2019 19:21:21 +0100 Subject: [PATCH 15/29] Make `Recursive Page Tables` a subsection --- blog/content/second-edition/posts/10-advanced-paging/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 5a1d5d78..3d4b9d18 100644 --- a/blog/content/second-edition/posts/10-advanced-paging/index.md +++ b/blog/content/second-edition/posts/10-advanced-paging/index.md @@ -72,7 +72,7 @@ So in order to access page table frames, we need to map some virtual pages to th - While both of the above approaches work, there is a third technique called **recursive page tables** that combines their advantages: It keeps all page table frames mapped at all times so that no temporary mappings are needed, and also keeps the mapped pages together to avoid fragmentation of the virtual address space. This is the technique that we will use for our implementation, therefore it is described in detail in the following section. -## Recursive Page Tables +### Recursive Page Tables The idea behind this approach sounds simple: _Map some entry of the level 4 page table to the frame of level 4 table itself_. By doing this, we effectively reserve a part of the virtual address space and map all current and future page table frames to that space. Thus, the single entry makes every table of every level accessible through a calculatable address. @@ -102,7 +102,7 @@ Accessing the tables of levels 3 and 4 works in the same way. For accessing the It might take some time to wrap your head around the concept, but it works quite well in practice. -### Address Calculation +#### Address Calculation We saw that we can access tables of all levels by following the recursive entry once or multiple times before the actual translation. Since the indexes into the tables of the four levels are derived directly from the virtual address, we need to construct special virtual addresses for this technique. Remember, the page table indexes are derived from the address in the following way: From bcc590e65f25a26d512579289e5ad7b3d2385439 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 25 Jan 2019 19:21:45 +0100 Subject: [PATCH 16/29] Create memory module in memory.rs instead of memory/mod.rs --- .../posts/10-advanced-paging/index.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 3d4b9d18..5f86e003 100644 --- a/blog/content/second-edition/posts/10-advanced-paging/index.md +++ b/blog/content/second-edition/posts/10-advanced-paging/index.md @@ -172,7 +172,7 @@ pub mod memory; ``` ```rust -// in src/memory/mod.rs +// in src/memory.rs use x86_64::PhysAddr; use x86_64::structures::paging::PageTable; @@ -277,7 +277,7 @@ The `x86_64` provides a [`RecursivePageTable`] type that implements safe abstrac [`RecursivePageTable`]: https://docs.rs/x86_64/0.3.5/x86_64/structures/paging/struct.RecursivePageTable.html ```rust -// in src/memory/mod.rs +// in src/memory.rs use x86_64::{VirtAddr, PhysAddr}; use x86_64::structures::paging::{Mapper, Page, PageTable, RecursivePageTable}; @@ -347,7 +347,7 @@ Let's start with the simple case and assume that we don't need to create new pag We implement it in a new `create_mapping` function in our `memory` module: ```rust -// in src/memory/mod.rs +// in src/memory.rs use x86_64::structures::paging::{FrameAllocator, PhysFrame, Size4KiB}; @@ -393,7 +393,7 @@ The [`map_to`] function can fail, so it returns a [`Result`]. Since this is just TODO ```rust -// in src/memory/mod.rs +// in src/memory.rs /// A FrameAllocator that always returns `None`. pub struct EmptyFrameAllocator; @@ -445,7 +445,7 @@ The white block in the middle of the screen is by our write to `0x1c00`, which m 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 +// in src/memory.rs TODO: update create_example_mapping @@ -528,7 +528,7 @@ Note that we now pass `boot_info.p4_table_addr` instead of a hardcoded address t 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 +// in src/memory.rs pub struct BootInfoFrameAllocator where I: Iterator { frames: I, @@ -551,7 +551,7 @@ The `frames` field can be initialized with an arbitrary [`Iterator`] of frames. The initialization of the `BootInfoFrameAllocator` happens in a new `init_frame_allocator` function: ```rust -// in src/memory/mod.rs +// in src/memory.rs use bootloader::bootinfo::{MemoryMap, MemoryRegionType}; From a3e7ad1fa8f1103140a779f619114780c825f206 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 25 Jan 2019 19:56:54 +0100 Subject: [PATCH 17/29] Fix typo in graphics and replace SVGs with PNGs The SVG have rendering problems on some devices, e.g. when the text is zoomed. --- .../posts/10-advanced-paging/index.md | 12 ++++++------ .../recursive-page-table-access-level-1.png | Bin 0 -> 52566 bytes .../recursive-page-table-access-level-1.svg | 2 -- .../recursive-page-table-access-level-2.png | Bin 0 -> 51800 bytes .../recursive-page-table-access-level-2.svg | 2 -- .../recursive-page-table-access-level-3.png | Bin 0 -> 53659 bytes .../recursive-page-table-access-level-3.svg | 2 -- .../10-advanced-paging/recursive-page-table.png | Bin 0 -> 46989 bytes .../10-advanced-paging/recursive-page-table.svg | 2 -- .../temporarily-mapped-page-tables.png | Bin 0 -> 44984 bytes .../temporarily-mapped-page-tables.svg | 2 -- 11 files changed, 6 insertions(+), 16 deletions(-) create mode 100644 blog/content/second-edition/posts/10-advanced-paging/recursive-page-table-access-level-1.png delete mode 100644 blog/content/second-edition/posts/10-advanced-paging/recursive-page-table-access-level-1.svg create mode 100644 blog/content/second-edition/posts/10-advanced-paging/recursive-page-table-access-level-2.png delete mode 100644 blog/content/second-edition/posts/10-advanced-paging/recursive-page-table-access-level-2.svg create mode 100644 blog/content/second-edition/posts/10-advanced-paging/recursive-page-table-access-level-3.png delete mode 100644 blog/content/second-edition/posts/10-advanced-paging/recursive-page-table-access-level-3.svg create mode 100644 blog/content/second-edition/posts/10-advanced-paging/recursive-page-table.png delete mode 100644 blog/content/second-edition/posts/10-advanced-paging/recursive-page-table.svg create mode 100644 blog/content/second-edition/posts/10-advanced-paging/temporarily-mapped-page-tables.png delete mode 100644 blog/content/second-edition/posts/10-advanced-paging/temporarily-mapped-page-tables.svg 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 5f86e003..16cd8d85 100644 --- a/blog/content/second-edition/posts/10-advanced-paging/index.md +++ b/blog/content/second-edition/posts/10-advanced-paging/index.md @@ -55,7 +55,7 @@ So in order to access page table frames, we need to map some virtual pages to th - Alternatively, we could **map the page tables frames only temporarily** when we need to access them. To be able to create the temporary mappings we only need a single identity-mapped level 1 table: - ![A virtual and a physical address space with an identity mapped level 1 table, which maps its 0th entry to the level 2 table frame, therey mapping that frame to page with address 0](temporarily-mapped-page-tables.svg) + ![A virtual and a physical address space with an identity mapped level 1 table, which maps its 0th entry to the level 2 table frame, therey mapping that frame to page with address 0](temporarily-mapped-page-tables.png) The level 1 table in this graphic controls the first 2 MiB of the virtual address space. This is because it is reachable by starting at the CR3 register and following the 0th entry in the level 4, level 3, and level 2 page tables. The entry with index `8` maps the virtual page at address `32 KiB` to the physical frame at address `32 KiB`, thereby identity mapping the level 1 table itself. The graphic shows this identity-mapping by the horizontal arrow at `32 KiB`. @@ -78,7 +78,7 @@ The idea behind this approach sounds simple: _Map some entry of the level 4 page Let's go through an example to understand how this all works: -![An example 4-level page hierarchy with each page table shown in physical memory. Entry 511 of the level 4 page is mapped to frame 4KiB, the frame of the level 4 table itself.](recursive-page-table.svg) +![An example 4-level page hierarchy with each page table shown in physical memory. Entry 511 of the level 4 page is mapped to frame 4KiB, the frame of the level 4 table itself.](recursive-page-table.png) The only difference to the [example at the beginning of this post] is the additional entry at index `511` in the level 4 table, which is mapped to physical frame `4 KiB`, the frame of the level 4 table itself. @@ -88,17 +88,17 @@ By letting the CPU follow this entry on a translation, it doesn't reach a level By following the recursive entry one or multiple times before we start the actual translation, we can effectively shorten the number of levels that the CPU traverses. For example, if we follow the recursive entry once and then proceed to the level 3 table, the CPU thinks that the level 3 table is a level 2 table. Going further, it treats the level 2 table as a level 1 table, and the level 1 table as the mapped frame. This means that we can now read and write the level 1 page table because the CPU thinks that it is the mapped frame. The graphic below illustrates the 5 translation steps: -![The above example 4-level page hierarchy with 5 arrows: "Step 0" from CR4 to level 4 table, "Step 1" from level 4 table to level 4 table, "Step 2" from level 4 table to level 3 table, "Step 3" from level 3 table to level 2 table, and "Step 4" from level 2 table to level 1 table.](recursive-page-table-access-level-1.svg) +![The above example 4-level page hierarchy with 5 arrows: "Step 0" from CR4 to level 4 table, "Step 1" from level 4 table to level 4 table, "Step 2" from level 4 table to level 3 table, "Step 3" from level 3 table to level 2 table, and "Step 4" from level 2 table to level 1 table.](recursive-page-table-access-level-1.png) Similarly, we can follow the recursive entry twice before starting the translation to reduce the number of traversed levels to two: -![The same 4-level page hierarchy with the following 4 arrows: "Step 0" from CR4 to level 4 table, "Steps 1&2" from level 4 table to level 4 table, "Step 3" from level 4 table to level 3 table, and "Step 4" from level 3 table to level 2 table.](recursive-page-table-access-level-2.svg) +![The same 4-level page hierarchy with the following 4 arrows: "Step 0" from CR4 to level 4 table, "Steps 1&2" from level 4 table to level 4 table, "Step 3" from level 4 table to level 3 table, and "Step 4" from level 3 table to level 2 table.](recursive-page-table-access-level-2.png) Let's go through it step by step: First the CPU follows the recursive entry on the level 4 table and thinks that it reaches a level 3 table. Then it follows the recursive entry again and thinks that it reaches a level 2 table. But in reality, it is still on the level 4 table. When the CPU now follows a different entry, it lands on a level 3 table, but thinks it is already on a level 1 table. So while the next entry points at a level 2 table, the CPU thinks that it points to the mapped frame, which allows us to read and write the level 2 table. Accessing the tables of levels 3 and 4 works in the same way. For accessing the level 3 table, we follow the recursive entry entry three times, tricking the CPU into thinking it is already on a level 1 table. Then we follow another entry and reach a level 3 table, which the CPU treats as a mapped frame. For accessing the level 4 table itself, we just follow the recursive entry four times until the CPU treats the level 4 table itself as mapped frame (in blue in the graphic below). -![The same 4-level page hierarchy with the following 3 arrows: "Step 0" from CR4 to level 4 table, "Steps 1,2,3" from level 4 table to level 4 table, and "Step 4" from level 4 table to level 3 table. In blue the alternative "Steps 1,2,3,4" arrow from level 4 table to level 4 table.](recursive-page-table-access-level-3.svg) +![The same 4-level page hierarchy with the following 3 arrows: "Step 0" from CR4 to level 4 table, "Steps 1,2,3" from level 4 table to level 4 table, and "Step 4" from level 4 table to level 3 table. In blue the alternative "Steps 1,2,3,4" arrow from level 4 table to level 4 table.](recursive-page-table-access-level-3.png) It might take some time to wrap your head around the concept, but it works quite well in practice. @@ -635,4 +635,4 @@ We're now able to map arbitrary pages and to allocate new physical frames when w TODO spellcheck TODO improve transition between sections TODO update post date -TODO check level 2 table diagram 24KiB -> 32KiB \ No newline at end of file +TODO use PNGs instead of SVGs? diff --git a/blog/content/second-edition/posts/10-advanced-paging/recursive-page-table-access-level-1.png b/blog/content/second-edition/posts/10-advanced-paging/recursive-page-table-access-level-1.png new file mode 100644 index 0000000000000000000000000000000000000000..1c6662ab32b22581afa4af5848ecd290b6eca93f GIT binary patch literal 52566 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