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 to fix build on latest nightly #990

Merged
merged 4 commits into from
May 17, 2021
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/edition-2/posts/04-testing/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,18 +174,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.13.2/x86_64/
[`x86_64`]: https://docs.rs/x86_64/0.14.2/x86_64/

```toml
# in Cargo.toml

[dependencies]
x86_64 = "0.13.2"
x86_64 = "0.14.2"
```

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

[`Port`]: https://docs.rs/x86_64/0.13.2/x86_64/instructions/port/struct.Port.html
[`Port`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/port/struct.Port.html

```rust
// in src/main.rs
Expand Down
22 changes: 11 additions & 11 deletions blog/content/edition-2/posts/05-cpu-exceptions/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,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.13.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
[`InterruptDescriptorTable` struct]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html

``` rust
#[repr(C)]
Expand Down Expand Up @@ -115,15 +115,15 @@ 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.13.2/x86_64/structures/idt/struct.Entry.html
[`HandlerFunc`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/type.HandlerFunc.html
[`HandlerFuncWithErrCode`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/type.HandlerFuncWithErrCode.html
[`PageFaultHandlerFunc`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/type.PageFaultHandlerFunc.html
[`idt::Entry<F>`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.Entry.html
[`HandlerFunc`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/type.HandlerFunc.html
[`HandlerFuncWithErrCode`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/type.HandlerFuncWithErrCode.html
[`PageFaultHandlerFunc`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/type.PageFaultHandlerFunc.html

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

```rust
type HandlerFunc = extern "x86-interrupt" fn(_: &mut InterruptStackFrame);
type HandlerFunc = extern "x86-interrupt" fn(_: InterruptStackFrame);
```

It's a [type alias] for an `extern "x86-interrupt" fn` type. The `extern` keyword defines a function with a [foreign calling convention] and is often used to communicate with C code (`extern "C" fn`). But what is the `x86-interrupt` calling convention?
Expand Down Expand Up @@ -195,7 +195,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.13.2/x86_64/structures/idt/struct.InterruptStackFrame.html
[`InterruptStackFrame`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptStackFrame.html

### Behind the Scenes
The `x86-interrupt` calling convention is a powerful abstraction that hides almost all of the messy details of the exception handling process. However, sometimes it's useful to know what's happening behind the curtain. Here is a short overview of the things that the `x86-interrupt` calling convention takes care of:
Expand Down Expand Up @@ -249,7 +249,7 @@ pub fn init_idt() {
}

extern "x86-interrupt" fn breakpoint_handler(
stack_frame: &mut InterruptStackFrame)
stack_frame: InterruptStackFrame)
{
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
}
Expand All @@ -263,7 +263,7 @@ When we try to compile it, the following error occurs:
error[E0658]: x86-interrupt ABI is experimental and subject to change (see issue #40180)
--> src/main.rs:53:1
|
53 | / extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut InterruptStackFrame) {
53 | / extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
54 | | println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
55 | | }
| |_^
Expand All @@ -277,7 +277,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`]: https://www.felixcloutier.com/x86/lgdt:lidt
[InterruptDescriptorTable::load]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html#method.load
[InterruptDescriptorTable::load]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html#method.load

```rust
// in src/interrupts.rs
Expand Down Expand Up @@ -457,7 +457,7 @@ blog_os::interrupts::test_breakpoint_exception... [ok]
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”]: @/edition-1/extra/naked-exceptions/_index.md
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
[first edition]: @/edition-1/_index.md

## What's next?
Expand Down
10 changes: 5 additions & 5 deletions blog/content/edition-2/posts/06-double-faults/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ lazy_static! {

// new
extern "x86-interrupt" fn double_fault_handler(
stack_frame: &mut InterruptStackFrame, _error_code: u64) -> !
stack_frame: InterruptStackFrame, _error_code: u64) -> !
{
panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
}
Expand Down Expand Up @@ -229,7 +229,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.13.2/x86_64/structures/tss/struct.TaskStateSegment.html
[`TaskStateSegment` struct]: https://docs.rs/x86_64/0.14.2/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 @@ -375,8 +375,8 @@ pub fn init() {

We reload the code segment register using [`set_cs`] and 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.13.2/x86_64/instructions/segmentation/fn.set_cs.html
[`load_tss`]: https://docs.rs/x86_64/0.13.2/x86_64/instructions/tables/fn.load_tss.html
[`set_cs`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/segmentation/fn.set_cs.html
[`load_tss`]: https://docs.rs/x86_64/0.14.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:

Expand Down Expand Up @@ -526,7 +526,7 @@ use blog_os::{exit_qemu, QemuExitCode, serial_println};
use x86_64::structures::idt::InterruptStackFrame;

extern "x86-interrupt" fn test_double_fault_handler(
_stack_frame: &mut InterruptStackFrame,
_stack_frame: InterruptStackFrame,
_error_code: u64,
) -> ! {
serial_println!("[ok]");
Expand Down
32 changes: 16 additions & 16 deletions blog/content/edition-2/posts/07-hardware-interrupts/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,29 +75,29 @@ Each controller can be configured through two [I/O ports], one “command” por

The default configuration of the PICs is not usable, because it sends interrupt vector numbers in the range 0–15 to the CPU. These numbers are already occupied by CPU exceptions, for example number 8 corresponds to a double fault. To fix this overlapping issue, we need to remap the PIC interrupts to different numbers. The actual range doesn't matter as long as it does not overlap with the exceptions, but typically the range 32–47 is chosen, because these are the first free numbers after the 32 exception slots.

The configuration happens by writing special values to the command and data ports of the PICs. Fortunately there is already a crate called [`pic8259_simple`], so we don't need to write the initialization sequence ourselves. In case you are interested how it works, check out [its source code][pic crate source], it's fairly small and well documented.
The configuration happens by writing special values to the command and data ports of the PICs. Fortunately there is already a crate called [`pic8259`], so we don't need to write the initialization sequence ourselves. In case you are interested how it works, check out [its source code][pic crate source], it's fairly small and well documented.

[pic crate source]: https://docs.rs/crate/pic8259_simple/0.2.0/source/src/lib.rs
[pic crate source]: https://docs.rs/crate/pic8259/0.10.0/source/src/lib.rs

To add the crate as dependency, we add the following to our project:

[`pic8259_simple`]: https://docs.rs/pic8259_simple/0.2.0/pic8259_simple/
[`pic8259`]: https://docs.rs/pic8259/0.10.0/pic8259/

```toml
# in Cargo.toml

[dependencies]
pic8259_simple = "0.2.0"
pic8259 = "0.10.1"
```

The main abstraction provided by the crate is the [`ChainedPics`] struct that represents the primary/secondary PIC layout we saw above. It is designed to be used in the following way:

[`ChainedPics`]: https://docs.rs/pic8259_simple/0.2.0/pic8259_simple/struct.ChainedPics.html
[`ChainedPics`]: https://docs.rs/pic8259/0.10.1/pic8259/struct.ChainedPics.html

```rust
// in src/interrupts.rs

use pic8259_simple::ChainedPics;
use pic8259::ChainedPics;
use spin;

pub const PIC_1_OFFSET: u8 = 32;
Expand Down Expand Up @@ -125,7 +125,7 @@ pub fn init() {

We use the [`initialize`] function to perform the PIC initialization. Like the `ChainedPics::new` function, this function is also unsafe because it can cause undefined behavior if the PIC is misconfigured.

[`initialize`]: https://docs.rs/pic8259_simple/0.2.0/pic8259_simple/struct.ChainedPics.html#method.initialize
[`initialize`]: https://docs.rs/pic8259/0.10.1/pic8259/struct.ChainedPics.html#method.initialize

If all goes well we should continue to see the "It did not crash" message when executing `cargo run`.

Expand Down Expand Up @@ -200,15 +200,15 @@ lazy_static! {
}

extern "x86-interrupt" fn timer_interrupt_handler(
_stack_frame: &mut InterruptStackFrame)
_stack_frame: InterruptStackFrame)
{
print!(".");
}
```

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.13.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.14.2/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 All @@ -225,7 +225,7 @@ To send the EOI, we use our static `PICS` struct again:
// in src/interrupts.rs

extern "x86-interrupt" fn timer_interrupt_handler(
_stack_frame: &mut InterruptStackFrame)
_stack_frame: InterruptStackFrame)
{
print!(".");

Expand Down Expand Up @@ -333,7 +333,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.13.2/x86_64/instructions/interrupts/fn.without_interrupts.html
[`without_interrupts`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/interrupts/fn.without_interrupts.html
[closure]: https://doc.rust-lang.org/book/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 @@ -538,7 +538,7 @@ lazy_static! {
}

extern "x86-interrupt" fn keyboard_interrupt_handler(
_stack_frame: &mut InterruptStackFrame)
_stack_frame: InterruptStackFrame)
{
print!("k");

Expand All @@ -563,7 +563,7 @@ To find out _which_ key was pressed, we need to query the keyboard controller. W
// in src/interrupts.rs

extern "x86-interrupt" fn keyboard_interrupt_handler(
_stack_frame: &mut InterruptStackFrame)
_stack_frame: InterruptStackFrame)
{
use x86_64::instructions::port::Port;

Expand All @@ -580,7 +580,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.13.2/x86_64/instructions/port/struct.Port.html
[`Port`]: https://docs.rs/x86_64/0.14.2/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 All @@ -604,7 +604,7 @@ To translate the scancodes to keys, we can use a match statement:
// in src/interrupts.rs

extern "x86-interrupt" fn keyboard_interrupt_handler(
_stack_frame: &mut InterruptStackFrame)
_stack_frame: InterruptStackFrame)
{
use x86_64::instructions::port::Port;

Expand Down Expand Up @@ -663,7 +663,7 @@ Now we can use this crate to rewrite our `keyboard_interrupt_handler`:
// in/src/interrupts.rs

extern "x86-interrupt" fn keyboard_interrupt_handler(
_stack_frame: &mut InterruptStackFrame)
_stack_frame: InterruptStackFrame)
{
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
use spin::Mutex;
Expand Down
Loading