mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-17 14:57:49 +00:00
Fix lot of dead links in both the 2nd and 1st edition
This commit is contained in:
@@ -247,9 +247,7 @@ You can test it on real hardware, too. Just burn the ISO to a disk or USB stick
|
||||
|
||||
## Build Automation
|
||||
|
||||
Right now we need to execute 4 commands in the right order every time we change a file. That's bad. So let's automate the build using a [Makefile][Makefile tutorial]. But first we should create some clean directory structure for our source files to separate the architecture specific files:
|
||||
|
||||
[Makefile tutorial]: http://mrbook.org/blog/tutorials/make/
|
||||
Right now we need to execute 4 commands in the right order every time we change a file. That's bad. So let's automate the build using a `Makefile`. But first we should create some clean directory structure for our source files to separate the architecture specific files:
|
||||
|
||||
```
|
||||
…
|
||||
|
||||
@@ -410,9 +410,7 @@ gdt64:
|
||||
dq 0 ; zero entry
|
||||
dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; code segment
|
||||
```
|
||||
We chose the `.rodata` section here because it's initialized read-only data. The `dq` command stands for `define quad` and outputs a 64-bit constant (similar to `dw` and `dd`). And the `(1<<43)` is a [bit shift] that sets bit 43.
|
||||
|
||||
[bit shift]: http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/bitshift.html
|
||||
We chose the `.rodata` section here because it's initialized read-only data. The `dq` command stands for `define quad` and outputs a 64-bit constant (similar to `dw` and `dd`). And the `(1<<43)` is a bit shift that sets bit 43.
|
||||
|
||||
### Loading the GDT
|
||||
To load our new 64-bit GDT, we have to tell the CPU its address and length. We do this by passing the memory location of a special pointer structure to the `lgdt` (load GDT) instruction. The pointer structure looks like this:
|
||||
|
||||
@@ -78,7 +78,7 @@ Let's break it down:
|
||||
[attribute]: https://doc.rust-lang.org/book/attributes.html
|
||||
[name mangling]: https://en.wikipedia.org/wiki/Name_mangling
|
||||
[calling convention]: https://en.wikipedia.org/wiki/Calling_convention
|
||||
[language item]: https://doc.rust-lang.org/book/lang-items.html
|
||||
[language item]: https://doc.rust-lang.org/1.10.0/book/lang-items.html
|
||||
[unwinding]: https://doc.rust-lang.org/nomicon/unwinding.html
|
||||
|
||||
## Building Rust
|
||||
@@ -265,7 +265,7 @@ We add a new `kernel` target that just executes `xargo build` and modify the `$(
|
||||
|
||||
But now `xargo build` is executed on every `make`, even if no source file was changed. And the ISO is recreated on every `make iso`/`make run`, too. We could try to avoid this by adding dependencies on all rust source and cargo configuration files to the `kernel` target, but the ISO creation takes only half a second on my machine and most of the time we will have changed a Rust file when we run `make`. So we keep it simple for now and let cargo do the bookkeeping of changed files (it does it anyway).
|
||||
|
||||
[github makefile]: https://github.com/phil-opp/blog_os/blob/post_3/Makefile
|
||||
[github makefile]: https://github.com/phil-opp/blog_os/blob/first_edition_post_3/Makefile
|
||||
|
||||
### Calling Rust
|
||||
Now we can call the main method in `long_mode_start`:
|
||||
@@ -299,7 +299,7 @@ pub extern fn rust_main() {
|
||||
```
|
||||
When we test it using `make run`, it fails with `undefined reference to 'memcpy'`. The `memcpy` function is one of the basic functions of the C library (`libc`). Usually the `libc` crate is linked to every Rust program together with the standard library, but we opted out through `#![no_std]`. We could try to fix this by adding the [libc crate] as `extern crate`. But `libc` is just a wrapper for the system `libc`, for example `glibc` on Linux, so this won't work for us. Instead we need to recreate the basic `libc` functions such as `memcpy`, `memmove`, `memset`, and `memcmp` in Rust.
|
||||
|
||||
[libc crate]: https://doc.rust-lang.org/nightly/libc/index.html
|
||||
[libc crate]: https://doc.rust-lang.org/1.10.0/libc/index.html
|
||||
|
||||
#### rlibc
|
||||
Fortunately there already is a crate for that: [rlibc]. When we look at its [source code][rlibc source] we see that it contains no magic, just some [raw pointer] operations in a while loop. To add `rlibc` as a dependency we just need to add two lines to the `Cargo.toml`:
|
||||
@@ -343,7 +343,7 @@ target/x86_64-blog_os/debug/libblog_os.a(core-92335f822fa6c9a6.0.o):
|
||||
```
|
||||
|
||||
[rlibc]: https://crates.io/crates/rlibc
|
||||
[rlibc source]: https://github.com/rust-lang/rlibc/blob/master/src/lib.rs
|
||||
[rlibc source]: https://github.com/alexcrichton/rlibc/blob/defb486e765846417a8e73329e8c5196f1dca49a/src/lib.rs
|
||||
[raw pointer]: https://doc.rust-lang.org/book/raw-pointers.html
|
||||
[crates.io]: https://crates.io
|
||||
|
||||
|
||||
@@ -93,9 +93,7 @@ We use a [C-like enum] here to explicitly specify the number for each color. Bec
|
||||
|
||||
Normally the compiler would issue a warning for each unused variant. By using the `#[allow(dead_code)]` attribute we disable these warnings for the `Color` enum.
|
||||
|
||||
To represent a full color code that specifies foreground and background color, we create a [newtype] on top of `u8`:
|
||||
|
||||
[newtype]: https://aturon.github.io/features/types/newtype.html
|
||||
To represent a full color code that specifies foreground and background color, we create a newtype on top of `u8`:
|
||||
|
||||
```rust
|
||||
struct ColorCode(u8);
|
||||
|
||||
@@ -130,7 +130,7 @@ bitflags! {
|
||||
```
|
||||
To extract the flags from the entry we create an `Entry::flags` method that uses [from_bits_truncate]:
|
||||
|
||||
[from_bits_truncate]: https://doc.rust-lang.org/bitflags/bitflags/index.html#methods-1
|
||||
[from_bits_truncate]: https://docs.rs/bitflags/0.9.1/bitflags/example_generated/struct.Flags.html#method.from_bits_truncate
|
||||
|
||||
```rust
|
||||
pub fn flags(&self) -> EntryFlags {
|
||||
@@ -653,7 +653,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]: ./first-edition/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/1.10.0/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:
|
||||
|
||||
|
||||
@@ -328,7 +328,7 @@ impl InactivePageTable {
|
||||
```
|
||||
We added two new arguments, `active_table` and `temporary_page`. We need an [inner scope] to ensure that the `table` variable is dropped before we try to unmap the temporary page again. This is required since the `table` variable exclusively borrows `temporary_page` as long as it's alive.
|
||||
|
||||
[inner scope]: http://rustbyexample.com/variable_bindings/scope.html
|
||||
[inner scope]: https://doc.rust-lang.org/rust-by-example/variable_bindings/scope.html
|
||||
|
||||
Now we are able to create valid inactive page tables, which are zeroed and recursively mapped. But we still can't modify them. To resolve this problem, we need to look at recursive mapping again.
|
||||
|
||||
@@ -622,7 +622,7 @@ impl Iterator for FrameIter {
|
||||
Instead of creating a custom iterator, we could have used the [Range] struct of the standard library. But it requires that we implement the [One] and [Add] traits for `Frame`. Then every module could perform arithmetic operations on frames, for example `let frame3 = frame1 + frame2`. This would violate our safety invariants because `frame3` could be already in use. The `range_inclusive` function does not have these problems because it is only available inside the `memory` module.
|
||||
|
||||
[Range]: https://doc.rust-lang.org/nightly/core/ops/struct.Range.html
|
||||
[One]: https://doc.rust-lang.org/nightly/core/num/trait.One.html
|
||||
[One]: https://doc.rust-lang.org/1.10.0/core/num/trait.One.html
|
||||
[Add]: https://doc.rust-lang.org/nightly/core/ops/trait.Add.html
|
||||
|
||||
### Page Align Sections
|
||||
@@ -784,7 +784,7 @@ pub fn switch(&mut self, new_table: InactivePageTable) -> InactivePageTable {
|
||||
```
|
||||
This function activates the given inactive table and returns the previous active table as a `InactivePageTable`. We don't need to flush the TLB here, as the CPU does it automatically when the P4 table is switched. In fact, the `tlb::flush_all` function, which we used above, does nothing more than [reloading the CR3 register].
|
||||
|
||||
[reloading the CR3 register]: https://github.com/gz/rust-x86/blob/master/src/shared/tlb.rs#L19
|
||||
[reloading the CR3 register]: https://docs.rs/x86_64/0.1.2/src/x86_64/instructions/tlb.rs.html#11-14
|
||||
|
||||
Now we are finally able to switch to the new table. We do it by adding the following lines to our `remap_the_kernel` function:
|
||||
|
||||
@@ -1093,8 +1093,8 @@ Now that we have a (mostly) safe kernel stack and a working page table module, w
|
||||
|
||||
[next post]: ./first-edition/posts/08-kernel-heap/index.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/btree_map/struct.BTreeMap.html
|
||||
[Vec]: https://doc.rust-lang.org/1.10.0/collections/vec/struct.Vec.html
|
||||
[BTreeMap]: https://doc.rust-lang.org/1.10.0/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.
|
||||
|
||||
@@ -24,9 +24,9 @@ As always, you can find the complete source code on [GitHub]. Please file [issue
|
||||
## Introduction
|
||||
The _heap_ is the memory area for long-lived allocations. The programmer can access it by using types like [Box][Box rustbyexample] or [Vec]. Behind the scenes, the compiler manages that memory by inserting calls to some memory allocator. By default, Rust links to the [jemalloc] allocator (for binaries) or the system allocator (for libraries). However, both rely on [system calls] such as [sbrk] and are thus unusable in our kernel. So we need to create and link our own allocator.
|
||||
|
||||
[Box rustbyexample]: http://rustbyexample.com/std/box.html
|
||||
[Box rustbyexample]: https://doc.rust-lang.org/rust-by-example/std/box.html
|
||||
[Vec]: https://doc.rust-lang.org/book/vectors.html
|
||||
[jemalloc]: http://www.canonware.com/jemalloc/
|
||||
[jemalloc]: http://jemalloc.net/
|
||||
[system calls]: https://en.wikipedia.org/wiki/System_call
|
||||
[sbrk]: https://en.wikipedia.org/wiki/Sbrk
|
||||
|
||||
@@ -42,7 +42,7 @@ These requirements make good allocators pretty complex. For example, [jemalloc]
|
||||
|
||||
The allocator interface in Rust is defined through the [`Alloc` trait], which looks like this:
|
||||
|
||||
[`Alloc` trait]: https://doc.rust-lang.org/nightly/alloc/allocator/trait.Alloc.html
|
||||
[`Alloc` trait]: https://doc.rust-lang.org/1.20.0/alloc/allocator/trait.Alloc.html
|
||||
|
||||
```rust
|
||||
pub unsafe trait Alloc {
|
||||
@@ -87,8 +87,8 @@ extern crate alloc;
|
||||
|
||||
We don't need to add anything to our Cargo.toml, since the `alloc` crate is part of the standard library and shipped with the Rust compiler. The `alloc` crate provides the [format!] and [vec!] macros, so we use `#[macro_use]` to import them.
|
||||
|
||||
[format!]: //doc.rust-lang.org/nightly/collections/macro.format!.html
|
||||
[vec!]: https://doc.rust-lang.org/nightly/collections/macro.vec!.html
|
||||
[format!]: https://doc.rust-lang.org/1.10.0/collections/macro.format!.html
|
||||
[vec!]: https://doc.rust-lang.org/1.10.0/collections/macro.vec!.html
|
||||
|
||||
When we try to compile our crate now, the following error occurs:
|
||||
|
||||
@@ -573,14 +573,14 @@ We can also use all other types of the `alloc` crate, including:
|
||||
- [BinaryHeap]
|
||||
- [BTreeMap] and [BTreeSet]
|
||||
|
||||
[Rc]: https://doc.rust-lang.org/nightly/alloc/rc/
|
||||
[Arc]: https://doc.rust-lang.org/nightly/alloc/arc/
|
||||
[String]: https://doc.rust-lang.org/nightly/collections/string/struct.String.html
|
||||
[Linked List]: https://doc.rust-lang.org/nightly/collections/linked_list/struct.LinkedList.html
|
||||
[VecDeque]: https://doc.rust-lang.org/nightly/collections/vec_deque/struct.VecDeque.html
|
||||
[BinaryHeap]: https://doc.rust-lang.org/nightly/collections/binary_heap/struct.BinaryHeap.html
|
||||
[BTreeMap]: https://doc.rust-lang.org/nightly/collections/btree_map/struct.BTreeMap.html
|
||||
[BTreeSet]: https://doc.rust-lang.org/nightly/collections/btree_set/struct.BTreeSet.html
|
||||
[Rc]: https://doc.rust-lang.org/1.10.0/alloc/rc/
|
||||
[Arc]: https://doc.rust-lang.org/1.10.0/alloc/arc/
|
||||
[String]: https://doc.rust-lang.org/1.10.0/collections/string/struct.String.html
|
||||
[Linked List]: https://doc.rust-lang.org/1.10.0/collections/linked_list/struct.LinkedList.html
|
||||
[VecDeque]: https://doc.rust-lang.org/1.10.0/collections/vec_deque/struct.VecDeque.html
|
||||
[BinaryHeap]: https://doc.rust-lang.org/1.10.0/collections/binary_heap/struct.BinaryHeap.html
|
||||
[BTreeMap]: https://doc.rust-lang.org/1.10.0/collections/btree_map/struct.BTreeMap.html
|
||||
[BTreeSet]: https://doc.rust-lang.org/1.10.0/collections/btree_set/struct.BTreeSet.html
|
||||
|
||||
## A better Allocator
|
||||
Right now, we leak every freed memory block. Thus, we run out of memory quickly, for example, by creating a new `String` in each iteration of a loop:
|
||||
|
||||
@@ -128,7 +128,7 @@ However, there is a major difference between exceptions and function calls: A fu
|
||||
[Calling conventions] specify the details of a function call. For example, they specify where function parameters are placed (e.g. in registers or on the stack) and how results are returned. On x86_64 Linux, the following rules apply for C functions (specified in the [System V ABI]):
|
||||
|
||||
[Calling conventions]: https://en.wikipedia.org/wiki/Calling_convention
|
||||
[System V ABI]: http://refspecs.linuxbase.org/elf/x86-64-abi-0.99.pdf
|
||||
[System V ABI]: http://refspecs.linuxbase.org/elf/gabi41.pdf
|
||||
|
||||
- the first six integer arguments are passed in registers `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`
|
||||
- additional arguments are passed on the stack
|
||||
@@ -449,7 +449,7 @@ The reason for the diffent instruction pointer values is that the stored value i
|
||||
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
|
||||
[AMD64 manual]: http://developer.amd.com/wordpress/media/2012/10/24593_APM_v21.pdf
|
||||
[AMD64 manual]: https://www.amd.com/system/files/TechDocs/24593.pdf
|
||||
|
||||
> `#BP` is a trap-type exception. The saved instruction pointer points to the byte after the `INT3` instruction.
|
||||
|
||||
|
||||
@@ -356,7 +356,7 @@ impl MemoryController {
|
||||
```
|
||||
The `MemoryController` struct holds the three types that are required for `alloc_stack` and provides a simpler interface (only one argument). The `alloc_stack` wrapper just takes the tree types as `&mut` through [destructuring] and forwards them to the `stack_allocator`. The [ref mut]-s are needed to take the inner fields by mutable reference. Note that we're re-exporting the `Stack` type since it is returned by `alloc_stack`.
|
||||
|
||||
[destructuring]: http://rust-lang.github.io/book/ch18-00-patterns.html#Destructuring
|
||||
[destructuring]: https://doc.rust-lang.org/1.10.0/book/patterns.html#destructuring
|
||||
[ref mut]: http://rust-lang.github.io/book/ch18-00-patterns.html#ref-and-ref-mut
|
||||
|
||||
The last step is to create a `StackAllocator` and return a `MemoryController` from `memory::init`:
|
||||
@@ -554,7 +554,7 @@ pub enum Descriptor {
|
||||
|
||||
The flag bits are common between all descriptor types, so we create a general `DescriptorFlags` type (using the [bitflags] macro):
|
||||
|
||||
[bitflags]: https://doc.rust-lang.org/bitflags/bitflags/macro.bitflags.html
|
||||
[bitflags]: https://docs.rs/bitflags/0.9.1/bitflags/macro.bitflags.html
|
||||
|
||||
```rust
|
||||
// in src/interrupts/gdt.rs
|
||||
@@ -882,8 +882,8 @@ pub fn init(memory_controller: &mut MemoryController) {
|
||||
|
||||
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`].
|
||||
|
||||
[`set_cs`]: https://docs.rs/x86/0.8.0/x86/shared/segmentation/fn.set_cs.html
|
||||
[`load_tss`]: https://docs.rs/x86/0.8.0/x86/shared/task/fn.load_tss.html
|
||||
[`set_cs`]: https://docs.rs/x86_64/0.1.2/x86_64/instructions/segmentation/fn.set_cs.html
|
||||
[`load_tss`]: https://docs.rs/x86_64/0.1.2/x86_64/instructions/tables/fn.load_tss.html
|
||||
|
||||
Now that we loaded a valid TSS and interrupt stack table, we can set the stack index for our double fault handler in the IDT:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user