Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update posts for x86_64 version 0.7.0 #607

Merged
merged 1 commit into from
May 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions blog/content/second-edition/posts/04-testing/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,18 +166,18 @@ The functionality of the `isa-debug-exit` device is very simple. When a `value`

Instead of manually invoking the `in` and `out` assembly instructions, we use the abstractions provided by the [`x86_64`] crate. To add a dependency on that crate, we add it to the `dependencies` section in our `Cargo.toml`:

[`x86_64`]: https://docs.rs/x86_64/0.6.0/x86_64/
[`x86_64`]: https://docs.rs/x86_64/0.7.0/x86_64/

```toml
# in Cargo.toml

[dependencies]
x86_64 = "0.6.0"
x86_64 = "0.7.0"
```

Now we can use the [`Port`] type provided by the crate to create an `exit_qemu` function:

[`Port`]: https://docs.rs/x86_64/0.6.0/x86_64/instructions/port/struct.Port.html
[`Port`]: https://docs.rs/x86_64/0.7.0/x86_64/instructions/port/struct.Port.html

```rust
// in src/main.rs
Expand Down
16 changes: 8 additions & 8 deletions blog/content/second-edition/posts/05-cpu-exceptions/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Don't worry about steps 4 and 5 for now, we will learn about the global descript
## An IDT Type
Instead of creating our own IDT type, we will use the [`InterruptDescriptorTable` struct] of the `x86_64` crate, which looks like this:

[`InterruptDescriptorTable` struct]: https://docs.rs/x86_64/0.6.0/x86_64/structures/idt/struct.InterruptDescriptorTable.html
[`InterruptDescriptorTable` struct]: https://docs.rs/x86_64/0.7.0/x86_64/structures/idt/struct.InterruptDescriptorTable.html

``` rust
#[repr(C)]
Expand Down Expand Up @@ -113,10 +113,10 @@ pub struct InterruptDescriptorTable {

The fields have the type [`idt::Entry<F>`], which is a struct that represents the fields of an IDT entry (see the table above). The type parameter `F` defines the expected handler function type. We see that some entries require a [`HandlerFunc`] and some entries require a [`HandlerFuncWithErrCode`]. The page fault even has its own special type: [`PageFaultHandlerFunc`].

[`idt::Entry<F>`]: https://docs.rs/x86_64/0.6.0/x86_64/structures/idt/struct.Entry.html
[`HandlerFunc`]: https://docs.rs/x86_64/0.6.0/x86_64/structures/idt/type.HandlerFunc.html
[`HandlerFuncWithErrCode`]: https://docs.rs/x86_64/0.6.0/x86_64/structures/idt/type.HandlerFuncWithErrCode.html
[`PageFaultHandlerFunc`]: https://docs.rs/x86_64/0.6.0/x86_64/structures/idt/type.PageFaultHandlerFunc.html
[`idt::Entry<F>`]: https://docs.rs/x86_64/0.7.0/x86_64/structures/idt/struct.Entry.html
[`HandlerFunc`]: https://docs.rs/x86_64/0.7.0/x86_64/structures/idt/type.HandlerFunc.html
[`HandlerFuncWithErrCode`]: https://docs.rs/x86_64/0.7.0/x86_64/structures/idt/type.HandlerFuncWithErrCode.html
[`PageFaultHandlerFunc`]: https://docs.rs/x86_64/0.7.0/x86_64/structures/idt/type.PageFaultHandlerFunc.html

Let's look at the `HandlerFunc` type first:

Expand Down Expand Up @@ -191,7 +191,7 @@ So the _interrupt stack frame_ looks like this:

In the `x86_64` crate, the interrupt stack frame is represented by the [`InterruptStackFrame`] 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.

[`InterruptStackFrame`]: https://docs.rs/x86_64/0.6.0/x86_64/structures/idt/struct.InterruptStackFrame.html
[`InterruptStackFrame`]: https://docs.rs/x86_64/0.7.0/x86_64/structures/idt/struct.InterruptStackFrame.html

Note that there is currently [a bug in LLVM] that leads to wrong error code arguments. The cause of the issue is already known and a solution is [being worked on].

Expand Down Expand Up @@ -279,7 +279,7 @@ This error occurs because the `x86-interrupt` calling convention is still unstab
In order that the CPU uses our new interrupt descriptor table, we need to load it using the [`lidt`] instruction. The `InterruptDescriptorTable` struct of the `x86_64` provides a [`load`][InterruptDescriptorTable::load] method function for that. Let's try to use it:

[`lidt`]: http://x86.renejeschke.de/html/file_module_x86_id_156.html
[InterruptDescriptorTable::load]: https://docs.rs/x86_64/0.6.0/x86_64/structures/idt/struct.InterruptDescriptorTable.html#method.load
[InterruptDescriptorTable::load]: https://docs.rs/x86_64/0.7.0/x86_64/structures/idt/struct.InterruptDescriptorTable.html#method.load

```rust
// in src/interrupts.rs
Expand Down Expand Up @@ -462,7 +462,7 @@ You can try this new test by running `cargo xtest` (all tests) or `cargo xtest -
The `x86-interrupt` calling convention and the [`InterruptDescriptorTable`] 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. Note that these posts are based on the [first edition] of this blog and might be out of date.

[“Handling Exceptions with Naked Functions”]: ./first-edition/extra/naked-exceptions/_index.md
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.6.0/x86_64/structures/idt/struct.InterruptDescriptorTable.html
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.7.0/x86_64/structures/idt/struct.InterruptDescriptorTable.html
[first edition]: ./first-edition/_index.md

## What's next?
Expand Down
6 changes: 3 additions & 3 deletions blog/content/second-edition/posts/06-double-faults/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ The _Privilege Stack Table_ is used by the CPU when the privilege level changes.
### Creating a TSS
Let's create a new TSS that contains a separate double fault stack in its interrupt stack table. For that we need a TSS struct. Fortunately, the `x86_64` crate already contains a [`TaskStateSegment` struct] that we can use.

[`TaskStateSegment` struct]: https://docs.rs/x86_64/0.6.0/x86_64/structures/tss/struct.TaskStateSegment.html
[`TaskStateSegment` struct]: https://docs.rs/x86_64/0.7.0/x86_64/structures/tss/struct.TaskStateSegment.html

We create the TSS in a new `gdt` module (the name will make sense later):

Expand Down Expand Up @@ -371,8 +371,8 @@ pub fn init() {

We reload the code segment register using [`set_cs`] and to load the TSS using [`load_tss`]. The functions are marked as `unsafe`, so we need an `unsafe` block to invoke them. The reason is that it might be possible to break memory safety by loading invalid selectors.

[`set_cs`]: https://docs.rs/x86_64/0.6.0/x86_64/instructions/segmentation/fn.set_cs.html
[`load_tss`]: https://docs.rs/x86_64/0.6.0/x86_64/instructions/tables/fn.load_tss.html
[`set_cs`]: https://docs.rs/x86_64/0.7.0/x86_64/instructions/segmentation/fn.set_cs.html
[`load_tss`]: https://docs.rs/x86_64/0.7.0/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:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ extern "x86-interrupt" fn timer_interrupt_handler(

Our `timer_interrupt_handler` has the same signature as our exception handlers, because the CPU reacts identically to exceptions and external interrupts (the only difference is that some exceptions push an error code). The [`InterruptDescriptorTable`] struct implements the [`IndexMut`] trait, so we can access individual entries through array indexing syntax.

[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.6.0/x86_64/structures/idt/struct.InterruptDescriptorTable.html
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.7.0/x86_64/structures/idt/struct.InterruptDescriptorTable.html
[`IndexMut`]: https://doc.rust-lang.org/core/ops/trait.IndexMut.html

In our timer interrupt handler, we print a dot to the screen. As the timer interrupt happens periodically, we would expect to see a dot appearing on each timer tick. However, when we run it we see that only a single dot is printed:
Expand Down Expand Up @@ -331,7 +331,7 @@ pub fn _print(args: fmt::Arguments) {

The [`without_interrupts`] function takes a [closure] and executes it in an interrupt-free environment. We use it to ensure that no interrupt can occur as long as the `Mutex` is locked. When we run our kernel now we see that it keeps running without hanging. (We still don't notice any dots, but this is because they're scrolling by too fast. Try to slow down the printing, e.g. by putting a `for _ in 0..10000 {}` inside the loop.)

[`without_interrupts`]: https://docs.rs/x86_64/0.6.0/x86_64/instructions/interrupts/fn.without_interrupts.html
[`without_interrupts`]: https://docs.rs/x86_64/0.7.0/x86_64/instructions/interrupts/fn.without_interrupts.html
[closure]: https://doc.rust-lang.org/book/second-edition/ch13-01-closures.html

We can apply the same change to our serial printing function to ensure that no deadlocks occur with it either:
Expand Down Expand Up @@ -602,7 +602,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(

We use the [`Port`] type of the `x86_64` crate to read a byte from the keyboard's data port. This byte is called the [_scancode_] and is a number that represents the key press/release. We don't do anything with the scancode yet, we just print it to the screen:

[`Port`]: https://docs.rs/x86_64/0.6.0/x86_64/instructions/port/struct.Port.html
[`Port`]: https://docs.rs/x86_64/0.7.0/x86_64/instructions/port/struct.Port.html
[_scancode_]: https://en.wikipedia.org/wiki/Scancode

![QEMU printing scancodes to the screen when keys are pressed](qemu-printing-scancodes.gif)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,8 @@ Let's take a closer look at the available flags:

The `x86_64` crate provides types for [page tables] and their [entries], so we don't need to create these structures ourselves.

[page tables]: https://docs.rs/x86_64/0.6.0/x86_64/structures/paging/struct.PageTable.html
[entries]: https://docs.rs/x86_64/0.6.0/x86_64/structures/paging/page_table/struct.PageTableEntry.html
[page tables]: https://docs.rs/x86_64/0.7.0/x86_64/structures/paging/struct.PageTable.html
[entries]: https://docs.rs/x86_64/0.7.0/x86_64/structures/paging/page_table/struct.PageTableEntry.html

### The Translation Lookaside Buffer

Expand All @@ -239,7 +239,7 @@ A 4-level page table makes the translation of virtual addresses expensive, becau
Unlike the other CPU caches, the TLB is not fully transparent and does not update or remove translations when the contents of page tables change. This means that the kernel must manually update the TLB whenever it modifies a page table. To do this, there is a special CPU instruction called [`invlpg`] (“invalidate page”) that removes the translation for the specified page from the TLB, so that it is loaded again from the page table on the next access. The TLB can also be flushed completely by reloading the `CR3` register, which simulates an address space switch. The `x86_64` crate provides Rust functions for both variants in the [`tlb` module].

[`invlpg`]: https://www.felixcloutier.com/x86/INVLPG.html
[`tlb` module]: https://docs.rs/x86_64/0.6.0/x86_64/instructions/tlb/index.html
[`tlb` module]: https://docs.rs/x86_64/0.7.0/x86_64/instructions/tlb/index.html

It is important to remember flushing the TLB on each page table modification because otherwise the CPU might keep using the old translation, which can lead to non-deterministic bugs that are very hard to debug.

Expand Down Expand Up @@ -292,8 +292,8 @@ extern "x86-interrupt" fn page_fault_handler(
The [`CR2`] register is automatically set by the CPU on a page fault and contains the accessed virtual address that caused the page fault. We use the [`Cr2::read`] function of the `x86_64` crate to read and print it. Normally the [`PageFaultErrorCode`] type would provide more information about the type of memory access that caused the page fault, but there is currently an [LLVM bug] that passes an invalid error code, so we ignore it for now. We can't continue execution without resolving the page fault, so we enter a [`hlt_loop`] at the end.

[`CR2`]: https://en.wikipedia.org/wiki/Control_register#CR2
[`Cr2::read`]: https://docs.rs/x86_64/0.6.0/x86_64/registers/control/struct.Cr2.html#method.read
[`PageFaultErrorCode`]: https://docs.rs/x86_64/0.6.0/x86_64/structures/idt/struct.PageFaultErrorCode.html
[`Cr2::read`]: https://docs.rs/x86_64/0.7.0/x86_64/registers/control/struct.Cr2.html#method.read
[`PageFaultErrorCode`]: https://docs.rs/x86_64/0.7.0/x86_64/structures/idt/struct.PageFaultErrorCode.html
[LLVM bug]: https://github.com/rust-lang/rust/issues/57270
[`hlt_loop`]: ./second-edition/posts/07-hardware-interrupts/index.md#the

Expand Down Expand Up @@ -365,9 +365,9 @@ pub extern "C" fn _start() -> ! {

The [`Cr3::read`] function of the `x86_64` returns the currently active level 4 page table from the `CR3` register. It returns a tuple of a [`PhysFrame`] and a [`Cr3Flags`] type. We are only interested in the frame, so we ignore the second element of the tuple.

[`Cr3::read`]: https://docs.rs/x86_64/0.6.0/x86_64/registers/control/struct.Cr3.html#method.read
[`PhysFrame`]: https://docs.rs/x86_64/0.6.0/x86_64/structures/paging/frame/struct.PhysFrame.html
[`Cr3Flags`]: https://docs.rs/x86_64/0.6.0/x86_64/registers/control/struct.Cr3Flags.html
[`Cr3::read`]: https://docs.rs/x86_64/0.7.0/x86_64/registers/control/struct.Cr3.html#method.read
[`PhysFrame`]: https://docs.rs/x86_64/0.7.0/x86_64/structures/paging/frame/struct.PhysFrame.html
[`Cr3Flags`]: https://docs.rs/x86_64/0.7.0/x86_64/registers/control/struct.Cr3Flags.html

When we run it, we see the following output:

Expand All @@ -377,7 +377,7 @@ Level 4 page table at: PhysAddr(0x1000)

So the currently active level 4 page table is stored at address `0x1000` in _physical_ memory, as indicated by the [`PhysAddr`] wrapper type. The question now is: how can we access this table from our kernel?

[`PhysAddr`]: https://docs.rs/x86_64/0.6.0/x86_64/struct.PhysAddr.html
[`PhysAddr`]: https://docs.rs/x86_64/0.7.0/x86_64/struct.PhysAddr.html

Accessing physical memory directly is not possible when paging is active, since programs could easily circumvent memory protection and access memory of other programs otherwise. So the only way to access the table is through some virtual page that is mapped to the physical frame at address `0x1000`. This problem of creating mappings for page table frames is a general problem, since the kernel needs to access the page tables regularly, for example when allocating a stack for a new thread.

Expand Down
Loading