mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 22:37:49 +00:00
Rename posts: Use ascending numbers instead of date
This commit is contained in:
@@ -321,5 +321,5 @@ 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].
|
||||
|
||||
[next post]: {{% relref "2015-08-25-entering-longmode.md" %}}
|
||||
[next post]: {{% relref "02-entering-longmode.md" %}}
|
||||
[long mode]: https://en.wikipedia.org/wiki/Long_mode
|
||||
@@ -11,7 +11,7 @@ aliases = [
|
||||
|
||||
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 "2015-08-18-multiboot-kernel.md" %}}
|
||||
[previous post]: {{% relref "01-multiboot-kernel.md" %}}
|
||||
[Rust]: http://www.rust-lang.org/
|
||||
[protected mode]: https://en.wikipedia.org/wiki/Protected_mode
|
||||
[long mode]: https://en.wikipedia.org/wiki/Long_mode
|
||||
@@ -42,7 +42,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.
|
||||
|
||||
[VGA text buffer]: https://en.wikipedia.org/wiki/VGA-compatible_text_mode
|
||||
[future post]: {{% relref "2015-10-23-printing-to-screen.md" %}}
|
||||
[future post]: {{% relref "04-printing-to-screen.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.
|
||||
|
||||
@@ -534,4 +534,4 @@ It's time to finally leave assembly behind[^leave_assembly_behind] and switch to
|
||||
[^leave_assembly_behind]: Actually we will still need some assembly in the future, but I'll try to minimize it.
|
||||
|
||||
[Rust]: https://www.rust-lang.org/
|
||||
[next post]: {{% relref "2015-09-02-set-up-rust.md" %}}
|
||||
[next post]: {{% relref "03-set-up-rust.md" %}}
|
||||
@@ -11,8 +11,8 @@ aliases = [
|
||||
|
||||
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 "2015-08-18-multiboot-kernel.md" %}}
|
||||
[long mode post]: {{% relref "2015-08-25-entering-longmode.md" %}}
|
||||
[multiboot post]: {{% relref "01-multiboot-kernel.md" %}}
|
||||
[long mode post]: {{% relref "02-entering-longmode.md" %}}
|
||||
[Rust]: https://www.rust-lang.org/
|
||||
|
||||
<!--more--><aside id="toc"></aside>
|
||||
@@ -401,10 +401,10 @@ Some notes:
|
||||
### 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.
|
||||
|
||||
[stack from the last post]: {{% relref "2015-08-25-entering-longmode.md#creating-a-stack" %}}
|
||||
[stack from the last post]: {{% relref "02-entering-longmode.md#creating-a-stack" %}}
|
||||
[overflow]: https://en.wikipedia.org/wiki/Stack_overflow
|
||||
|
||||
## 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.
|
||||
|
||||
[next post]: {{% relref "2015-10-23-printing-to-screen.md" %}}
|
||||
[next post]: {{% relref "04-printing-to-screen.md" %}}
|
||||
@@ -9,7 +9,7 @@ aliases = [
|
||||
|
||||
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 "2015-09-02-set-up-rust.md" %}}
|
||||
[previous post]: {{% relref "03-set-up-rust.md" %}}
|
||||
[Rust]: https://www.rust-lang.org/
|
||||
[raw pointers]: https://doc.rust-lang.org/book/raw-pointers.html
|
||||
[formatting macros]: https://doc.rust-lang.org/std/fmt/#related-macros
|
||||
@@ -459,7 +459,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.
|
||||
|
||||
[next post]: {{% relref "2015-11-15-allocating-frames.md" %}}
|
||||
[next post]: {{% relref "05-allocating-frames.md" %}}
|
||||
|
||||
## Other Rust OS Projects
|
||||
Now that you know the very basics of OS development in Rust, you should also check out the following projects:
|
||||
@@ -424,10 +424,10 @@ Now we have a working frame allocator. It is a bit rudimentary and cannot free f
|
||||
## 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.
|
||||
|
||||
[next post]: {{% relref "2015-12-09-page-tables.md" %}}
|
||||
[next post]: {{% relref "06-page-tables.md" %}}
|
||||
|
||||
## 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.
|
||||
|
||||
[Bare Metal Rust]: http://www.randomhacks.net/bare-metal-rust/
|
||||
[Printing to Screen]: {{% relref "2015-10-23-printing-to-screen.md" %}}
|
||||
[Printing to Screen]: {{% relref "04-printing-to-screen.md" %}}
|
||||
@@ -49,7 +49,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.
|
||||
|
||||
[previous post]: {{% relref "2015-11-15-allocating-frames.md#a-memory-module" %}}
|
||||
[previous post]: {{% relref "05-allocating-frames.md#a-memory-module" %}}
|
||||
|
||||
### Page Table Entries
|
||||
To model page table entries, we create a new `entry` submodule:
|
||||
@@ -651,7 +651,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.
|
||||
|
||||
[VGA text buffer]: {{% relref "2015-10-23-printing-to-screen.md#the-text-buffer" %}}
|
||||
[VGA text buffer]: {{% relref "04-printing-to-screen.md#the-text-buffer" %}}
|
||||
[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:
|
||||
@@ -938,7 +938,7 @@ This post has become pretty long. So let's summarize what we've done:
|
||||
## 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.
|
||||
|
||||
[next post]: {{% relref "2016-01-01-remap-the-kernel.md" %}}
|
||||
[next post]: {{% relref "07-remap-the-kernel.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`.
|
||||
|
||||
@@ -29,12 +29,12 @@ _Updates_:
|
||||
## 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.
|
||||
[previous post]: {{% relref "2015-12-09-page-tables.md" %}}
|
||||
[previous post]: {{% relref "06-page-tables.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.
|
||||
|
||||
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 "2015-11-15-allocating-frames.md#kernel-elf-sections" %}}
|
||||
[information about kernel sections]: {{% relref "05-allocating-frames.md#kernel-elf-sections" %}}
|
||||
|
||||
## 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].
|
||||
@@ -287,7 +287,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.
|
||||
[some clever solution]: {{% relref "2015-12-09-page-tables.md#some-clever-solution" %}}
|
||||
[some clever solution]: {{% relref "06-page-tables.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.
|
||||
@@ -557,7 +557,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.
|
||||
|
||||
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 "2015-11-15-allocating-frames.md#the-multiboot-information-structure" %}}
|
||||
[Multiboot information structure]: {{% relref "05-allocating-frames.md#the-multiboot-information-structure" %}}
|
||||
|
||||
Let's resolve the above `TODO` by identity mapping the sections:
|
||||
|
||||
@@ -804,7 +804,7 @@ Let's cross our fingers and run it…
|
||||
|
||||
### 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 "2015-09-02-set-up-rust.md#debugging" %}}
|
||||
[qemu debugging]: {{% relref "03-set-up-rust.md#debugging" %}}
|
||||
|
||||
```bash
|
||||
> qemu-system-x86_64 -d int -no-reboot -cdrom build/os-x86_64.iso
|
||||
@@ -824,12 +824,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
|
||||
|
||||
- `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 "2015-08-25-entering-longmode.md#loading-the-gdt" %}}
|
||||
[GDT segment]: {{% relref "02-entering-longmode.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.
|
||||
|
||||
- `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 "2015-10-23-printing-to-screen.md#the-vga-text-buffer" %}}
|
||||
[VGA text buffer]: {{% relref "04-printing-to-screen.md#the-vga-text-buffer" %}}
|
||||
|
||||
So let's find out which function caused the exception:
|
||||
|
||||
@@ -1024,7 +1024,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 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 "2015-12-09-page-tables.md#translate" %}}
|
||||
[silent stack overflow]: {{% relref "06-page-tables.md#translate" %}}
|
||||
|
||||
```nasm
|
||||
; in src/arch/x86_64/boot.asm
|
||||
@@ -1084,7 +1084,7 @@ Unfortunately stack probes require compiler support. They already work on Window
|
||||
|
||||
## 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].
|
||||
[next post]: {{% relref "2016-04-11-kernel-heap.md" %}}
|
||||
[next post]: {{% relref "08-kernel-heap.md" %}}
|
||||
[Box]: https://doc.rust-lang.org/nightly/alloc/boxed/struct.Box.html
|
||||
[Vec]: https://doc.rust-lang.org/nightly/collections/vec/struct.Vec.html
|
||||
[BTreeMap]: https://doc.rust-lang.org/nightly/collections/struct.BTreeMap.html
|
||||
@@ -5,8 +5,8 @@ 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.
|
||||
|
||||
[frame allocator]: {{% relref "2015-11-15-allocating-frames.md" %}}
|
||||
[page table module]: {{% relref "2015-12-09-page-tables.md" %}}
|
||||
[frame allocator]: {{% relref "05-allocating-frames.md" %}}
|
||||
[page table module]: {{% relref "06-page-tables.md" %}}
|
||||
[alloc]: https://doc.rust-lang.org/nightly/alloc/index.html
|
||||
[collections]: https://doc.rust-lang.org/nightly/collections/index.html
|
||||
|
||||
@@ -460,8 +460,8 @@ That's it. Now our `memory::init` function can only be called once. The macro wo
|
||||
### 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:
|
||||
|
||||
[page table]: {{% relref "2015-12-09-page-tables.md" %}}
|
||||
[kernel remapping]: {{% relref "2016-01-01-remap-the-kernel.md" %}}
|
||||
[page table]: {{% relref "06-page-tables.md" %}}
|
||||
[kernel remapping]: {{% relref "07-remap-the-kernel.md" %}}
|
||||
|
||||
```rust
|
||||
// in src/memory/paging/mod.rs
|
||||
@@ -848,4 +848,4 @@ Now we're able to use heap storage in our kernel without leaking memory. This al
|
||||
## 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_.
|
||||
|
||||
[next post]: {{% relref "2016-05-28-catching-exceptions.md" %}}
|
||||
[next post]: {{% relref "09-catching-exceptions.md" %}}
|
||||
Reference in New Issue
Block a user