Merge pull request #326 from phil-opp/gutenberg
Switch from hugo to gutenberg
@@ -4,9 +4,9 @@
|
|||||||
import io
|
import io
|
||||||
from github import Github
|
from github import Github
|
||||||
|
|
||||||
g = Github("e62e298b2a46ca5cf474cb9a4c0c1bc950b09d46")
|
g = Github()
|
||||||
|
|
||||||
with io.open("layouts/partials/recent-updates.html", 'w', encoding='utf8') as recent_updates:
|
with io.open("templates/recent-updates.html", 'w', encoding='utf8') as recent_updates:
|
||||||
recent_updates.truncate()
|
recent_updates.truncate()
|
||||||
|
|
||||||
recent_updates.write(u"<ul>\n")
|
recent_updates.write(u"<ul>\n")
|
||||||
|
|||||||
@@ -1,29 +1,10 @@
|
|||||||
baseurl = "http://os.phil-opp.com/"
|
|
||||||
languageCode = "en-us"
|
|
||||||
title = "Writing an OS in Rust"
|
title = "Writing an OS in Rust"
|
||||||
rssURI = "atom.xml"
|
base_url = "https://os.phil-opp.com"
|
||||||
copyright = "© 2016. All rights reserved."
|
|
||||||
|
|
||||||
PygmentsCodeFences = true
|
highlight_code = true
|
||||||
pygmentsuseclasses = true
|
highlight_theme = "monokai"
|
||||||
|
generate_rss = true
|
||||||
|
|
||||||
uglyurls = true
|
[extra]
|
||||||
|
subtitle = "Philipp Oppermann's blog"
|
||||||
disableKinds = ["section", "taxonomy", "taxonomyTerm"]
|
author = { name = "Philipp Oppermann" }
|
||||||
|
|
||||||
[author]
|
|
||||||
name = "Philipp Oppermann"
|
|
||||||
email = "dev@phil-opp.com"
|
|
||||||
|
|
||||||
[params]
|
|
||||||
subtitle = "Philipp Oppermann's blog"
|
|
||||||
date_format = "Jan 2, 2006"
|
|
||||||
|
|
||||||
[permalinks]
|
|
||||||
post = "/:slug.html"
|
|
||||||
old-posts = "/:slug.html"
|
|
||||||
additional-resource = "/:filename.html"
|
|
||||||
page = "/:filename.html"
|
|
||||||
|
|
||||||
[blackfriday]
|
|
||||||
plainIDAnchors = true
|
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
+++
|
|
||||||
title = "Handling Exceptions using Naked Functions"
|
|
||||||
+++
|
|
||||||
|
|
||||||
<p>These posts explain how to handle CPU exceptions using naked functions.
|
|
||||||
Historically, these posts were the main exception handling posts before the
|
|
||||||
<code>x86-interrupt</code> calling convention and the <code>x86_64</code> crate existed.
|
|
||||||
Our new way of handling exceptions can be found in the
|
|
||||||
<a href="/handling-exceptions.html">“Handling Exceptions”</a> post.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="posts exceptions">
|
|
||||||
|
|
||||||
<article class="post">
|
|
||||||
<h2 class="post-title">
|
|
||||||
<a href="/catching-exceptions.html">
|
|
||||||
Catching Exceptions
|
|
||||||
</a>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<p>In this post, we start exploring exceptions. We set up an interrupt descriptor table and add handler functions. At the end of this post, our kernel will be able to catch divide-by-zero faults.</p>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
|
|
||||||
|
|
||||||
<article class="post">
|
|
||||||
<h2 class="post-title">
|
|
||||||
<a href="/better-exception-messages.html">
|
|
||||||
Better Exception Messages
|
|
||||||
</a>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<p>In this post, we explore exceptions in more detail. Our goal is to print additional information when an exception occurs, for example the values of the instruction and stack pointer. In the course of this, we will explore inline assembly and naked functions. We will also add a handler function for page faults and read the associated error code.</p>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
|
|
||||||
|
|
||||||
<article class="post">
|
|
||||||
<h2 class="post-title">
|
|
||||||
<a href="/returning-from-exceptions.html">
|
|
||||||
Returning from Exceptions
|
|
||||||
</a>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<p>In this post, we learn how to return from exceptions correctly. In the course of this, we will explore the <code>iretq</code> instruction, the C calling convention, multimedia registers, and the red zone.</p>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
6
blog/content/extra/_index.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
+++
|
||||||
|
title = "Extra Content"
|
||||||
|
insert_anchor = "left"
|
||||||
|
render = false
|
||||||
|
sort_by = "order"
|
||||||
|
+++
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
+++
|
+++
|
||||||
title = "Cross Compile Binutils"
|
title = "Cross Compile Binutils"
|
||||||
|
template = "plain.html"
|
||||||
|
url = "cross-compile-binutils"
|
||||||
|
order = 2
|
||||||
+++
|
+++
|
||||||
|
|
||||||
The [GNU Binutils] are a collection of various binary tools such as `ld`, `as`, `objdump`, or `readelf`. These tools are platform-specific, so you need to compile them again if your host system and target system are different. In our case, we need `ld` and `objdump` for the x86_64 architecture.
|
The [GNU Binutils] are a collection of various binary tools such as `ld`, `as`, `objdump`, or `readelf`. These tools are platform-specific, so you need to compile them again if your host system and target system are different. In our case, we need `ld` and `objdump` for the x86_64 architecture.
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
+++
|
+++
|
||||||
title = "Cross Compiling: libcore"
|
title = "Cross Compiling: libcore"
|
||||||
|
template = "plain.html"
|
||||||
|
url = "cross-compile-libcore"
|
||||||
|
order = 3
|
||||||
+++
|
+++
|
||||||
|
|
||||||
If you get an `error: can't find crate for 'core'`, you're probably compiling for a different target (e.g. you're passing the `target` option to `cargo build`). Now the compiler complains that it can't find the `core` library. This document gives a quick overview how to fix this problem. For more details, see the [rust-cross] project.
|
If you get an `error: can't find crate for 'core'`, you're probably compiling for a different target (e.g. you're passing the `target` option to `cargo build`). Now the compiler complains that it can't find the `core` library. This document gives a quick overview how to fix this problem. For more details, see the [rust-cross] project.
|
||||||
@@ -1,19 +1,25 @@
|
|||||||
+++
|
+++
|
||||||
title = "Catching Exceptions"
|
title = "Catching Exceptions"
|
||||||
|
order = 1
|
||||||
|
url = "catching-exceptions"
|
||||||
date = "2016-05-28"
|
date = "2016-05-28"
|
||||||
|
[extra]
|
||||||
updated = "2016-06-25"
|
updated = "2016-06-25"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In this post, we start exploring exceptions. We set up an interrupt descriptor table and add handler functions. At the end of this post, our kernel will be able to catch divide-by-zero faults.
|
In this post, we start exploring exceptions. We set up an interrupt descriptor table and add handler functions. At the end of this post, our kernel will be able to catch divide-by-zero faults.
|
||||||
|
|
||||||
<!--more--><aside id="toc"></aside>
|
<!-- more --><aside id="toc"></aside>
|
||||||
|
|
||||||
As always, the complete source code is on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page.
|
As always, the complete source code is on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page.
|
||||||
|
|
||||||
[Github]: https://github.com/phil-opp/blog_os/tree/catching_exceptions
|
[Github]: https://github.com/phil-opp/blog_os/tree/catching_exceptions
|
||||||
[issues]: https://github.com/phil-opp/blog_os/issues
|
[issues]: https://github.com/phil-opp/blog_os/issues
|
||||||
|
|
||||||
> **Note**: This post describes how to handle exceptions using naked functions (see <a href="/handling-exceptions-with-naked-fns.html">“Handling Exceptions with Naked Functions”</a> for an overview). Our new way of handling exceptions can be found in the <a href="/handling-exceptions.html">“Handling Exceptions”</a> post.
|
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
|
||||||
|
|
||||||
|
[“Handling Exceptions with Naked Functions”]: ./extra/naked-exceptions/_index.md
|
||||||
|
[“Handling Exceptions”]: ./posts/09-handling-exceptions/index.md
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
An exception signals that something is wrong with the current instruction. For example, the CPU issues an exception if the current instruction tries to divide by 0. When an exception occurs, the CPU interrupts its current work and immediately calls a specific exception handler function, depending on the exception type.
|
An exception signals that something is wrong with the current instruction. For example, the CPU issues an exception if the current instruction tries to divide by 0. When an exception occurs, the CPU interrupts its current work and immediately calls a specific exception handler function, depending on the exception type.
|
||||||
@@ -239,7 +245,7 @@ It is important that the function is [diverging], i.e. it must never return. The
|
|||||||
|
|
||||||
[diverging]: https://doc.rust-lang.org/book/functions.html#diverging-functions
|
[diverging]: https://doc.rust-lang.org/book/functions.html#diverging-functions
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
If our handler function returned normally, it would try to pop the return address from the stack. But it might get some completely different value then. For example, the CPU pushes an error code for some exceptions. Bad things would happen if we interpreted this error code as return address and jumped to it. Therefore interrupt handler functions must diverge[^fn-must-diverge].
|
If our handler function returned normally, it would try to pop the return address from the stack. But it might get some completely different value then. For example, the CPU pushes an error code for some exceptions. Bad things would happen if we interpreted this error code as return address and jumped to it. Therefore interrupt handler functions must diverge[^fn-must-diverge].
|
||||||
|
|
||||||
@@ -548,7 +554,7 @@ pub extern "C" fn rust_main(...) {
|
|||||||
|
|
||||||
It works! We see a `EXCEPTION: DIVIDE BY ZERO` message at the bottom of our screen:
|
It works! We see a `EXCEPTION: DIVIDE BY ZERO` message at the bottom of our screen:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## What's next?
|
## What's next?
|
||||||
We've successfully caught our first exception! However, our `EXCEPTION: DIVIDE BY ZERO` message doesn't contain much information about the cause of the exception. The next post improves the situation by printing i.a. the current stack pointer and address of the causing instruction. We will also explore other exceptions such as page faults, for which the CPU pushes an _error code_ on the stack.
|
We've successfully caught our first exception! However, our `EXCEPTION: DIVIDE BY ZERO` message doesn't contain much information about the cause of the exception. The next post improves the situation by printing i.a. the current stack pointer and address of the causing instruction. We will also explore other exceptions such as page faults, for which the CPU pushes an _error code_ on the stack.
|
||||||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@@ -1,28 +1,33 @@
|
|||||||
+++
|
+++
|
||||||
title = "Better Exception Messages"
|
title = "Better Exception Messages"
|
||||||
|
order = 2
|
||||||
|
url = "better-exception-messages"
|
||||||
date = "2016-08-03"
|
date = "2016-08-03"
|
||||||
|
[extra]
|
||||||
updated = "2016-11-01"
|
updated = "2016-11-01"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In this post, we explore exceptions in more detail. Our goal is to print additional information when an exception occurs, for example the values of the instruction and stack pointer. In the course of this, we will explore inline assembly and naked functions. We will also add a handler function for page faults and read the associated error code.
|
In this post, we explore exceptions in more detail. Our goal is to print additional information when an exception occurs, for example the values of the instruction and stack pointer. In the course of this, we will explore inline assembly and naked functions. We will also add a handler function for page faults and read the associated error code.
|
||||||
|
|
||||||
<!--more--><aside id="toc"></aside>
|
<!-- more --><aside id="toc"></aside>
|
||||||
|
|
||||||
As always, the complete source code is on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a [gitter chat] and a [comment section] at the end of this page.
|
As always, the complete source code is on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a [gitter chat] and a comment section at the end of this page.
|
||||||
|
|
||||||
[Github]: https://github.com/phil-opp/blog_os/tree/better_exception_messages
|
[Github]: https://github.com/phil-opp/blog_os/tree/better_exception_messages
|
||||||
[issues]: https://github.com/phil-opp/blog_os/issues
|
[issues]: https://github.com/phil-opp/blog_os/issues
|
||||||
[gitter chat]: https://gitter.im/phil-opp/blog_os
|
[gitter chat]: https://gitter.im/phil-opp/blog_os
|
||||||
[comment section]: #disqus_thread
|
|
||||||
|
|
||||||
> **Note**: This post describes how to handle exceptions using naked functions (see <a href="/handling-exceptions-with-naked-fns.html">“Handling Exceptions with Naked Functions”</a> for an overview). Our new way of handling exceptions can be found in the <a href="/handling-exceptions.html">“Handling Exceptions”</a> post.
|
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
|
||||||
|
|
||||||
|
[“Handling Exceptions with Naked Functions”]: ./extra/naked-exceptions/_index.md
|
||||||
|
[“Handling Exceptions”]: ./posts/09-handling-exceptions/index.md
|
||||||
|
|
||||||
## Exceptions in Detail
|
## Exceptions in Detail
|
||||||
An exception signals that something is wrong with the currently-executed instruction. Whenever an exception occurs, the CPU interrupts its current work and starts an internal exception routine.
|
An exception signals that something is wrong with the currently-executed instruction. Whenever an exception occurs, the CPU interrupts its current work and starts an internal exception routine.
|
||||||
|
|
||||||
This routine involves reading the interrupt descriptor table and invoking the registered handler function. But first, the CPU pushes various information onto the stack, which describe the current state and provide information about the cause of the exception:
|
This routine involves reading the interrupt descriptor table and invoking the registered handler function. But first, the CPU pushes various information onto the stack, which describe the current state and provide information about the cause of the exception:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The pushed information contain the instruction and stack pointer, the current CPU flags, and (for some exceptions) an error code, which contains further information about the cause of the exception. Let's look at the fields in detail:
|
The pushed information contain the instruction and stack pointer, the current CPU flags, and (for some exceptions) an error code, which contains further information about the cause of the exception. Let's look at the fields in detail:
|
||||||
|
|
||||||
@@ -84,7 +89,7 @@ So the inline assembly loads the stack pointer value to `stack_frame` at the ver
|
|||||||
### Testing it
|
### Testing it
|
||||||
Let's try it by executing `make run`:
|
Let's try it by executing `make run`:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Those `ExceptionStackFrame` values look very wrong. The instruction pointer definitely shouldn't be 1 and the code segment should be `0x8` instead of some big number. So what's going on here?
|
Those `ExceptionStackFrame` values look very wrong. The instruction pointer definitely shouldn't be 1 and the code segment should be `0x8` instead of some big number. So what's going on here?
|
||||||
|
|
||||||
@@ -194,7 +199,7 @@ call divide_by_zero_handler
|
|||||||
```
|
```
|
||||||
It moves the exception stack frame pointer from `rsp` to `rdi`, where the first argument is expected, and then calls the main handler. Let's create the corresponding inline assembly to complete our wrapper function:
|
It moves the exception stack frame pointer from `rsp` to `rdi`, where the first argument is expected, and then calls the main handler. Let's create the corresponding inline assembly to complete our wrapper function:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=4 5 6" >}}
|
```rust
|
||||||
#[naked]
|
#[naked]
|
||||||
extern "C" fn divide_by_zero_wrapper() -> ! {
|
extern "C" fn divide_by_zero_wrapper() -> ! {
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -203,7 +208,7 @@ extern "C" fn divide_by_zero_wrapper() -> ! {
|
|||||||
: "rdi" : "intel");
|
: "rdi" : "intel");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{< / highlight >}}<!--end_-->
|
```
|
||||||
|
|
||||||
Instead of `call divide_by_zero_handler`, we use a placeholder again. The reason is Rust's name mangling, which changes the name of the `divide_by_zero_handler` function. To circumvent this, we pass a function pointer as input parameter (after the second colon). The `"i"` tells the compiler that it is an immediate value, which can be directly inserted for the placeholder. We also specify a clobber after the third colon, which tells the compiler that we change the value of the `rdi` register.
|
Instead of `call divide_by_zero_handler`, we use a placeholder again. The reason is Rust's name mangling, which changes the name of the `divide_by_zero_handler` function. To circumvent this, we pass a function pointer as input parameter (after the second colon). The `"i"` tells the compiler that it is an immediate value, which can be directly inserted for the placeholder. We also specify a clobber after the third colon, which tells the compiler that we change the value of the `rdi` register.
|
||||||
|
|
||||||
@@ -219,7 +224,7 @@ error: computation may converge in a function marked as diverging
|
|||||||
```
|
```
|
||||||
The reason is that we marked our `divide_by_zero_wrapper` function as diverging (the `!`). We call another diverging function in inline assembly, so it is clear that the function diverges. However, the Rust compiler doesn't understand inline assembly, so it doesn't know that. To fix this, we tell the compiler that all code after the `asm!` macro is unreachable:
|
The reason is that we marked our `divide_by_zero_wrapper` function as diverging (the `!`). We call another diverging function in inline assembly, so it is clear that the function diverges. However, the Rust compiler doesn't understand inline assembly, so it doesn't know that. To fix this, we tell the compiler that all code after the `asm!` macro is unreachable:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=7" >}}
|
```rust
|
||||||
#[naked]
|
#[naked]
|
||||||
extern "C" fn divide_by_zero_wrapper() -> ! {
|
extern "C" fn divide_by_zero_wrapper() -> ! {
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -229,7 +234,7 @@ extern "C" fn divide_by_zero_wrapper() -> ! {
|
|||||||
::core::intrinsics::unreachable();
|
::core::intrinsics::unreachable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{< / highlight >}}<!--end_-->
|
```
|
||||||
|
|
||||||
The [intrinsics::unreachable] function is unstable, so we need to add `#![feature(core_intrinsics)]` to our `src/lib.rs`. It is just an annotation for the compiler and produces no real code. (Not to be confused with the [unreachable!] macro, which is completely different!)
|
The [intrinsics::unreachable] function is unstable, so we need to add `#![feature(core_intrinsics)]` to our `src/lib.rs`. It is just an annotation for the compiler and produces no real code. (Not to be confused with the [unreachable!] macro, which is completely different!)
|
||||||
|
|
||||||
@@ -239,7 +244,7 @@ The [intrinsics::unreachable] function is unstable, so we need to add `#
|

|
||||||
|
|
||||||
## Testing on real Hardware
|
## Testing on real Hardware
|
||||||
Virtual machines such as QEMU are very convenient to quickly test our kernel. However, they might behave a bit different than real hardware in some situations. So we should test our kernel on real hardware, too.
|
Virtual machines such as QEMU are very convenient to quickly test our kernel. However, they might behave a bit different than real hardware in some situations. So we should test our kernel on real hardware, too.
|
||||||
@@ -270,7 +275,7 @@ Now we should be able to boot from this USB stick. When we do it, we see that it
|
|||||||
|
|
||||||
However, this section wouldn't exist if there weren't a problem. To trigger this problem, we add some example code to the start of our `divide_by_zero_handler`:
|
However, this section wouldn't exist if there weren't a problem. To trigger this problem, we add some example code to the start of our `divide_by_zero_handler`:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=4 5 6" >}}
|
```rust
|
||||||
// in src/interrupts/mod.rs
|
// in src/interrupts/mod.rs
|
||||||
|
|
||||||
extern "C" fn divide_by_zero_handler(...) {
|
extern "C" fn divide_by_zero_handler(...) {
|
||||||
@@ -281,7 +286,7 @@ extern "C" fn divide_by_zero_handler(...) {
|
|||||||
println!(...);
|
println!(...);
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
This is just some garbage code that doesn't do anything useful. When we try it in QEMU using `make run`, it still works fine. However, when we burn it to an USB stick again and boot from it on real hardware, we see that our computer reboots just before printing the exception message.
|
This is just some garbage code that doesn't do anything useful. When we try it in QEMU using `make run`, it still works fine. However, when we burn it to an USB stick again and boot from it on real hardware, we see that our computer reboots just before printing the exception message.
|
||||||
|
|
||||||
@@ -404,7 +409,7 @@ In order to fix this bug, we need to make sure that the stack pointer is correct
|
|||||||
|
|
||||||
The problem is that we're pushing an uneven number of 8 byte registers. Thus we need to align the stack pointer again before the `call` instruction:
|
The problem is that we're pushing an uneven number of 8 byte registers. Thus we need to align the stack pointer again before the `call` instruction:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=5" >}}
|
```rust
|
||||||
#[naked]
|
#[naked]
|
||||||
extern "C" fn divide_by_zero_wrapper() -> ! {
|
extern "C" fn divide_by_zero_wrapper() -> ! {
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -416,7 +421,7 @@ extern "C" fn divide_by_zero_wrapper() -> ! {
|
|||||||
::core::intrinsics::unreachable();
|
::core::intrinsics::unreachable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{< / highlight >}}<!--end_-->
|
```
|
||||||
|
|
||||||
The additional `sub rsp, 8` instruction aligns the stack pointer to a 16 byte boundary. Now it should work on real hardware (and in QEMU KVM mode) again.
|
The additional `sub rsp, 8` instruction aligns the stack pointer to a 16 byte boundary. Now it should work on real hardware (and in QEMU KVM mode) again.
|
||||||
|
|
||||||
@@ -448,7 +453,7 @@ The macro takes a single Rust identifier (`ident`) as argument and expands to a
|
|||||||
|
|
||||||
Now we can remove the `divide_by_zero_wrapper` and use our new `handler!` macro instead:
|
Now we can remove the `divide_by_zero_wrapper` and use our new `handler!` macro instead:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=6" >}}
|
```rust
|
||||||
// in src/interrupts/mod.rs
|
// in src/interrupts/mod.rs
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -458,7 +463,7 @@ lazy_static! {
|
|||||||
idt
|
idt
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
Note that the `handler!` macro needs to be defined above the static `IDT`, because macros are only available after their definition.
|
Note that the `handler!` macro needs to be defined above the static `IDT`, because macros are only available after their definition.
|
||||||
|
|
||||||
@@ -558,7 +563,7 @@ extern "C" fn page_fault_handler(stack_frame: &ExceptionStackFrame,
|
|||||||
|
|
||||||
We need to register our new handler function in the static interrupt descriptor table (IDT):
|
We need to register our new handler function in the static interrupt descriptor table (IDT):
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=10" >}}
|
```rust
|
||||||
// in src/interrupts/mod.rs
|
// in src/interrupts/mod.rs
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -573,7 +578,7 @@ lazy_static! {
|
|||||||
idt
|
idt
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
Page faults have the vector number 14, so we set the 14th IDT entry.
|
Page faults have the vector number 14, so we set the 14th IDT entry.
|
||||||
|
|
||||||
@@ -600,7 +605,7 @@ pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
|||||||
|
|
||||||
We get the following output:
|
We get the following output:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### The Page Fault Error Code
|
### The Page Fault Error Code
|
||||||
“Error code 2” is not really an useful error message. Let's improve this by creating a `PageFaultErrorCode` type:
|
“Error code 2” is not really an useful error message. Let's improve this by creating a `PageFaultErrorCode` type:
|
||||||
@@ -645,7 +650,7 @@ The `from_bits` function tries to convert the `u64` into a `PageFaultErrorCode`.
|
|||||||
|
|
||||||
Now we get a useful error message when a page fault occurs, which allows us to debug it more easily:
|
Now we get a useful error message when a page fault occurs, which allows us to debug it more easily:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
As expected, the page fault was caused by write to `0xdeadbeaf`. The `PROTECTION_VIOLATION` flag is not set, so the accessed page was not present.
|
As expected, the page fault was caused by write to `0xdeadbeaf`. The `PROTECTION_VIOLATION` flag is not set, so the accessed page was not present.
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
@@ -1,21 +1,26 @@
|
|||||||
+++
|
+++
|
||||||
title = "Returning from Exceptions"
|
title = "Returning from Exceptions"
|
||||||
|
order = 3
|
||||||
|
url = "returning-from-exceptions"
|
||||||
date = "2016-09-21"
|
date = "2016-09-21"
|
||||||
|
[extra]
|
||||||
updated = "2016-11-01"
|
updated = "2016-11-01"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In this post, we learn how to return from exceptions correctly. In the course of this, we will explore the `iretq` instruction, the C calling convention, multimedia registers, and the red zone.
|
In this post, we learn how to return from exceptions correctly. In the course of this, we will explore the `iretq` instruction, the C calling convention, multimedia registers, and the red zone.
|
||||||
|
|
||||||
<!--more--><aside id="toc"></aside>
|
<!-- more --><aside id="toc"></aside>
|
||||||
|
|
||||||
As always, the complete source code is on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a [gitter chat] and a [comment section] at the end of this page.
|
As always, the complete source code is on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a [gitter chat] and a comment section at the end of this page.
|
||||||
|
|
||||||
[Github]: https://github.com/phil-opp/blog_os/tree/returning_from_exceptions
|
[Github]: https://github.com/phil-opp/blog_os/tree/returning_from_exceptions
|
||||||
[issues]: https://github.com/phil-opp/blog_os/issues
|
[issues]: https://github.com/phil-opp/blog_os/issues
|
||||||
[gitter chat]: https://gitter.im/phil-opp/blog_os
|
[gitter chat]: https://gitter.im/phil-opp/blog_os
|
||||||
[comment section]: #disqus_thread
|
|
||||||
|
|
||||||
> **Note**: This post describes how to handle exceptions using naked functions (see <a href="/handling-exceptions-with-naked-fns.html">“Handling Exceptions with Naked Functions”</a> for an overview). Our new way of handling exceptions can be found in the <a href="/handling-exceptions.html">“Handling Exceptions”</a> post.
|
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
|
||||||
|
|
||||||
|
[“Handling Exceptions with Naked Functions”]: ./extra/naked-exceptions/_index.md
|
||||||
|
[“Handling Exceptions”]: ./posts/09-handling-exceptions/index.md
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
Most exceptions are fatal and can't be resolved. For example, we can't return from a divide-by-zero exception in a reasonable way. However, there are some exceptions that we can resolve:
|
Most exceptions are fatal and can't be resolved. For example, we can't return from a divide-by-zero exception in a reasonable way. However, there are some exceptions that we can resolve:
|
||||||
@@ -35,7 +40,7 @@ The breakpoint exception is commonly used in debuggers: When the user sets a bre
|
|||||||
|
|
||||||
For our use case, we don't need to overwrite any instructions (it wouldn't even be possible since we [set the page table flags] to read-only). Instead, we just want to print a message when the breakpoint instruction is executed and then continue the program.
|
For our use case, we don't need to overwrite any instructions (it wouldn't even be possible since we [set the page table flags] to read-only). Instead, we just want to print a message when the breakpoint instruction is executed and then continue the program.
|
||||||
|
|
||||||
[set the page table flags]: {{% relref "07-remap-the-kernel.md#using-the-correct-flags" %}}
|
[set the page table flags]: ./posts/07-remap-the-kernel/index.md#using-the-correct-flags
|
||||||
|
|
||||||
### Catching Breakpoints
|
### Catching Breakpoints
|
||||||
Let's start by defining a handler function for the breakpoint exception:
|
Let's start by defining a handler function for the breakpoint exception:
|
||||||
@@ -55,7 +60,7 @@ We print an error message and also output the instruction pointer and the rest o
|
|||||||
|
|
||||||
We need to register our new handler function in the interrupt descriptor table (IDT):
|
We need to register our new handler function in the interrupt descriptor table (IDT):
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=8" >}}
|
```rust
|
||||||
// in src/interrupts/mod.rs
|
// in src/interrupts/mod.rs
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -70,14 +75,14 @@ lazy_static! {
|
|||||||
idt
|
idt
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
We set the IDT entry with number 3 since it's the vector number of the breakpoint exception.
|
We set the IDT entry with number 3 since it's the vector number of the breakpoint exception.
|
||||||
|
|
||||||
#### Testing it
|
#### Testing it
|
||||||
In order to test it, we insert an `int3` instruction in our `rust_main`:
|
In order to test it, we insert an `int3` instruction in our `rust_main`:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=3 12 13" >}}
|
```rust
|
||||||
// in src/lib.rs
|
// in src/lib.rs
|
||||||
...
|
...
|
||||||
#[macro_use] // needed for the `int!` macro
|
#[macro_use] // needed for the `int!` macro
|
||||||
@@ -95,24 +100,24 @@ pub extern "C" fn rust_main(...) {
|
|||||||
println!("It did not crash!");
|
println!("It did not crash!");
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
When we execute `make run`, we see the following:
|
When we execute `make run`, we see the following:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
It works! Now we “just” need to return from the breakpoint handler somehow so that we see the `It did not crash` message again.
|
It works! Now we “just” need to return from the breakpoint handler somehow so that we see the `It did not crash` message again.
|
||||||
|
|
||||||
## Returning from Exceptions
|
## Returning from Exceptions
|
||||||
So how do we return from exceptions? To make it easier, we look at a normal function return first:
|
So how do we return from exceptions? To make it easier, we look at a normal function return first:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
When calling a function, the `call` instruction pushes the return address on the stack. When the called function is finished, it can return to the parent function through the `ret` instruction, which pops the return address from the stack and then jumps to it.
|
When calling a function, the `call` instruction pushes the return address on the stack. When the called function is finished, it can return to the parent function through the `ret` instruction, which pops the return address from the stack and then jumps to it.
|
||||||
|
|
||||||
The exception stack frame, in contrast, looks a bit different:
|
The exception stack frame, in contrast, looks a bit different:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Instead of pushing a return address, the CPU pushes the stack and instruction pointers (with their segment descriptors), the RFLAGS register, and an optional error code. It also aligns the stack pointer to a 16 byte boundary before pushing values.
|
Instead of pushing a return address, the CPU pushes the stack and instruction pointers (with their segment descriptors), the RFLAGS register, and an optional error code. It also aligns the stack pointer to a 16 byte boundary before pushing values.
|
||||||
|
|
||||||
@@ -137,18 +142,18 @@ EXCEPTION: BREAKPOINT at 0x110970
|
|||||||
|
|
||||||
So let's disassemble the instruction at `0x110970` and its predecessor:
|
So let's disassemble the instruction at `0x110970` and its predecessor:
|
||||||
|
|
||||||
{{< highlight shell "hl_lines=3" >}}
|
```shell
|
||||||
> objdump -d build/kernel-x86_64.bin | grep -B1 "110970:"
|
> objdump -d build/kernel-x86_64.bin | grep -B1 "110970:"
|
||||||
11096f: cc int3
|
11096f: cc int3
|
||||||
110970: 48 c7 01 2a 00 00 00 movq $0x2a,(%rcx)
|
110970: 48 c7 01 2a 00 00 00 movq $0x2a,(%rcx)
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
We see that `0x110970` indeed points to the next instruction after `int3`. So we can simply jump to the stored instruction pointer when we want to return from the breakpoint exception.
|
We see that `0x110970` indeed points to the next instruction after `int3`. So we can simply jump to the stored instruction pointer when we want to return from the breakpoint exception.
|
||||||
|
|
||||||
### Implementation
|
### Implementation
|
||||||
Let's update our `handler!` macro to support non-diverging exception handlers:
|
Let's update our `handler!` macro to support non-diverging exception handlers:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=12 16 17 18" >}}
|
```rust
|
||||||
// in src/interrupts/mod.rs
|
// in src/interrupts/mod.rs
|
||||||
|
|
||||||
macro_rules! handler {
|
macro_rules! handler {
|
||||||
@@ -173,7 +178,7 @@ macro_rules! handler {
|
|||||||
wrapper
|
wrapper
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
{{< / highlight >}}<!--end*-->
|
```
|
||||||
|
|
||||||
When an exception handler returns from the `call` instruction, we use the `iretq` instruction to continue the interrupted program. Note that we need to undo the stack pointer alignment before, so that `rsp` points to the end of the exception stack frame again.
|
When an exception handler returns from the `call` instruction, we use the `iretq` instruction to continue the interrupted program. Note that we need to undo the stack pointer alignment before, so that `rsp` points to the end of the exception stack frame again.
|
||||||
|
|
||||||
@@ -202,14 +207,14 @@ Note that we also removed the `loop {}` at the end of our `breakpoint_handler` s
|
|||||||
### Testing
|
### Testing
|
||||||
Let's try our new `iretq` logic:
|
Let's try our new `iretq` logic:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Instead of the expected _“It did not crash”_ message after the breakpoint exception, we get a page fault. The strange thing is that our kernel tried to access address `0x1`, which should never happen. So it seems like we messed up something important.
|
Instead of the expected _“It did not crash”_ message after the breakpoint exception, we get a page fault. The strange thing is that our kernel tried to access address `0x1`, which should never happen. So it seems like we messed up something important.
|
||||||
|
|
||||||
### Debugging
|
### Debugging
|
||||||
Let's debug it using GDB. For that we execute `make debug` in one terminal (which starts QEMU with the `-s -S` flags) and then `make gdb` (which starts and connects GDB) in a second terminal. For more information about GDB debugging, check out our [Set Up GDB] guide.
|
Let's debug it using GDB. For that we execute `make debug` in one terminal (which starts QEMU with the `-s -S` flags) and then `make gdb` (which starts and connects GDB) in a second terminal. For more information about GDB debugging, check out our [Set Up GDB] guide.
|
||||||
|
|
||||||
[Set Up GDB]: {{% relref "set-up-gdb.md" %}}
|
[Set Up GDB]: ./extra/set-up-gdb/index.md
|
||||||
|
|
||||||
First we want to check if our `iretq` was successful. Therefore we set a breakpoint on the `println!("It did not crash line!")` statement in `src/lib.rs`. Let's assume that it's on line 61:
|
First we want to check if our `iretq` was successful. Therefore we set a breakpoint on the `println!("It did not crash line!")` statement in `src/lib.rs`. Let's assume that it's on line 61:
|
||||||
|
|
||||||
@@ -297,7 +302,7 @@ Unfortunately, Rust does not support such a calling convention. It was [proposed
|
|||||||
|
|
||||||
[interrupt calling conventions]: https://github.com/rust-lang/rfcs/pull/1275
|
[interrupt calling conventions]: https://github.com/rust-lang/rfcs/pull/1275
|
||||||
[Naked functions]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
|
[Naked functions]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
|
||||||
[naked fn post]: {{% relref "better-exception-messages.md#naked-functions" %}}
|
[naked fn post]: ./extra/naked-exceptions/02-better-exception-messages/index.md#naked-functions
|
||||||
|
|
||||||
### A naked wrapper function
|
### A naked wrapper function
|
||||||
|
|
||||||
@@ -375,7 +380,7 @@ We need to declare these macros _above_ our `handler` macro, since macros are on
|
|||||||
|
|
||||||
Now we can use these macros to fix our `handler!` macro:
|
Now we can use these macros to fix our `handler!` macro:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=8 10 11 17 19" >}}
|
```rust
|
||||||
// in src/interrupts/mod.rs
|
// in src/interrupts/mod.rs
|
||||||
|
|
||||||
macro_rules! handler {
|
macro_rules! handler {
|
||||||
@@ -403,7 +408,7 @@ macro_rules! handler {
|
|||||||
wrapper
|
wrapper
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
{{< / highlight >}}<!--end*-->
|
```
|
||||||
|
|
||||||
It's important that we save the registers first, before we modify any of them. After the `call` instruction (but before `iretq`) we restore the registers again. Because we're now changing `rsp` (by pushing the register values) before we load it into `rdi`, we would get a wrong exception stack frame pointer. Therefore we need to adjust it by adding the number of bytes we push. We push 9 registers that are 8 bytes each, so `9 * 8` bytes in total.
|
It's important that we save the registers first, before we modify any of them. After the `call` instruction (but before `iretq`) we restore the registers again. Because we're now changing `rsp` (by pushing the register values) before we load it into `rdi`, we would get a wrong exception stack frame pointer. Therefore we need to adjust it by adding the number of bytes we push. We push 9 registers that are 8 bytes each, so `9 * 8` bytes in total.
|
||||||
|
|
||||||
@@ -412,7 +417,7 @@ Note that we no longer need to manually align the stack pointer, because we're p
|
|||||||
### Testing it again
|
### Testing it again
|
||||||
Let's test it again with our corrected `handler!` macro:
|
Let's test it again with our corrected `handler!` macro:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The page fault is gone and we see the _“It did not crash”_ message again!
|
The page fault is gone and we see the _“It did not crash”_ message again!
|
||||||
|
|
||||||
@@ -434,7 +439,7 @@ However, auto-vectorization causes a problem for us: Most of the multimedia regi
|
|||||||
|
|
||||||
We don't use any multimedia registers explicitly, but the Rust compiler might auto-vectorize our code (including the exception handlers). Thus we could silently clobber the multimedia registers, which leads to the same problems as above:
|
We don't use any multimedia registers explicitly, but the Rust compiler might auto-vectorize our code (including the exception handlers). Thus we could silently clobber the multimedia registers, which leads to the same problems as above:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
This example shows a program that is using the first three multimedia registers (`mm0` to `mm2`). At some point, an exception occurs and control is transfered to the exception handler. The exception handler uses `mm1` for its own data and thus overwrites the previous value. When the exception is resolved, the CPU continues the interrupted program again. However, the program is now corrupt since it relies on the original `mm1` value.
|
This example shows a program that is using the first three multimedia registers (`mm0` to `mm2`). At some point, an exception occurs and control is transfered to the exception handler. The exception handler uses `mm1` for its own data and thus overwrites the previous value. When the exception is resolved, the CPU continues the interrupted program again. However, the program is now corrupt since it relies on the original `mm1` value.
|
||||||
|
|
||||||
@@ -516,7 +521,7 @@ The other fields are used for conditional compilation. This allows crate authors
|
|||||||
#### Disabling MMX and SSE
|
#### Disabling MMX and SSE
|
||||||
In order to disable the multimedia extensions, we create a new target named `x86_64-blog_os`. To describe this target, we create a file named `x86_64-blog_os.json` in the project root with the following content:
|
In order to disable the multimedia extensions, we create a new target named `x86_64-blog_os`. To describe this target, we create a file named `x86_64-blog_os.json` in the project root with the following content:
|
||||||
|
|
||||||
{{< highlight json "hl_lines=8" >}}
|
```json
|
||||||
{
|
{
|
||||||
"llvm-target": "x86_64-unknown-linux-gnu",
|
"llvm-target": "x86_64-unknown-linux-gnu",
|
||||||
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
|
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
|
||||||
@@ -526,7 +531,7 @@ In order to disable the multimedia extensions, we create a new target named `x86
|
|||||||
"os": "none",
|
"os": "none",
|
||||||
"features": "-mmx,-sse"
|
"features": "-mmx,-sse"
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
It's equal to `x86_64-unknown-linux-gnu` target but has one additional option: `"features": "-mmx,-sse"`. So we added two target _features_: `-mmx` and `-sse`. The minus prefix defines that our target does _not_ support this feature. So by specifying `-mmx` and `-sse`, we disable the default `mmx` and `sse` features.
|
It's equal to `x86_64-unknown-linux-gnu` target but has one additional option: `"features": "-mmx,-sse"`. So we added two target _features_: `-mmx` and `-sse`. The minus prefix defines that our target does _not_ support this feature. So by specifying `-mmx` and `-sse`, we disable the default `mmx` and `sse` features.
|
||||||
|
|
||||||
@@ -565,7 +570,7 @@ It doesn't compile anymore. The error tells us that the Rust compiler no longer
|
|||||||
The [core library] is implicitly linked to all `no_std` crates and contains things such as `Result`, `Option`, and iterators. We've used that library without problems since [the very beginning], so why is it no longer available?
|
The [core library] is implicitly linked to all `no_std` crates and contains things such as `Result`, `Option`, and iterators. We've used that library without problems since [the very beginning], so why is it no longer available?
|
||||||
|
|
||||||
[core library]: https://doc.rust-lang.org/nightly/core/index.html
|
[core library]: https://doc.rust-lang.org/nightly/core/index.html
|
||||||
[the very beginning]: {{% relref "03-set-up-rust.md" %}}
|
[the very beginning]: ./posts/03-set-up-rust/index.md
|
||||||
|
|
||||||
The problem is that the core library is distributed together with the Rust compiler as a _precompiled_ library. So it is only valid for the host triple, which is `x86_64-unknown-linux-gnu` in our case. If we want to compile code for other targets, we need to recompile `core` for these targets first.
|
The problem is that the core library is distributed together with the Rust compiler as a _precompiled_ library. So it is only valid for the host triple, which is `x86_64-unknown-linux-gnu` in our case. If we want to compile code for other targets, we need to recompile `core` for these targets first.
|
||||||
|
|
||||||
@@ -609,13 +614,13 @@ Remember when we discussed calling conventions above? The calling convention def
|
|||||||
|
|
||||||
In order to fix this problem, we need to change our float ABI. The idea is to avoid normal hardware-supported floats and use a pure software implementation instead. We can do so by enabling the `soft-float` feature for our target. For that, we edit `x86_64-blog_os.json`:
|
In order to fix this problem, we need to change our float ABI. The idea is to avoid normal hardware-supported floats and use a pure software implementation instead. We can do so by enabling the `soft-float` feature for our target. For that, we edit `x86_64-blog_os.json`:
|
||||||
|
|
||||||
{{< highlight json "hl_lines=4" >}}
|
```json
|
||||||
{
|
{
|
||||||
"llvm-target": "x86_64-unknown-linux-gnu",
|
"llvm-target": "x86_64-unknown-linux-gnu",
|
||||||
...
|
...
|
||||||
"features": "-mmx,-sse,+soft-float"
|
"features": "-mmx,-sse,+soft-float"
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
The plus prefix tells LLVM to enable the `soft-float` feature.
|
The plus prefix tells LLVM to enable the `soft-float` feature.
|
||||||
|
|
||||||
@@ -689,7 +694,7 @@ The [red zone] is an optimization of the [System V ABI] that allows functions to
|
|||||||
|
|
||||||
[red zone]: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
|
[red zone]: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The image shows the stack frame of a function with `n` local variables. On function entry, the stack pointer is adjusted to make room on the stack for the local variables.
|
The image shows the stack frame of a function with `n` local variables. On function entry, the stack pointer is adjusted to make room on the stack for the local variables.
|
||||||
|
|
||||||
@@ -697,7 +702,7 @@ The red zone is defined as the 128 bytes below the adjusted stack pointer. The f
|
|||||||
|
|
||||||
However, this optimization leads to huge problems with exceptions. Let's assume that an exception occurs while a function uses the red zone:
|
However, this optimization leads to huge problems with exceptions. Let's assume that an exception occurs while a function uses the red zone:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. It might fail or cause another exception, but it could also lead to strange bugs that [take weeks to debug].
|
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. It might fail or cause another exception, but it could also lead to strange bugs that [take weeks to debug].
|
||||||
|
|
||||||
@@ -722,14 +727,14 @@ _This will not work._ The problem is that the CPU pushes the exception stack fra
|
|||||||
### Disabling the Red Zone
|
### Disabling the Red Zone
|
||||||
The red zone is a property of our target, so in order to disable it we edit our `x86_64-blog_os.json` a last time:
|
The red zone is a property of our target, so in order to disable it we edit our `x86_64-blog_os.json` a last time:
|
||||||
|
|
||||||
{{< highlight json "hl_lines=5" >}}
|
```json
|
||||||
{
|
{
|
||||||
"llvm-target": "x86_64-unknown-linux-gnu",
|
"llvm-target": "x86_64-unknown-linux-gnu",
|
||||||
...
|
...
|
||||||
"features": "-mmx,-sse,+soft-float",
|
"features": "-mmx,-sse,+soft-float",
|
||||||
"disable-redzone": true
|
"disable-redzone": true
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
We add one additional option at the end: `"disable-redzone": true`. As you might guess, this option disables the red zone optimization.
|
We add one additional option at the end: `"disable-redzone": true`. As you might guess, this option disables the red zone optimization.
|
||||||
|
|
||||||
@@ -738,7 +743,7 @@ Now we have a red zone free kernel!
|
|||||||
## Exceptions with Error Codes
|
## Exceptions with Error Codes
|
||||||
We're now able to correctly return from exceptions without error codes. However, we still can't return from exceptions that push an error code (e.g. page faults). Let's fix that by updating our `handler_with_error_code` macro:
|
We're now able to correctly return from exceptions without error codes. However, we still can't return from exceptions that push an error code (e.g. page faults). Let's fix that by updating our `handler_with_error_code` macro:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=13 15" >}}
|
```rust
|
||||||
// in src/interrupts/mod.rs
|
// in src/interrupts/mod.rs
|
||||||
|
|
||||||
macro_rules! handler_with_error_code {
|
macro_rules! handler_with_error_code {
|
||||||
@@ -760,7 +765,7 @@ macro_rules! handler_with_error_code {
|
|||||||
wrapper
|
wrapper
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
{{< / highlight >}}<!--end*-->
|
```
|
||||||
|
|
||||||
First, we change the type of the handler function: no more `-> !`, so it no longer needs to diverge. We also add an `iretq` instruction at the end.
|
First, we change the type of the handler function: no more `-> !`, so it no longer needs to diverge. We also add an `iretq` instruction at the end.
|
||||||
|
|
||||||
@@ -776,7 +781,7 @@ Now we can make our `page_fault_handler` non-diverging:
|
|||||||
|
|
||||||
However, now we have the same problem as above: The handler function will overwrite the scratch registers and cause bugs when returning. Let's fix this by invoking `save_scratch_registers` at the beginning:
|
However, now we have the same problem as above: The handler function will overwrite the scratch registers and cause bugs when returning. Let's fix this by invoking `save_scratch_registers` at the beginning:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=8 11 14 18" >}}
|
```rust
|
||||||
// in src/interrupts/mod.rs
|
// in src/interrupts/mod.rs
|
||||||
|
|
||||||
macro_rules! handler_with_error_code {
|
macro_rules! handler_with_error_code {
|
||||||
@@ -802,14 +807,15 @@ macro_rules! handler_with_error_code {
|
|||||||
wrapper
|
wrapper
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
{{< / highlight >}}<!--end*-->
|
```
|
||||||
|
|
||||||
Now we backup the scratch registers to the stack right at the beginning and restore them just before the `iretq`. Like in the `handler` macro, we now need to add `10*8` to `rdi` in order to get the correct exception stack frame pointer (`save_scratch_registers` pushes nine 8 byte registers, plus the error code). We also need to undo the stack pointer alignment after the `call` [^fn-stack-alignment].
|
Now we backup the scratch registers to the stack right at the beginning and restore them just before the `iretq`. Like in the `handler` macro, we now need to add `10*8` to `rdi` in order to get the correct exception stack frame pointer (`save_scratch_registers` pushes nine 8 byte registers, plus the error code). We also need to undo the stack pointer alignment after the `call` [^fn-stack-alignment].
|
||||||
|
|
||||||
[^fn-stack-alignment]: The stack alignment is actually wrong here, since we additionally pushed an uneven number of registers. However, the `pop rsi` is wrong too, since the error code is no longer at the top of the stack. When we fix that problem, the stack alignment becomes correct again. So I left it in to keep things simple.
|
[^fn-stack-alignment]: The stack alignment is actually wrong here, since we additionally pushed an uneven number of registers. However, the `pop rsi` is wrong too, since the error code is no longer at the top of the stack. When we fix that problem, the stack alignment becomes correct again. So I left it in to keep things simple.
|
||||||
|
|
||||||
Now we have one last bug: We `pop` the error code into `rsi`, but the error code is no longer at the top of the stack (since `save_scratch_registers` pushed 9 registers on top of it). So we need to do it differently:
|
Now we have one last bug: We `pop` the error code into `rsi`, but the error code is no longer at the top of the stack (since `save_scratch_registers` pushed 9 registers on top of it). So we need to do it differently:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=9 19" >}}
|
```rust
|
||||||
// in src/interrupts/mod.rs
|
// in src/interrupts/mod.rs
|
||||||
|
|
||||||
macro_rules! handler_with_error_code {
|
macro_rules! handler_with_error_code {
|
||||||
@@ -836,7 +842,7 @@ macro_rules! handler_with_error_code {
|
|||||||
wrapper
|
wrapper
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
{{< / highlight >}}<!--end*-->
|
```
|
||||||
|
|
||||||
Instead of using `pop`, we're calculating the error code address manually (`save_scratch_registers` pushes nine 8 byte registers) and load it into `rsi` using a `mov`. So now the error code stays on the stack. But `iretq` doesn't handle the error code, so we need to pop it before invoking `iretq`.
|
Instead of using `pop`, we're calculating the error code address manually (`save_scratch_registers` pushes nine 8 byte registers) and load it into `rsi` using a `mov`. So now the error code stays on the stack. But `iretq` doesn't handle the error code, so we need to pop it before invoking `iretq`.
|
||||||
|
|
||||||
@@ -876,7 +882,7 @@ The error code should still be `CAUSED_BY_WRITE` and the exception stack frame v
|
|||||||
#### Returning from Page Faults
|
#### Returning from Page Faults
|
||||||
Let's see what happens if we comment out the trailing `loop` in our page fault handler:
|
Let's see what happens if we comment out the trailing `loop` in our page fault handler:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
We see that the same error message is printed over and over again. Here is what happens:
|
We see that the same error message is printed over and over again. Here is what happens:
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
6
blog/content/extra/naked-exceptions/_index.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
+++
|
||||||
|
title = "Handling Exceptions using naked Functions"
|
||||||
|
sort_by = "order"
|
||||||
|
template = "handling-exceptions-with-naked-fns.html"
|
||||||
|
insert_anchor = "left"
|
||||||
|
+++
|
||||||
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
@@ -1,5 +1,8 @@
|
|||||||
+++
|
+++
|
||||||
title = "Set Up GDB"
|
title = "Set Up GDB"
|
||||||
|
template = "plain.html"
|
||||||
|
url = "set-up-gdb"
|
||||||
|
order = 4
|
||||||
+++
|
+++
|
||||||
|
|
||||||
There are a lot of things that can go wrong when developing an OS. So it's a good idea to add a debugger to our toolset, which allows us to set breakpoints and examine variables. We will use [GDB](https://www.gnu.org/software/gdb/) as QEMU supports it out of the box.
|
There are a lot of things that can go wrong when developing an OS. So it's a good idea to add a debugger to our toolset, which allows us to set breakpoints and examine variables. We will use [GDB](https://www.gnu.org/software/gdb/) as QEMU supports it out of the box.
|
||||||
@@ -66,7 +69,7 @@ After connecting to QEMU, you can use various gdb commands to control execution
|
|||||||
- `print` or `p`: Prints the value of a variable. You can use Cs `*` and `&` operators. To print in hexadecimal, use `p/x`.
|
- `print` or `p`: Prints the value of a variable. You can use Cs `*` and `&` operators. To print in hexadecimal, use `p/x`.
|
||||||
- `tui enable`: Enables the text user interface, which provides a graphical interface (see below). To disable it again, run `tui disable`.
|
- `tui enable`: Enables the text user interface, which provides a graphical interface (see below). To disable it again, run `tui disable`.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Of course there are many more commands. Feel free to send a PR if you think this list is missing something important. For a more complete GDB overview, check out [Beej's Quick Guide][bggdb] or the [website for Harvard's CS161 course][CS161].
|
Of course there are many more commands. Feel free to send a PR if you think this list is missing something important. For a more complete GDB overview, check out [Beej's Quick Guide][bggdb] or the [website for Harvard's CS161 course][CS161].
|
||||||
|
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
+++
|
+++
|
||||||
title = "Talks"
|
title = "Talks"
|
||||||
|
url = "talks"
|
||||||
|
template = "plain.html"
|
||||||
|
order = 1
|
||||||
+++
|
+++
|
||||||
|
|
||||||
- “Open Source OS Development in Rust” at HTWG Konstanz, May 22, 2017: [slides](https://phil-opp.github.io/talk-konstanz-may-2017/)
|
- “Open Source OS Development in Rust” at HTWG Konstanz, May 22, 2017: [slides](https://phil-opp.github.io/talk-konstanz-may-2017/)
|
||||||
13
blog/content/pages/404.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
+++
|
||||||
|
title = "Page not found"
|
||||||
|
url = "404.html"
|
||||||
|
template = "plain.html"
|
||||||
|
+++
|
||||||
|
|
||||||
|
Sorry, we've misplaced that URL or it's pointing to something that doesn't exist.
|
||||||
|
|
||||||
|
[Head back home](/) to try finding it again.
|
||||||
|
|
||||||
|
If you followed a link on this site, please [report it]!
|
||||||
|
|
||||||
|
[report it]: https://github.com/phil-opp/blog_os/issues
|
||||||
4
blog/content/pages/_index.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
+++
|
||||||
|
title = "Pages"
|
||||||
|
render = false
|
||||||
|
+++
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
+++
|
+++
|
||||||
title = "Contact"
|
title = "Contact"
|
||||||
|
url = "contact"
|
||||||
|
template = "plain.html"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
Philipp Oppermann
|
Philipp Oppermann
|
||||||
@@ -1,18 +1,15 @@
|
|||||||
+++
|
+++
|
||||||
title = "A minimal x86 kernel"
|
title = "A minimal x86 kernel"
|
||||||
slug = "multiboot-kernel"
|
order = 1
|
||||||
|
url = "multiboot-kernel/"
|
||||||
date = "2015-08-18"
|
date = "2015-08-18"
|
||||||
aliases = [
|
|
||||||
"/2015/08/18/multiboot-kernel/",
|
|
||||||
"/rust-os/multiboot-kernel.html",
|
|
||||||
]
|
|
||||||
+++
|
+++
|
||||||
|
|
||||||
This post explains how to create a minimal x86 operating system kernel. In fact, it will just boot and print `OK` to the screen. The following blog posts we will extend it using the [Rust] programming language.
|
This post explains how to create a minimal x86 operating system kernel. In fact, it will just boot and print `OK` to the screen. The following blog posts we will extend it using the [Rust] programming language.
|
||||||
|
|
||||||
[Rust]: http://www.rust-lang.org/
|
[Rust]: http://www.rust-lang.org/
|
||||||
|
|
||||||
<!--more--><aside id="toc"></aside>
|
<!-- more --><aside id="toc"></aside>
|
||||||
|
|
||||||
I tried to explain everything in detail and to keep the code as simple as possible. If you have any questions, suggestions or other issues, please leave a comment or [create an issue] on Github. The source code is available in a [repository][source code], too.
|
I tried to explain everything in detail and to keep the code as simple as possible. If you have any questions, suggestions or other issues, please leave a comment or [create an issue] on Github. The source code is available in a [repository][source code], too.
|
||||||
|
|
||||||
@@ -80,8 +77,6 @@ If you don't know x86 assembly, here is some quick guide:
|
|||||||
- `dd` stands for `define double` (32bit) and `dw` stands for `define word` (16bit). They just output the specified 32bit/16bit constant.
|
- `dd` stands for `define double` (32bit) and `dw` stands for `define word` (16bit). They just output the specified 32bit/16bit constant.
|
||||||
- the additional `0x100000000` in the checksum calculation is a small hack[^fn-checksum_hack] to avoid a compiler warning
|
- the additional `0x100000000` in the checksum calculation is a small hack[^fn-checksum_hack] to avoid a compiler warning
|
||||||
|
|
||||||
[^fn-checksum_hack]: The formula from the table, `-(magic + architecture + header_length)`, creates a negative value that doesn't fit into 32bit. By subtracting from `0x100000000` (= 2^(32)) instead, we keep the value positive without changing its truncated value. Without the additional sign bit(s) the result fits into 32bit and the compiler is happy :).
|
|
||||||
|
|
||||||
We can already _assemble_ this file (which I called `multiboot_header.asm`) using `nasm`. It produces a flat binary by default, so the resulting file just contains our 24 bytes (in little endian if you work on a x86 machine):
|
We can already _assemble_ this file (which I called `multiboot_header.asm`) using `nasm`. It produces a flat binary by default, so the resulting file just contains our 24 bytes (in little endian if you work on a x86 machine):
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -140,7 +135,7 @@ To create the ELF _executable_, we need to [link] the object files together. We
|
|||||||
[link]: https://en.wikipedia.org/wiki/Linker_(computing)
|
[link]: https://en.wikipedia.org/wiki/Linker_(computing)
|
||||||
[linker script]: https://sourceware.org/binutils/docs/ld/Scripts.html
|
[linker script]: https://sourceware.org/binutils/docs/ld/Scripts.html
|
||||||
|
|
||||||
```
|
```ld
|
||||||
ENTRY(start)
|
ENTRY(start)
|
||||||
|
|
||||||
SECTIONS {
|
SECTIONS {
|
||||||
@@ -166,8 +161,6 @@ Let's translate it:
|
|||||||
- the `.text` output section contains all input sections named `.text`
|
- the `.text` output section contains all input sections named `.text`
|
||||||
- Sections named `.multiboot_header` are added to the first output section (`.boot`) to ensure they are at the beginning of the executable. This is necessary because GRUB expects to find the Multiboot header very early in the file.
|
- Sections named `.multiboot_header` are added to the first output section (`.boot`) to ensure they are at the beginning of the executable. This is necessary because GRUB expects to find the Multiboot header very early in the file.
|
||||||
|
|
||||||
[^Linker 1M]: We don't want to load the kernel to e.g. `0x0` because there are many special memory areas below the 1MB mark (for example the so-called VGA buffer at `0xb8000`, that we use to print `OK` to the screen).
|
|
||||||
|
|
||||||
So let's create the ELF object files and link them using our new linker script:
|
So let's create the ELF object files and link them using our new linker script:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -191,7 +184,7 @@ Idx Name Size VMA LMA File off Algn
|
|||||||
CONTENTS, ALLOC, LOAD, READONLY, CODE
|
CONTENTS, ALLOC, LOAD, READONLY, CODE
|
||||||
```
|
```
|
||||||
_Note_: The `ld` and `objdump` commands are platform specific. If you're _not_ working on x86_64 architecture, you will need to [cross compile binutils]. Then use `x86_64‑elf‑ld` and `x86_64‑elf‑objdump` instead of `ld` and `objdump`.
|
_Note_: The `ld` and `objdump` commands are platform specific. If you're _not_ working on x86_64 architecture, you will need to [cross compile binutils]. Then use `x86_64‑elf‑ld` and `x86_64‑elf‑objdump` instead of `ld` and `objdump`.
|
||||||
[cross compile binutils]: {{% relref "cross-compile-binutils.md" %}}
|
[cross compile binutils]: ./extra/cross-compile-binutils.md
|
||||||
|
|
||||||
## Creating the ISO
|
## Creating the ISO
|
||||||
The last step is to create a bootable ISO image with GRUB. We need to create the following directory structure and copy the `kernel.bin` to the right place:
|
The last step is to create a bootable ISO image with GRUB. We need to create the following directory structure and copy the `kernel.bin` to the right place:
|
||||||
@@ -235,7 +228,7 @@ Now it's time to boot our OS. We will use [QEMU]:
|
|||||||
```
|
```
|
||||||
qemu-system-x86_64 -cdrom os.iso
|
qemu-system-x86_64 -cdrom os.iso
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
Notice the green `OK` in the upper left corner. If it does not work for you, take a look at the comment section.
|
Notice the green `OK` in the upper left corner. If it does not work for you, take a look at the comment section.
|
||||||
|
|
||||||
@@ -321,5 +314,10 @@ Now we can invoke `make` and all updated assembly files are compiled and linked.
|
|||||||
|
|
||||||
In the [next post] we will create a page table and do some CPU configuration to switch to the 64-bit [long mode].
|
In the [next post] we will create a page table and do some CPU configuration to switch to the 64-bit [long mode].
|
||||||
|
|
||||||
[next post]: {{% relref "02-entering-longmode.md" %}}
|
[next post]: ./posts/02-entering-longmode/index.md
|
||||||
[long mode]: https://en.wikipedia.org/wiki/Long_mode
|
[long mode]: https://en.wikipedia.org/wiki/Long_mode
|
||||||
|
|
||||||
|
## Footnotes
|
||||||
|
[^fn-checksum_hack]: The formula from the table, `-(magic + architecture + header_length)`, creates a negative value that doesn't fit into 32bit. By subtracting from `0x100000000` (= 2^(32)) instead, we keep the value positive without changing its truncated value. Without the additional sign bit(s) the result fits into 32bit and the compiler is happy :).
|
||||||
|
|
||||||
|
[^Linker 1M]: We don't want to load the kernel to e.g. `0x0` because there are many special memory areas below the 1MB mark (for example the so-called VGA buffer at `0xb8000`, that we use to print `OK` to the screen).
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
@@ -1,22 +1,20 @@
|
|||||||
+++
|
+++
|
||||||
title = "Entering Long Mode"
|
title = "Entering Long Mode"
|
||||||
slug = "entering-longmode"
|
order = 2
|
||||||
|
url = "entering-longmode"
|
||||||
date = "2015-08-25"
|
date = "2015-08-25"
|
||||||
|
[extra]
|
||||||
updated = "2015-10-29"
|
updated = "2015-10-29"
|
||||||
aliases = [
|
|
||||||
"/2015/08/25/entering-longmode/",
|
|
||||||
"/rust-os/entering-longmode.html",
|
|
||||||
]
|
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In the [previous post] we created a minimal multiboot kernel. It just prints `OK` and hangs. The goal is to extend it and call 64-bit [Rust] code. But the CPU is currently in [protected mode] and allows only 32-bit instructions and up to 4GiB memory. So we need to set up _Paging_ and switch to the 64-bit [long mode] first.
|
In the [previous post] we created a minimal multiboot kernel. It just prints `OK` and hangs. The goal is to extend it and call 64-bit [Rust] code. But the CPU is currently in [protected mode] and allows only 32-bit instructions and up to 4GiB memory. So we need to set up _Paging_ and switch to the 64-bit [long mode] first.
|
||||||
|
|
||||||
[previous post]: {{% relref "01-multiboot-kernel.md" %}}
|
[previous post]: ./posts/01-multiboot-kernel/index.md
|
||||||
[Rust]: http://www.rust-lang.org/
|
[Rust]: http://www.rust-lang.org/
|
||||||
[protected mode]: https://en.wikipedia.org/wiki/Protected_mode
|
[protected mode]: https://en.wikipedia.org/wiki/Protected_mode
|
||||||
[long mode]: https://en.wikipedia.org/wiki/Long_mode
|
[long mode]: https://en.wikipedia.org/wiki/Long_mode
|
||||||
|
|
||||||
<!--more--><aside id="toc"></aside>
|
<!-- more --><aside id="toc"></aside>
|
||||||
|
|
||||||
I tried to explain everything in detail and to keep the code as simple as possible. If you have any questions, suggestions, or issues, please leave a comment or [create an issue] on Github. The source code is available in a [repository][source code], too.
|
I tried to explain everything in detail and to keep the code as simple as possible. If you have any questions, suggestions, or issues, please leave a comment or [create an issue] on Github. The source code is available in a [repository][source code], too.
|
||||||
|
|
||||||
@@ -39,7 +37,7 @@ error:
|
|||||||
At address `0xb8000` begins the so-called [VGA text buffer]. It's an array of screen characters that are displayed by the graphics card. A [future post] will cover the VGA buffer in detail and create a Rust interface to it. But for now, manual bit-fiddling is the easiest option.
|
At address `0xb8000` begins the so-called [VGA text buffer]. It's an array of screen characters that are displayed by the graphics card. A [future post] will cover the VGA buffer in detail and create a Rust interface to it. But for now, manual bit-fiddling is the easiest option.
|
||||||
|
|
||||||
[VGA text buffer]: https://en.wikipedia.org/wiki/VGA-compatible_text_mode
|
[VGA text buffer]: https://en.wikipedia.org/wiki/VGA-compatible_text_mode
|
||||||
[future post]: {{% relref "04-printing-to-screen.md" %}}
|
[future post]: ./posts/04-printing-to-screen/index.md
|
||||||
|
|
||||||
A screen character consists of a 8 bit color code and a 8 bit [ASCII] character. We used the color code `4f` for all characters, which means white text on red background. `0x52` is an ASCII `R`, `0x45` is an `E`, `0x3a` is a `:`, and `0x20` is a space. The second space is overwritten by the given ASCII byte. Finally the CPU is stopped with the `hlt` instruction.
|
A screen character consists of a 8 bit color code and a 8 bit [ASCII] character. We used the color code `4f` for all characters, which means white text on red background. `0x52` is an ASCII `R`, `0x45` is an `E`, `0x3a` is a `:`, and `0x20` is a space. The second space is overwritten by the given ASCII byte. Finally the CPU is stopped with the `hlt` instruction.
|
||||||
|
|
||||||
@@ -214,11 +212,7 @@ As I don't like these names, I will call them P4, P3, P2, and P1 from now on.
|
|||||||
|
|
||||||
Each page table contains 512 entries and one entry is 8 bytes, so they fit exactly in one page (`512*8 = 4096`). To translate a virtual address to a physical address the CPU[^hardware_lookup] will do the following[^virtual_physical_translation_source]:
|
Each page table contains 512 entries and one entry is 8 bytes, so they fit exactly in one page (`512*8 = 4096`). To translate a virtual address to a physical address the CPU[^hardware_lookup] will do the following[^virtual_physical_translation_source]:
|
||||||
|
|
||||||
[^hardware_lookup]: In the x86 architecture, the page tables are _hardware walked_, so the CPU will look at the table on its own when it needs a translation. Other architectures, for example MIPS, just throw an exception and let the OS translate the virtual address.
|

|
||||||
|
|
||||||
[^virtual_physical_translation_source]: Image source: [Wikipedia](https://commons.wikimedia.org/wiki/File:X86_Paging_64bit.svg), with modified font size, page table naming, and removed sign extended bits. The modified file is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
1. Get the address of the P4 table from the CR3 register
|
1. Get the address of the P4 table from the CR3 register
|
||||||
2. Use bits 39-47 (9 bits) as an index into P4 (`2^9 = 512 = number of entries`)
|
2. Use bits 39-47 (9 bits) as an index into P4 (`2^9 = 512 = number of entries`)
|
||||||
@@ -258,8 +252,6 @@ To identity map the first gigabyte of our kernel with 512 2MiB pages, we need on
|
|||||||
|
|
||||||
We can add these two tables at the beginning[^page_table_alignment] of the `.bss` section:
|
We can add these two tables at the beginning[^page_table_alignment] of the `.bss` section:
|
||||||
|
|
||||||
[^page_table_alignment]: Page tables need to be page-aligned as the bits 0-11 are used for flags. By putting these tables at the beginning of `.bss`, the linker can just page align the whole section and we don't have unused padding bytes in between.
|
|
||||||
|
|
||||||
```nasm
|
```nasm
|
||||||
...
|
...
|
||||||
|
|
||||||
@@ -499,8 +491,8 @@ _Congratulations_! You have successfully wrestled through this CPU configuration
|
|||||||
#### One Last Thing
|
#### One Last Thing
|
||||||
Above, we reloaded the code segment register `cs` with the new GDT offset. However, the data segment registers `ss`, `ds`, `es`, `fs`, and `gs` still contain the data segment offsets of the old GDT. This isn't necessarily bad, since they're ignored by almost all instructions in 64-bit mode. However, there are a few instructions that expect a valid data segment descriptor _or the null descriptor_ in those registers. An example is the the [iretq] instruction that we'll need in the [_Returning from Exceptions_] post.
|
Above, we reloaded the code segment register `cs` with the new GDT offset. However, the data segment registers `ss`, `ds`, `es`, `fs`, and `gs` still contain the data segment offsets of the old GDT. This isn't necessarily bad, since they're ignored by almost all instructions in 64-bit mode. However, there are a few instructions that expect a valid data segment descriptor _or the null descriptor_ in those registers. An example is the the [iretq] instruction that we'll need in the [_Returning from Exceptions_] post.
|
||||||
|
|
||||||
[iretq]: {{% relref "returning-from-exceptions.md#the-iretq-instruction" %}}
|
[iretq]: ./extra/naked-exceptions/03-returning-from-exceptions/index.md#the
|
||||||
[_Returning from Exceptions_]: {{% relref "returning-from-exceptions.md" %}}
|
[_Returning from Exceptions_]: ./extra/naked-exceptions/03-returning-from-exceptions/index.md
|
||||||
|
|
||||||
To avoid future problems, we reload all data segment registers with null:
|
To avoid future problems, we reload all data segment registers with null:
|
||||||
|
|
||||||
@@ -522,4 +514,11 @@ long_mode_start:
|
|||||||
It's time to finally leave assembly behind and switch to [Rust]. Rust is a systems language without garbage collections that guarantees memory safety. Through a real type system and many abstractions it feels like a high-level language but can still be low-level enough for OS development. The [next post] describes the Rust setup.
|
It's time to finally leave assembly behind and switch to [Rust]. Rust is a systems language without garbage collections that guarantees memory safety. Through a real type system and many abstractions it feels like a high-level language but can still be low-level enough for OS development. The [next post] describes the Rust setup.
|
||||||
|
|
||||||
[Rust]: https://www.rust-lang.org/
|
[Rust]: https://www.rust-lang.org/
|
||||||
[next post]: {{% relref "03-set-up-rust.md" %}}
|
[next post]: ./posts/03-set-up-rust/index.md
|
||||||
|
|
||||||
|
## Footnotes
|
||||||
|
[^hardware_lookup]: In the x86 architecture, the page tables are _hardware walked_, so the CPU will look at the table on its own when it needs a translation. Other architectures, for example MIPS, just throw an exception and let the OS translate the virtual address.
|
||||||
|
|
||||||
|
[^virtual_physical_translation_source]: Image source: [Wikipedia](https://commons.wikimedia.org/wiki/File:X86_Paging_64bit.svg), with modified font size, page table naming, and removed sign extended bits. The modified file is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
|
||||||
|
|
||||||
|
[^page_table_alignment]: Page tables need to be page-aligned as the bits 0-11 are used for flags. By putting these tables at the beginning of `.bss`, the linker can just page align the whole section and we don't have unused padding bytes in between.
|
||||||
@@ -1,21 +1,19 @@
|
|||||||
+++
|
+++
|
||||||
title = "Set Up Rust"
|
title = "Set Up Rust"
|
||||||
|
order = 3
|
||||||
|
url = "set-up-rust"
|
||||||
date = "2015-09-02"
|
date = "2015-09-02"
|
||||||
|
[extra]
|
||||||
updated = "2017-04-12"
|
updated = "2017-04-12"
|
||||||
aliases = [
|
|
||||||
"/2015/09/02/setup-rust/",
|
|
||||||
"/setup-rust.html",
|
|
||||||
"/rust-os/setup-rust.html",
|
|
||||||
]
|
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In the previous posts we created a [minimal Multiboot kernel][multiboot post] and [switched to Long Mode][long mode post]. Now we can finally switch to [Rust] code. Rust is a high-level language without runtime. It allows us to not link the standard library and write bare metal code. Unfortunately the setup is not quite hassle-free yet.
|
In the previous posts we created a [minimal Multiboot kernel][multiboot post] and [switched to Long Mode][long mode post]. Now we can finally switch to [Rust] code. Rust is a high-level language without runtime. It allows us to not link the standard library and write bare metal code. Unfortunately the setup is not quite hassle-free yet.
|
||||||
|
|
||||||
[multiboot post]: {{% relref "01-multiboot-kernel.md" %}}
|
[multiboot post]: ./posts/01-multiboot-kernel/index.md
|
||||||
[long mode post]: {{% relref "02-entering-longmode.md" %}}
|
[long mode post]: ./posts/02-entering-longmode/index.md
|
||||||
[Rust]: https://www.rust-lang.org/
|
[Rust]: https://www.rust-lang.org/
|
||||||
|
|
||||||
<!--more--><aside id="toc"></aside>
|
<!-- more --><aside id="toc"></aside>
|
||||||
|
|
||||||
This blog post tries to set up Rust step-by-step and point out the different problems. If you have any questions, problems, or suggestions please [file an issue] or create a comment at the bottom. The code from this post is in a [Github repository], too.
|
This blog post tries to set up Rust step-by-step and point out the different problems. If you have any questions, problems, or suggestions please [file an issue] or create a comment at the bottom. The code from this post is in a [Github repository], too.
|
||||||
|
|
||||||
@@ -88,7 +86,7 @@ Let's define some properties of our target system:
|
|||||||
- **No SSE**: Our target might not have [SSE] support. Even if it does, we probably don't want to use SSE instructions in our kernel, because it makes interrupt handling much slower. We will explain this in detail in the [“Handling Exceptions”] post.
|
- **No SSE**: Our target might not have [SSE] support. Even if it does, we probably don't want to use SSE instructions in our kernel, because it makes interrupt handling much slower. We will explain this in detail in the [“Handling Exceptions”] post.
|
||||||
- **No hardware floats**: The `x86_64` architecture uses SSE instructions for floating point operations, which we don't want to use (see the previous point). So we also need to avoid hardware floating point operations in our kernel. Instead, we will use _soft floats_, which are basically software functions that emulate floating point operations using normal integers.
|
- **No hardware floats**: The `x86_64` architecture uses SSE instructions for floating point operations, which we don't want to use (see the previous point). So we also need to avoid hardware floating point operations in our kernel. Instead, we will use _soft floats_, which are basically software functions that emulate floating point operations using normal integers.
|
||||||
|
|
||||||
[“Handling Exceptions”]: {{% relref "09-handling-exceptions.md" %}}
|
[“Handling Exceptions”]: ./posts/09-handling-exceptions/index.md
|
||||||
|
|
||||||
### Target Specifications
|
### Target Specifications
|
||||||
Rust allows us to define [custom targets] through a JSON configuration file. A minimal target specification equal to `x86_64-unknown-linux-gnu` (the default 64-bit Linux target) looks like this:
|
Rust allows us to define [custom targets] through a JSON configuration file. A minimal target specification equal to `x86_64-unknown-linux-gnu` (the default 64-bit Linux target) looks like this:
|
||||||
@@ -149,7 +147,7 @@ The [red zone] is an optimization of the [System V ABI] that allows functions to
|
|||||||
[red zone]: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
|
[red zone]: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
|
||||||
[System V ABI]: http://wiki.osdev.org/System_V_ABI
|
[System V ABI]: http://wiki.osdev.org/System_V_ABI
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The image shows the stack frame of a function with `n` local variables. On function entry, the stack pointer is adjusted to make room on the stack for the local variables.
|
The image shows the stack frame of a function with `n` local variables. On function entry, the stack pointer is adjusted to make room on the stack for the local variables.
|
||||||
|
|
||||||
@@ -157,7 +155,7 @@ The red zone is defined as the 128 bytes below the adjusted stack pointer. The f
|
|||||||
|
|
||||||
However, this optimization leads to huge problems with exceptions or hardware interrupts. Let's assume that an exception occurs while a function uses the red zone:
|
However, this optimization leads to huge problems with exceptions or hardware interrupts. Let's assume that an exception occurs while a function uses the red zone:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. This might lead to strange bugs that [take weeks to debug].
|
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. This might lead to strange bugs that [take weeks to debug].
|
||||||
|
|
||||||
@@ -478,10 +476,10 @@ Some notes:
|
|||||||
### Stack Overflows
|
### Stack Overflows
|
||||||
Since we still use the small 64 byte [stack from the last post], we must be careful not to [overflow] it. Normally, Rust tries to avoid stack overflows through _guard pages_: The page below the stack isn't mapped and such a stack overflow triggers a page fault (instead of silently overwriting random memory). But we can't unmap the page below our stack right now since we currently use only a single big page. Fortunately the stack is located just above the page tables. So some important page table entry would probably get overwritten on stack overflow and then a page fault occurs, too.
|
Since we still use the small 64 byte [stack from the last post], we must be careful not to [overflow] it. Normally, Rust tries to avoid stack overflows through _guard pages_: The page below the stack isn't mapped and such a stack overflow triggers a page fault (instead of silently overwriting random memory). But we can't unmap the page below our stack right now since we currently use only a single big page. Fortunately the stack is located just above the page tables. So some important page table entry would probably get overwritten on stack overflow and then a page fault occurs, too.
|
||||||
|
|
||||||
[stack from the last post]: {{% relref "02-entering-longmode.md#creating-a-stack" %}}
|
[stack from the last post]: ./posts/02-entering-longmode/index.md#creating-a-stack
|
||||||
[overflow]: https://en.wikipedia.org/wiki/Stack_overflow
|
[overflow]: https://en.wikipedia.org/wiki/Stack_overflow
|
||||||
|
|
||||||
## What's next?
|
## What's next?
|
||||||
Until now we write magic bits to some memory location when we want to print something to screen. In the [next post] we create a abstraction for the VGA text buffer that allows us to print strings in different colors and provides a simple interface.
|
Until now we write magic bits to some memory location when we want to print something to screen. In the [next post] we create a abstraction for the VGA text buffer that allows us to print strings in different colors and provides a simple interface.
|
||||||
|
|
||||||
[next post]: {{% relref "04-printing-to-screen.md" %}}
|
[next post]: ./posts/04-printing-to-screen/index.md
|
||||||
92
blog/content/posts/03-set-up-rust/red-zone-overwrite.svg
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
|
||||||
|
<svg width="17cm" height="11cm" viewBox="-60 -21 340 212" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<text font-size="9.03111" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="90" y="65.9389">
|
||||||
|
<tspan x="90" y="65.9389"></tspan>
|
||||||
|
</text>
|
||||||
|
<g>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="204" y1="70" x2="176.236" y2="70"/>
|
||||||
|
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="185.118,65 175.118,70 185.118,75 "/>
|
||||||
|
</g>
|
||||||
|
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="208" y="72.75">
|
||||||
|
<tspan x="208" y="72.75">Old Stack Pointer</tspan>
|
||||||
|
</text>
|
||||||
|
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="75" y="230">
|
||||||
|
<tspan x="75" y="230"></tspan>
|
||||||
|
</text>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="70" x2="-4" y2="70"/>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="190" x2="-4" y2="190"/>
|
||||||
|
<g>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="-12" y1="72.2361" x2="-12" y2="187.764"/>
|
||||||
|
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-7,81.118 -12,71.118 -17,81.118 "/>
|
||||||
|
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-17,178.882 -12,188.882 -7,178.882 "/>
|
||||||
|
</g>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="0" y1="-20" x2="0" y2="0"/>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="170" y1="-20" x2="170" y2="0"/>
|
||||||
|
<g>
|
||||||
|
<rect style="fill: #00ff00" x="0" y="0" width="170" height="20"/>
|
||||||
|
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="0" width="170" height="20"/>
|
||||||
|
</g>
|
||||||
|
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="13.125">
|
||||||
|
<tspan x="85" y="13.125">Return Address</tspan>
|
||||||
|
</text>
|
||||||
|
<text font-size="9.03097" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="84" y="4">
|
||||||
|
<tspan x="84" y="4"></tspan>
|
||||||
|
</text>
|
||||||
|
<g>
|
||||||
|
<rect style="fill: #dddddd" x="0" y="20" width="170" height="20"/>
|
||||||
|
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="20" width="170" height="20"/>
|
||||||
|
</g>
|
||||||
|
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="33.125">
|
||||||
|
<tspan x="85" y="33.125">Local Variable 1</tspan>
|
||||||
|
</text>
|
||||||
|
<g>
|
||||||
|
<rect style="fill: #cccccc" x="0" y="40" width="170" height="32"/>
|
||||||
|
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x="0" y="40" width="170" height="32"/>
|
||||||
|
</g>
|
||||||
|
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="85" y="59.125">
|
||||||
|
<tspan x="85" y="59.125">Local Variables 2..n</tspan>
|
||||||
|
</text>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="40" x2="170" y2="40"/>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="72" x2="120" y2="72"/>
|
||||||
|
<g>
|
||||||
|
<rect style="fill: #ff3333" x="0" y="70" width="170" height="120"/>
|
||||||
|
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="70" width="170" height="120"/>
|
||||||
|
</g>
|
||||||
|
<text font-size="9.03111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="40" y="179.125">
|
||||||
|
<tspan x="40" y="179.125">Red Zone</tspan>
|
||||||
|
</text>
|
||||||
|
<text font-size="7.9021" style="fill: #000000;text-anchor:end;font-family:sans-serif;font-style:normal;font-weight:normal" x="-20" y="132.75">
|
||||||
|
<tspan x="-20" y="132.75">128 bytes</tspan>
|
||||||
|
</text>
|
||||||
|
<g>
|
||||||
|
<rect style="fill: #ffc200" x="30" y="70" width="140" height="30"/>
|
||||||
|
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="70" width="140" height="30"/>
|
||||||
|
</g>
|
||||||
|
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="88.125">
|
||||||
|
<tspan x="100" y="88.125">Exception Stack Frame</tspan>
|
||||||
|
</text>
|
||||||
|
<g>
|
||||||
|
<rect style="fill: #ffe000" x="30" y="100" width="140" height="30"/>
|
||||||
|
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="100" width="140" height="30"/>
|
||||||
|
</g>
|
||||||
|
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="118.125">
|
||||||
|
<tspan x="100" y="118.125">Register Backup</tspan>
|
||||||
|
</text>
|
||||||
|
<g>
|
||||||
|
<rect style="fill: #c6db97" x="30" y="130" width="140" height="30"/>
|
||||||
|
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="30" y="130" width="140" height="30"/>
|
||||||
|
</g>
|
||||||
|
<text font-size="8.46654" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="100" y="147.925">
|
||||||
|
<tspan x="100" y="147.925">Handler Function Stack Frame</tspan>
|
||||||
|
</text>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #ff3333" x1="30" y1="70" x2="30" y2="160"/>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #ff3333" x1="30" y1="160" x2="170" y2="160"/>
|
||||||
|
<g>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="204" y1="160" x2="176.236" y2="160"/>
|
||||||
|
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="185.118,155 175.118,160 185.118,165 "/>
|
||||||
|
</g>
|
||||||
|
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="208" y="162.635">
|
||||||
|
<tspan x="208" y="162.635">New Stack Pointer</tspan>
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.2 KiB |
62
blog/content/posts/03-set-up-rust/red-zone.svg
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
|
||||||
|
<svg width="14cm" height="9cm" viewBox="-60 -21 270 172" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<text font-size="9.03111" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="90" y="65.9389">
|
||||||
|
<tspan x="90" y="65.9389"></tspan>
|
||||||
|
</text>
|
||||||
|
<g>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="154" y1="70" x2="126.236" y2="70"/>
|
||||||
|
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="135.118,65 125.118,70 135.118,75 "/>
|
||||||
|
</g>
|
||||||
|
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="158" y="72.75">
|
||||||
|
<tspan x="158" y="72.75">Stack Pointer</tspan>
|
||||||
|
</text>
|
||||||
|
<text font-size="7.90222" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="75" y="230">
|
||||||
|
<tspan x="75" y="230"></tspan>
|
||||||
|
</text>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="70" x2="-4" y2="70"/>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="-20" y1="150" x2="-4" y2="150"/>
|
||||||
|
<g>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="-12" y1="72.2361" x2="-12" y2="147.764"/>
|
||||||
|
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-7,81.118 -12,71.118 -17,81.118 "/>
|
||||||
|
<polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="-17,138.882 -12,148.882 -7,138.882 "/>
|
||||||
|
</g>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="0" y1="-20" x2="0" y2="0"/>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x1="120" y1="-20" x2="120" y2="0"/>
|
||||||
|
<g>
|
||||||
|
<rect style="fill: #00ff00" x="0" y="0" width="120" height="20"/>
|
||||||
|
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="0" width="120" height="20"/>
|
||||||
|
</g>
|
||||||
|
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="13.125">
|
||||||
|
<tspan x="60" y="13.125">Return Address</tspan>
|
||||||
|
</text>
|
||||||
|
<text font-size="9.03097" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="84" y="4">
|
||||||
|
<tspan x="84" y="4"></tspan>
|
||||||
|
</text>
|
||||||
|
<g>
|
||||||
|
<rect style="fill: #dddddd" x="0" y="20" width="120" height="20"/>
|
||||||
|
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="20" width="120" height="20"/>
|
||||||
|
</g>
|
||||||
|
<text font-size="9.03111" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="33.125">
|
||||||
|
<tspan x="60" y="33.125">Local Variable 1</tspan>
|
||||||
|
</text>
|
||||||
|
<g>
|
||||||
|
<rect style="fill: #cccccc" x="0" y="40" width="120" height="32"/>
|
||||||
|
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke-dasharray: 4; stroke: #000000" x="0" y="40" width="120" height="32"/>
|
||||||
|
</g>
|
||||||
|
<text font-size="9.03097" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="59.125">
|
||||||
|
<tspan x="60" y="59.125">Local Variables 2..n</tspan>
|
||||||
|
</text>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="40" x2="120" y2="40"/>
|
||||||
|
<line style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x1="0" y1="72" x2="120" y2="72"/>
|
||||||
|
<g>
|
||||||
|
<rect style="fill: #ff3333" x="0" y="70" width="120" height="80"/>
|
||||||
|
<rect style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" x="0" y="70" width="120" height="80"/>
|
||||||
|
</g>
|
||||||
|
<text font-size="9.03111" style="fill: #ffffff;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="60" y="113.125">
|
||||||
|
<tspan x="60" y="113.125">Red Zone</tspan>
|
||||||
|
</text>
|
||||||
|
<text font-size="7.9021" style="fill: #000000;text-anchor:end;font-family:sans-serif;font-style:normal;font-weight:normal" x="-20" y="112.75">
|
||||||
|
<tspan x="-20" y="112.75">128 bytes</tspan>
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
@@ -1,21 +1,20 @@
|
|||||||
+++
|
+++
|
||||||
title = "Printing to Screen"
|
title = "Printing to Screen"
|
||||||
|
order = 4
|
||||||
|
url = "printing-to-screen"
|
||||||
date = "2015-10-23"
|
date = "2015-10-23"
|
||||||
|
[extra]
|
||||||
updated = "2016-10-31"
|
updated = "2016-10-31"
|
||||||
aliases = [
|
|
||||||
"/2015/10/23/printing-to-screen/",
|
|
||||||
"/rust-os/printing-to-screen.html",
|
|
||||||
]
|
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In the [previous post] we switched from assembly to [Rust], a systems programming language that provides great safety. But so far we are using unsafe features like [raw pointers] whenever we want to print to screen. In this post we will create a Rust module that provides a safe and easy-to-use interface for the VGA text buffer. It will support Rust's [formatting macros], too.
|
In the [previous post] we switched from assembly to [Rust], a systems programming language that provides great safety. But so far we are using unsafe features like [raw pointers] whenever we want to print to screen. In this post we will create a Rust module that provides a safe and easy-to-use interface for the VGA text buffer. It will support Rust's [formatting macros], too.
|
||||||
|
|
||||||
[previous post]: {{% relref "03-set-up-rust.md" %}}
|
[previous post]: ./posts/03-set-up-rust/index.md
|
||||||
[Rust]: https://www.rust-lang.org/
|
[Rust]: https://www.rust-lang.org/
|
||||||
[raw pointers]: https://doc.rust-lang.org/book/raw-pointers.html
|
[raw pointers]: https://doc.rust-lang.org/book/raw-pointers.html
|
||||||
[formatting macros]: https://doc.rust-lang.org/std/fmt/#related-macros
|
[formatting macros]: https://doc.rust-lang.org/std/fmt/#related-macros
|
||||||
|
|
||||||
<!--more--><aside id="toc"></aside>
|
<!-- more --><aside id="toc"></aside>
|
||||||
|
|
||||||
This post uses recent unstable features, so you need an up-to-date nighly compiler. If you have any questions, problems, or suggestions please [file an issue] or create a comment at the bottom. The code from this post is also available on [Github][code repository].
|
This post uses recent unstable features, so you need an up-to-date nighly compiler. If you have any questions, problems, or suggestions please [file an issue] or create a comment at the bottom. The code from this post is also available on [Github][code repository].
|
||||||
|
|
||||||
@@ -212,10 +211,10 @@ To fix it, we can implement the [Copy] trait for the `ColorCode` type. The easie
|
|||||||
[Copy]: https://doc.rust-lang.org/nightly/core/marker/trait.Copy.html
|
[Copy]: https://doc.rust-lang.org/nightly/core/marker/trait.Copy.html
|
||||||
[derive macro]: http://rustbyexample.com/trait/derive.html
|
[derive macro]: http://rustbyexample.com/trait/derive.html
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=1" >}}
|
```rust
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct ColorCode(u8);
|
struct ColorCode(u8);
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
We also derive the [Clone] trait, since it's a requirement for `Copy`, and the [Debug] trait, which allows us to print this field for debugging purposes.
|
We also derive the [Clone] trait, since it's a requirement for `Copy`, and the [Debug] trait, which allows us to print this field for debugging purposes.
|
||||||
|
|
||||||
@@ -228,7 +227,7 @@ However, the [documentation for Copy] says: _“if your type can implement Copy,
|
|||||||
|
|
||||||
[documentation for Copy]: https://doc.rust-lang.org/core/marker/trait.Copy.html#when-should-my-type-be-copy
|
[documentation for Copy]: https://doc.rust-lang.org/core/marker/trait.Copy.html#when-should-my-type-be-copy
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=2 6" >}}
|
```rust
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
@@ -237,7 +236,7 @@ pub enum Color {...}
|
|||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct ScreenChar {...}
|
struct ScreenChar {...}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
### Try it out!
|
### Try it out!
|
||||||
To write some characters to the screen, you can create a temporary function:
|
To write some characters to the screen, you can create a temporary function:
|
||||||
@@ -257,7 +256,7 @@ It just creates a new Writer that points to the VGA buffer at `0xb8000`. Then it
|
|||||||
|
|
||||||
[byte character]: https://doc.rust-lang.org/reference.html#characters-and-strings
|
[byte character]: https://doc.rust-lang.org/reference.html#characters-and-strings
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Volatile
|
### Volatile
|
||||||
We just saw that our `H` was printed correctly. However, it might not work with future Rust compilers that optimize more aggressively.
|
We just saw that our `H` was printed correctly. However, it might not work with future Rust compilers that optimize more aggressively.
|
||||||
@@ -313,7 +312,7 @@ Instead of a `ScreenChar`, we're now using a `Volatile<ScreenChar>`. (The `Volat
|
|||||||
|
|
||||||
This means that we have to update our `Writer::write_byte` method:
|
This means that we have to update our `Writer::write_byte` method:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=8 11" >}}
|
```rust
|
||||||
impl Writer {
|
impl Writer {
|
||||||
pub fn write_byte(&mut self, byte: u8) {
|
pub fn write_byte(&mut self, byte: u8) {
|
||||||
match byte {
|
match byte {
|
||||||
@@ -331,7 +330,7 @@ impl Writer {
|
|||||||
}
|
}
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
Instead of a normal assignment using `=`, we're now using the `write` method. This guarantees that the compiler will never optimize away this write.
|
Instead of a normal assignment using `=`, we're now using the `write` method. This guarantees that the compiler will never optimize away this write.
|
||||||
|
|
||||||
@@ -373,14 +372,14 @@ The `Ok(())` is just a `Ok` Result containing the `()` type. We can drop the `pu
|
|||||||
|
|
||||||
Now we can use Rust's built-in `write!`/`writeln!` formatting macros:
|
Now we can use Rust's built-in `write!`/`writeln!` formatting macros:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=2 4 5 6" >}}
|
```rust
|
||||||
// in the `print_something` function
|
// in the `print_something` function
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
let mut writer = Writer {...};
|
let mut writer = Writer {...};
|
||||||
writer.write_byte(b'H');
|
writer.write_byte(b'H');
|
||||||
writer.write_str("ello! ");
|
writer.write_str("ello! ");
|
||||||
write!(writer, "The numbers are {} and {}", 42, 1.0/3.0);
|
write!(writer, "The numbers are {} and {}", 42, 1.0/3.0);
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
Now you should see a `Hello! The numbers are 42 and 0.3333333333333333` at the bottom of the screen.
|
Now you should see a `Hello! The numbers are 42 and 0.3333333333333333` at the bottom of the screen.
|
||||||
|
|
||||||
@@ -561,7 +560,7 @@ pub fn clear_screen() {
|
|||||||
### Hello World using `println`
|
### Hello World using `println`
|
||||||
To use `println` in `lib.rs`, we need to import the macros of the VGA buffer module first. Therefore we add a `#[macro_use]` attribute to the module declaration:
|
To use `println` in `lib.rs`, we need to import the macros of the VGA buffer module first. Therefore we add a `#[macro_use]` attribute to the module declaration:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=3 9 10" >}}
|
```rust
|
||||||
// in src/lib.rs
|
// in src/lib.rs
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@@ -575,13 +574,13 @@ pub extern fn rust_main() {
|
|||||||
|
|
||||||
loop{}
|
loop{}
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
Since we imported the macros at crate level, they are available in all modules and thus provide an easy and safe interface to the VGA buffer.
|
Since we imported the macros at crate level, they are available in all modules and thus provide an easy and safe interface to the VGA buffer.
|
||||||
|
|
||||||
As expected, we now see a _“Hello World!”_ on a cleared screen:
|
As expected, we now see a _“Hello World!”_ on a cleared screen:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Deadlocks
|
### Deadlocks
|
||||||
Whenever we use locks, we must be careful to not accidentally introduce _deadlocks_. A [deadlock] occurs when a thread/program waits for a lock that will never be released. Normally, this happens when multiple threads access multiple locks. For example, when thread A holds lock 1 and tries to acquire lock 2 and -- at the same time -- thread B holds lock 2 and tries to acquire lock 1.
|
Whenever we use locks, we must be careful to not accidentally introduce _deadlocks_. A [deadlock] occurs when a thread/program waits for a lock that will never be released. Normally, this happens when multiple threads access multiple locks. For example, when thread A holds lock 1 and tries to acquire lock 2 and -- at the same time -- thread B holds lock 2 and tries to acquire lock 1.
|
||||||
@@ -631,7 +630,7 @@ Now the macro only evaluates the arguments (through `format_args!`) and passes t
|
|||||||
|
|
||||||
Thus, we fixed the deadlock:
|
Thus, we fixed the deadlock:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
We see that both “inner” and “outer” are printed.
|
We see that both “inner” and “outer” are printed.
|
||||||
|
|
||||||
@@ -640,7 +639,7 @@ In the next posts we will map the kernel pages correctly so that accessing `0x0`
|
|||||||
|
|
||||||
The [next post] describes the Multiboot information structure and creates a frame allocator using the information about memory areas.
|
The [next post] describes the Multiboot information structure and creates a frame allocator using the information about memory areas.
|
||||||
|
|
||||||
[next post]: {{% relref "05-allocating-frames.md" %}}
|
[next post]: ./posts/05-allocating-frames/index.md
|
||||||
|
|
||||||
## Other Rust OS Projects
|
## Other Rust OS Projects
|
||||||
Now that you know the very basics of OS development in Rust, you should also check out the following projects:
|
Now that you know the very basics of OS development in Rust, you should also check out the following projects:
|
||||||
@@ -649,8 +648,7 @@ Now that you know the very basics of OS development in Rust, you should also che
|
|||||||
_Note_: You need to [cross compile binutils] to build it (or you create some symbolic links[^fn-symlink] if you're on x86_64).
|
_Note_: You need to [cross compile binutils] to build it (or you create some symbolic links[^fn-symlink] if you're on x86_64).
|
||||||
[Rust Bare-Bones Kernel]: https://github.com/thepowersgang/rust-barebones-kernel
|
[Rust Bare-Bones Kernel]: https://github.com/thepowersgang/rust-barebones-kernel
|
||||||
[higher half]: http://wiki.osdev.org/Higher_Half_Kernel
|
[higher half]: http://wiki.osdev.org/Higher_Half_Kernel
|
||||||
[cross compile binutils]: {{% relref "cross-compile-binutils.md" %}}
|
[cross compile binutils]: ./posts/cross-compile-binutils/index.md
|
||||||
[^fn-symlink]: You will need to symlink `x86_64-none_elf-XXX` to `/usr/bin/XXX` where `XXX` is in {`as`, `ld`, `objcopy`, `objdump`, `strip`}. The `x86_64-none_elf-XXX` files must be in some folder that is in your `$PATH`. But then you can only build for your x86_64 host architecture, so use this hack only for testing.
|
|
||||||
|
|
||||||
- [RustOS]: More advanced kernel that supports allocation, keyboard inputs, and threads. It also has a scheduler and a basic network driver.
|
- [RustOS]: More advanced kernel that supports allocation, keyboard inputs, and threads. It also has a scheduler and a basic network driver.
|
||||||
[RustOS]: https://github.com/RustOS-Fork-Holding-Ground/RustOS
|
[RustOS]: https://github.com/RustOS-Fork-Holding-Ground/RustOS
|
||||||
@@ -661,3 +659,6 @@ _Note_: You need to [cross compile binutils] to build it (or you create some sym
|
|||||||
- [Redox]: Probably the most complete Rust OS today. It has an active community and over 1000 Github stars. File systems, network, an audio player, a picture viewer, and much more. Just take a look at the [screenshots][redox screenshots].
|
- [Redox]: Probably the most complete Rust OS today. It has an active community and over 1000 Github stars. File systems, network, an audio player, a picture viewer, and much more. Just take a look at the [screenshots][redox screenshots].
|
||||||
[Redox]: https://github.com/redox-os/redox
|
[Redox]: https://github.com/redox-os/redox
|
||||||
[redox screenshots]: https://github.com/redox-os/redox#what-it-looks-like
|
[redox screenshots]: https://github.com/redox-os/redox#what-it-looks-like
|
||||||
|
|
||||||
|
## Footnotes
|
||||||
|
[^fn-symlink]: You will need to symlink `x86_64-none_elf-XXX` to `/usr/bin/XXX` where `XXX` is in {`as`, `ld`, `objcopy`, `objdump`, `strip`}. The `x86_64-none_elf-XXX` files must be in some folder that is in your `$PATH`. But then you can only build for your x86_64 host architecture, so use this hack only for testing.
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
@@ -1,11 +1,13 @@
|
|||||||
+++
|
+++
|
||||||
title = "Allocating Frames"
|
title = "Allocating Frames"
|
||||||
|
order = 5
|
||||||
|
url = "allocating-frames"
|
||||||
date = "2015-11-15"
|
date = "2015-11-15"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In this post we create an allocator that provides free physical frames for a future paging module. To get the required information about available and used memory we use the Multiboot information structure. Additionally, we improve the `panic` handler to print the corresponding message and source line.
|
In this post we create an allocator that provides free physical frames for a future paging module. To get the required information about available and used memory we use the Multiboot information structure. Additionally, we improve the `panic` handler to print the corresponding message and source line.
|
||||||
|
|
||||||
<!--more--><aside id="toc"></aside>
|
<!-- more --><aside id="toc"></aside>
|
||||||
|
|
||||||
The full source code is available on [Github][source repo]. Feel free to open issues there if you have any problems or improvements. You can also leave a comment at the bottom.
|
The full source code is available on [Github][source repo]. Feel free to open issues there if you have any problems or improvements. You can also leave a comment at the bottom.
|
||||||
|
|
||||||
@@ -175,7 +177,7 @@ These lines are taken from the default linker script of `ld`, which can be obtai
|
|||||||
|
|
||||||
Now there are only 12 sections left and we get a much more useful output:
|
Now there are only 12 sections left and we get a much more useful output:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
If you like, you can compare this output to the `objdump -h build/kernel-x86_64.bin` output. You will see that the start addresses and sizes match exactly for each section. The sections with flags `0x0` are mostly debug sections, so they don't need to be loaded. And the last few sections of the QEMU output aren't in the `objdump` output because they are special sections such as string tables.
|
If you like, you can compare this output to the `objdump -h build/kernel-x86_64.bin` output. You will see that the start addresses and sizes match exactly for each section. The sections with flags `0x0` are mostly debug sections, so they don't need to be loaded. And the last few sections of the QEMU output aren't in the `objdump` output because they are special sections such as string tables.
|
||||||
|
|
||||||
@@ -425,10 +427,10 @@ Now we have a working frame allocator. It is a bit rudimentary and cannot free f
|
|||||||
## What's next?
|
## What's next?
|
||||||
The [next post] will be about paging again. We will use the frame allocator to create a safe module that allows us to switch page tables and map pages. Then we will use this module and the information from the Elf-sections tag to remap the kernel correctly.
|
The [next post] will be about paging again. We will use the frame allocator to create a safe module that allows us to switch page tables and map pages. Then we will use this module and the information from the Elf-sections tag to remap the kernel correctly.
|
||||||
|
|
||||||
[next post]: {{% relref "06-page-tables.md" %}}
|
[next post]: ./posts/06-page-tables/index.md
|
||||||
|
|
||||||
## Recommended Posts
|
## Recommended Posts
|
||||||
Eric Kidd started the [Bare Metal Rust] series last week. Like this post, it builds upon the code from [Printing to Screen], but tries to support keyboard input instead of wrestling through memory management details.
|
Eric Kidd started the [Bare Metal Rust] series last week. Like this post, it builds upon the code from [Printing to Screen], but tries to support keyboard input instead of wrestling through memory management details.
|
||||||
|
|
||||||
[Bare Metal Rust]: http://www.randomhacks.net/bare-metal-rust/
|
[Bare Metal Rust]: http://www.randomhacks.net/bare-metal-rust/
|
||||||
[Printing to Screen]: {{% relref "04-printing-to-screen.md" %}}
|
[Printing to Screen]: ./posts/04-printing-to-screen/index.md
|
||||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
929
blog/content/posts/06-page-tables/X86_Paging_64bit.svg
Normal file
@@ -0,0 +1,929 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="7.2973523in"
|
||||||
|
height="4.1279249in"
|
||||||
|
viewBox="-2141 2141 8777.1352 4929.2808"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="X86_Paging_64bit.svg">
|
||||||
|
<metadata
|
||||||
|
id="metadata370">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs368" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1080"
|
||||||
|
inkscape:window-height="1868"
|
||||||
|
id="namedview366"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:zoom="1.4404005"
|
||||||
|
inkscape:cx="383.13253"
|
||||||
|
inkscape:cy="73.778068"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g4" />
|
||||||
|
<g
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
id="g4"
|
||||||
|
transform="translate(-2845,-286.64442)">
|
||||||
|
<rect
|
||||||
|
x="8503"
|
||||||
|
y="4015"
|
||||||
|
width="708"
|
||||||
|
height="3307"
|
||||||
|
rx="0"
|
||||||
|
style="fill:#dfdfdf"
|
||||||
|
id="rect8" />
|
||||||
|
<polyline
|
||||||
|
points="3365,2598 3365,3070"
|
||||||
|
id="polyline10" />
|
||||||
|
<polyline
|
||||||
|
points="3189,2598 3189,3070"
|
||||||
|
id="polyline12" />
|
||||||
|
<polyline
|
||||||
|
points="3012,2598 3012,3070"
|
||||||
|
id="polyline14" />
|
||||||
|
<polyline
|
||||||
|
points="2834,2598 2834,3070"
|
||||||
|
id="polyline16" />
|
||||||
|
<polyline
|
||||||
|
points="2657,2598 2657,3070"
|
||||||
|
id="polyline18" />
|
||||||
|
<polyline
|
||||||
|
points="2480,2598 2480,3070"
|
||||||
|
id="polyline20" />
|
||||||
|
<polyline
|
||||||
|
points="2303,2598 2303,3070"
|
||||||
|
id="polyline22" />
|
||||||
|
<polyline
|
||||||
|
points="2125,2456 2125,3070"
|
||||||
|
id="polyline24" />
|
||||||
|
<polyline
|
||||||
|
points="1948,2598 1948,3070"
|
||||||
|
id="polyline26" />
|
||||||
|
<polyline
|
||||||
|
points="1772,2598 1772,3070"
|
||||||
|
id="polyline28" />
|
||||||
|
<polyline
|
||||||
|
points="1594,2598 1594,3070"
|
||||||
|
id="polyline30" />
|
||||||
|
<polyline
|
||||||
|
points="1417,2598 1417,3070"
|
||||||
|
id="polyline32" />
|
||||||
|
<polyline
|
||||||
|
points="1239,2598 1239,3070"
|
||||||
|
id="polyline34" />
|
||||||
|
<polyline
|
||||||
|
points="1063,2598 1063,3070"
|
||||||
|
id="polyline36" />
|
||||||
|
<polyline
|
||||||
|
points="886,2598 886,3070"
|
||||||
|
id="polyline38" />
|
||||||
|
<polyline
|
||||||
|
points="708,2456 708,3070"
|
||||||
|
id="polyline40" />
|
||||||
|
<polyline
|
||||||
|
points="3543,2456 3543,3070"
|
||||||
|
id="polyline74" />
|
||||||
|
<polyline
|
||||||
|
points="-2125,2598 3543,2598"
|
||||||
|
id="polyline76"
|
||||||
|
transform="matrix(0.49978943,0,0,1,1772.2461,0)" />
|
||||||
|
<polyline
|
||||||
|
points="3543,3070 -1948,3070 -2125,3070"
|
||||||
|
id="polyline78"
|
||||||
|
transform="matrix(0.49786923,0,0,1,1779.0493,0)" />
|
||||||
|
<polyline
|
||||||
|
points="9035,2598 9035,3070"
|
||||||
|
id="polyline80" />
|
||||||
|
<polyline
|
||||||
|
points="8858,2598 8858,3070"
|
||||||
|
id="polyline82" />
|
||||||
|
<polyline
|
||||||
|
points="8681,2598 8681,3070"
|
||||||
|
id="polyline84" />
|
||||||
|
<polyline
|
||||||
|
points="8503,2598 8503,3070"
|
||||||
|
id="polyline86" />
|
||||||
|
<polyline
|
||||||
|
points="8326,2598 8326,3070"
|
||||||
|
id="polyline88" />
|
||||||
|
<polyline
|
||||||
|
points="8150,2598 8150,3070"
|
||||||
|
id="polyline90" />
|
||||||
|
<polyline
|
||||||
|
points="7972,2598 7972,3070"
|
||||||
|
id="polyline92" />
|
||||||
|
<polyline
|
||||||
|
points="7795,2456 7795,3070"
|
||||||
|
id="polyline94" />
|
||||||
|
<polyline
|
||||||
|
points="7617,2598 7617,3070"
|
||||||
|
id="polyline96" />
|
||||||
|
<polyline
|
||||||
|
points="7441,2598 7441,3070"
|
||||||
|
id="polyline98" />
|
||||||
|
<polyline
|
||||||
|
points="7264,2598 7264,3070"
|
||||||
|
id="polyline100" />
|
||||||
|
<polyline
|
||||||
|
points="7086,2598 7086,3070"
|
||||||
|
id="polyline102" />
|
||||||
|
<polyline
|
||||||
|
points="6909,2598 6909,3070"
|
||||||
|
id="polyline104" />
|
||||||
|
<polyline
|
||||||
|
points="6732,2598 6732,3070"
|
||||||
|
id="polyline106" />
|
||||||
|
<polyline
|
||||||
|
points="6555,2598 6555,3070"
|
||||||
|
id="polyline108" />
|
||||||
|
<polyline
|
||||||
|
points="6377,2456 6377,3070"
|
||||||
|
id="polyline110" />
|
||||||
|
<polyline
|
||||||
|
points="6200,2598 6200,3070"
|
||||||
|
id="polyline112" />
|
||||||
|
<polyline
|
||||||
|
points="6024,2598 6024,3070"
|
||||||
|
id="polyline114" />
|
||||||
|
<polyline
|
||||||
|
points="5846,2598 5846,3070"
|
||||||
|
id="polyline116" />
|
||||||
|
<polyline
|
||||||
|
points="5669,2598 5669,3070"
|
||||||
|
id="polyline118" />
|
||||||
|
<polyline
|
||||||
|
points="5491,2598 5491,3070"
|
||||||
|
id="polyline120" />
|
||||||
|
<polyline
|
||||||
|
points="5315,2598 5315,3070"
|
||||||
|
id="polyline122" />
|
||||||
|
<polyline
|
||||||
|
points="5138,2598 5138,3070"
|
||||||
|
id="polyline124" />
|
||||||
|
<polyline
|
||||||
|
points="4960,2409 4960,3070"
|
||||||
|
id="polyline126" />
|
||||||
|
<polyline
|
||||||
|
points="4783,2598 4783,3070"
|
||||||
|
id="polyline128" />
|
||||||
|
<polyline
|
||||||
|
points="4606,2598 4606,3070"
|
||||||
|
id="polyline130" />
|
||||||
|
<polyline
|
||||||
|
points="4429,2598 4429,3070"
|
||||||
|
id="polyline132" />
|
||||||
|
<polyline
|
||||||
|
points="4251,2598 4251,3070"
|
||||||
|
id="polyline134" />
|
||||||
|
<polyline
|
||||||
|
points="4074,2598 4074,3070"
|
||||||
|
id="polyline136" />
|
||||||
|
<polyline
|
||||||
|
points="3898,2598 3898,3070"
|
||||||
|
id="polyline138" />
|
||||||
|
<polyline
|
||||||
|
points="3720,2598 3720,3070"
|
||||||
|
id="polyline140" />
|
||||||
|
<polyline
|
||||||
|
points="9212,2456 9212,3070"
|
||||||
|
id="polyline142" />
|
||||||
|
<polyline
|
||||||
|
points="3543,2598 9212,2598"
|
||||||
|
id="polyline144" />
|
||||||
|
<polyline
|
||||||
|
points="9212,3070 3720,3070 0,3070"
|
||||||
|
id="polyline146"
|
||||||
|
transform="matrix(0.92335242,0,0,1,706.07747,0)" />
|
||||||
|
<rect
|
||||||
|
x="5102"
|
||||||
|
y="4488"
|
||||||
|
width="1181"
|
||||||
|
height="2362"
|
||||||
|
rx="0"
|
||||||
|
style="fill:#dfdfdf"
|
||||||
|
id="rect148" />
|
||||||
|
<rect
|
||||||
|
x="3307"
|
||||||
|
y="4251"
|
||||||
|
width="1181"
|
||||||
|
height="2362"
|
||||||
|
rx="0"
|
||||||
|
style="fill:#dfdfdf"
|
||||||
|
id="rect150" />
|
||||||
|
<rect
|
||||||
|
x="6897"
|
||||||
|
y="4724"
|
||||||
|
width="1181"
|
||||||
|
height="2362"
|
||||||
|
rx="0"
|
||||||
|
style="fill:#dfdfdf"
|
||||||
|
id="rect152" />
|
||||||
|
<rect
|
||||||
|
x="1511"
|
||||||
|
y="4015"
|
||||||
|
width="1181"
|
||||||
|
height="2362"
|
||||||
|
rx="0"
|
||||||
|
style="fill:#dfdfdf"
|
||||||
|
id="rect154" />
|
||||||
|
<rect
|
||||||
|
x="1417"
|
||||||
|
y="6850"
|
||||||
|
width="1417"
|
||||||
|
height="472"
|
||||||
|
rx="0"
|
||||||
|
id="rect156" />
|
||||||
|
<rect
|
||||||
|
x="8503"
|
||||||
|
y="5433"
|
||||||
|
width="708"
|
||||||
|
height="236"
|
||||||
|
rx="0"
|
||||||
|
style="fill:#dfdfdf"
|
||||||
|
id="rect158" />
|
||||||
|
<rect
|
||||||
|
x="5102"
|
||||||
|
y="5669"
|
||||||
|
width="1181"
|
||||||
|
height="472"
|
||||||
|
rx="0"
|
||||||
|
id="rect160" />
|
||||||
|
<rect
|
||||||
|
x="3307"
|
||||||
|
y="5433"
|
||||||
|
width="1181"
|
||||||
|
height="472"
|
||||||
|
rx="0"
|
||||||
|
id="rect162" />
|
||||||
|
<rect
|
||||||
|
x="6897"
|
||||||
|
y="5905"
|
||||||
|
width="1181"
|
||||||
|
height="472"
|
||||||
|
rx="0"
|
||||||
|
id="rect164" />
|
||||||
|
<rect
|
||||||
|
x="1511"
|
||||||
|
y="5196"
|
||||||
|
width="1181"
|
||||||
|
height="472"
|
||||||
|
rx="0"
|
||||||
|
id="rect166" />
|
||||||
|
<circle
|
||||||
|
cx="6141"
|
||||||
|
cy="5905"
|
||||||
|
r="47"
|
||||||
|
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||||
|
id="circle170" />
|
||||||
|
<circle
|
||||||
|
cx="4346"
|
||||||
|
cy="5669"
|
||||||
|
r="47"
|
||||||
|
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||||
|
id="circle172" />
|
||||||
|
<circle
|
||||||
|
cx="7937"
|
||||||
|
cy="6141"
|
||||||
|
r="47"
|
||||||
|
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||||
|
id="circle174" />
|
||||||
|
<circle
|
||||||
|
cx="2551"
|
||||||
|
cy="5433"
|
||||||
|
r="47"
|
||||||
|
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||||
|
id="circle176" />
|
||||||
|
<polyline
|
||||||
|
points="3118,3590 2834,3779"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline178" />
|
||||||
|
<polyline
|
||||||
|
points="1322,3590 1039,3779"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline180" />
|
||||||
|
<polyline
|
||||||
|
points="4913,3590 4629,3779"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline184" />
|
||||||
|
<polyline
|
||||||
|
points="6519,3590 6236,3779"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline186" />
|
||||||
|
<polyline
|
||||||
|
points="8314,3590 8031,3779"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline188" />
|
||||||
|
<circle
|
||||||
|
cx="1653"
|
||||||
|
cy="7086"
|
||||||
|
r="47"
|
||||||
|
style="fill:#0000ff;stroke:#0000ff;stroke-width:32"
|
||||||
|
id="circle190" />
|
||||||
|
<polyline
|
||||||
|
points="1653,7086 1181,7086 1181,6377 1354,6377"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline192" />
|
||||||
|
<polygon
|
||||||
|
points="1512,6377 1355,6330 1355,6425 1355,6425 "
|
||||||
|
style="fill:#0000ff;stroke:#0000ff"
|
||||||
|
id="polygon194" />
|
||||||
|
<polyline
|
||||||
|
points="7937,6141 8173,6141 8173,7322 8346,7322"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline196" />
|
||||||
|
<polygon
|
||||||
|
points="8504,7322 8347,7275 8347,7370 8347,7370 "
|
||||||
|
style="fill:#0000ff;stroke:#0000ff"
|
||||||
|
id="polygon198" />
|
||||||
|
<polyline
|
||||||
|
points="8173,3543 8173,5669 8346,5669"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline200" />
|
||||||
|
<polygon
|
||||||
|
points="8504,5669 8347,5622 8347,5716 8347,5716 "
|
||||||
|
style="fill:#0000ff;stroke:#0000ff"
|
||||||
|
id="polygon202" />
|
||||||
|
<polyline
|
||||||
|
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline204" />
|
||||||
|
<polyline
|
||||||
|
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline206" />
|
||||||
|
<polyline
|
||||||
|
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline208" />
|
||||||
|
<polyline
|
||||||
|
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline210" />
|
||||||
|
<polyline
|
||||||
|
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline212" />
|
||||||
|
<polyline
|
||||||
|
points="4771,3543 4771,6141 4944,6141"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline214" />
|
||||||
|
<polygon
|
||||||
|
points="5103,6141 4945,6094 4945,6188 4945,6188 "
|
||||||
|
style="fill:#0000ff;stroke:#0000ff"
|
||||||
|
id="polygon216" />
|
||||||
|
<polyline
|
||||||
|
points="6377,3543 6377,4015 6519,4157 6519,6377 6740,6377"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline218" />
|
||||||
|
<polygon
|
||||||
|
points="6898,6377 6741,6330 6741,6425 6741,6425 "
|
||||||
|
style="fill:#0000ff;stroke:#0000ff"
|
||||||
|
id="polygon220" />
|
||||||
|
<polyline
|
||||||
|
points="6141,5905 6377,5905 6377,7086 6740,7086"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline222" />
|
||||||
|
<polygon
|
||||||
|
points="6898,7086 6741,7039 6741,7133 6741,7133 "
|
||||||
|
style="fill:#0000ff;stroke:#0000ff"
|
||||||
|
id="polygon224" />
|
||||||
|
<polyline
|
||||||
|
points="4346,5669 4629,5669 4629,6850 4944,6850"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline226" />
|
||||||
|
<polygon
|
||||||
|
points="5103,6850 4945,6803 4945,6897 4945,6897 "
|
||||||
|
style="fill:#0000ff;stroke:#0000ff"
|
||||||
|
id="polygon228" />
|
||||||
|
<polyline
|
||||||
|
points="2598,5433 2834,5433 2834,6614 3149,6614"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline230" />
|
||||||
|
<polygon
|
||||||
|
points="3308,6614 3150,6566 3150,6661 3150,6661 "
|
||||||
|
style="fill:#0000ff;stroke:#0000ff"
|
||||||
|
id="polygon232" />
|
||||||
|
<polyline
|
||||||
|
points="1181,3543 1181,5669 1401,5669"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline234" />
|
||||||
|
<polygon
|
||||||
|
points="1560,5669 1402,5622 1402,5716 1402,5716 "
|
||||||
|
style="fill:#0000ff;stroke:#0000ff"
|
||||||
|
id="polygon236" />
|
||||||
|
<polyline
|
||||||
|
points="2976,3543 2976,5905 3149,5905"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline238" />
|
||||||
|
<polygon
|
||||||
|
points="3308,5905 3150,5858 3150,5952 3150,5952 "
|
||||||
|
style="fill:#0000ff;stroke:#0000ff"
|
||||||
|
id="polygon240" />
|
||||||
|
<g
|
||||||
|
style="fill:#000000;stroke-width:0"
|
||||||
|
id="g242">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="2281.8145"
|
||||||
|
y="7165"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text244"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">CR3 register</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="3507"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text246"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">32</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="2161"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text248"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">39</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="2090"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text250"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">40</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="744"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text252"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">47</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="672"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text254"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="-672"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text256"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="-744"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text258"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="-2090"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text260"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="9176"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text262"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">0</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="7759"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text264"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">8</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="6342"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text266"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">16</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="4924"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text268"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">24</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="3579"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text270"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">31</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="6413"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text272"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">15</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="7830"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text274"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">7</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="4996"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text276"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">23</text>
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-1,1,0,8976,4724)"
|
||||||
|
id="g278">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="404"
|
||||||
|
id="text280"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-1,1,0,8976,6614)"
|
||||||
|
id="g282">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="404"
|
||||||
|
id="text284"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-1,1,0,9448,5622)"
|
||||||
|
id="g286">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text288"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||||
|
style="font-size:200.46390561px"
|
||||||
|
id="tspan3509">4K memory page</tspan></text>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="-2125"
|
||||||
|
y="2314"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text290"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="5622"
|
||||||
|
y="5958.457"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text292"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||||
|
style="font-size:200.46391296px"
|
||||||
|
id="tspan3497">P2 entry</tspan></text>
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-1,1,0,5723,6614)"
|
||||||
|
id="g296">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="404"
|
||||||
|
id="text298"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-1,1,0,5723,5196)"
|
||||||
|
id="g300">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="404"
|
||||||
|
id="text302"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="5692"
|
||||||
|
y="4393"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text304"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||||
|
style="font-size:200.46390561px"
|
||||||
|
id="tspan3505">P2 table</tspan></text>
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-1,1,0,3928,6377)"
|
||||||
|
id="g306">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="404"
|
||||||
|
id="text308"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-1,1,0,3928,4960)"
|
||||||
|
id="g310">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="404"
|
||||||
|
id="text312"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="3826"
|
||||||
|
y="5722.457"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text314"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||||
|
style="font-size:200.46391296px"
|
||||||
|
id="tspan3495">P3 entry</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="3874"
|
||||||
|
y="3968"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text318"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="3874"
|
||||||
|
y="4157"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text320"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||||
|
style="font-size:200.46390561px"
|
||||||
|
id="tspan3503">P3 table</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="7417"
|
||||||
|
y="6194.457"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text322"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||||
|
style="font-size:200.46391296px"
|
||||||
|
id="tspan3499">P1 entry </tspan></text>
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-1,1,0,7519,6850)"
|
||||||
|
id="g326">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="404"
|
||||||
|
id="text328"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-1,1,0,7519,5433)"
|
||||||
|
id="g330">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="404"
|
||||||
|
id="text332"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="7487"
|
||||||
|
y="4629"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text334"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||||
|
style="font-size:200.46390561px"
|
||||||
|
id="tspan3507">P1 table</tspan></text>
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-1,1,0,2133,6141)"
|
||||||
|
id="g336">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="404"
|
||||||
|
id="text338"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-1,1,0,2133,4724)"
|
||||||
|
id="g340">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="404"
|
||||||
|
id="text342"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
x="2004.2715"
|
||||||
|
y="5486.457"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text344"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">
|
||||||
|
<tspan
|
||||||
|
style="font-size:200.46390561px"
|
||||||
|
id="tspan3493">P4 entry</tspan>
|
||||||
|
</text>
|
||||||
|
<text
|
||||||
|
x="2031"
|
||||||
|
y="5622"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text346"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||||
|
<text
|
||||||
|
x="2078"
|
||||||
|
y="3941.2715"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text348"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle">
|
||||||
|
<tspan
|
||||||
|
style="font-size:200.46391296px"
|
||||||
|
id="tspan3501">P4 table</tspan>
|
||||||
|
</text>
|
||||||
|
<text
|
||||||
|
x="3165"
|
||||||
|
y="3732"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text350"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||||
|
<text
|
||||||
|
x="1370"
|
||||||
|
y="3732"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text352"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||||
|
<text
|
||||||
|
x="1370"
|
||||||
|
y="6708"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text354"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
|
||||||
|
<text
|
||||||
|
x="4960"
|
||||||
|
y="3732"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text356"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||||
|
<text
|
||||||
|
x="6566"
|
||||||
|
y="3732"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text358"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||||
|
<text
|
||||||
|
x="8362"
|
||||||
|
y="3732"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text360"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">12</text>
|
||||||
|
<text
|
||||||
|
x="1181"
|
||||||
|
y="7532.2715"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text364"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 27 KiB |
@@ -1,12 +1,13 @@
|
|||||||
+++
|
+++
|
||||||
title = "Page Tables"
|
title = "Page Tables"
|
||||||
slug = "modifying-page-tables"
|
order = 6
|
||||||
|
url = "page-tables"
|
||||||
date = "2015-12-09"
|
date = "2015-12-09"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In this post we will create a paging module, which allows us to access and modify the 4-level page table. We will explore recursive page table mapping and use some Rust features to make it safe. Finally we will create functions to translate virtual addresses and to map and unmap pages.
|
In this post we will create a paging module, which allows us to access and modify the 4-level page table. We will explore recursive page table mapping and use some Rust features to make it safe. Finally we will create functions to translate virtual addresses and to map and unmap pages.
|
||||||
|
|
||||||
<!--more--><aside id="toc"></aside>
|
<!-- more --><aside id="toc"></aside>
|
||||||
|
|
||||||
You can find the source code and this post itself on [Github][source repository]. Please file an issue there if you have any problems or improvement suggestions. There is also a comment section at the end of this page. Note that this post requires a current Rust nightly.
|
You can find the source code and this post itself on [Github][source repository]. Please file an issue there if you have any problems or improvement suggestions. There is also a comment section at the end of this page. Note that this post requires a current Rust nightly.
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ _Paging_ is a memory management scheme that separates virtual and physical memor
|
|||||||
|
|
||||||
The x86 architecture uses a 4-level page table in 64-bit mode. A virtual address has the following structure:
|
The x86 architecture uses a 4-level page table in 64-bit mode. A virtual address has the following structure:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The bits 48–63 are so-called _sign extension_ bits and must be copies of bit 47. The following 36 bits define the page table indexes (9 bits per table) and the last 12 bits specify the offset in the 4KiB page.
|
The bits 48–63 are so-called _sign extension_ bits and must be copies of bit 47. The following 36 bits define the page table indexes (9 bits per table) and the last 12 bits specify the offset in the 4KiB page.
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ Each table has 2^9 = 512 entries and each entry is 8 byte. Thus a page table fit
|
|||||||
|
|
||||||
To translate an address, the CPU reads the P4 address from the CR3 register. Then it uses the indexes to walk the tables:
|
To translate an address, the CPU reads the P4 address from the CR3 register. Then it uses the indexes to walk the tables:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The P4 entry points to a P3 table, where the next 9 bits of the address are used to select an entry. The P3 entry then points to a P2 table and the P2 entry points to a P1 table. The P1 entry, which is specified through bits 12–20, finally points to the physical frame.
|
The P4 entry points to a P3 table, where the next 9 bits of the address are used to select an entry. The P3 entry then points to a P2 table and the P2 entry points to a P1 table. The P1 entry, which is specified through bits 12–20, finally points to the physical frame.
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ pub struct Page {
|
|||||||
```
|
```
|
||||||
We import the `PAGE_SIZE` and define a constant for the number of entries per table. To make future function signatures more expressive, we can use the type aliases `PhysicalAddress` and `VirtualAddress`. The `Page` struct is similar to the `Frame` struct in the [previous post], but represents a virtual page instead of a physical frame.
|
We import the `PAGE_SIZE` and define a constant for the number of entries per table. To make future function signatures more expressive, we can use the type aliases `PhysicalAddress` and `VirtualAddress`. The `Page` struct is similar to the `Frame` struct in the [previous post], but represents a virtual page instead of a physical frame.
|
||||||
|
|
||||||
[previous post]: {{% relref "05-allocating-frames.md#a-memory-module" %}}
|
[previous post]: ./posts/05-allocating-frames/index.md#a-memory-module
|
||||||
|
|
||||||
### Page Table Entries
|
### Page Table Entries
|
||||||
To model page table entries, we create a new `entry` submodule:
|
To model page table entries, we create a new `entry` submodule:
|
||||||
@@ -232,19 +233,19 @@ We will solve the problem in another way using a trick called _recursive mapping
|
|||||||
### Recursive Mapping
|
### Recursive Mapping
|
||||||
The trick is to map the P4 table recursively: The last entry doesn't point to a P3 table, but to the P4 table itself. We can use this entry to remove a translation level so that we land on a page table instead. For example, we can “loop” once to access a P1 table:
|
The trick is to map the P4 table recursively: The last entry doesn't point to a P3 table, but to the P4 table itself. We can use this entry to remove a translation level so that we land on a page table instead. For example, we can “loop” once to access a P1 table:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
By selecting the 511th P4 entry, which points points to the P4 table itself, the P4 table is used as the P3 table. Similarly, the P3 table is used as a P2 table and the P2 table is treated like a P1 table. Thus the P1 table becomes the target page and can be accessed through the offset.
|
By selecting the 511th P4 entry, which points points to the P4 table itself, the P4 table is used as the P3 table. Similarly, the P3 table is used as a P2 table and the P2 table is treated like a P1 table. Thus the P1 table becomes the target page and can be accessed through the offset.
|
||||||
|
|
||||||
It's also possible to access P2 tables by looping twice. And if we select the 511th entry three times, we can access and modify P3 tables:
|
It's also possible to access P2 tables by looping twice. And if we select the 511th entry three times, we can access and modify P3 tables:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
So we just need to specify the desired P3 table in the address through the P1 index. By choosing the 511th entry multiple times, we stay on the P4 table until the address's P1 index becomes the actual P4 index.
|
So we just need to specify the desired P3 table in the address through the P1 index. By choosing the 511th entry multiple times, we stay on the P4 table until the address's P1 index becomes the actual P4 index.
|
||||||
|
|
||||||
To access the P4 table itself, we loop once more and thus never leave the frame:
|
To access the P4 table itself, we loop once more and thus never leave the frame:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
So we can access and modify page tables of all levels by just setting one P4 entry once. Most work is done by the CPU, we just the recursive entry to remove one or more translation levels. It may seem a bit strange at first, but it's a clean and simple solution once you wrapped your head around it.
|
So we can access and modify page tables of all levels by just setting one P4 entry once. Most work is done by the CPU, we just the recursive entry to remove one or more translation levels. It may seem a bit strange at first, but it's a clean and simple solution once you wrapped your head around it.
|
||||||
|
|
||||||
@@ -345,8 +346,6 @@ _What happens if we call them on a P1 table?_
|
|||||||
|
|
||||||
Well, they would calculate the address of the next table (which does not exist) and treat it as a page table. Either they construct an invalid address (if `XXX < 400`)[^fn-invalid-address] or access the mapped page itself. That way, we could easily corrupt memory or cause CPU exceptions by accident. So these two functions are not _safe_ in Rust terms. Thus we need to make them `unsafe` functions unless we find some clever solution.
|
Well, they would calculate the address of the next table (which does not exist) and treat it as a page table. Either they construct an invalid address (if `XXX < 400`)[^fn-invalid-address] or access the mapped page itself. That way, we could easily corrupt memory or cause CPU exceptions by accident. So these two functions are not _safe_ in Rust terms. Thus we need to make them `unsafe` functions unless we find some clever solution.
|
||||||
|
|
||||||
[^fn-invalid-address]: If the `XXX` part of the address is smaller than `0o400`, it's binary representation doesn't start with `1`. But the sign extension bits, which should be a copy of that bit, are `1` instead of `0`. Thus the address is not valid.
|
|
||||||
|
|
||||||
## Some Clever Solution
|
## Some Clever Solution
|
||||||
We can use Rust's type system to statically guarantee that the `next_table` methods can only be called on P4, P3, and P2 tables, but not on a P1 table. The idea is to add a `Level` parameter to the `Table` type and implement the `next_table` methods only for level 4, 3, and 2.
|
We can use Rust's type system to statically guarantee that the `next_table` methods can only be called on P4, P3, and P2 tables, but not on a P1 table. The idea is to add a `Level` parameter to the `Table` type and implement the `next_table` methods only for level 4, 3, and 2.
|
||||||
|
|
||||||
@@ -651,7 +650,7 @@ pub struct ActivePageTable {
|
|||||||
```
|
```
|
||||||
We can't store the `Table<Level4>` directly because it needs to be at a special memory location (like the [VGA text buffer]). We could use a raw pointer or `&mut` instead of [Unique], but Unique indicates ownership better.
|
We can't store the `Table<Level4>` directly because it needs to be at a special memory location (like the [VGA text buffer]). We could use a raw pointer or `&mut` instead of [Unique], but Unique indicates ownership better.
|
||||||
|
|
||||||
[VGA text buffer]: {{% relref "04-printing-to-screen.md#the-text-buffer" %}}
|
[VGA text buffer]: ./posts/04-printing-to-screen/index.md#the-text-buffer
|
||||||
[Unique]: https://doc.rust-lang.org/nightly/core/ptr/struct.Unique.html
|
[Unique]: https://doc.rust-lang.org/nightly/core/ptr/struct.Unique.html
|
||||||
|
|
||||||
Because the `ActivePageTable` owns the unique recursive mapped P4 table, there must be only one `ActivePageTable` instance. Thus we make the constructor function unsafe:
|
Because the `ActivePageTable` owns the unique recursive mapped P4 table, there must be only one `ActivePageTable` instance. Thus we make the constructor function unsafe:
|
||||||
@@ -880,10 +879,13 @@ This post has become pretty long. So let's summarize what we've done:
|
|||||||
## What's next?
|
## What's next?
|
||||||
In the [next post] we will extend this module and add a function to modify inactive page tables. Through that function, we will create a new page table hierarchy that maps the kernel correctly using 4KiB pages. Then we will switch to the new table to get a safer kernel environment.
|
In the [next post] we will extend this module and add a function to modify inactive page tables. Through that function, we will create a new page table hierarchy that maps the kernel correctly using 4KiB pages. Then we will switch to the new table to get a safer kernel environment.
|
||||||
|
|
||||||
[next post]: {{% relref "07-remap-the-kernel.md" %}}
|
[next post]: ./posts/07-remap-the-kernel/index.md
|
||||||
|
|
||||||
Afterwards, we will use this paging module to build a heap allocator. This will allow us to use allocation and collection types such as `Box` and `Vec`.
|
Afterwards, we will use this paging module to build a heap allocator. This will allow us to use allocation and collection types such as `Box` and `Vec`.
|
||||||
|
|
||||||
<small>Image sources: [^virtual_physical_translation_source]</small>
|
<small>Image sources: [^virtual_physical_translation_source]</small>
|
||||||
|
|
||||||
|
## Footnotes
|
||||||
|
[^fn-invalid-address]: If the `XXX` part of the address is smaller than `0o400`, it's binary representation doesn't start with `1`. But the sign extension bits, which should be a copy of that bit, are `1` instead of `0`. Thus the address is not valid.
|
||||||
|
|
||||||
[^virtual_physical_translation_source]: Image sources: Modified versions of an image from [Wikipedia](https://commons.wikimedia.org/wiki/File:X86_Paging_64bit.svg). The modified files are licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
|
[^virtual_physical_translation_source]: Image sources: Modified versions of an image from [Wikipedia](https://commons.wikimedia.org/wiki/File:X86_Paging_64bit.svg). The modified files are licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
|
||||||
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@@ -1,12 +1,15 @@
|
|||||||
+++
|
+++
|
||||||
title = "Remap the Kernel"
|
title = "Remap the Kernel"
|
||||||
|
order = 7
|
||||||
|
url = "remap-the-kernel"
|
||||||
date = "2016-01-01"
|
date = "2016-01-01"
|
||||||
|
[extra]
|
||||||
updated = "2016-03-06"
|
updated = "2016-03-06"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In this post we will create a new page table to map the kernel sections correctly. Therefore we will extend the paging module to support modifications of _inactive_ page tables as well. Then we will switch to the new table and secure our kernel stack by creating a guard page.
|
In this post we will create a new page table to map the kernel sections correctly. Therefore we will extend the paging module to support modifications of _inactive_ page tables as well. Then we will switch to the new table and secure our kernel stack by creating a guard page.
|
||||||
|
|
||||||
<!--more--><aside id="toc"></aside>
|
<!-- more --><aside id="toc"></aside>
|
||||||
|
|
||||||
As always, you can find the source code on [Github]. Don't hesitate to file issues there if you have any problems or improvement suggestions. There is also a comment section at the end of this page. Note that this post requires a current Rust nightly.
|
As always, you can find the source code on [Github]. Don't hesitate to file issues there if you have any problems or improvement suggestions. There is also a comment section at the end of this page. Note that this post requires a current Rust nightly.
|
||||||
|
|
||||||
@@ -15,17 +18,18 @@ As always, you can find the source code on [Github]. Don't hesitate to file issu
|
|||||||
## Motivation
|
## Motivation
|
||||||
|
|
||||||
In the [previous post], we had a strange bug in the `unmap` function. Its reason was a silent stack overflow, which corrupted the page tables. Fortunately, our kernel stack is right above the page tables so that we noticed the overflow relatively quickly. This won't be the case when we add threads with new stacks in the future. Then a silent stack overflow could overwrite some data without us noticing. But eventually some completely unrelated function fails because a variable changed its value.
|
In the [previous post], we had a strange bug in the `unmap` function. Its reason was a silent stack overflow, which corrupted the page tables. Fortunately, our kernel stack is right above the page tables so that we noticed the overflow relatively quickly. This won't be the case when we add threads with new stacks in the future. Then a silent stack overflow could overwrite some data without us noticing. But eventually some completely unrelated function fails because a variable changed its value.
|
||||||
[previous post]: {{% relref "06-page-tables.md" %}}
|
|
||||||
|
[previous post]: ./posts/06-page-tables/index.md
|
||||||
|
|
||||||
As you can imagine, these kinds of bugs are horrendous to debug. For that reason we will create a new hierarchical page table in this post, which has _guard page_ below the stack. A guard page is basically an unmapped page that causes a page fault when accessed. Thus we can catch stack overflows right when they happen.
|
As you can imagine, these kinds of bugs are horrendous to debug. For that reason we will create a new hierarchical page table in this post, which has _guard page_ below the stack. A guard page is basically an unmapped page that causes a page fault when accessed. Thus we can catch stack overflows right when they happen.
|
||||||
|
|
||||||
Also, we will use the [information about kernel sections] to map the various sections individually instead of blindly mapping the first gigabyte. To improve safety even further, we will set the correct page table flags for the various sections. Thus it won't be possible to modify the contents of `.text` or to execute code from `.data` anymore.
|
Also, we will use the [information about kernel sections] to map the various sections individually instead of blindly mapping the first gigabyte. To improve safety even further, we will set the correct page table flags for the various sections. Thus it won't be possible to modify the contents of `.text` or to execute code from `.data` anymore.
|
||||||
[information about kernel sections]: {{% relref "05-allocating-frames.md#kernel-elf-sections" %}}
|
|
||||||
|
[information about kernel sections]: ./posts/05-allocating-frames/index.md#kernel-elf-sections
|
||||||
|
|
||||||
## Preparation
|
## Preparation
|
||||||
There are many things that can go wrong when we switch to a new table. Therefore it's a good idea to [set up a debugger][set up gdb]. You should not need it when you follow this post, but it's good to know how to debug a problem when it occurs[^fn-debug-notes].
|
There are many things that can go wrong when we switch to a new table. Therefore it's a good idea to [set up a debugger][set up gdb]. You should not need it when you follow this post, but it's good to know how to debug a problem when it occurs[^fn-debug-notes].
|
||||||
[set up gdb]: {{% relref "set-up-gdb.md" %}}
|
[set up gdb]: ./extra/set-up-gdb/index.md
|
||||||
[^fn-debug-notes]: For this post the most useful GDB command is probably `p/x *((long int*)0xfffffffffffff000)@512`. It prints all entries of the recursively mapped P4 table by interpreting it as an array of 512 long ints (the `@512` is GDB's array syntax). Of course you can also print other tables by adjusting the address.
|
|
||||||
|
|
||||||
We also update the `Page` and `Frame` types to make our lives easier. The `Page` struct gets some derived traits:
|
We also update the `Page` and `Frame` types to make our lives easier. The `Page` struct gets some derived traits:
|
||||||
|
|
||||||
@@ -62,9 +66,7 @@ We can't do that for a `Frame`. If we wanted to be sure that a given frame is un
|
|||||||
## Recap: The Paging Module
|
## Recap: The Paging Module
|
||||||
This post builds upon the post about [page tables][previous post], so let's start by quickly recapitulating what we've done there.
|
This post builds upon the post about [page tables][previous post], so let's start by quickly recapitulating what we've done there.
|
||||||
|
|
||||||
We created a `memory::paging` module, which reads and modifies the hierarchical page table through recursive mapping. The owner of the active P4 table and thus all subtables is an `ActivePageTable`[^fn-apt-renamed] struct, which must be instantiated only once.
|
We created a `memory::paging` module, which reads and modifies the hierarchical page table through recursive mapping. The owner of the active P4 table and thus all subtables is an `ActivePageTable` struct, which must be instantiated only once.
|
||||||
|
|
||||||
[^fn-apt-renamed]: It was renamed in [#88](https://github.com/phil-opp/blog_os/pull/88). The previous name was `RecursivePageTable`.
|
|
||||||
|
|
||||||
The `ActivePageTable` struct provides the following interface:
|
The `ActivePageTable` struct provides the following interface:
|
||||||
|
|
||||||
@@ -273,7 +275,7 @@ pub fn map_table_frame(&mut self,
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
This function interprets the given frame as a page table frame and returns a `Table` reference. We return a table of level 1 because it [forbids calling the `next_table` methods][some clever solution]. Calling `next_table` must not be possible since it's not a page of the recursive mapping. To be able to return a `Table<Level1>`, we need to make the `Level1` enum in `memory/paging/table.rs` public.
|
This function interprets the given frame as a page table frame and returns a `Table` reference. We return a table of level 1 because it [forbids calling the `next_table` methods][some clever solution]. Calling `next_table` must not be possible since it's not a page of the recursive mapping. To be able to return a `Table<Level1>`, we need to make the `Level1` enum in `memory/paging/table.rs` public.
|
||||||
[some clever solution]: {{% relref "06-page-tables.md#some-clever-solution" %}}
|
[some clever solution]: ./posts/06-page-tables/index.md#some-clever-solution
|
||||||
|
|
||||||
|
|
||||||
The `unsafe` block is safe since the `VirtualAddress` returned by the `map` function is always valid and the type cast just reinterprets the frame's content.
|
The `unsafe` block is safe since the `VirtualAddress` returned by the `map` function is always valid and the type cast just reinterprets the frame's content.
|
||||||
@@ -328,11 +330,11 @@ Recursive mapping works by mapping the last P4 entry to the P4 table itself. Thu
|
|||||||
|
|
||||||
For example, accessing a P3 table requires lopping three times:
|
For example, accessing a P3 table requires lopping three times:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
We can use the same mechanism to access inactive tables. The trick is to change the recursive mapping of the active P4 table to point to the inactive P4 table:
|
We can use the same mechanism to access inactive tables. The trick is to change the recursive mapping of the active P4 table to point to the inactive P4 table:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Now the inactive table can be accessed exactly as the active table, even the magic addresses are the same. This allows us to use the `ActivePageTable` interface and the existing mapping methods for inactive tables, too. Note that everything besides the recursive mapping continues to work exactly as before since we've never changed the active table in the CPU.
|
Now the inactive table can be accessed exactly as the active table, even the magic addresses are the same. This allows us to use the `ActivePageTable` interface and the existing mapping methods for inactive tables, too. Note that everything besides the recursive mapping continues to work exactly as before since we've never changed the active table in the CPU.
|
||||||
|
|
||||||
@@ -363,7 +365,7 @@ It overwrites the 511th P4 entry and points it to the inactive table frame. Then
|
|||||||
|
|
||||||
Now that the recursive mapping points to the given inactive table, we execute the closure in the new context. The closure can call all active table methods such as `translate` or `map_to`. It could even call `with` again and chain another inactive table! Wait… that would not work:
|
Now that the recursive mapping points to the given inactive table, we execute the closure in the new context. The closure can call all active table methods such as `translate` or `map_to`. It could even call `with` again and chain another inactive table! Wait… that would not work:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Here the closure called `with` again and thus changed the recursive mapping of the inactive table to point to a second inactive table. Now we want to modify the P1 of the _second_ inactive table, but instead we land on the P1 of the _first_ inactive table since we never follow the pointer to the second table. Only when modifying the P2, P3, or P4 table we really access the second inactive table. This inconsistency would break our mapping functions completely.
|
Here the closure called `with` again and thus changed the recursive mapping of the inactive table to point to a second inactive table. Now we want to modify the P1 of the _second_ inactive table, but instead we land on the P1 of the _first_ inactive table since we never follow the pointer to the second table. Only when modifying the P2, P3, or P4 table we really access the second inactive table. This inconsistency would break our mapping functions completely.
|
||||||
|
|
||||||
@@ -459,13 +461,13 @@ Why is it unsafe? Because reading the CR3 register leads to a CPU exception if t
|
|||||||
|
|
||||||
Now that we have a backup of the original P4 frame, we need a way to restore it after the closure has run. So we need to somehow modify the 511th entry of the original P4 frame, which is still the active table in the CPU. But we can't access it because the recursive mapping now points to the inactive table:
|
Now that we have a backup of the original P4 frame, we need a way to restore it after the closure has run. So we need to somehow modify the 511th entry of the original P4 frame, which is still the active table in the CPU. But we can't access it because the recursive mapping now points to the inactive table:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
It's just not possible to access the active P4 entry in 4 steps, so we can't reach it through the 4-level page table.
|
It's just not possible to access the active P4 entry in 4 steps, so we can't reach it through the 4-level page table.
|
||||||
|
|
||||||
We could try to overwrite the recursive mapping of the _inactive_ P4 table and point it back to the original P4 frame:
|
We could try to overwrite the recursive mapping of the _inactive_ P4 table and point it back to the original P4 frame:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Now we can reach the active P4 entry in 4 steps and could restore the original mapping in the active table. But this hack has a drawback: The inactive table is now invalid since it is no longer recursive mapped. We would need to fix it by using a temporary page again (as above).
|
Now we can reach the active P4 entry in 4 steps and could restore the original mapping in the active table. But this hack has a drawback: The inactive table is now invalid since it is no longer recursive mapped. We would need to fix it by using a temporary page again (as above).
|
||||||
|
|
||||||
@@ -542,7 +544,7 @@ pub fn remap_the_kernel<A>(allocator: &mut A, boot_info: &BootInformation)
|
|||||||
First, we create a temporary page at page number `0xcafebabe`. We could use `0xdeadbeaf` or `0x123456789` as well, as long as the page is unused. The `active_table` and the `new_table` are created using their constructor functions.
|
First, we create a temporary page at page number `0xcafebabe`. We could use `0xdeadbeaf` or `0x123456789` as well, as long as the page is unused. The `active_table` and the `new_table` are created using their constructor functions.
|
||||||
|
|
||||||
Then we use the `with` function to temporary change the recursive mapping and execute the closure as if the `new_table` were active. This allows us to map the sections in the new table without changing the active mapping. To get the kernel sections, we use the [Multiboot information structure].
|
Then we use the `with` function to temporary change the recursive mapping and execute the closure as if the `new_table` were active. This allows us to map the sections in the new table without changing the active mapping. To get the kernel sections, we use the [Multiboot information structure].
|
||||||
[Multiboot information structure]: {{% relref "05-allocating-frames.md#the-multiboot-information-structure" %}}
|
[Multiboot information structure]: ./posts/05-allocating-frames/index.md#the-multiboot-information-structure
|
||||||
|
|
||||||
Let's resolve the above `TODO` by identity mapping the sections:
|
Let's resolve the above `TODO` by identity mapping the sections:
|
||||||
|
|
||||||
@@ -791,7 +793,7 @@ Let's cross our fingers and run it…
|
|||||||
|
|
||||||
### Debugging
|
### Debugging
|
||||||
A QEMU boot loop indicates that some CPU exception occured. We can see all thrown CPU exception by starting QEMU with `-d int` (as described [here][qemu debugging]):
|
A QEMU boot loop indicates that some CPU exception occured. We can see all thrown CPU exception by starting QEMU with `-d int` (as described [here][qemu debugging]):
|
||||||
[qemu debugging]: {{% relref "03-set-up-rust.md#debugging" %}}
|
[qemu debugging]: ./posts/03-set-up-rust/index.md#debugging
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
> qemu-system-x86_64 -d int -no-reboot -cdrom build/os-x86_64.iso
|
> qemu-system-x86_64 -d int -no-reboot -cdrom build/os-x86_64.iso
|
||||||
@@ -811,12 +813,12 @@ These lines are the important ones. We can read many useful information from the
|
|||||||
[page fault error code]: http://wiki.osdev.org/Exceptions#Error_code
|
[page fault error code]: http://wiki.osdev.org/Exceptions#Error_code
|
||||||
|
|
||||||
- `IP=0008:000000000010ab97` or `pc=000000000010ab97`: The program counter register tells us that the exception occurred when the CPU tried to execute the instruction at `0x10ab97`. We can disassemble this address to see the corresponding function. The `0008:` prefix in `IP` indicates the code [GDT segment].
|
- `IP=0008:000000000010ab97` or `pc=000000000010ab97`: The program counter register tells us that the exception occurred when the CPU tried to execute the instruction at `0x10ab97`. We can disassemble this address to see the corresponding function. The `0008:` prefix in `IP` indicates the code [GDT segment].
|
||||||
[GDT segment]: {{% relref "02-entering-longmode.md#loading-the-gdt" %}}
|
[GDT segment]: ./posts/02-entering-longmode/index.md#loading-the-gdt
|
||||||
|
|
||||||
- `SP=0010:00000000001182d0`: The stack pointer was `0x1182d0` (the `0010:` prefix indicates the data [GDT segment]). This tells us if it the stack overflowed.
|
- `SP=0010:00000000001182d0`: The stack pointer was `0x1182d0` (the `0010:` prefix indicates the data [GDT segment]). This tells us if it the stack overflowed.
|
||||||
|
|
||||||
- `CR2=00000000000b8f00`: Finally the most useful register. It tells us which virtual address caused the page fault. In our case it's `0xb8f00`, which is part of the [VGA text buffer].
|
- `CR2=00000000000b8f00`: Finally the most useful register. It tells us which virtual address caused the page fault. In our case it's `0xb8f00`, which is part of the [VGA text buffer].
|
||||||
[VGA text buffer]: {{% relref "04-printing-to-screen.md#the-vga-text-buffer" %}}
|
[VGA text buffer]: ./posts/04-printing-to-screen/index.md#the-vga-text-buffer
|
||||||
|
|
||||||
So let's find out which function caused the exception:
|
So let's find out which function caused the exception:
|
||||||
|
|
||||||
@@ -1011,7 +1013,7 @@ If we haven't forgotten to set the `WRITABLE` flag somewhere, it should still wo
|
|||||||
The final step is to create a guard page for our kernel stack.
|
The final step is to create a guard page for our kernel stack.
|
||||||
|
|
||||||
The decision to place the kernel stack right above the page tables was already useful to detect a silent stack overflow in the [previous post][silent stack overflow]. Now we profit from it again. Let's look at our assembly `.bss` section again to understand why:
|
The decision to place the kernel stack right above the page tables was already useful to detect a silent stack overflow in the [previous post][silent stack overflow]. Now we profit from it again. Let's look at our assembly `.bss` section again to understand why:
|
||||||
[silent stack overflow]: {{% relref "06-page-tables.md#translate" %}}
|
[silent stack overflow]: ./posts/06-page-tables/index.md#translate
|
||||||
|
|
||||||
```nasm
|
```nasm
|
||||||
; in src/arch/x86_64/boot.asm
|
; in src/arch/x86_64/boot.asm
|
||||||
@@ -1071,7 +1073,10 @@ Unfortunately stack probes require compiler support. They already work on Window
|
|||||||
|
|
||||||
## What's next?
|
## What's next?
|
||||||
Now that we have a (mostly) safe kernel stack and a working page table module, we can add a virtual memory allocator. The [next post] will explore Rust's allocator API and create a very basic allocator. At the end of that post, we will be able to use Rust's allocation and collections types such as [Box], [Vec], or even [BTreeMap].
|
Now that we have a (mostly) safe kernel stack and a working page table module, we can add a virtual memory allocator. The [next post] will explore Rust's allocator API and create a very basic allocator. At the end of that post, we will be able to use Rust's allocation and collections types such as [Box], [Vec], or even [BTreeMap].
|
||||||
[next post]: {{% relref "08-kernel-heap.md" %}}
|
[next post]: ./posts/08-kernel-heap/index.md
|
||||||
[Box]: https://doc.rust-lang.org/nightly/alloc/boxed/struct.Box.html
|
[Box]: https://doc.rust-lang.org/nightly/alloc/boxed/struct.Box.html
|
||||||
[Vec]: https://doc.rust-lang.org/nightly/collections/vec/struct.Vec.html
|
[Vec]: https://doc.rust-lang.org/nightly/collections/vec/struct.Vec.html
|
||||||
[BTreeMap]: https://doc.rust-lang.org/nightly/collections/btree_map/struct.BTreeMap.html
|
[BTreeMap]: https://doc.rust-lang.org/nightly/collections/btree_map/struct.BTreeMap.html
|
||||||
|
|
||||||
|
## Footnotes
|
||||||
|
[^fn-debug-notes]: For this post the most useful GDB command is probably `p/x *((long int*)0xfffffffffffff000)@512`. It prints all entries of the recursively mapped P4 table by interpreting it as an array of 512 long ints (the `@512` is GDB's array syntax). Of course you can also print other tables by adjusting the address.
|
||||||
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
@@ -0,0 +1,819 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="900.00409"
|
||||||
|
height="530"
|
||||||
|
viewBox="-2141 2141 12027.89 7032.1014"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="recursive_mapping_access_p3.svg">
|
||||||
|
<metadata
|
||||||
|
id="metadata370">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs368" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="998"
|
||||||
|
id="namedview366"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="2.7755576e-17"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="236.56561"
|
||||||
|
inkscape:cy="267.91265"
|
||||||
|
inkscape:window-x="1080"
|
||||||
|
inkscape:window-y="568"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g4"
|
||||||
|
units="in" />
|
||||||
|
<g
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
id="g4"
|
||||||
|
transform="translate(-1087.0637,717.51407)">
|
||||||
|
<polyline
|
||||||
|
points="3365,2598 3365,3070"
|
||||||
|
id="polyline10"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="3189,2598 3189,3070"
|
||||||
|
id="polyline12"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="3012,2598 3012,3070"
|
||||||
|
id="polyline14"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="2834,2598 2834,3070"
|
||||||
|
id="polyline16"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="2657,2598 2657,3070"
|
||||||
|
id="polyline18"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="2480,2598 2480,3070"
|
||||||
|
id="polyline20"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="2303,2598 2303,3070"
|
||||||
|
id="polyline22"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="2125,2456 2125,3070"
|
||||||
|
id="polyline24"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="1948,2598 1948,3070"
|
||||||
|
id="polyline26"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="1772,2598 1772,3070"
|
||||||
|
id="polyline28"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="1594,2598 1594,3070"
|
||||||
|
id="polyline30"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="1417,2598 1417,3070"
|
||||||
|
id="polyline32"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="1239,2598 1239,3070"
|
||||||
|
id="polyline34"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="1063,2598 1063,3070"
|
||||||
|
id="polyline36"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="886,2598 886,3070"
|
||||||
|
id="polyline38"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="708,2456 708,3070"
|
||||||
|
id="polyline40"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="3543,2456 3543,3070"
|
||||||
|
id="polyline74"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="-2125,2598 3543,2598"
|
||||||
|
id="polyline76"
|
||||||
|
transform="matrix(0.70622791,0,0,1.3955346,455.54989,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="3543,3070 -1948,3070 -2125,3070"
|
||||||
|
id="polyline78"
|
||||||
|
transform="matrix(0.70351457,0,0,1.3955346,465.16321,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="9035,2598 9035,3070"
|
||||||
|
id="polyline80"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="8858,2598 8858,3070"
|
||||||
|
id="polyline82"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="8681,2598 8681,3070"
|
||||||
|
id="polyline84"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="8503,2598 8503,3070"
|
||||||
|
id="polyline86"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="8326,2598 8326,3070"
|
||||||
|
id="polyline88"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="8150,2598 8150,3070"
|
||||||
|
id="polyline90"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="7972,2598 7972,3070"
|
||||||
|
id="polyline92"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="7795,2456 7795,3070"
|
||||||
|
id="polyline94"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="7617,2598 7617,3070"
|
||||||
|
id="polyline96"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="7441,2598 7441,3070"
|
||||||
|
id="polyline98"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="7264,2598 7264,3070"
|
||||||
|
id="polyline100"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="7086,2598 7086,3070"
|
||||||
|
id="polyline102"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="6909,2598 6909,3070"
|
||||||
|
id="polyline104"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="6732,2598 6732,3070"
|
||||||
|
id="polyline106"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="6555,2598 6555,3070"
|
||||||
|
id="polyline108"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="6377,2456 6377,3070"
|
||||||
|
id="polyline110"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="6200,2598 6200,3070"
|
||||||
|
id="polyline112"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="6024,2598 6024,3070"
|
||||||
|
id="polyline114"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="5846,2598 5846,3070"
|
||||||
|
id="polyline116"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="5669,2598 5669,3070"
|
||||||
|
id="polyline118"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="5491,2598 5491,3070"
|
||||||
|
id="polyline120"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="5315,2598 5315,3070"
|
||||||
|
id="polyline122"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="5138,2598 5138,3070"
|
||||||
|
id="polyline124"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="4960,2409 4960,3070"
|
||||||
|
id="polyline126"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="4783,2598 4783,3070"
|
||||||
|
id="polyline128"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="4606,2598 4606,3070"
|
||||||
|
id="polyline130"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="4429,2598 4429,3070"
|
||||||
|
id="polyline132"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="4251,2598 4251,3070"
|
||||||
|
id="polyline134"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="4074,2598 4074,3070"
|
||||||
|
id="polyline136"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="3898,2598 3898,3070"
|
||||||
|
id="polyline138"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="3720,2598 3720,3070"
|
||||||
|
id="polyline140"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="9212,2456 9212,3070"
|
||||||
|
id="polyline142"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="3543,2598 9212,2598"
|
||||||
|
id="polyline144"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="9212,3070 3720,3070 0,3070"
|
||||||
|
id="polyline146"
|
||||||
|
transform="matrix(1.3047439,0,0,1.3955346,-1051.0007,-1956.6141)" />
|
||||||
|
<rect
|
||||||
|
x="4021.6787"
|
||||||
|
y="4311.5088"
|
||||||
|
width="1668.8131"
|
||||||
|
height="3296.2527"
|
||||||
|
rx="0"
|
||||||
|
style="fill:#dfdfdf;stroke-width:11.23412323"
|
||||||
|
id="rect150" />
|
||||||
|
<polyline
|
||||||
|
points="3118,3590 2834,3779"
|
||||||
|
style="stroke:#008200;stroke-opacity:1"
|
||||||
|
id="polyline178"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="1322,3590 1039,3779"
|
||||||
|
style="stroke:#008200;stroke-opacity:1"
|
||||||
|
id="polyline180"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="4913,3590 4629,3779"
|
||||||
|
style="stroke:#008200;stroke-opacity:1"
|
||||||
|
id="polyline184"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="6519,3590 6236,3779"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline186"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="8314,3590 8031,3779"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline188"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="9212,3070 9070,3307 8314,3307 8173,3543 8031,3307 7228,3307 7086,3070"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline204"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="3898,3070 4015,3307 4629,3307 4771,3543 4913,3307 5385,3307 5491,3070"
|
||||||
|
style="stroke:#008200;stroke-opacity:1"
|
||||||
|
id="polyline206"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="5491,3070 5622,3307 6236,3307 6377,3543 6519,3307 6909,3307 7086,3070"
|
||||||
|
style="stroke:#0000ff"
|
||||||
|
id="polyline208"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="2303,3070 2409,3307 2834,3307 2976,3543 3118,3307 3779,3307 3898,3070"
|
||||||
|
style="stroke:#008200;stroke-opacity:1"
|
||||||
|
id="polyline210"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<polyline
|
||||||
|
points="708,3070 850,3307 1039,3307 1181,3543 1322,3307 2173,3307 2303,3070"
|
||||||
|
style="stroke:#008200;stroke-opacity:1"
|
||||||
|
id="polyline212"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)" />
|
||||||
|
<g
|
||||||
|
style="fill:#000000;stroke-width:0"
|
||||||
|
id="g242"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-2048.7241,-1956.6141)">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="5546.7881"
|
||||||
|
y="6876.4409"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text320-2-1-5"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||||
|
id="tspan3503-48-5-5">0</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="3507"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text246"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">32</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="2161"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text248"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">39</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="2090"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text250"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">40</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="744"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text252"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">47</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="672"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text254"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="-672"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text256"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="-744"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text258"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="-2090"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text260"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="9176"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text262"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">0</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="7759"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text264"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">8</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="6342"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text266"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">16</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="4924"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text268"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:end">24</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="3579"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text270"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">31</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="6413"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text272"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">15</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="7830"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text274"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">7</text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="4996"
|
||||||
|
y="2551"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text276"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:'Helvetica Narrow';text-anchor:start">23</text>
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-1,1,0,8976,6614)"
|
||||||
|
id="g282" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="-2125"
|
||||||
|
y="2314"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text290"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||||
|
<g
|
||||||
|
transform="matrix(0,-1,1,0,4916.9545,5254.403)"
|
||||||
|
id="g306">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="404"
|
||||||
|
id="text308"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="3874"
|
||||||
|
y="3968"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text318"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="4862.9551"
|
||||||
|
y="4397.5566"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text320"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||||
|
style="font-size:200.46391296px"
|
||||||
|
id="tspan3503">P4 table</tspan></text>
|
||||||
|
<text
|
||||||
|
x="2031"
|
||||||
|
y="5622"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text346"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle" />
|
||||||
|
<text
|
||||||
|
x="3165"
|
||||||
|
y="3732"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text350"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
|
||||||
|
<text
|
||||||
|
x="1370"
|
||||||
|
y="3732"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text352"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
|
||||||
|
<text
|
||||||
|
x="1370"
|
||||||
|
y="6708"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text354"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff" />
|
||||||
|
<text
|
||||||
|
x="4960"
|
||||||
|
y="3732"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text356"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#008200;fill-opacity:1;">9</text>
|
||||||
|
<text
|
||||||
|
x="6566"
|
||||||
|
y="3732"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text358"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">9</text>
|
||||||
|
<text
|
||||||
|
x="8362"
|
||||||
|
y="3732"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text360"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start;fill:#0000ff">12</text>
|
||||||
|
<text
|
||||||
|
x="1181"
|
||||||
|
y="7532.2715"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text364"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:start" />
|
||||||
|
<text
|
||||||
|
x="1946.0945"
|
||||||
|
y="6998.52"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text344-8-6"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.75857544px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
sodipodi:linespacing="125%">
|
||||||
|
<tspan
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:217.16923523px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||||
|
id="tspan3493-3-2" />
|
||||||
|
</text>
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:7.99999952;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
x="4294.9473"
|
||||||
|
y="4493.6152"
|
||||||
|
width="1181"
|
||||||
|
height="471.99994"
|
||||||
|
rx="0"
|
||||||
|
id="rect162-5" />
|
||||||
|
<circle
|
||||||
|
cx="5335.9546"
|
||||||
|
cy="4720.2134"
|
||||||
|
style="fill:#008200;stroke:#008200;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
|
||||||
|
id="circle172-4"
|
||||||
|
r="46.999996" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="4815.9546"
|
||||||
|
y="4773.6704"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text314-5"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||||
|
style="font-size:200.46391296px;stroke-width:0"
|
||||||
|
id="tspan3495-6">P4 entry</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="4925.6357"
|
||||||
|
y="7173.5298"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text314-5-0"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||||
|
style="font-size:200.46391296px;stroke-width:0"
|
||||||
|
id="tspan3495-6-4">Recursive</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="4925.6357"
|
||||||
|
y="7440.8149"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text314-5-0-9"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||||
|
style="font-size:200.46391296px;stroke-width:0"
|
||||||
|
id="tspan3495-6-4-4">Mapping</tspan></text>
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:6.53430033;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
x="4296.5396"
|
||||||
|
y="5560.2471"
|
||||||
|
width="1182.4656"
|
||||||
|
height="314.50067"
|
||||||
|
rx="0"
|
||||||
|
id="rect162-5-1" />
|
||||||
|
<g
|
||||||
|
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
transform="matrix(0,-0.99999994,0.99999994,0,4915.3734,6277.1653)"
|
||||||
|
id="g306-2">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="404"
|
||||||
|
id="text308-3"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:404px;font-family:Helvetica;text-anchor:middle">...</text>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="3097.1609"
|
||||||
|
y="2954.5652"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text320-4"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
inkscape:transform-center-x="-789.14735"
|
||||||
|
inkscape:transform-center-y="1300.106"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:318.2364502px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:0"
|
||||||
|
id="tspan3503-4">111111111111111111111111111</tspan></text>
|
||||||
|
<rect
|
||||||
|
x="8208.377"
|
||||||
|
y="4073.4419"
|
||||||
|
width="691.0863"
|
||||||
|
height="3268.5144"
|
||||||
|
rx="0"
|
||||||
|
style="fill:#dfdfdf;stroke:#000000;stroke-width:7.85773897;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
id="rect8" />
|
||||||
|
<rect
|
||||||
|
x="8208.377"
|
||||||
|
y="5474.9399"
|
||||||
|
width="691.0863"
|
||||||
|
height="233.25354"
|
||||||
|
rx="0"
|
||||||
|
style="fill:#dfdfdf;stroke:#000000;stroke-width:7.85773897;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
id="rect158" />
|
||||||
|
<g
|
||||||
|
style="fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
transform="matrix(0,-0.98836234,0.97611052,0,9106.1205,5822.055)"
|
||||||
|
id="g286">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="96.892052"
|
||||||
|
y="38.756821"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text288"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle"><tspan
|
||||||
|
style="font-size:200.46391296px"
|
||||||
|
id="tspan3509">P3 table </tspan></text>
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:14.53380773;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
d="m 2308.9448,-2797.8507 -483.0169,0 0,-2544.6425 -1879.786585,0 0,339.1222"
|
||||||
|
id="path8874"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="4809.9756"
|
||||||
|
y="5774.4951"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text314-5-5"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:152px;font-family:Helvetica;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"><tspan
|
||||||
|
style="font-size:200.46392822px;stroke-width:0"
|
||||||
|
id="tspan3495-6-7">P4 entry</tspan></text>
|
||||||
|
<ellipse
|
||||||
|
ry="46.999996"
|
||||||
|
rx="46.999992"
|
||||||
|
cx="5336.855"
|
||||||
|
cy="5714.1816"
|
||||||
|
style="fill:#0000ff;stroke:#0000ff;stroke-width:31.99999809;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
id="circle172-4-4" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:9.55735683px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 5392.0276,5712.0004 1512.0496,0 0,1631.4957 1257.8109,0"
|
||||||
|
id="path9464"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#0000ff;stroke-width:9.51690483px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 8173.2452,3542.5202 0,364.8071 -584.0141,0 0,1802.0952 528.0052,0"
|
||||||
|
id="path9466"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
x="5610.3569"
|
||||||
|
y="4990.1772"
|
||||||
|
font-style="normal"
|
||||||
|
font-weight="normal"
|
||||||
|
font-size="152"
|
||||||
|
id="text320-2-1"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:130.85745239px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:'Helvetica, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#4d4d4d;stroke-width:0"
|
||||||
|
id="tspan3503-48-5">511</tspan></text>
|
||||||
|
</g>
|
||||||
|
<polygon
|
||||||
|
points="3308,6614 3150,6566 3150,6661 3150,6661 "
|
||||||
|
style="fill:#008200;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
|
||||||
|
id="polygon232-2"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-646.4618,-1622.6914)" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.76697159px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 5574.5569,4637.287 719.9709,0 0,3526.7653 -2765.702,0 0,-560.545 294.8212,0"
|
||||||
|
id="path5354"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.52740288px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 3843.9848,5007.8363 -4223.58417,0 0,-2024.2908"
|
||||||
|
id="path5356"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.65159035px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 2156.7076,2986.8221 0,1988.2634 1691.863,0"
|
||||||
|
id="path5358"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#008200;stroke-width:18.78581619px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 4696.351,2980.052 0,497.5653 -1975.7645,0 0,1464.6642 1128.3337,0"
|
||||||
|
id="path5360"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<polygon
|
||||||
|
points="3150,6661 3308,6614 3150,6566 3150,6661 "
|
||||||
|
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
id="polygon232-2-7"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-625.83944,-2988.2046)" />
|
||||||
|
<polygon
|
||||||
|
points="3150,6661 3150,6661 3308,6614 3150,6566 "
|
||||||
|
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
id="polygon232-2-7-3"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,4891.7749,-3219.444)" />
|
||||||
|
<polygon
|
||||||
|
points="3150,6661 3150,6661 3308,6614 3150,6566 "
|
||||||
|
style="fill:#0000ff;stroke:#0000ff;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter"
|
||||||
|
id="polygon232-2-7-3-5"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,4896.989,-940.27671)" />
|
||||||
|
<polygon
|
||||||
|
points="3150,6566 3150,6661 3150,6661 3308,6614 "
|
||||||
|
style="fill:#008200;stroke:#008200;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;fill-opacity:1;stroke-opacity:1"
|
||||||
|
id="polygon232-2-7-7"
|
||||||
|
transform="matrix(1.4130509,0,0,1.3955346,-628.09888,-4256.4264)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
@@ -1,16 +1,18 @@
|
|||||||
+++
|
+++
|
||||||
title = "Kernel Heap"
|
title = "Kernel Heap"
|
||||||
|
order = 8
|
||||||
|
url = "kernel-heap"
|
||||||
date = "2016-04-11"
|
date = "2016-04-11"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In the previous posts we have created a [frame allocator] and a [page table module]. Now we are ready to create a kernel heap and a memory allocator. Thus, we will unlock `Box`, `Vec`, `BTreeMap`, and the rest of the [alloc] and [collections] crates.
|
In the previous posts we have created a [frame allocator] and a [page table module]. Now we are ready to create a kernel heap and a memory allocator. Thus, we will unlock `Box`, `Vec`, `BTreeMap`, and the rest of the [alloc] and [collections] crates.
|
||||||
|
|
||||||
[frame allocator]: {{% relref "05-allocating-frames.md" %}}
|
[frame allocator]: ./posts/05-allocating-frames/index.md
|
||||||
[page table module]: {{% relref "06-page-tables.md" %}}
|
[page table module]: ./posts/06-page-tables/index.md
|
||||||
[alloc]: https://doc.rust-lang.org/nightly/alloc/index.html
|
[alloc]: https://doc.rust-lang.org/nightly/alloc/index.html
|
||||||
[collections]: https://doc.rust-lang.org/nightly/collections/index.html
|
[collections]: https://doc.rust-lang.org/nightly/collections/index.html
|
||||||
|
|
||||||
<!--more--><aside id="toc"></aside>
|
<!-- more --><aside id="toc"></aside>
|
||||||
|
|
||||||
As always, you can find the complete source code on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page.
|
As always, you can find the complete source code on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page.
|
||||||
|
|
||||||
@@ -468,8 +470,8 @@ That's it. Now our `memory::init` function can only be called once. The macro wo
|
|||||||
### Mapping the Heap
|
### Mapping the Heap
|
||||||
Now we're ready to map the heap pages. In order to do it, we need access to the `ActivePageTable` or `Mapper` instance (see the [page table] and [kernel remapping] posts). Therefore we return it from the `paging::remap_the_kernel` function:
|
Now we're ready to map the heap pages. In order to do it, we need access to the `ActivePageTable` or `Mapper` instance (see the [page table] and [kernel remapping] posts). Therefore we return it from the `paging::remap_the_kernel` function:
|
||||||
|
|
||||||
[page table]: {{% relref "06-page-tables.md" %}}
|
[page table]: ./posts/06-page-tables/index.md
|
||||||
[kernel remapping]: {{% relref "07-remap-the-kernel.md" %}}
|
[kernel remapping]: ./posts/07-remap-the-kernel/index.md
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// in src/memory/paging/mod.rs
|
// in src/memory/paging/mod.rs
|
||||||
@@ -620,25 +622,25 @@ We will choose another solution with different tradoffs. It's not clearly “bet
|
|||||||
|
|
||||||
A freed memory block is not used anymore and no one needs the stored information. It is still mapped to a virtual address and backed by a physical page. So we just store the information about the freed block _in the block itself_. We keep a pointer to the first block and store a pointer to the next block in each block. Thus, we create a single linked list:
|
A freed memory block is not used anymore and no one needs the stored information. It is still mapped to a virtual address and backed by a physical page. So we just store the information about the freed block _in the block itself_. We keep a pointer to the first block and store a pointer to the next block in each block. Thus, we create a single linked list:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
In the following, we call a freed block a _hole_. Each hole stores its size and a pointer to the next hole. If a hole is larger than needed, we leave the remaining memory unused. By storing a pointer to the first hole, we are able to traverse the complete list.
|
In the following, we call a freed block a _hole_. Each hole stores its size and a pointer to the next hole. If a hole is larger than needed, we leave the remaining memory unused. By storing a pointer to the first hole, we are able to traverse the complete list.
|
||||||
|
|
||||||
#### Initialization
|
#### Initialization
|
||||||
When the heap is created, all of its memory is unused. Thus, it forms a single large hole:
|
When the heap is created, all of its memory is unused. Thus, it forms a single large hole:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The optional pointer to the next hole is set to `None`.
|
The optional pointer to the next hole is set to `None`.
|
||||||
|
|
||||||
#### Allocation
|
#### Allocation
|
||||||
In order to allocate a block of memory, we need to find a hole that satisfies the size and alignment requirements. If the found hole is larger than required, we split it into two smaller holes. For example, when we allocate a 24 byte block right after initialization, we split the single hole into a hole of size 24 and a hole with the remaining size:
|
In order to allocate a block of memory, we need to find a hole that satisfies the size and alignment requirements. If the found hole is larger than required, we split it into two smaller holes. For example, when we allocate a 24 byte block right after initialization, we split the single hole into a hole of size 24 and a hole with the remaining size:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Then we use the new 24 byte hole to perform the allocation:
|
Then we use the new 24 byte hole to perform the allocation:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
To find a suitable hole, we can use several search strategies:
|
To find a suitable hole, we can use several search strategies:
|
||||||
|
|
||||||
@@ -657,11 +659,11 @@ However, both best fit and worst fit have a significant problem: They need to sc
|
|||||||
#### Deallocation
|
#### Deallocation
|
||||||
To deallocate a block of memory, we can just insert its corresponding hole somewhere into the list. However, we need to merge adjacent holes. Otherwise, we are unable to reuse the freed memory for larger allocations. For example:
|
To deallocate a block of memory, we can just insert its corresponding hole somewhere into the list. However, we need to merge adjacent holes. Otherwise, we are unable to reuse the freed memory for larger allocations. For example:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
In order to use these adjacent holes for a large allocation, we need to merge them to a single large hole first:
|
In order to use these adjacent holes for a large allocation, we need to merge them to a single large hole first:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The easiest way to ensure that adjacent holes are always merged, is to keep the hole list sorted by address. Thus, we only need to check the predecessor and the successor in the list when we free a memory block. If they are adjacent to the freed block, we merge the corresponding holes. Else, we insert the freed block as a new hole at the correct position.
|
The easiest way to ensure that adjacent holes are always merged, is to keep the hole list sorted by address. Thus, we only need to check the predecessor and the successor in the list when we free a memory block. If they are adjacent to the freed block, we merge the corresponding holes. Else, we insert the freed block as a new hole at the correct position.
|
||||||
|
|
||||||
@@ -675,12 +677,10 @@ The detailed implementation would go beyond the scope of this post, since it con
|
|||||||
I created the [linked_list_allocator] crate to handle all of these cases. It consists of a [Heap struct] that provides an `allocate_first_fit` and a `deallocate` method. If you are interested in the implementation details, check out the [source code][linked_list_allocator source].
|
I created the [linked_list_allocator] crate to handle all of these cases. It consists of a [Heap struct] that provides an `allocate_first_fit` and a `deallocate` method. If you are interested in the implementation details, check out the [source code][linked_list_allocator source].
|
||||||
|
|
||||||
[linked_list_allocator]: https://crates.io/crates/linked_list_allocator
|
[linked_list_allocator]: https://crates.io/crates/linked_list_allocator
|
||||||
[Heap struct]: http://phil-opp.github.io/linked-list-allocator/linked_list_allocator/struct.Heap.html
|
[Heap struct]: http://phil-opp.github.io/linked_list_allocator/struct.Heap.html
|
||||||
[linked_list_allocator source]: https://github.com/phil-opp/linked-list-allocator
|
[linked_list_allocator source]: https://github.com/phil-opp/linked-list-allocator
|
||||||
|
|
||||||
So we just need to implement Rust's allocation modules and integrate it into our kernel. We start by creating a new `hole_list_allocator`[^1] crate inside the `libs` directory:
|
So we just need to implement Rust's allocation modules and integrate it into our kernel. We start by creating a new `hole_list_allocator` crate inside the `libs` directory:
|
||||||
|
|
||||||
[^1]: The name `linked_list_allocator` is already taken, sorry :P.
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
> cd libs
|
> cd libs
|
||||||
@@ -860,4 +860,4 @@ Now we're able to use heap storage in our kernel without leaking memory. This al
|
|||||||
## What's next?
|
## What's next?
|
||||||
This post concludes the section about memory management for now. We will revisit this topic eventually, but now it's time to explore other topics. The upcoming posts will be about CPU exceptions and interrupts. We will catch all page, double, and triple faults and create a driver to read keyboard input. The [next post] starts by setting up a so-called _Interrupt Descriptor Table_.
|
This post concludes the section about memory management for now. We will revisit this topic eventually, but now it's time to explore other topics. The upcoming posts will be about CPU exceptions and interrupts. We will catch all page, double, and triple faults and create a driver to read keyboard input. The [next post] starts by setting up a so-called _Interrupt Descriptor Table_.
|
||||||
|
|
||||||
[next post]: {{% relref "09-handling-exceptions.md" %}}
|
[next post]: ./posts/09-handling-exceptions/index.md
|
||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
@@ -1,5 +1,7 @@
|
|||||||
+++
|
+++
|
||||||
title = "Handling Exceptions"
|
title = "Handling Exceptions"
|
||||||
|
order = 9
|
||||||
|
url = "handling-exceptions"
|
||||||
date = "2017-03-26"
|
date = "2017-03-26"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
@@ -7,7 +9,7 @@ In this post, we start exploring CPU exceptions. Exceptions occur in various err
|
|||||||
|
|
||||||
[breakpoint exceptions]: http://wiki.osdev.org/Exceptions#Breakpoint
|
[breakpoint exceptions]: http://wiki.osdev.org/Exceptions#Breakpoint
|
||||||
|
|
||||||
<!--more--><aside id="toc"></aside>
|
<!-- more --><aside id="toc"></aside>
|
||||||
|
|
||||||
As always, the complete source code is available on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page.
|
As always, the complete source code is available on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page.
|
||||||
|
|
||||||
@@ -158,7 +160,7 @@ Since we don't know when an exception occurs, we can't backup any registers befo
|
|||||||
### The Exception Stack Frame
|
### The Exception Stack Frame
|
||||||
On a normal function call (using the `call` instruction), the CPU pushes the return address before jumping to the target function. On function return (using the `ret` instruction), the CPU pops this return address and jumps to it. So the stack frame of a normal function call looks like this:
|
On a normal function call (using the `call` instruction), the CPU pushes the return address before jumping to the target function. On function return (using the `ret` instruction), the CPU pops this return address and jumps to it. So the stack frame of a normal function call looks like this:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
For exception and interrupt handlers, however, pushing a return address would not suffice, since interrupt handlers often run in a different context (stack pointer, CPU flags, etc.). Instead, the CPU performs the following steps when an interrupt occurs:
|
For exception and interrupt handlers, however, pushing a return address would not suffice, since interrupt handlers often run in a different context (stack pointer, CPU flags, etc.). Instead, the CPU performs the following steps when an interrupt occurs:
|
||||||
|
|
||||||
@@ -174,7 +176,7 @@ For exception and interrupt handlers, however, pushing a return address would no
|
|||||||
|
|
||||||
So the _exception stack frame_ looks like this:
|
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.
|
||||||
|
|
||||||
@@ -191,7 +193,7 @@ The `x86-interrupt` calling convention is a powerful abstraction that hides almo
|
|||||||
If you are interested in more details: We also have a series of posts that explains exception handling using [naked functions] linked [at the end of this post][too-much-magic].
|
If you are interested in more details: We also have a series of posts that explains exception handling using [naked functions] linked [at the end of this post][too-much-magic].
|
||||||
|
|
||||||
[naked functions]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
|
[naked functions]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
|
||||||
[too-much-magic]: {{% relref "#too-much-magic" %}}
|
[too-much-magic]: #too-much-magic
|
||||||
|
|
||||||
## Implementation
|
## Implementation
|
||||||
Now that we've understood the theory, it's time to handle CPU exceptions in our kernel. We start by creating a new `interrupts` module:
|
Now that we've understood the theory, it's time to handle CPU exceptions in our kernel. We start by creating a new `interrupts` module:
|
||||||
@@ -225,7 +227,7 @@ The breakpoint exception is commonly used in debuggers: When the user sets a bre
|
|||||||
|
|
||||||
For our use case, we don't need to overwrite any instructions (it wouldn't even be possible since we [set the page table flags] to read-only). Instead, we just want to print a message when the breakpoint instruction is executed and then continue the program.
|
For our use case, we don't need to overwrite any instructions (it wouldn't even be possible since we [set the page table flags] to read-only). Instead, we just want to print a message when the breakpoint instruction is executed and then continue the program.
|
||||||
|
|
||||||
[set the page table flags]: {{% relref "07-remap-the-kernel.md#using-the-correct-flags" %}}
|
[set the page table flags]: ./posts/07-remap-the-kernel/index.md#using-the-correct-flags
|
||||||
|
|
||||||
So let's create a simple `breakpoint_handler` function and add it to our IDT:
|
So let's create a simple `breakpoint_handler` function and add it to our IDT:
|
||||||
|
|
||||||
@@ -404,7 +406,7 @@ pub extern "C" fn rust_main(...) {
|
|||||||
|
|
||||||
When we run it in QEMU now (using `make run`), we see the following:
|
When we run it in QEMU now (using `make run`), we see the following:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
It works! The CPU successfully invokes our breakpoint handler, which prints the message, and then returns back to the `rust_main` function, where the `It did not crash!` message is printed.
|
It works! The CPU successfully invokes our breakpoint handler, which prints the message, and then returns back to the `rust_main` function, where the `It did not crash!` message is printed.
|
||||||
|
|
||||||
@@ -442,8 +444,6 @@ The answer is that the stored instruction pointer only points to the causing ins
|
|||||||
|
|
||||||
The reason for the diffent instruction pointer values is that the stored value is also the return address. So for faults, the instruction that caused the exception is restarted and might cause the same exception again if it's not resolved. This would not make much sense for traps, since invoking the breakpoint exception again would just cause another breakpoint exception[^fn-breakpoint-restart-use-cases]. Thus the instruction pointer points to the _next_ instruction for these exceptions.
|
The reason for the diffent instruction pointer values is that the stored value is also the return address. So for faults, the instruction that caused the exception is restarted and might cause the same exception again if it's not resolved. This would not make much sense for traps, since invoking the breakpoint exception again would just cause another breakpoint exception[^fn-breakpoint-restart-use-cases]. Thus the instruction pointer points to the _next_ instruction for these exceptions.
|
||||||
|
|
||||||
[^fn-breakpoint-restart-use-cases]: There are valid use cases for restarting an instruction that caused a breakpoint. The most common use case is a debugger: When setting a breakpoint on some code line, the debugger overwrites the corresponding instruction with an `int3` instruction, so that the CPU traps when that line is executed. When the user continues execution, the debugger swaps in the original instruction and continues the program from the replaced instruction.
|
|
||||||
|
|
||||||
In some cases, the distinction between faults and traps is vague. For example, the [debug exception] behaves like a fault in some cases, but like a trap in others. So to find out the meaning of the saved instruction pointer, it is a good idea to read the official documentation for the exception, which can be found in the [AMD64 manual] in Section 8.2. For example, for the breakpoint exception it says:
|
In some cases, the distinction between faults and traps is vague. For example, the [debug exception] behaves like a fault in some cases, but like a trap in others. So to find out the meaning of the saved instruction pointer, it is a good idea to read the official documentation for the exception, which can be found in the [AMD64 manual] in Section 8.2. For example, for the breakpoint exception it says:
|
||||||
|
|
||||||
[debug exception]: http://wiki.osdev.org/Exceptions#Debug
|
[debug exception]: http://wiki.osdev.org/Exceptions#Debug
|
||||||
@@ -459,10 +459,13 @@ The documentation of the [`Idt`] struct and the [OSDev Wiki][osdev wiki exceptio
|
|||||||
## Too much Magic?
|
## Too much Magic?
|
||||||
The `x86-interrupt` calling convention and the [`Idt`] 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.
|
The `x86-interrupt` calling convention and the [`Idt`] 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.
|
||||||
|
|
||||||
[“Handling Exceptions with Naked Functions”]: {{% relref "handling-exceptions-with-naked-fns.html" %}}
|
[“Handling Exceptions with Naked Functions”]: /extra/handling-exceptions-with-naked-fns
|
||||||
|
|
||||||
## What's next?
|
## What's next?
|
||||||
We've successfully caught our first exception and returned from it! The next step is to add handlers for other common exceptions such as page faults. We also need to make sure that we never cause a [triple fault], since it causes a complete system reset. The next post explains how we can avoid this by correctly catching [double faults].
|
We've successfully caught our first exception and returned from it! The next step is to add handlers for other common exceptions such as page faults. We also need to make sure that we never cause a [triple fault], since it causes a complete system reset. The next post explains how we can avoid this by correctly catching [double faults].
|
||||||
|
|
||||||
[triple fault]: http://wiki.osdev.org/Triple_Fault
|
[triple fault]: http://wiki.osdev.org/Triple_Fault
|
||||||
[double faults]: http://wiki.osdev.org/Double_Fault#Double_Fault
|
[double faults]: http://wiki.osdev.org/Double_Fault#Double_Fault
|
||||||
|
|
||||||
|
## Footnotes
|
||||||
|
[^fn-breakpoint-restart-use-cases]: There are valid use cases for restarting an instruction that caused a breakpoint. The most common use case is a debugger: When setting a breakpoint on some code line, the debugger overwrites the corresponding instruction with an `int3` instruction, so that the CPU traps when that line is executed. When the user continues execution, the debugger swaps in the original instruction and continues the program from the replaced instruction.
|
||||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
@@ -1,30 +1,31 @@
|
|||||||
+++
|
+++
|
||||||
title = "Double Faults"
|
title = "Double Faults"
|
||||||
|
order = 10
|
||||||
|
url = "double-faults"
|
||||||
date = "2017-01-02"
|
date = "2017-01-02"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In this post we explore double faults in detail. We also set up an _Interrupt Stack Table_ to catch double faults on a separate kernel stack. This way, we can completely prevent triple faults, even on kernel stack overflow.
|
In this post we explore double faults in detail. We also set up an _Interrupt Stack Table_ to catch double faults on a separate kernel stack. This way, we can completely prevent triple faults, even on kernel stack overflow.
|
||||||
|
|
||||||
<!--more--><aside id="toc"></aside>
|
<!-- more --><aside id="toc"></aside>
|
||||||
|
|
||||||
As always, the complete source code is available on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a [gitter chat] and a [comment section] at the end of this page.
|
As always, the complete source code is available on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a [gitter chat] and a comment section at the end of this page.
|
||||||
|
|
||||||
[Github]: https://github.com/phil-opp/blog_os/tree/post_10
|
[Github]: https://github.com/phil-opp/blog_os/tree/post_10
|
||||||
[issues]: https://github.com/phil-opp/blog_os/issues
|
[issues]: https://github.com/phil-opp/blog_os/issues
|
||||||
[gitter chat]: https://gitter.im/phil-opp/blog_os
|
[gitter chat]: https://gitter.im/phil-opp/blog_os
|
||||||
[comment section]: #disqus_thread
|
|
||||||
|
|
||||||
## What is a Double Fault?
|
## What is a Double Fault?
|
||||||
In simplified terms, a double fault is a special exception that occurs when the CPU fails to invoke an exception handler. For example, it occurs when a page fault is triggered but there is no page fault handler registered in the [Interrupt Descriptor Table][IDT] (IDT). So it's kind of similar to catch-all blocks in programming languages with exceptions, e.g. `catch(...)` in C++ or `catch(Exception e)` in Java or C#.
|
In simplified terms, a double fault is a special exception that occurs when the CPU fails to invoke an exception handler. For example, it occurs when a page fault is triggered but there is no page fault handler registered in the [Interrupt Descriptor Table][IDT] (IDT). So it's kind of similar to catch-all blocks in programming languages with exceptions, e.g. `catch(...)` in C++ or `catch(Exception e)` in Java or C#.
|
||||||
|
|
||||||
[IDT]: {{% relref "09-handling-exceptions.md#the-interrupt-descriptor-table" %}}
|
[IDT]: ./posts/09-handling-exceptions/index.md#the-interrupt-descriptor-table
|
||||||
|
|
||||||
A double fault behaves like a normal exception. It has the vector number `8` and we can define a normal handler function for it in the IDT. It is really important to provide a double fault handler, because if a double fault is unhandled a fatal _triple fault_ occurs. Triple faults can't be caught and most hardware reacts with a system reset.
|
A double fault behaves like a normal exception. It has the vector number `8` and we can define a normal handler function for it in the IDT. It is really important to provide a double fault handler, because if a double fault is unhandled a fatal _triple fault_ occurs. Triple faults can't be caught and most hardware reacts with a system reset.
|
||||||
|
|
||||||
### Triggering a Double Fault
|
### Triggering a Double Fault
|
||||||
Let's provoke a double fault by triggering an exception for that we didn't define a handler function:
|
Let's provoke a double fault by triggering an exception for that we didn't define a handler function:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=10" >}}
|
```rust
|
||||||
// in src/lib.rs
|
// in src/lib.rs
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -41,13 +42,13 @@ pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
|||||||
println!("It did not crash!");
|
println!("It did not crash!");
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
We try to write to address `0xdeadbeaf`, but the corresponding page is not present in the page tables. Thus, a page fault occurs. We haven't registered a page fault handler in our [IDT], so a double fault occurs.
|
We try to write to address `0xdeadbeaf`, but the corresponding page is not present in the page tables. Thus, a page fault occurs. We haven't registered a page fault handler in our [IDT], so a double fault occurs.
|
||||||
|
|
||||||
When we start our kernel now, we see that it enters an endless boot loop:
|
When we start our kernel now, we see that it enters an endless boot loop:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The reason for the boot loop is the following:
|
The reason for the boot loop is the following:
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ So in order to prevent this triple fault, we need to either provide a handler fu
|
|||||||
### A Double Fault Handler
|
### A Double Fault Handler
|
||||||
A double fault is a normal exception with an error code, so we can use our `handler_with_error_code` macro to create a wrapper function:
|
A double fault is a normal exception with an error code, so we can use our `handler_with_error_code` macro to create a wrapper function:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=8 14" >}}
|
```rust
|
||||||
// in src/interrupts.rs
|
// in src/interrupts.rs
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -82,13 +83,13 @@ extern "x86-interrupt" fn double_fault_handler(
|
|||||||
println!("\nEXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
|
println!("\nEXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
{{< / highlight >}}<!--end_-->
|
```
|
||||||
|
|
||||||
Our handler prints a short error message and dumps the exception stack frame. The error code of the double fault handler is always zero, so there's no reason to print it.
|
Our handler prints a short error message and dumps the exception stack frame. The error code of the double fault handler is always zero, so there's no reason to print it.
|
||||||
|
|
||||||
When we start our kernel now, we should see that the double fault handler is invoked:
|
When we start our kernel now, we should see that the double fault handler is invoked:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
It worked! Here is what happens this time:
|
It worked! Here is what happens this time:
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ For example, what happens if… :
|
|||||||
3. a divide-by-zero handler causes a breakpoint exception, but the breakpoint handler is swapped out?
|
3. a divide-by-zero handler causes a breakpoint exception, but the breakpoint handler is swapped out?
|
||||||
4. our kernel overflows its stack and the [guard page] is hit?
|
4. our kernel overflows its stack and the [guard page] is hit?
|
||||||
|
|
||||||
[guard page]: {{% relref "07-remap-the-kernel.md#creating-a-guard-page" %}}
|
[guard page]: ./posts/07-remap-the-kernel/index.md#creating-a-guard-page
|
||||||
|
|
||||||
Fortunately, the AMD64 manual ([PDF][AMD64 manual]) has an exact definition (in Section 8.2.9). According to it, a “double fault exception _can_ occur when a second exception occurs during the handling of a prior (first) exception handler”. The _“can”_ is important: Only very specific combinations of exceptions lead to a double fault. These combinations are:
|
Fortunately, the AMD64 manual ([PDF][AMD64 manual]) has an exact definition (in Section 8.2.9). According to it, a “double fault exception _can_ occur when a second exception occurs during the handling of a prior (first) exception handler”. The _“can”_ is important: Only very specific combinations of exceptions lead to a double fault. These combinations are:
|
||||||
|
|
||||||
@@ -158,7 +159,7 @@ So the CPU tries to call our _double fault handler_ now. However, on a double fa
|
|||||||
|
|
||||||
Let's try it ourselves! We can easily provoke a kernel stack overflow by calling a function that recurses endlessly:
|
Let's try it ourselves! We can easily provoke a kernel stack overflow by calling a function that recurses endlessly:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=9 10 11 14" >}}
|
```rust
|
||||||
// in src/lib.rs
|
// in src/lib.rs
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -177,7 +178,7 @@ pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
|||||||
println!("It did not crash!");
|
println!("It did not crash!");
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
When we try this code in QEMU, we see that the system enters a boot-loop again.
|
When we try this code in QEMU, we see that the system enters a boot-loop again.
|
||||||
|
|
||||||
@@ -196,7 +197,7 @@ struct InterruptStackTable {
|
|||||||
|
|
||||||
For each exception handler, we can choose an stack from the IST through the `options` field in the corresponding [IDT entry]. For example, we could use the first stack in the IST for our double fault handler. Then the CPU would automatically switch to this stack whenever a double fault occurs. This switch would happen before anything is pushed, so it would prevent the triple fault.
|
For each exception handler, we can choose an stack from the IST through the `options` field in the corresponding [IDT entry]. For example, we could use the first stack in the IST for our double fault handler. Then the CPU would automatically switch to this stack whenever a double fault occurs. This switch would happen before anything is pushed, so it would prevent the triple fault.
|
||||||
|
|
||||||
[IDT entry]: {{% relref "09-handling-exceptions.md#the-interrupt-descriptor-table" %}}
|
[IDT entry]: ./posts/09-handling-exceptions/index.md#the-interrupt-descriptor-table
|
||||||
|
|
||||||
### Allocating a new Stack
|
### Allocating a new Stack
|
||||||
In order to fill an Interrupt Stack Table later, we need a way to allocate new stacks. Therefore we extend our `memory` module with a new `stack_allocator` submodule:
|
In order to fill an Interrupt Stack Table later, we need a way to allocate new stacks. Therefore we extend our `memory` module with a new `stack_allocator` submodule:
|
||||||
@@ -227,7 +228,7 @@ impl StackAllocator {
|
|||||||
```
|
```
|
||||||
We create a simple `StackAllocator` that allocates stacks from a given range of pages (`PageIter` is an Iterator over a range of pages; we introduced it [in the kernel heap post].).
|
We create a simple `StackAllocator` that allocates stacks from a given range of pages (`PageIter` is an Iterator over a range of pages; we introduced it [in the kernel heap post].).
|
||||||
|
|
||||||
[in the kernel heap post]: {{% relref "08-kernel-heap.md#mapping-the-heap" %}}
|
[in the kernel heap post]: ./posts/08-kernel-heap/index.md#mapping-the-heap
|
||||||
|
|
||||||
We add a `alloc_stack` method that allocates a new stack:
|
We add a `alloc_stack` method that allocates a new stack:
|
||||||
|
|
||||||
@@ -282,8 +283,8 @@ impl StackAllocator {
|
|||||||
```
|
```
|
||||||
The method takes mutable references to the [ActivePageTable] and a [FrameAllocator], since it needs to map the new virtual stack pages to physical frames. We define that the stack size is a multiple of the page size.
|
The method takes mutable references to the [ActivePageTable] and a [FrameAllocator], since it needs to map the new virtual stack pages to physical frames. We define that the stack size is a multiple of the page size.
|
||||||
|
|
||||||
[ActivePageTable]: {{% relref "06-page-tables.md#page-table-ownership" %}}
|
[ActivePageTable]: ./posts/06-page-tables/index.md#page-table-ownership
|
||||||
[FrameAllocator]: {{% relref "05-allocating-frames.md#a-frame-allocator" %}}
|
[FrameAllocator]: ./posts/05-allocating-frames/index.md#a-frame-allocator
|
||||||
|
|
||||||
Instead of operating directly on `self.range`, we [clone] it and only write it back on success. This way, subsequent stack allocations can still succeed if there are pages left (e.g., a call with `size_in_pages = 3` can still succeed after a failed call with `size_in_pages = 100`).
|
Instead of operating directly on `self.range`, we [clone] it and only write it back on success. This way, subsequent stack allocations can still succeed if there are pages left (e.g., a call with `size_in_pages = 3` can still succeed after a failed call with `size_in_pages = 100`).
|
||||||
|
|
||||||
@@ -294,7 +295,7 @@ In order to be able to clone `PageIter`, we add a `#[derive(Clone)]` to its defi
|
|||||||
The actual allocation is straightforward: First, we choose the next page as [guard page]. Then we choose the next `size_in_pages` pages as stack pages using [Iterator::nth]. If all three variables are `Some`, the allocation succeeded and we map the stack pages to physical frames using [ActivePageTable::map]. The guard page remains unmapped.
|
The actual allocation is straightforward: First, we choose the next page as [guard page]. Then we choose the next `size_in_pages` pages as stack pages using [Iterator::nth]. If all three variables are `Some`, the allocation succeeded and we map the stack pages to physical frames using [ActivePageTable::map]. The guard page remains unmapped.
|
||||||
|
|
||||||
[Iterator::nth]: https://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.nth
|
[Iterator::nth]: https://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.nth
|
||||||
[ActivePageTable::map]: {{% relref "06-page-tables.md#more-mapping-functions" %}}
|
[ActivePageTable::map]: ./posts/06-page-tables/index.md#more-mapping-functions
|
||||||
|
|
||||||
Finally, we create and return a new `Stack`, which we define as follows:
|
Finally, we create and return a new `Stack`, which we define as follows:
|
||||||
|
|
||||||
@@ -400,7 +401,7 @@ impl Add<usize> for Page {
|
|||||||
#### Allocating a Double Fault Stack
|
#### Allocating a Double Fault Stack
|
||||||
Now we can allocate a new double fault stack by passing the memory controller to our `interrupts::init` function:
|
Now we can allocate a new double fault stack by passing the memory controller to our `interrupts::init` function:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=8 11 12 21 22 23" >}}
|
```rust
|
||||||
// in src/lib.rs
|
// in src/lib.rs
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -427,7 +428,7 @@ pub fn init(memory_controller: &mut MemoryController) {
|
|||||||
|
|
||||||
IDT.load();
|
IDT.load();
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
We allocate a 4096 bytes stack (one page) for our double fault handler. Now we just need some way to tell the CPU that it should use this stack for handling double faults.
|
We allocate a 4096 bytes stack (one page) for our double fault handler. Now we just need some way to tell the CPU that it should use this stack for handling double faults.
|
||||||
|
|
||||||
@@ -468,7 +469,7 @@ use x86_64::structures::tss::TaskStateSegment;
|
|||||||
|
|
||||||
Let's create a new TSS in our `interrupts::init` function:
|
Let's create a new TSS in our `interrupts::init` function:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=3 9 10" >}}
|
```rust
|
||||||
// in src/interrupts.rs
|
// in src/interrupts.rs
|
||||||
|
|
||||||
use x86_64::VirtualAddress;
|
use x86_64::VirtualAddress;
|
||||||
@@ -485,7 +486,7 @@ pub fn init(memory_controller: &mut MemoryController) {
|
|||||||
|
|
||||||
IDT.load();
|
IDT.load();
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
We define that the 0th IST entry is the double fault stack (any other IST index would work too). We create a new TSS through the `TaskStateSegment::new` function and load the top address (stacks grow downwards) of the double fault stack into the 0th entry.
|
We define that the 0th IST entry is the double fault stack (any other IST index would work too). We create a new TSS through the `TaskStateSegment::new` function and load the top address (stacks grow downwards) of the double fault stack into the 0th entry.
|
||||||
|
|
||||||
@@ -502,7 +503,7 @@ The Global Descriptor Table (GDT) is a relict that was used for [memory segmenta
|
|||||||
|
|
||||||
We already created a GDT [when switching to long mode]. Back then, we used assembly to create valid code and data segment descriptors, which were required to enter 64-bit mode. We could just edit that assembly file and add an additional TSS descriptor. However, we now have the expressiveness of Rust, so let's do it in Rust instead.
|
We already created a GDT [when switching to long mode]. Back then, we used assembly to create valid code and data segment descriptors, which were required to enter 64-bit mode. We could just edit that assembly file and add an additional TSS descriptor. However, we now have the expressiveness of Rust, so let's do it in Rust instead.
|
||||||
|
|
||||||
[when switching to long mode]: {{% relref "02-entering-longmode.md#the-global-descriptor-table" %}}
|
[when switching to long mode]: ./posts/02-entering-longmode/index.md#the-global-descriptor-table
|
||||||
|
|
||||||
We start by creating a new `interrupts::gdt` submodule. For that we need to rename the `src/interrupts.rs` file to `src/interrupts/mod.rs`. Then we can create a new submodule:
|
We start by creating a new `interrupts::gdt` submodule. For that we need to rename the `src/interrupts.rs` file to `src/interrupts/mod.rs`. Then we can create a new submodule:
|
||||||
|
|
||||||
@@ -735,7 +736,7 @@ We now have a double fault stack and are able to create and load a TSS (which co
|
|||||||
|
|
||||||
We already created a new TSS in our `interrupts::init` function. Now we can load this TSS by creating a new GDT:
|
We already created a new TSS in our `interrupts::init` function. Now we can load this TSS by creating a new GDT:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=10 11 12 13" >}}
|
```rust
|
||||||
// in src/interrupts/mod.rs
|
// in src/interrupts/mod.rs
|
||||||
|
|
||||||
pub fn init(memory_controller: &mut MemoryController) {
|
pub fn init(memory_controller: &mut MemoryController) {
|
||||||
@@ -753,7 +754,7 @@ pub fn init(memory_controller: &mut MemoryController) {
|
|||||||
|
|
||||||
IDT.load();
|
IDT.load();
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
However, when we try to compile it, the following errors occur:
|
However, when we try to compile it, the following errors occur:
|
||||||
|
|
||||||
@@ -812,7 +813,7 @@ The `Once` type allows us to initialize a `static` at runtime. It is safe becaus
|
|||||||
|
|
||||||
So let's rewrite our `interrupts::init` function to use the static `TSS` and `GDT`:
|
So let's rewrite our `interrupts::init` function to use the static `TSS` and `GDT`:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=5 9 10 12 17 18" >}}
|
```rust
|
||||||
pub fn init(memory_controller: &mut MemoryController) {
|
pub fn init(memory_controller: &mut MemoryController) {
|
||||||
let double_fault_stack = memory_controller.alloc_stack(1)
|
let double_fault_stack = memory_controller.alloc_stack(1)
|
||||||
.expect("could not allocate double fault stack");
|
.expect("could not allocate double fault stack");
|
||||||
@@ -835,7 +836,7 @@ pub fn init(memory_controller: &mut MemoryController) {
|
|||||||
|
|
||||||
IDT.load();
|
IDT.load();
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
Now it should compile again!
|
Now it should compile again!
|
||||||
|
|
||||||
@@ -848,7 +849,7 @@ We're almost done. We successfully loaded our new GDT, which contains a TSS desc
|
|||||||
|
|
||||||
For the first two steps, we need access to the `code_selector` and `tss_selector` variables outside of the closure. We can achieve this by moving the `let` declarations out of the closure:
|
For the first two steps, we need access to the `code_selector` and `tss_selector` variables outside of the closure. We can achieve this by moving the `let` declarations out of the closure:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=3 4 5 8 9 12 13 20 22" >}}
|
```rust
|
||||||
// in src/interrupts/mod.rs
|
// in src/interrupts/mod.rs
|
||||||
pub fn init(memory_controller: &mut MemoryController) {
|
pub fn init(memory_controller: &mut MemoryController) {
|
||||||
use x86_64::structures::gdt::SegmentSelector;
|
use x86_64::structures::gdt::SegmentSelector;
|
||||||
@@ -875,7 +876,7 @@ pub fn init(memory_controller: &mut MemoryController) {
|
|||||||
|
|
||||||
IDT.load();
|
IDT.load();
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
We first set the descriptors to `empty` and then update them from inside the closure (which implicitly borrows them as `&mut`). Now we're able to reload the code segment register using [`set_cs`] and to load the TSS using [`load_tss`].
|
We first set the descriptors to `empty` and then update them from inside the closure (which implicitly borrows them as `&mut`). Now we're able to reload the code segment register using [`set_cs`] and to load the TSS using [`load_tss`].
|
||||||
|
|
||||||
@@ -884,7 +885,7 @@ We first set the descriptors to `empty` and then update them from inside the clo
|
|||||||
|
|
||||||
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:
|
||||||
|
|
||||||
{{< highlight rust "hl_lines=7 9" >}}
|
```rust
|
||||||
// in src/interrupt/mod.rs
|
// in src/interrupt/mod.rs
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -898,13 +899,13 @@ lazy_static! {
|
|||||||
...
|
...
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
{{< / highlight >}}
|
```
|
||||||
|
|
||||||
The `set_stack_index` method is unsafe because the the caller must ensure that the used index is valid and not already used for another exception.
|
The `set_stack_index` method is unsafe because the the caller must ensure that the used index is valid and not already used for another exception.
|
||||||
|
|
||||||
That's it! Now the CPU should switch to the double fault stack whenever a double fault occurs. Thus, we are able to catch _all_ double faults, including kernel stack overflows:
|
That's it! Now the CPU should switch to the double fault stack whenever a double fault occurs. Thus, we are able to catch _all_ double faults, including kernel stack overflows:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
From now on we should never see a triple fault again!
|
From now on we should never see a triple fault again!
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
6
blog/content/posts/_index.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
+++
|
||||||
|
title = "Posts"
|
||||||
|
sort_by = "order"
|
||||||
|
insert_anchor = "left"
|
||||||
|
render = false
|
||||||
|
+++
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{{ partial "header.html" . }}
|
|
||||||
|
|
||||||
<h1>Page not found</h1>
|
|
||||||
<p class="lead">
|
|
||||||
Sorry, we've misplaced that URL or it's pointing to something that doesn't exist.
|
|
||||||
<a href="/">Head back home</a> to try finding it again.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>If you followed a link on this site, please <a href="https://github.com/phil-opp/blog_os/issues">report it</a>!
|
|
||||||
|
|
||||||
{{ partial "footer.html" . }}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<a href="{{ .RelPermalink }}">{{ .Title }}</a>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{{ partial "header.html" . }}
|
|
||||||
|
|
||||||
<h1>Page not found</h1>
|
|
||||||
<p class="lead">
|
|
||||||
Sorry, we've misplaced that URL or it's pointing to something that doesn't exist.
|
|
||||||
<a href="/">Head back home</a> to try finding it again.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>If you followed a link on this site, please <a href="https://github.com/phil-opp/blog_os/issues">report it</a>!
|
|
||||||
|
|
||||||
{{ partial "footer.html" . }}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{{ partial "header.html" . }}
|
|
||||||
|
|
||||||
<article class="post">
|
|
||||||
<h1 class="post-title">{{ .Title }}</h1>
|
|
||||||
|
|
||||||
{{ .Content }}
|
|
||||||
</article>
|
|
||||||
|
|
||||||
{{ partial "disqus.html" . }}
|
|
||||||
{{ partial "anchorjs.html" . }}
|
|
||||||
{{ partial "footer.html" . }}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
{{ partial "header.html" . }}
|
|
||||||
|
|
||||||
<div class="front-page-introduction"><p>
|
|
||||||
This blog series creates a small operating system in the
|
|
||||||
<a href="https://www.rust-lang.org/">Rust programming language</a>. Each post
|
|
||||||
is a small tutorial and includes all needed code, so you can follow along if
|
|
||||||
you like. The source code is also available in the corresponding
|
|
||||||
<a href="https://github.com/phil-opp/blog_os">Github repository</a>.
|
|
||||||
</p>
|
|
||||||
<p>Latest post: {{ range first 1 (where .Site.Pages "Section" "post") }}
|
|
||||||
<strong><a href="{{ .RelPermalink }}">{{ .Title }}</a></strong>
|
|
||||||
{{ end }}
|
|
||||||
</p></div>
|
|
||||||
|
|
||||||
<div id="bare-bones" class="post-category bare-bones">Bare Bones</div>
|
|
||||||
<div class="posts bare-bones">
|
|
||||||
{{ range first 4 (where .Site.Pages.ByDate "Section" "post") }}
|
|
||||||
{{ .Render "teaser" }}
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="memory-management" class="post-category memory-management">Memory Management</div>
|
|
||||||
<div class="posts memory-management">
|
|
||||||
{{ range first 4 (after 4 (where .Site.Pages.ByDate "Section" "post")) }}
|
|
||||||
{{ .Render "teaser" }}
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="interrupts" class="post-category exceptions">Exceptions</div>
|
|
||||||
<div class="posts exceptions">
|
|
||||||
{{ range first 1 (after 9 (where .Site.Pages.ByDate "Section" "post")) }}
|
|
||||||
{{ .Render "teaser" }}
|
|
||||||
{{ end }}
|
|
||||||
{{ range first 1 (after 8 (where .Site.Pages.ByDate "Section" "post")) }}
|
|
||||||
{{ .Render "teaser" }}
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<h1>Additional Resources</h1>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
{{ range (where .Site.Pages.ByDate "Section" "additional-resource") }}
|
|
||||||
<li>{{ .Render "link" }}
|
|
||||||
{{ end }}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<aside id="recent-updates">
|
|
||||||
<h1>Recent Updates</h1>
|
|
||||||
{{ partial "recent-updates.html" . }}
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
{{ partial "footer.html" . }}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
{{ partial "header.html" . }}
|
|
||||||
|
|
||||||
<article class="post">
|
|
||||||
<h1 class="post-title">{{ .Title }}</h1>
|
|
||||||
<time datetime="{{ .Date.Format "2006-01-02" }}" class="post-date">
|
|
||||||
{{ .Date.Format .Site.Params.date_format }}
|
|
||||||
{{ if isset .Params "updated" }}
|
|
||||||
(updated on {{ .Params.updated | dateFormat .Site.Params.date_format }})
|
|
||||||
{{ end }}
|
|
||||||
</time>
|
|
||||||
|
|
||||||
{{ .Content }}
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<div class="PageNavigation">
|
|
||||||
{{ if .PrevInSection }}
|
|
||||||
<a class="prev" href="{{ .PrevInSection.RelPermalink }}">« {{ .PrevInSection.Title }}</a>
|
|
||||||
{{ end }}
|
|
||||||
{{ if .NextInSection }}
|
|
||||||
<a class="next" href="{{ .NextInSection.RelPermalink }}">{{ .NextInSection.Title }} »</a>
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
{{ partial "disqus.html" . }}
|
|
||||||
{{ partial "anchorjs.html" . }}
|
|
||||||
{{ partial "footer.html" . }}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<article class="post">
|
|
||||||
<h1 class="post-title">
|
|
||||||
<a href="{{ .RelPermalink }}">
|
|
||||||
{{ .Title }}
|
|
||||||
</a>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
{{ .Summary }}
|
|
||||||
|
|
||||||
</article>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{{ partial "header.html" . }}
|
|
||||||
|
|
||||||
<article class="post">
|
|
||||||
<h1 class="post-title">{{ .Title }}</h1>
|
|
||||||
|
|
||||||
{{ .Content }}
|
|
||||||
</article>
|
|
||||||
|
|
||||||
{{ partial "anchorjs.html" . }}
|
|
||||||
{{ partial "footer.html" . }}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<script>
|
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
||||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
||||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
|
||||||
|
|
||||||
ga('create', 'UA-65296949-1', 'auto');
|
|
||||||
ga('send', 'pageview');
|
|
||||||
|
|
||||||
</script>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<script type="text/javascript">
|
|
||||||
anchors.options = {
|
|
||||||
placement: 'left',
|
|
||||||
};
|
|
||||||
anchors.add('article h2, article h3, article h4, article h5, article h6');
|
|
||||||
</script>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<div id="disqus_thread"></div>
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
// Don't ever inject Disqus on localhost--it creates unwanted
|
|
||||||
// discussions from 'localhost:1313' on your Disqus account...
|
|
||||||
if (window.location.hostname == "localhost")
|
|
||||||
return;
|
|
||||||
|
|
||||||
var d = document, s = d.createElement('script');
|
|
||||||
|
|
||||||
s.src = '//phil-opp.disqus.com/embed.js';
|
|
||||||
|
|
||||||
s.setAttribute('data-timestamp', +new Date());
|
|
||||||
(d.head || d.body).appendChild(s);
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
</main>
|
|
||||||
|
|
||||||
<footer class="footer">
|
|
||||||
<small>{{ $year := .Site.LastChange.Format "2006"}}
|
|
||||||
©
|
|
||||||
<time datetime="{{ $year }}">{{ $year }}</time>. All rights reserved.
|
|
||||||
<a href="/contact.html">Contact</a>
|
|
||||||
</small>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
|
|
||||||
<!-- Enable responsiveness on mobile devices-->
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
|
|
||||||
{{ with .Site.Params.author }}<meta name="author" content="{{ . }}">{{ end }}
|
|
||||||
{{ with .Site.Params.description }}<meta name="description" content="{{ . }}">{{ end }}
|
|
||||||
|
|
||||||
<title>
|
|
||||||
{{ .Title }}{{ if not .IsHome }} · {{ .Site.Title }}{{end}}
|
|
||||||
</title>
|
|
||||||
|
|
||||||
<link rel="canonical" href="{{ .Permalink }}">
|
|
||||||
|
|
||||||
<!-- CSS -->
|
|
||||||
<link rel="stylesheet" href="/css/poole.css">
|
|
||||||
<link rel="stylesheet" href="/css/main.css">
|
|
||||||
<link rel="stylesheet" href="/css/syntax.css">
|
|
||||||
|
|
||||||
<!-- Icons -->
|
|
||||||
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/images/apple-touch-icon-precomposed.png">
|
|
||||||
<link rel="shortcut icon" href="/images/favicon.ico">
|
|
||||||
|
|
||||||
<link href="/atom.xml" rel="alternate" type="application/rss+xml" title="{{ .Site.Title }}" />
|
|
||||||
<link href="/atom.xml" rel="feed" type="application/rss+xml" title="{{ .Site.Title }}" />
|
|
||||||
|
|
||||||
<!-- JS -->
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/anchor-js/2.0.0/anchor.min.js"></script>
|
|
||||||
<script src="js/toc.min.js"></script>
|
|
||||||
<script src="js/main.js"></script>
|
|
||||||
{{ partial "analytics.html" . }}
|
|
||||||
|
|
||||||
<!-- redirect from phil-opp.github.io/blog_os -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
if (window.location.hostname == "phil-opp.github.io") {
|
|
||||||
window.location.href = "http://os.phil-opp.com/";
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
{{ partial "head.html" . }}
|
|
||||||
<body>
|
|
||||||
<div class="container content">
|
|
||||||
|
|
||||||
<header class="masthead">
|
|
||||||
<h3 class="masthead-title">
|
|
||||||
<a href="/" title="Home">{{ .Site.Title }}</a>
|
|
||||||
<span class="navigation">
|
|
||||||
<small><a href="/atom.xml"><img src="/images/feed-icon.png" alt="RSS"></a></small><!-- image source: https://commons.wikimedia.org/wiki/File:Generic_Feed-icon.svg -->
|
|
||||||
</span>
|
|
||||||
<small>{{ replace .Site.Params.subtitle " " " " | safeHTML }}</small>
|
|
||||||
</h3>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
{{ partial "header.html" . }}
|
|
||||||
|
|
||||||
<article class="post">
|
|
||||||
<h1 class="post-title">{{ .Title }}</h1>
|
|
||||||
<time datetime="{{ .Date.Format "2006-01-02" }}" class="post-date">
|
|
||||||
{{ .Date.Format .Site.Params.date_format }}
|
|
||||||
{{ if isset .Params "updated" }}
|
|
||||||
(updated on {{ .Params.updated | dateFormat .Site.Params.date_format }})
|
|
||||||
{{ end }}
|
|
||||||
</time>
|
|
||||||
|
|
||||||
{{ .Content }}
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<div class="PageNavigation">
|
|
||||||
{{ if .PrevInSection }}
|
|
||||||
<a class="prev" href="{{ .PrevInSection.RelPermalink }}">« {{ .PrevInSection.Title }}</a>
|
|
||||||
{{ end }}
|
|
||||||
{{ if .NextInSection }}
|
|
||||||
<a class="next" href="{{ .NextInSection.RelPermalink }}">{{ .NextInSection.Title }} »</a>
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
{{ partial "disqus.html" . }}
|
|
||||||
{{ partial "anchorjs.html" . }}
|
|
||||||
{{ partial "footer.html" . }}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<article class="post">
|
|
||||||
<h1 class="post-title">
|
|
||||||
<a href="{{ .RelPermalink }}">
|
|
||||||
{{ .Title }}
|
|
||||||
</a>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
{{ .Summary }}
|
|
||||||
|
|
||||||
</article>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
||||||
<channel>
|
|
||||||
<title>{{ .Site.Title }}</title>
|
|
||||||
<link>{{ .Permalink }}</link>
|
|
||||||
<description>Recent content on {{ .Site.Title }}</description>
|
|
||||||
<generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}
|
|
||||||
<language>{{.}}</language>{{end}}{{ with .Site.Author.email }}
|
|
||||||
<managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
|
|
||||||
<webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
|
|
||||||
<copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
|
|
||||||
<lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
|
|
||||||
<atom:link href="{{.URL}}" rel="self" type="application/rss+xml" />
|
|
||||||
{{ range first 15 (where .Data.Pages "Section" "post") }}
|
|
||||||
<item>
|
|
||||||
<title>{{ .Title }}</title>
|
|
||||||
<link>{{ .Permalink }}</link>
|
|
||||||
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
|
|
||||||
{{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
|
|
||||||
<guid>{{ .Permalink }}</guid>
|
|
||||||
<description>{{ .Content | html }}</description>
|
|
||||||
</item>
|
|
||||||
{{ end }}
|
|
||||||
</channel>
|
|
||||||
</rss>
|
|
||||||
11
blog/static/_redirects
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Redirects from what the browser requests to what we serve
|
||||||
|
/2015/08/18/multiboot-kernel/ /multiboot-kernel/
|
||||||
|
/rust-os/multiboot-kernel.html /multiboot-kernel/
|
||||||
|
/2015/08/25/entering-longmode/ /entering-longmode/
|
||||||
|
/rust-os/entering-longmode.html /entering-longmode/
|
||||||
|
/2015/09/02/setup-rust/ /set-up-rust/
|
||||||
|
/setup-rust.html /set-up-rust/
|
||||||
|
/rust-os/setup-rust.html /set-up-rust/
|
||||||
|
/2015/10/23/printing-to-screen/ /printing-to-screen/
|
||||||
|
/rust-os/printing-to-screen.html /printing-to-screen/
|
||||||
|
/atom.xml /rss.xml
|
||||||
@@ -41,6 +41,10 @@ main img {
|
|||||||
margin-right: -0.5rem;
|
margin-right: -0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.posts.neutral {
|
||||||
|
border: 2px solid #999;
|
||||||
|
}
|
||||||
|
|
||||||
.posts.bare-bones {
|
.posts.bare-bones {
|
||||||
border: 2px solid #66f;
|
border: 2px solid #66f;
|
||||||
}
|
}
|
||||||
@@ -196,3 +200,22 @@ aside#recent-updates time {
|
|||||||
a code {
|
a code {
|
||||||
color: #268bd2;
|
color: #268bd2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.gutenberg-anchor {
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
margin-left: -1.5em;
|
||||||
|
padding-right: 1em;
|
||||||
|
font-size: 0.6em;
|
||||||
|
vertical-align: baseline;
|
||||||
|
line-height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
:hover>a.gutenberg-anchor {
|
||||||
|
opacity: 1;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.gutenberg-anchor:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 17 KiB |
@@ -4,11 +4,13 @@ window.onload = function() {
|
|||||||
if (container != null) {
|
if (container != null) {
|
||||||
var toc = initTOC({
|
var toc = initTOC({
|
||||||
selector: 'h2, h3',
|
selector: 'h2, h3',
|
||||||
scope: '.post',
|
scope: 'main',
|
||||||
overwrite: false,
|
overwrite: false,
|
||||||
prefix: 'toc'
|
prefix: 'toc'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
toc.innerHTML = toc.innerHTML.split("🔗\n").join("");
|
||||||
|
|
||||||
var heading = document.createElement("H2");
|
var heading = document.createElement("H2");
|
||||||
var heading_text = document.createTextNode("Table of Contents");
|
var heading_text = document.createTextNode("Table of Contents");
|
||||||
heading.appendChild(heading_text);
|
heading.appendChild(heading_text);
|
||||||
|
|||||||
60
blog/templates/base.html
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<!doctype html>
|
||||||
|
|
||||||
|
<html lang="{{ config.language_code }}">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="description" content="{{ config.description }}">
|
||||||
|
<meta name="author" content="{{ config.extra.author.name }}">
|
||||||
|
|
||||||
|
<link href="/css/poole.css" rel="stylesheet">
|
||||||
|
<link href="/css/main.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<script src="/js/toc.min.js"></script>
|
||||||
|
<script src="/js/main.js"></script>
|
||||||
|
|
||||||
|
<title>{% block title %}{% endblock title %}</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container content">
|
||||||
|
<header class="masthead">
|
||||||
|
<h3 class="masthead-title">
|
||||||
|
<a href="/" title="Home">{{ config.title }}</a>
|
||||||
|
<small>{{ config.extra.subtitle }}</small>
|
||||||
|
</h3>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>{% block main %}{% endblock main %}</main>
|
||||||
|
|
||||||
|
<div>{% block after_main %}{% endblock after_main %}</div>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<hr>
|
||||||
|
<small>
|
||||||
|
© <time datetime="2017">2017</time>. All rights reserved.
|
||||||
|
<a href="{{ get_url(link="pages/contact.md") }}">Contact</a>
|
||||||
|
</small>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Piwik -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var _paq = _paq || [];
|
||||||
|
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||||
|
_paq.push(['trackPageView']);
|
||||||
|
_paq.push(['enableLinkTracking']);
|
||||||
|
(function() {
|
||||||
|
var u="//analytics.phil-opp.com/";
|
||||||
|
_paq.push(['setTrackerUrl', u+'piwik.php']);
|
||||||
|
_paq.push(['setSiteId', '1']);
|
||||||
|
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||||
|
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<!-- End Piwik Code -->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
11
blog/templates/handling-exceptions-with-naked-fns.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{% extends "section.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ super() }}{% endblock title %}
|
||||||
|
|
||||||
|
{% block main %}{{ super() }}{% endblock main %}
|
||||||
|
|
||||||
|
{% block introduction %}
|
||||||
|
|
||||||
|
<p>These posts explain how to handle CPU exceptions using naked functions. Historically, these posts were the main exception handling posts before the <code>x86-interrupt</code> calling convention and the <code>x86_64</code> crate existed. Our new way of handling exceptions can be found in the <a href="{{ get_url(link="posts/09-handling-exceptions/index.md") }}">“Handling Exceptions”</a> post.</p>
|
||||||
|
|
||||||
|
{% endblock introduction %}
|
||||||
61
blog/templates/index.html
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% import "macros.html" as macros %}
|
||||||
|
|
||||||
|
{% block title %}{{ config.title }}{% endblock title %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{% set posts = sections.posts %}
|
||||||
|
<div class="front-page-introduction">
|
||||||
|
<p>
|
||||||
|
This blog series creates a small operating system in the
|
||||||
|
<a href="https://www.rust-lang.org/">Rust programming language</a>. Each post is a small tutorial and includes all
|
||||||
|
needed code, so you can follow along if you like. The source code is also available in the corresponding
|
||||||
|
<a href="https://github.com/phil-opp/blog_os">Github repository</a>.
|
||||||
|
</p>
|
||||||
|
<p>Latest post:
|
||||||
|
{% set latest_post = posts.pages|reverse|last %}
|
||||||
|
<strong><a href="{{ latest_post.path }}">{{ latest_post.title }}</a></strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="bare-bones" class="post-category bare-bones">Bare Bones</div>
|
||||||
|
<div class="posts bare-bones">
|
||||||
|
{{ macros::post_link(page=posts.pages.9) }}
|
||||||
|
{{ macros::post_link(page=posts.pages.8) }}
|
||||||
|
{{ macros::post_link(page=posts.pages.7) }}
|
||||||
|
{{ macros::post_link(page=posts.pages.6) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="memory-management" class="post-category memory-management">Memory Management</div>
|
||||||
|
<div class="posts memory-management">
|
||||||
|
{{ macros::post_link(page=posts.pages.5) }}
|
||||||
|
{{ macros::post_link(page=posts.pages.4) }}
|
||||||
|
{{ macros::post_link(page=posts.pages.3) }}
|
||||||
|
{{ macros::post_link(page=posts.pages.2) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="interrupts" class="post-category exceptions">Exceptions</div>
|
||||||
|
<div class="posts exceptions">
|
||||||
|
{{ macros::post_link(page=posts.pages.1) }}
|
||||||
|
{{ macros::post_link(page=posts.pages.0) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
{% set extra = sections.extra %}
|
||||||
|
<h1>{{ extra.title }}</h1>
|
||||||
|
<ul>
|
||||||
|
{% for subsection in extra.subsections|reverse %}
|
||||||
|
<li><a href="{{ subsection.path }}">{{ subsection.title }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
{% for page in extra.pages|reverse %}
|
||||||
|
<li><a href="{{ page.path }}">{{ page.title }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<aside id="recent-updates">
|
||||||
|
<h1>Recent Updates</h1>
|
||||||
|
{% include "recent-updates.html" %}
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
{% endblock main %}
|
||||||
6
blog/templates/macros.html
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{% macro post_link(page) %}
|
||||||
|
<article>
|
||||||
|
<h2 class="post-title"><a href="{{ page.path }}">{{ page.title }}</a></h2>
|
||||||
|
{{ page.summary | safe }}
|
||||||
|
</article>
|
||||||
|
{% endmacro post_link %}
|
||||||
35
blog/templates/page.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ page.title }} | {{ config.title }}{% endblock title %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
<h1>{{ page.title }}</h1>
|
||||||
|
<time datetime="{{ page.date | date(format="%Y-%m-%d") }}" class="post-date">
|
||||||
|
{{ page.date | date(format="%b %d, %Y") }}
|
||||||
|
{% if page.extra.updated %} (updated on {{ page.extra.updated | date(format="%b %d, %Y") }}) {% endif %}
|
||||||
|
</time>
|
||||||
|
{{ page.content | safe }}
|
||||||
|
{% endblock main %}
|
||||||
|
|
||||||
|
{% block after_main %}
|
||||||
|
<hr>
|
||||||
|
<div class="PageNavigation">
|
||||||
|
{% if page.previous %}
|
||||||
|
<a class="prev" href="{{ page.previous.path }}">« {{ page.previous.title }}</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if page.next %}
|
||||||
|
<a class="next" href="{{ page.next.path }}">{{ page.next.title }} »</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<script data-isso="//comments.phil-opp.com/fcgi-bin/isso"
|
||||||
|
src="//comments.phil-opp.com/fcgi-bin/isso/js/embed.min.js"></script>
|
||||||
|
<section>
|
||||||
|
<h2>Comments</h2>
|
||||||
|
<section id="isso-thread"></section>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{% endblock after_main %}
|
||||||
|
|
||||||
|
|
||||||