Fix lot of dead links in both the 2nd and 1st edition

This commit is contained in:
Philipp Oppermann
2019-07-07 10:33:12 +02:00
parent 265f9f9bd5
commit bc5631d9a8
30 changed files with 146 additions and 153 deletions

View File

@@ -377,7 +377,7 @@ We can't know when the next IDT will be loaded. Maybe never. So in the worst cas
This is exactly the definition of a [static lifetime]. So we can easily ensure that the IDT lives long enough by adding a `'static` requirement to the signature of the `load` function:
[static lifetime]: http://rustbyexample.com/scope/lifetime/static_lifetime.html
[static lifetime]: https://doc.rust-lang.org/rust-by-example/scope/lifetime/static_lifetime.html
```rust
pub fn load(&'static self) {...}
@@ -517,9 +517,9 @@ That's a not our exception handler. The reason is that Rust itself checks for a
### Inline Assembly
In order to cause a divide-by-zero exception, we need to execute a [div] or [idiv] assembly instruction with operand 0. We could write a small assembly function and call it from our Rust code. An easier way is to use Rust's [inline assembly] macro.
[div]: http://x86.renejeschke.de/html/file_module_x86_id_72.html
[idiv]: http://x86.renejeschke.de/html/file_module_x86_id_137.html
[inline assembly]: https://doc.rust-lang.org/book/inline-assembly.html
[div]: https://www.felixcloutier.com/x86/div
[idiv]: https://www.felixcloutier.com/x86/idiv
[inline assembly]: https://doc.rust-lang.org/1.10.0/book/inline-assembly.html
Inline assembly allows us to write raw x86 assembly within a Rust function. The feature is unstable, so we need to add `#![feature(asm)]` to our `src/lib.rs`. Then we're able to write a `divide_by_zero` function:

View File

@@ -75,7 +75,7 @@ extern "C" fn divide_by_zero_handler() -> ! {
```
We're using [inline assembly] here to load the value from the `rsp` register into `stack_frame`. The syntax is a bit strange, so here's a quick explanation:
[inline assembly]: https://doc.rust-lang.org/nightly/book/inline-assembly.html
[inline assembly]: https://doc.rust-lang.org/1.10.0/book/inline-assembly.html
- The `asm!` macro emits raw assembly instructions. This is the only way to read raw register values in Rust.
- We insert a single assembly instruction: `mov $0, rsp`. It moves the value of `rsp` to some register (the `$0` is a placeholder for an arbitrary register, which gets filled by the compiler).
@@ -339,7 +339,7 @@ objdump -d build/kernel-x86_64.bin | grep "10cf08:"
```
The [movaps] instruction is an [SSE] instruction that moves aligned 128bit values. It can fail for a number of reasons:
[movaps]: http://x86.renejeschke.de/html/file_module_x86_id_180.html
[movaps]: https://www.felixcloutier.com/x86/movaps
[SSE]: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
1. For an illegal memory operand effective address in the CS, DS, ES, FS or GS segments.
@@ -497,7 +497,7 @@ Invalid opcode faults have the vector number 6, so we set the 6th IDT entry. Thi
We can test our new handler with the special [ud2] instruction, which generates a invalid opcode:
[ud2]: http://x86.renejeschke.de/html/file_module_x86_id_318.html
[ud2]: https://www.felixcloutier.com/x86/ud
```rust
// in src/lib.rs

View File

@@ -459,7 +459,7 @@ In order to fix this problem, we need to backup all caller-saved multimedia regi
The Rust compiler (and LLVM) assume that the `x86_64-unknown-linux-gnu` target supports only MMX and SSE, so we don't need to save the `ymm0` through `ymm15`. But we need to save `xmm0` through `xmm15` and also `mm0` through `mm7`. There is a special instruction to do this: [fxsave]. This instruction saves the floating point and multimedia state to a given address. It needs _512 bytes_ to store that state.
[fxsave]: http://x86.renejeschke.de/html/file_module_x86_id_128.html
[fxsave]: https://www.felixcloutier.com/x86/fxsave
In order to save/restore the multimedia registers, we _could_ add new macros:
@@ -482,7 +482,7 @@ macro_rules! restore_multimedia_registers {
```
First, we reserve the 512 bytes on the stack and then we use `fxsave` to backup the multimedia registers. In order to restore them later, we use the [fxrstor] instruction. Note that `fxsave` and `fxrstor` require a 16 byte aligned memory address.
[fxrstor]: http://x86.renejeschke.de/html/file_module_x86_id_127.html
[fxrstor]: https://www.felixcloutier.com/x86/fxrstor
However, _we won't do it that way_. The problem is the large amount of memory required. We will reuse the same code when we handle hardware interrupts in a future post. So for each mouse click, pressed key, or arrived network package we need to write 512 bytes to memory. This would be a huge performance problem.

View File

@@ -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:
```

View File

@@ -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:

View File

@@ -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

View File

@@ -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);

View File

@@ -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:

View File

@@ -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.

View File

@@ -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:

View File

@@ -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.

View File

@@ -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: