From 531a069a6eca3eeaaa6156fe1c42cfd4c5414bc6 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 21 Mar 2017 19:22:35 +0100 Subject: [PATCH 01/12] =?UTF-8?q?Create=20a=20new=20=E2=80=9CHandling=20Ex?= =?UTF-8?q?ceptions=E2=80=9D=20post?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This post combines the previous exception handling posts “Catching Exceptions”, “Better Exception Messages” and “Returning from Exceptions” into a single post by using the `x86-interrupt` calling convention and the `Idt` type of the `x86_64` crate. --- blog/content/post/handling-exceptions.md | 447 ++++++++++++++++++ .../images/qemu-breakpoint-exception.png | Bin 0 -> 17268 bytes 2 files changed, 447 insertions(+) create mode 100644 blog/content/post/handling-exceptions.md create mode 100644 blog/static/images/qemu-breakpoint-exception.png diff --git a/blog/content/post/handling-exceptions.md b/blog/content/post/handling-exceptions.md new file mode 100644 index 00000000..1400e9f1 --- /dev/null +++ b/blog/content/post/handling-exceptions.md @@ -0,0 +1,447 @@ ++++ +title = "Handling Exceptions" +date = "2017-03-09" ++++ + +In this post, we start exploring CPU exceptions. Exceptions occur in various erroneous situations, for example when accessing an invalid memory address or when dividing by zero. To catch them, we have to set up an _interrupt descriptor table_ that provides handler functions. At the end of this post, our kernel will be able to catch [breakpoint exceptions] and to resume normal execution afterwards. + +[breakpoint exceptions]: http://wiki.osdev.org/Exceptions#Breakpoint + + + +As always, the complete source code is available on [Github]. Please file [issues] for any problems, questions, or improvement suggestions. There is also a comment section at the end of this page. + +[Github]: https://github.com/phil-opp/blog_os/tree/handling_exceptions +[issues]: https://github.com/phil-opp/blog_os/issues + +## Exceptions +An exception signals that something is wrong with the current instruction. For example, the CPU issues an exception if the current instruction tries to divide by 0. When an exception occurs, the CPU interrupts its current work and immediately calls a specific exception handler function, depending on the exception type. + +We've already seen several types of exceptions in our kernel: + +- **Invalid Opcode**: This exception occurs when the current instruction is invalid. For example, this exception occurred when we tried to use SSE instructions before enabling SSE. Without SSE, the CPU didn't know the `movups` and `movaps` instructions, so it throws an exception when it stumbles over them. +- **Page Fault**: A page fault occurs on illegal memory accesses. For example, if the current instruction tries to read from an unmapped page or tries to write to a read-only page. +- **Double Fault**: When an exception occurs, the CPU tries to call the corresponding handler function. If another exception exception occurs _while calling the exception handler_, the CPU raises a double fault exception. This exception also occurs when there is no handler function registered for an exception. +- **Triple Fault**: If an exception occurs while the CPU tries to call the double fault handler function, it issues a fatal _triple fault_. We can't catch or handle a triple fault. Most processors react by resetting themselves and rebooting the operating system. This causes the bootloops we experienced in the previous posts. + +For the full list of exceptions check out the [OSDev wiki][exceptions]. + +[exceptions]: http://wiki.osdev.org/Exceptions + +### The Interrupt Descriptor Table +In order to catch and handle exceptions, we have to set up a so-called _Interrupt Descriptor Table_ (IDT). In this table we can specify a handler function for each CPU exception. The hardware uses this table directly, so we need to follow a predefined format. Each entry must have the following 16-byte structure: + +Type| Name | Description +----|--------------------------|----------------------------------- +u16 | Function Pointer [0:15] | The lower bits of the pointer to the handler function. +u16 | GDT selector | Selector of a code segment in the GDT. +u16 | Options | (see below) +u16 | Function Pointer [16:31] | The middle bits of the pointer to the handler function. +u32 | Function Pointer [32:63] | The remaining bits of the pointer to the handler function. +u32 | Reserved | + +The options field has the following format: + +Bits | Name | Description +------|-----------------------------------|----------------------------------- +0-2 | Interrupt Stack Table Index | 0: Don't switch stacks, 1-7: Switch to the n-th stack in the Interrupt Stack Table when this handler is called. +3-7 | Reserved | +8 | 0: Interrupt Gate, 1: Trap Gate | If this bit is 0, interrupts are disabled when this handler is called. +9-11 | must be one | +12 | must be zero | +13‑14 | Descriptor Privilege Level (DPL) | The minimal privilege level required for calling this handler. +15 | Present | + +Each exception has a predefined IDT index. For example the invalid opcode exception has table index 6 and the page fault exception has table index 14. Thus, the hardware can automatically load the corresponding IDT entry for each exception. The [Exception Table][exceptions] in the OSDev wiki shows the IDT indexes of all exceptions in the “Vector nr.” column. + +When an exception occurs, the CPU roughly does the following: + +1. Push some registers on the stack, including the instruction pointer and the [RFLAGS] register. (We will use these values later in this post.) +2. Read the corresponding entry from the Interrupt Descriptor Table (IDT). For example, the CPU reads the 14-th entry when a page fault occurs. +3. Check if the entry is present. Raise a double fault if not. +4. Disable interrupts if the entry is an interrupt gate (bit 40 not set). +5. Load the specified GDT selector into the CS segment. +6. Jump to the specified handler function. + +[RFLAGS]: https://en.wikipedia.org/wiki/FLAGS_register + +## An IDT Type +Instead of creating our own IDT type, we will use the [`Idt` struct] of the `x86_64` crate, which looks like this: + +[`Idt` struct]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.Idt.html + +``` rust +#[repr(C)] +pub struct Idt { + pub divide_by_zero: IdtEntry, + pub debug: IdtEntry, + pub non_maskable_interrupt: IdtEntry, + pub breakpoint: IdtEntry, + pub overflow: IdtEntry, + pub bound_range_exceeded: IdtEntry, + pub invalid_opcode: IdtEntry, + pub device_not_available: IdtEntry, + pub double_fault: IdtEntry, + pub invalid_tss: IdtEntry, + pub segment_not_present: IdtEntry, + pub stack_segment_fault: IdtEntry, + pub general_protection_fault: IdtEntry, + pub page_fault: IdtEntry, + pub x87_floating_point: IdtEntry, + pub alignment_check: IdtEntry, + pub machine_check: IdtEntry, + pub simd_floating_point: IdtEntry, + pub virtualization: IdtEntry, + pub security_exception: IdtEntry, + pub interrupts: [IdtEntry; 224], + // some fields omitted +} +``` + +The fields have the type [`IdtEntry`], 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`]. + +[`IdtEntry`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.IdtEntry.html +[`HandlerFunc`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/type.HandlerFunc.html +[`HandlerFuncWithErrCode`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/type.HandlerFuncWithErrCode.html +[`PageFaultHandlerFunc`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/type.PageFaultHandlerFunc.html + +Let's look at the `HandlerFunc` type first: + +```rust +type HandlerFunc = extern "x86-interrupt" fn(_: &mut ExceptionStackFrame); +``` + +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? + +[type alias]: https://doc.rust-lang.org/book/type-aliases.html +[foreign calling convention]: https://doc.rust-lang.org/book/ffi.html#foreign-calling-conventions + +## The Interrupt Calling Convention +Exceptions are quite similar to function calls: The CPU jumps to the first instruction of the called function and executes it. Afterwards, if the function is not diverging, the CPU jumps to the return address and continues the execution of the parent function. + +However, there is a major difference between exceptions and function calls: A function call is invoked voluntary by a compiler inserted `call` instruction, while an exception might occur at _any_ instruction. In order to understand the consequences of this difference, we need to examine function calls in more detail. + +[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 + +- the first six integer arguments are passed in registers `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9` +- additional arguments are passed on the stack +- results are returned in `rax` and `rdx` + +Note that Rust does not follow the C ABI (in fact, [there isn't even a Rust ABI yet][rust abi]). So these rules apply only to functions declared as `extern "C" fn`. + +[rust abi]: https://github.com/rust-lang/rfcs/issues/600 + +### Preserved and Scratch Registers +The calling convention divides the registers in two parts: _preserved_ and _scratch_ registers. + +The values of _preserved_ registers must remain unchanged across function calls. So a called function (the _“callee”_) is only allowed to overwrite these registers if it restores their original values before returning. Therefore these registers are called _“callee-saved”_. A common pattern is to save these registers to the stack at the function's beginning and restore them just before returning. + +In contrast, a called function is allowed to overwrite _scratch_ registers without restrictions. If the caller wants to preserve the value of a scratch register across a function call, it needs to backup and restore it before the function call (e.g. by pushing it to the stack). So the scratch registers are _caller-saved_. + +On x86_64, the C calling convention specifies the following preserved and scratch registers: + +preserved registers | scratch registers +---|--- +`rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11` +_callee-saved_ | _caller-saved_ + +The compiler knows these rules, so it generates the code accordingly. For example, most functions begin with a `push rbp`, which backups `rbp` on the stack (because it's a callee-saved register). + +### Preserving all Registers +In contrast to function calls, exceptions can occur on _any_ instruction. In most cases we don't even know at compile time if the generated code will cause an exception. For example, the compiler can't know if an instruction causes a stack overflow or a page fault. + +Since we don't know when an exception occurs, we can't backup any registers before. This means that we can't use a calling convention that relies on caller-saved registers for exception handlers. Instead, we need a calling convention means that preserves _all registers_. The `x86-interrupt` calling convention is such a calling convention, so it guarantees that all register values are restored to their original values on function return. + +### The Exception Stack Frame +On a normal function call (using the `call` instruction), the CPU pushes the return address before jumping to the target function. On function return (using the `ret` instruction), the CPU pops this return address and jumps to it. So the stack frame of a normal function call looks like this: + +![function stack frame](images/function-stack-frame.svg) + +For exception and interrupt handlers, however, pushing a return address would not suffice, since interrupt handlers often run in a different context (stack pointer, CPU flags, etc.). Instead, the CPU performs the following steps when an interrupt occurs: + +1. **Aligning the stack pointer**: An interrupt can occur at any instructions, so the stack pointer can have any value, too. However, some CPU instructions (e.g. some SSE instructions) require that the stack pointer is aligned on a 16 byte boundary, therefore the CPU performs such an alignment right after the interrupt. +2. **Switching stacks** (in some cases): A stack switch occurs when the CPU privilege level changes, for example when a CPU exception occurs in an user mode program. It is also possible to configure stack switches for specific interrupts using the so-called _Interrupt Stack Table_ (described in the next post). +3. **Pushing the old stack pointer**: The CPU pushes the values of the stack pointer (`rsp`) and the stack segment (`ss`) registers at the time when the interrupt occured (before the alignment). This makes it possible to restore the original stack pointer when returning from an interrupt handler. +4. **Pushing and updating the `RFLAGS` register**: The [`RFLAGS`] register contains various control and status bits. On interrupt entry, the CPU changes some bits and pushes the old value. +5. **Pushing the instruction pointer**: Before jumping to the interrupt handler function, the CPU pushes the instruction pointer (`rip`) and the code segment (`cs`). This is comparable to the return address push of a normal function call. +6. **Pushing an error code** (for some exceptions): For some specific exceptions such as page faults, the CPU pushes an error code, which describes the cause of the exception. +7. **Invoking the interrupt handler**: The CPU reads the address and the segment descriptor of the interrupt handler function from the corresponding field in the IDT. It then invokes this handler by loading the values into the `rip` and `cs` registers. + +[`RFLAGS`]: https://en.wikipedia.org/wiki/FLAGS_register + +So the _exception stack frame_ looks like this: + +![exception stack frame](images/exception-stack-frame.svg) + +In the `x86_64` crate, the exception stack frame is represented by the [`ExceptionStackFrame`] 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. + +[`ExceptionStackFrame`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.ExceptionStackFrame.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: + +- **Retrieving the arguments**: Most calling conventions expect that the arguments are passed in registers. This is not possible for exception handlers, since we must not overwrite any register values before backing them up on the stack. Instead, the `x86-interrupt` calling convention is aware that the arguments already lie on the stack at a specific offset. +- **Returning using `iretq`**: Since the exception stack frame completely differs from stack frames of normal function calls, we can't return from handlers functions through the normal `ret` instruction. Instead, the `iretq` instruction must be used. +- **Handling the error code**: The error code, which is pushed for some exceptions, makes things much more complex. It changes the stack alignment (see the next point) and needs to be popped off the stack before returning. The `x86-64` handles all that complexity. However, it doesn't know which handler function is used for which exception, so it needs to deduce that information from the number of function arguments. That means that the programmer is still responsible to use the correct function type for each exception. Luckily, the `Idt` type defined by the `x86_64` crate ensures that the correct function types are used. +- **Aligning the stack**: There are some instructions (especially SSE instructions) that require a 16-byte stack alignment. The CPU ensures this alignment whenever an exception occurs, but for some exceptions it destroys it again later when it pushes an error code. The `x86-interrupt` calling convention takes care of this by realigning the stack in this case. + +If you are interested in more details: We also have a series of posts that explains exception handling using [naked functions] linked [at the end of this post][too-much-magic]. + +[naked functions]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md +[too-much-magic]: {{% relref "#too-much-magic" %}} + +## Implementation +Now that we've understood the theory, it's time to handle CPU exceptions in our kernel. We start by creating a new `interrupts` module: + +``` rust +// in src/lib.rs +... +mod interrupts; +... +``` + +In the new module, we create an `init` function, that creates a new `Idt`: + +``` rust +// in src/interrupts.rs + +use x86_64::structures::idt::Idt; + +pub fn init() { + let mut idt = Idt::new(); +} +``` + +Now we can add handler functions. We start by adding a handler for the [breakpoint exception]. The breakpoint exception is the perfect exception to test exception handling. Its only purpose is to temporary pause a program when the breakpoint instruction `int3` is executed. + +[breakpoint exception]: http://wiki.osdev.org/Exceptions#Breakpoint + +The breakpoint exception is commonly used in debuggers: When the user sets a breakpoint, the debugger overwrites the corresponding instruction with the `int3` instruction so that the CPU throws the breakpoint exception when it reaches that line. When the user wants to continue the program, the debugger replaces the `int3` instruction with the original instruction again and continues the program. For more details, see the ["_How debuggers work_"] series. + +["_How debuggers work_"]: http://eli.thegreenplace.net/2011/01/27/how-debuggers-work-part-2-breakpoints + +For our use case, we don't need to overwrite any instructions (it wouldn't even be possible since we [set the page table flags] to read-only). Instead, we just want to print a message when the breakpoint instruction is executed and then continue the program. + +[set the page table flags]: {{% relref "07-remap-the-kernel.md#using-the-correct-flags" %}} + +So let's create a simple `breakpoint_handler` function and add it to our IDT: + +```rust +/// in src/interrupts.rs + +use x86_64::structures::idt::ExceptionStackFrame; + +pub fn init() { + let mut idt = Idt::new(); + idt.breakpoint.set_handler_fn(breakpoint_handler); +} + +extern "x86-interrupt" fn breakpoint_handler( + stack_frame: &mut ExceptionStackFrame) +{ + println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame); +} +``` + +Our handler just outputs a message and pretty-prints the exception stack frame. + +### Loading the IDT +In order that the CPU uses our new interrupt descriptor table, we need to load it using the [`lidt`] instruction. The `Idt` struct of the `x86_64` provides a [`load`][Idt::load] method function for that. Let's try to use it: + +[`lidt`]: http://x86.renejeschke.de/html/file_module_x86_id_156.html +[Idt::load]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.Idt.html#method.load + +```rust +pub fn init() { + let mut idt = Idt::new(); + idt.breakpoint.set_handler_fn(breakpoint_handler); + idt.load(); +} +``` + +When we try to compile it now, the following error occurs: + +``` +error: `idt` does not live long enough + --> src/interrupts/mod.rs:43:5 + | +43 | idt.load(); + | ^^^ does not live long enough +44 | } + | - borrowed value only lives until here + | + = note: borrowed value must be valid for the static lifetime... +``` + +So the `load` methods expects a `&'static self`, that is a reference that is valid for the complete runtime of the program. The reason is that the CPU will access this table on every interrupt until we load a different IDT. So using a shorter lifetime than `'static` could lead to use-after-free bugs. + +In fact, this is exactly what happens here. Our `idt` is created on the stack, so it is only valid inside the `init` function. Afterwards the stack memory is reused for other functions, so the CPU would interpret random stack memory as IDT. Luckily, the `Idt::load` method encodes this lifetime requirement in its function definition, so that the Rust compiler is able to prevent this possible bug at compile time. + +In order to fix this problem, we need to store our `idt` at a place where it has a `'static` lifetime. To achieve this, we could either allocate our IDT on the heap using `Box` and then convert it to a `'static` reference or we can store the IDT as a `static`. Let's try the latter: + +```rust +static IDT: Idt = Idt::new(); + +pub fn init() { + IDT.breakpoint.set_handler_fn(breakpoint_handler); + IDT.load(); +} +``` + +There are two problems with this. First, statics are immutable, so we can't modify the breakpoint entry from our `init` function. Second, the `Idt::new` function is not a [`const` function], so it can't be used to initialize a `static`. We could solve this problem by using a [`static mut`] of type `Option`: + +[`const` function]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md +[`static mut`]: https://doc.rust-lang.org/book/const-and-static.html#mutability + +```rust +static mut IDT: Option = None; + +pub fn init() { + unsafe { + let IDT = Some(Idt::new()); + let idt = IDT.as_mut_ref().unwrap(); + idt.breakpoint.set_handler_fn(breakpoint_handler); + idt.load(); + } +} +``` + +This variant compiles without errors but it's far from idiomatic. `static mut`s are very prone to data races, so we need an [`unsafe` block] on each access. Also, we need to explicitly `unwrap` the `IDT` on each use, since might be `None`. + +[`unsafe` block]: https://doc.rust-lang.org/book/unsafe.html#unsafe-superpowers + +#### Lazy Statics to the Rescue +The one-time initialization of statics with non-const functions is a common problem in Rust. Fortunately, there already exists a good solution in a crate named [lazy_static]. This crate provides a `lazy_static!` macro that defines a lazily initialized `static`. Instead of computing its value at compile time, the `static` laziliy initializes itself when it's accessed the first time. Thus, the initialization happens at runtime so that arbitrarily complex initialization code is possible. + +[lazy_static]: https://docs.rs/lazy_static/0.2.4/lazy_static/ + +Let's add the `lazy_static` crate to our project: + +```rust +// in src/lib.rs + +#[macro_use] +extern crate lazy_static; +``` + +```toml +# in Cargo.toml + +[dependencies.lazy_static] +version = "0.2.4" +features = ["spin_no_std"] +``` +We need the `spin_no_std` feature, since we don't link the standard library. We also need the `#[macro_use]` attribute on the `extern crate` line to import the `lazy_static!` macro. + +Now we can create our static IDT using `lazy_static`: + +```rust +lazy_static! { + static ref IDT: Idt = { + let mut idt = Idt::new(); + idt.breakpoint.set_handler_fn(breakpoint_handler); + idt + }; +} + +pub fn init() { + IDT.load(); +} +``` + +Note how this solution requires no `unsafe` blocks or `unwrap` calls. + +> ##### Aside: How does the `lazy_static!` macro work? +> +> The macro generates a `static` of type `Once`. The [`Once`][spin::Once] type is provided by the `spin` crate and allows deferred one-time initialization. It is implemented using an [`AtomicUsize`] for synchronization and an [`UnsafeCell`] for storing the (possibly unitialized) value. So this solution also uses `unsafe` behind the scenes, but it is abstracted away in a safe interface. + +[spin::Once]: https://docs.rs/spin/0.4.5/spin/struct.Once.html +[`AtomicUsize`]: https://doc.rust-lang.org/nightly/core/sync/atomic/struct.AtomicUsize.html +[`UnsafeCell`]: https://doc.rust-lang.org/nightly/core/cell/struct.UnsafeCell.html + +### Testing it +Now we should be able to handle breakpoint exceptions! Let's try it in our `rust_main`: + +```rust +// in src/lib.rs + +pub extern "C" fn rust_main(...) { + ... + memory::init(boot_info); + + // initialize our IDT + interrupts::init(); + + // invoke a breakpoint exception + x86_64::instructions::interrupts::int3(); + + println!("It did not crash!"); + loop {} +} +``` + +When we run it in QEMU now (using `make run`), we see the following: + +![QEMU printing `EXCEPTION: BREAKPOINT` and the exception stack frame](images/qemu-breakpoint-exception.png) + +It works! The CPU successfully invokes our breakpoint handler, which prints the message, and then returns back to the `rust_main` function, where the `It did not crash!` message is printed. + +We see that the exception stack frame tells us the instruction and stack pointers at the time when the exception occured. This information is very useful when debugging unexpected exceptions. For example, we can look at the corresponding assembly line using `objdump`: + +``` +> objdump -d build/kernel-x86_64.bin | grep -B5 "1140a6:" +00000000001140a0 : + 1140a0: 55 push %rbp + 1140a1: 48 89 e5 mov %rsp,%rbp + 1140a4: 50 push %rax + 1140a5: cc int3 + 1140a6: 48 83 c4 08 add $0x8,%rsp +``` + +The `-d` flags disassembles the `code` section and `-C` flag makes function names more readable by [demangling] them. The `-B` flag of `grep` specifies the number of preceding lines that should be shown (5 in our case). + +[demangling]: https://en.wikipedia.org/wiki/Name_mangling + +We clearly see the `int3` exception that caused the breakpoint exception at address `1140a5`. Wait… the stored instruction pointer was `1140a6`, which is a normal `add` operation. What's happening here? + +### Faults, Aborts, and Traps +The answer is that the stored instruction pointer only points to the causing instruction for _fault_ type exceptions, but not for _trap_ or _abort_ type exceptions. The difference between these types is the following: + +- **Faults** are exceptions that can be corrected so that the program can continue as if nothing happened. An example is the [page fault], which can often be resolved by loading the accessed page from the disk into memory. +- **Aborts** are fatal exceptions that can't be recovered. Examples are [machine check exception] or the [double fault]. +- **Traps** are only reported to the kernel, but don't hinder the continuation of the program. Examples are the breakpoint exception and the [overflow exception]. + +[page fault]: http://wiki.osdev.org/Exceptions#Page_Fault +[machine check exception]: http://wiki.osdev.org/Exceptions#Machine_Check +[double fault]: http://wiki.osdev.org/Exceptions#Double_Fault +[overflow exception]: http://wiki.osdev.org/Exceptions#Overflow + +The reason for the diffent instruction pointer values is that the stored value is also the return address. So for faults, the instruction that caused the exception is restarted and might cause the same exception again if it's not resolved. This would not make much sense for traps, since invoking the breakpoint exception again would just cause another breakpoint exception[^fn-breakpoint-restart-use-cases]. Thus the instruction pointer points to the _next_ instruction for these exceptions. + +[^fn-breakpoint-restart-use-cases]: There are valid use cases for restarting an instruction that caused a breakpoint. The most common use case is a debugger: When setting a breakpoint on some code line, the debugger overwrites the corresponding instruction with an `int3` instruction, so that the CPU traps when that line is executed. When the user continues execution, the debugger swaps in the original instruction and continues the program from the replaced instruction. + +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 + +> `#BP` is a trap-type exception. The saved instruction pointer points to the byte after the `INT3` instruction. + +The documentation of the [`Idt`] struct and the [OSDev Wiki][osdev wiki exceptions] also contain this information. + +[`Idt`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.Idt.html +[osdev wiki exceptions]: http://wiki.osdev.org/Exceptions + +## Too much Magic? +The `x86-interrupt` calling convention and the [`Idt`] 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. + +[“Handling Exceptions with Naked Functions”]: {{% relref "handling-exceptions-with-naked-fns.md" %}} + +## What's next? +We've successfully caught our first exception and returned from it! The next step is to add handlers for other common exceptions such as page faults. We also need to make sure that we never cause a [triple fault], since it causes a complete system reset. The next post explains how we can avoid this by correctly catching [double faults]. + +[triple fault]: http://wiki.osdev.org/Triple_Fault +[double faults]: http://wiki.osdev.org/Double_Fault#Double_Fault diff --git a/blog/static/images/qemu-breakpoint-exception.png b/blog/static/images/qemu-breakpoint-exception.png new file mode 100644 index 0000000000000000000000000000000000000000..cbf23d273c338421d6b5bcf68c7296765a21dde4 GIT binary patch literal 17268 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 C--I6k literal 0 HcmV?d00001 From 09b5e7b136a8ecef6b245500df830c3fc8c7f2fd Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 25 Mar 2017 16:23:37 +0100 Subject: [PATCH 02/12] Fix stack size in posts (it was increased to 16kB) --- blog/content/post/05-allocating-frames.md | 4 ++-- blog/content/post/07-remap-the-kernel.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/blog/content/post/05-allocating-frames.md b/blog/content/post/05-allocating-frames.md index ed2958d3..67e2a6ce 100644 --- a/blog/content/post/05-allocating-frames.md +++ b/blog/content/post/05-allocating-frames.md @@ -12,13 +12,13 @@ The full source code is available on [Github][source repo]. Feel free to open is [source repo]: https://github.com/phil-opp/blog_os/tree/allocating_frames ## Preparation -We still have a really tiny stack of 64 bytes, which won't suffice for this post. So we increase it to 4096 bytes (one page) in `boot.asm`: +We still have a really tiny stack of 64 bytes, which won't suffice for this post. So we increase it to 16kB (four pages) in `boot.asm`: ```asm section .bss ... stack_bottom: - resb 4096 + resb 4096 * 4 stack_top: ``` diff --git a/blog/content/post/07-remap-the-kernel.md b/blog/content/post/07-remap-the-kernel.md index 34dfdff3..220c9915 100644 --- a/blog/content/post/07-remap-the-kernel.md +++ b/blog/content/post/07-remap-the-kernel.md @@ -1022,7 +1022,7 @@ p3_table: p2_table: resb 4096 stack_bottom: - resb 4096 * 2 + resb 4096 * 4 stack_top: ``` From 129ce4ca254b5cd4aa14ae06232a6bf297c41ea0 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 25 Mar 2017 16:27:46 +0100 Subject: [PATCH 03/12] Add an aside for resolving stack overflow errors --- blog/content/post/handling-exceptions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/blog/content/post/handling-exceptions.md b/blog/content/post/handling-exceptions.md index 1400e9f1..b14fd357 100644 --- a/blog/content/post/handling-exceptions.md +++ b/blog/content/post/handling-exceptions.md @@ -389,6 +389,8 @@ When we run it in QEMU now (using `make run`), we see the following: It works! The CPU successfully invokes our breakpoint handler, which prints the message, and then returns back to the `rust_main` function, where the `It did not crash!` message is printed. +> **Aside**: If it doesn't work and a boot loop occurs, this might be caused by a kernel stack overflow. Try increasing the stack size to at least 16kB (4096 * 4 bytes) in the `boot.asm` file. + We see that the exception stack frame tells us the instruction and stack pointers at the time when the exception occured. This information is very useful when debugging unexpected exceptions. For example, we can look at the corresponding assembly line using `objdump`: ``` From 3b1f79571462d772f9cc82273af20a56f9d1a573 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 25 Mar 2017 16:28:26 +0100 Subject: [PATCH 04/12] The Handling Exceptions post will be the 9th post --- .../post/{handling-exceptions.md => 09-handling-exceptions.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename blog/content/post/{handling-exceptions.md => 09-handling-exceptions.md} (100%) diff --git a/blog/content/post/handling-exceptions.md b/blog/content/post/09-handling-exceptions.md similarity index 100% rename from blog/content/post/handling-exceptions.md rename to blog/content/post/09-handling-exceptions.md From d3a06840149b6b5d922682409e50503b71035605 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 25 Mar 2017 16:37:16 +0100 Subject: [PATCH 05/12] =?UTF-8?q?Hack:=20Put=20=E2=80=9CHandling=20Excepti?= =?UTF-8?q?ons=E2=80=9D=20before=20=E2=80=9CDouble=20Faults=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blog/layouts/index.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/blog/layouts/index.html b/blog/layouts/index.html index 4b9674e8..9d86a39b 100644 --- a/blog/layouts/index.html +++ b/blog/layouts/index.html @@ -28,7 +28,10 @@
- {{ range first 4 (after 8 (where .Site.Pages.ByDate "Section" "post")) }} + {{ range first 1 (after 9 (where .Site.Pages.ByDate "Section" "post")) }} + {{ .Render "teaser" }} + {{ end }} + {{ range first 1 (after 8 (where .Site.Pages.ByDate "Section" "post")) }} {{ .Render "teaser" }} {{ end }}
From 839cf7fba41af01a518503771560770cce03bb54 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 25 Mar 2017 16:38:35 +0100 Subject: [PATCH 06/12] The double faults post is the 10th post now --- .../content/post/{12-double-faults.md => 10-double-faults.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename blog/content/post/{12-double-faults.md => 10-double-faults.md} (99%) diff --git a/blog/content/post/12-double-faults.md b/blog/content/post/10-double-faults.md similarity index 99% rename from blog/content/post/12-double-faults.md rename to blog/content/post/10-double-faults.md index cd7bf600..1102a4f7 100644 --- a/blog/content/post/12-double-faults.md +++ b/blog/content/post/10-double-faults.md @@ -17,7 +17,7 @@ As always, the complete source code is available on [Github]. Please file [issue ## What is a Double Fault? In simplified terms, a double fault is a special exception that occurs when the CPU fails to invoke an exception handler. For example, it occurs when a page fault is triggered but there is no page fault handler registered in the [Interrupt Descriptor Table][IDT] (IDT). So it's kind of similar to catch-all blocks in programming languages with exceptions, e.g. `catch(...)` in C++ or `catch(Exception e)` in Java or C#. -[IDT]: {{% relref "09-catching-exceptions.md#the-interrupt-descriptor-table" %}} +[IDT]: {{% relref "09-handling-exceptions.md#the-interrupt-descriptor-table" %}} A double fault behaves like a normal exception. It has the vector number `8` and we can define a normal handler function for it in the IDT. It is really important to provide a double fault handler, because if a double fault is unhandled a fatal _triple fault_ occurs. Triple faults can't be caught and most hardware reacts with a system reset. @@ -208,7 +208,7 @@ struct InterruptStackTable { For each exception handler, we can choose an stack from the IST through the `options` field in the corresponding [IDT entry]. For example, we could use the first stack in the IST for our double fault handler. Then the CPU would automatically switch to this stack whenever a double fault occurs. This switch would happen before anything is pushed, so it would prevent the triple fault. -[IDT entry]: {{% relref "09-catching-exceptions.md#the-interrupt-descriptor-table" %}} +[IDT entry]: {{% relref "09-handling-exceptions.md#the-interrupt-descriptor-table" %}} ### Allocating a new Stack In order to fill an Interrupt Stack Table later, we need a way to allocate new stacks. Therefore we extend our `memory` module with a new `stack_allocator` submodule: From 3e5eceb1b33e603eebd49e55cb03ae3e25897724 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 25 Mar 2017 16:39:33 +0100 Subject: [PATCH 07/12] Move old exception posts to old_posts folder --- .../better-exception-messages.md} | 0 .../catching-exceptions.md} | 0 .../returning-from-exceptions.md} | 2 +- blog/content/post/02-entering-longmode.md | 4 ++-- blog/content/post/08-kernel-heap.md | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename blog/content/{post/10-better-exception-messages.md => old-posts/better-exception-messages.md} (100%) rename blog/content/{post/09-catching-exceptions.md => old-posts/catching-exceptions.md} (100%) rename blog/content/{post/11-returning-from-exceptions.md => old-posts/returning-from-exceptions.md} (99%) diff --git a/blog/content/post/10-better-exception-messages.md b/blog/content/old-posts/better-exception-messages.md similarity index 100% rename from blog/content/post/10-better-exception-messages.md rename to blog/content/old-posts/better-exception-messages.md diff --git a/blog/content/post/09-catching-exceptions.md b/blog/content/old-posts/catching-exceptions.md similarity index 100% rename from blog/content/post/09-catching-exceptions.md rename to blog/content/old-posts/catching-exceptions.md diff --git a/blog/content/post/11-returning-from-exceptions.md b/blog/content/old-posts/returning-from-exceptions.md similarity index 99% rename from blog/content/post/11-returning-from-exceptions.md rename to blog/content/old-posts/returning-from-exceptions.md index baa15403..e5b98a3f 100644 --- a/blog/content/post/11-returning-from-exceptions.md +++ b/blog/content/old-posts/returning-from-exceptions.md @@ -295,7 +295,7 @@ Unfortunately, Rust does not support such a calling convention. It was [proposed [interrupt calling conventions]: https://github.com/rust-lang/rfcs/pull/1275 [Naked functions]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md -[naked fn post]: {{% relref "10-better-exception-messages.md#naked-functions" %}} +[naked fn post]: {{% relref "better-exception-messages.md#naked-functions" %}} ### A naked wrapper function diff --git a/blog/content/post/02-entering-longmode.md b/blog/content/post/02-entering-longmode.md index 2ba2a91c..d891813b 100644 --- a/blog/content/post/02-entering-longmode.md +++ b/blog/content/post/02-entering-longmode.md @@ -499,8 +499,8 @@ _Congratulations_! You have successfully wrestled through this CPU configuration #### One Last Thing Above, we reloaded the code segment register `cs` with the new GDT offset. However, the data segment registers `ss`, `ds`, `es`, `fs`, and `gs` still contain the data segment offsets of the old GDT. This isn't necessarily bad, since they're ignored by almost all instructions in 64-bit mode. However, there are a few instructions that expect a valid data segment descriptor _or the null descriptor_ in those registers. An example is the the [iretq] instruction that we'll need in the [_Returning from Exceptions_] post. -[iretq]: {{% relref "11-returning-from-exceptions.md#the-iretq-instruction" %}} -[_Returning from Exceptions_]: {{% relref "11-returning-from-exceptions.md" %}} +[iretq]: {{% relref "returning-from-exceptions.md#the-iretq-instruction" %}} +[_Returning from Exceptions_]: {{% relref "returning-from-exceptions.md" %}} To avoid future problems, we reload all data segment registers with null: diff --git a/blog/content/post/08-kernel-heap.md b/blog/content/post/08-kernel-heap.md index 5103c530..3cd6d1e4 100644 --- a/blog/content/post/08-kernel-heap.md +++ b/blog/content/post/08-kernel-heap.md @@ -835,4 +835,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 "09-catching-exceptions.md" %}} +[next post]: {{% relref "09-handling-exceptions.md" %}} From 43a41233b2d315dfa8c999ecded3f631a1f59152 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 25 Mar 2017 17:00:16 +0100 Subject: [PATCH 08/12] Add notes on old posts --- blog/content/old-posts/better-exception-messages.md | 2 ++ blog/content/old-posts/catching-exceptions.md | 2 ++ blog/content/old-posts/returning-from-exceptions.md | 2 ++ 3 files changed, 6 insertions(+) diff --git a/blog/content/old-posts/better-exception-messages.md b/blog/content/old-posts/better-exception-messages.md index e4e74eb0..d2451765 100644 --- a/blog/content/old-posts/better-exception-messages.md +++ b/blog/content/old-posts/better-exception-messages.md @@ -15,6 +15,8 @@ As always, the complete source code is on [Github]. Please file [issues] for any [gitter chat]: https://gitter.im/phil-opp/blog_os [comment section]: #disqus_thread +> **Note**: This post describes how to handle exceptions using naked functions (see “Handling Exceptions with Naked Functions” for an overview). Our new way of handling exceptions can be found in the “Handling Exceptions” post. + ## Exceptions in Detail An exception signals that something is wrong with the currently-executed instruction. Whenever an exception occurs, the CPU interrupts its current work and starts an internal exception routine. diff --git a/blog/content/old-posts/catching-exceptions.md b/blog/content/old-posts/catching-exceptions.md index be1c26fc..475fa03a 100644 --- a/blog/content/old-posts/catching-exceptions.md +++ b/blog/content/old-posts/catching-exceptions.md @@ -13,6 +13,8 @@ As always, the complete source code is on [Github]. Please file [issues] for any [Github]: https://github.com/phil-opp/blog_os/tree/catching_exceptions [issues]: https://github.com/phil-opp/blog_os/issues +> **Note**: This post describes how to handle exceptions using naked functions (see “Handling Exceptions with Naked Functions” for an overview). Our new way of handling exceptions can be found in the “Handling Exceptions” post. + ## Exceptions An exception signals that something is wrong with the current instruction. For example, the CPU issues an exception if the current instruction tries to divide by 0. When an exception occurs, the CPU interrupts its current work and immediately calls a specific exception handler function, depending on the exception type. diff --git a/blog/content/old-posts/returning-from-exceptions.md b/blog/content/old-posts/returning-from-exceptions.md index e5b98a3f..bf0b568f 100644 --- a/blog/content/old-posts/returning-from-exceptions.md +++ b/blog/content/old-posts/returning-from-exceptions.md @@ -15,6 +15,8 @@ As always, the complete source code is on [Github]. Please file [issues] for any [gitter chat]: https://gitter.im/phil-opp/blog_os [comment section]: #disqus_thread +> **Note**: This post describes how to handle exceptions using naked functions (see “Handling Exceptions with Naked Functions” for an overview). Our new way of handling exceptions can be found in the “Handling Exceptions” post. + ## Introduction Most exceptions are fatal and can't be resolved. For example, we can't return from a divide-by-zero exception in a reasonable way. However, there are some exceptions that we can resolve: From 26417cc670381407fff216eae8fad40b4b2d8919 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 25 Mar 2017 17:00:36 +0100 Subject: [PATCH 09/12] Keep original URLs for old-posts --- blog/config.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/blog/config.toml b/blog/config.toml index 5c88a84b..75f944ef 100644 --- a/blog/config.toml +++ b/blog/config.toml @@ -21,6 +21,7 @@ disableKinds = ["section", "taxonomy", "taxonomyTerm"] [permalinks] post = "/:slug.html" + old-posts = "/:slug.html" additional-resource = "/:filename.html" page = "/:filename.html" From 260b416ae4dd4ad96338c0c8908a9d1a0a0864e6 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 25 Mar 2017 17:01:35 +0100 Subject: [PATCH 10/12] Create a index page for handling exceptions using naked fns --- .../handling-exceptions-with-naked-fns.html | 48 +++++++++++++++++++ blog/content/post/09-handling-exceptions.md | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 blog/content/additional-resource/handling-exceptions-with-naked-fns.html diff --git a/blog/content/additional-resource/handling-exceptions-with-naked-fns.html b/blog/content/additional-resource/handling-exceptions-with-naked-fns.html new file mode 100644 index 00000000..757a067f --- /dev/null +++ b/blog/content/additional-resource/handling-exceptions-with-naked-fns.html @@ -0,0 +1,48 @@ ++++ +title = "Handling Exceptions using Naked Functions" ++++ + +

These posts explain how to handle CPU exceptions using naked functions. + Historically, these posts were the main exception handling posts before the + x86-interrupt calling convention and the x86_64 crate existed. + Our new way of handling exceptions can be found in the + “Handling Exceptions” post. +

+ +
+ +
+

+ + Catching Exceptions + +

+ +

In this post, we start exploring exceptions. We set up an interrupt descriptor table and add handler functions. At the end of this post, our kernel will be able to catch divide-by-zero faults.

+ +
+ + +
+

+ + Better Exception Messages + +

+ +

In this post, we explore exceptions in more detail. Our goal is to print additional information when an exception occurs, for example the values of the instruction and stack pointer. In the course of this, we will explore inline assembly and naked functions. We will also add a handler function for page faults and read the associated error code.

+ +
+ + +
+

+ + Returning from Exceptions + +

+ +

In this post, we learn how to return from exceptions correctly. In the course of this, we will explore the iretq instruction, the C calling convention, multimedia registers, and the red zone.

+ +
+
diff --git a/blog/content/post/09-handling-exceptions.md b/blog/content/post/09-handling-exceptions.md index b14fd357..bb871069 100644 --- a/blog/content/post/09-handling-exceptions.md +++ b/blog/content/post/09-handling-exceptions.md @@ -440,7 +440,7 @@ The documentation of the [`Idt`] struct and the [OSDev Wiki][osdev wiki exceptio ## Too much Magic? The `x86-interrupt` calling convention and the [`Idt`] 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. -[“Handling Exceptions with Naked Functions”]: {{% relref "handling-exceptions-with-naked-fns.md" %}} +[“Handling Exceptions with Naked Functions”]: {{% relref "handling-exceptions-with-naked-fns.html" %}} ## What's next? We've successfully caught our first exception and returned from it! The next step is to add handlers for other common exceptions such as page faults. We also need to make sure that we never cause a [triple fault], since it causes a complete system reset. The next post explains how we can avoid this by correctly catching [double faults]. From 97ef5188956aed1f859fc73cba7a29c97785948d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 25 Mar 2017 18:09:45 +0100 Subject: [PATCH 11/12] =?UTF-8?q?Rewrite=20=E2=80=9CDouble=20Faults?= =?UTF-8?q?=E2=80=9D=20post=20based=20on=20new=20=E2=80=9CHandling=20Excep?= =?UTF-8?q?tions=E2=80=9D=20post?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blog/content/post/10-double-faults.md | 145 +++++++------------------- 1 file changed, 36 insertions(+), 109 deletions(-) diff --git a/blog/content/post/10-double-faults.md b/blog/content/post/10-double-faults.md index 1102a4f7..dd1a2b3f 100644 --- a/blog/content/post/10-double-faults.md +++ b/blog/content/post/10-double-faults.md @@ -33,23 +33,17 @@ pub extern "C" fn rust_main(multiboot_information_address: usize) { // initialize our IDT interrupts::init(); - // trigger a debug exception - unsafe { int!(1) }; + // trigger a page fault + unsafe { + *(0xdeadbeaf as *mut u64) = 42; + }; println!("It did not crash!"); loop {} } {{< / highlight >}} -We use the [int! macro] of the `x86_64` crate to trigger the exception with vector number `1`, which is the [debug exception]. The debug exception occurs for example when a breakpoint defined in the [debug registers] is hit. Like the [breakpoint exception], it is mainly used for [implementing debuggers]. - -[int! macro]: https://docs.rs/x86_64/0.1.0/x86_64/macro.int!.html -[debug exception]: http://wiki.osdev.org/Exceptions#Debug -[debug registers]: https://en.wikipedia.org/wiki/X86_debug_register -[breakpoint exception]: http://wiki.osdev.org/Exceptions#Breakpoint -[implementing debuggers]: http://www.ksyash.com/2011/01/210/ - -We haven't registered a handler function for the debug exception in our [IDT], so the `int!(1)` line should cause a double fault in the CPU. +We try to write to address `0xdeadbeaf`, but the corresponding page is not present in the page tables. Thus, a page fault occurs. We haven't registered a page fault handler in our [IDT], so a double fault occurs. When we start our kernel now, we see that it enters an endless boot loop: @@ -57,39 +51,33 @@ When we start our kernel now, we see that it enters an endless boot loop: The reason for the boot loop is the following: -1. The CPU executes the [int 1] instruction, which causes a software-invoked `Debug` exception. -2. The CPU looks at the corresponding entry in the IDT and sees that the present bit isn't set. Thus, it can't call the debug exception handler and a double fault occurs. +1. The CPU tries to write to `0xdeadbeaf`, which causes a page fault. +2. The CPU looks at the corresponding entry in the IDT and sees that the present bit isn't set. Thus, it can't call the page fault handler and a double fault occurs. 3. The CPU looks at the IDT entry of the double fault handler, but this entry is also non-present. Thus, a _triple_ fault occurs. 4. A triple fault is fatal. QEMU reacts to it like most real hardware and issues a system reset. -[int 1]: https://en.wikipedia.org/wiki/INT_(x86_instruction) - -So in order to prevent this triple fault, we need to either provide a handler function for debug exceptions or a double fault handler. We will do the latter, since we want to avoid triple faults in all cases. +So in order to prevent this triple fault, we need to either provide a handler function for page faults or a double fault handler. Let's start with the latter, since we want to avoid triple faults in all cases. ### A Double Fault Handler A double fault is a normal exception with an error code, so we can use our `handler_with_error_code` macro to create a wrapper function: -{{< highlight rust "hl_lines=11 18" >}} +{{< highlight rust "hl_lines=8 14" >}} // in src/interrupts/mod.rs lazy_static! { static ref IDT: idt::Idt = { let mut idt = idt::Idt::new(); - idt.set_handler(0, handler!(divide_by_zero_handler)); - idt.set_handler(3, handler!(breakpoint_handler)); - idt.set_handler(6, handler!(invalid_opcode_handler)); - // new double fault handler - idt.set_handler(8, handler_with_error_code!(double_fault_handler)); - idt.set_handler(14, handler_with_error_code!(page_fault_handler)); + idt.breakpoint.set_handler_fn(breakpoint_handler); + idt.double_fault.set_handler_fn(double_fault_handler) idt }; } // our new double fault handler -extern "C" fn double_fault_handler(stack_frame: &ExceptionStackFrame, - _error_code: u64) +extern "x86-interrupt" fn double_fault_handler( + stack_frame: &mut ExceptionStackFrame, _error_code: u64) { println!("\nEXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); loop {} @@ -104,8 +92,8 @@ When we start our kernel now, we should see that the double fault handler is inv It worked! Here is what happens this time: -1. The CPU executes the `int 1` instruction macro, which causes a software-invoked `Debug` exception. -2. Like before, the CPU looks at the corresponding entry in the IDT and sees that the present bit isn't set. Thus, it can't call the debug exception handler and a double fault occurs. +1. The CPU executes tries to write to `0xdeadbeaf`, which causes a page fault. +2. Like before, the CPU looks at the corresponding entry in the IDT and sees that the present bit isn't set. Thus, a double fault occurs. 3. The CPU jumps to the – now present – double fault handler. The triple fault (and the boot-loop) no longer occurs, since the CPU can now call the double fault handler. @@ -468,9 +456,9 @@ I/O Map Base Address | `u16` The _Privilege Stack Table_ is used by the CPU when the privilege level changes. For example, if an exception occurs while the CPU is in user mode (privilege level 3), the CPU normally switches to kernel mode (privilege level 0) before invoking the exception handler. In that case, the CPU would switch to the 0th stack in the Privilege Stack Table (since 0 is the target privilege level). We don't have any user mode programs yet, so we ignore this table for now. #### Creating a TSS -Let's create a new TSS that contains our double fault stack in its interrupt stack table. For that we need a TSS struct. Fortunately, the `x86` crate already contains a [`TaskStateSegment` struct] that we can use: +Let's create a new TSS that contains our 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/0.8.0/x86/bits64/task/struct.TaskStateSegment.html +[`TaskStateSegment` struct]: https://docs.rs/x86_64/0.1.1/x86_64/structures/tss/struct.TaskStateSegment.html ```rust // in src/interrupts/mod.rs @@ -483,6 +471,8 @@ Let's create a new TSS in our `interrupts::init` function: {{< highlight rust "hl_lines=3 9 10" >}} // in src/interrupts/mod.rs +use x86_64::VirtualAddress; + const DOUBLE_FAULT_IST_INDEX: usize = 0; pub fn init(memory_controller: &mut MemoryController) { @@ -490,7 +480,8 @@ pub fn init(memory_controller: &mut MemoryController) { .expect("could not allocate double fault stack"); let mut tss = TaskStateSegment::new(); - tss.ist[DOUBLE_FAULT_IST_INDEX] = double_fault_stack.top() as u64; + tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX] = VirtualAddress( + double_fault_stack.top()); IDT.load(); } @@ -709,12 +700,10 @@ To load the GDT, we add a new `load` method: impl Gdt { pub fn load(&'static self) { use x86_64::instructions::tables::{DescriptorTablePointer, lgdt}; - use x86_64::instructions::segmentation; use core::mem::size_of; let ptr = DescriptorTablePointer { - base: self.table.as_ptr() as - *const segmentation::SegmentDescriptor, + base: self.table.as_ptr() as u64, limit: (self.table.len() * size_of::() - 1) as u16, }; @@ -722,10 +711,10 @@ impl Gdt { } } ``` -We use the [`DescriptorTablePointer` struct] and the [`lgdt` function] provided by the `x86` crate to load our GDT. Again, we require a `'static` reference since the GDT possibly needs to live for the rest of the run time. +We use the [`DescriptorTablePointer` struct] and the [`lgdt` function] provided by the `x86_64` crate to load our GDT. Again, we require a `'static` reference since the GDT possibly needs to live for the rest of the run time. -[`DescriptorTablePointer` struct]: https://docs.rs/x86/0.8.0/x86/shared/dtables/struct.DescriptorTablePointer.html -[`lgdt` function]: https://docs.rs/x86/0.8.0/x86/shared/dtables/fn.lgdt.html +[`DescriptorTablePointer` struct]: https://docs.rs/x86_64/0.1.1/x86_64/instructions/tables/struct.DescriptorTablePointer.html +[`lgdt` function]: https://docs.rs/x86_64/0.1.1/x86_64/instructions/tables/fn.lgdt.html ### Putting it together We now have a double fault stack and are able to create and load a TSS (which contains an IST). So let's put everything together to catch kernel stack overflows. @@ -740,7 +729,8 @@ pub fn init(memory_controller: &mut MemoryController) { .expect("could not allocate double fault stack"); let mut tss = TaskStateSegment::new(); - tss.ist[DOUBLE_FAULT_IST_INDEX] = double_fault_stack.top() as u64; + tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX] = VirtualAddress( + double_fault_stack.top()); let mut gdt = gdt::Gdt::new(); let code_selector = gdt.add_entry(gdt::Descriptor::kernel_code_segment()); @@ -808,14 +798,15 @@ The `Once` type allows us to initialize a `static` at runtime. It is safe becaus So let's rewrite our `interrupts::init` function to use the static `TSS` and `GDT`: -{{< highlight rust "hl_lines=5 8 9 11 16 17" >}} +{{< highlight rust "hl_lines=5 9 10 12 17 18" >}} pub fn init(memory_controller: &mut MemoryController) { let double_fault_stack = memory_controller.alloc_stack(1) .expect("could not allocate double fault stack"); let tss = TSS.call_once(|| { let mut tss = TaskStateSegment::new(); - tss.ist[DOUBLE_FAULT_IST_INDEX] = double_fault_stack.top() as u64; + tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX] = VirtualAddress( + double_fault_stack.top()); tss }); @@ -879,32 +870,23 @@ We first set the descriptors to `empty` and then update them from inside the clo 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: -{{< highlight rust "hl_lines=8" >}} +{{< highlight rust "hl_lines=7 9" >}} // in src/interrupt/mod.rs lazy_static! { static ref IDT: idt::Idt = { let mut idt = idt::Idt::new(); ... - idt.set_handler(8, handler_with_error_code!(double_fault_handler)) - .set_stack_index(DOUBLE_FAULT_IST_INDEX as u16); + unsafe { + idt.double_fault.set_handler_fn(double_fault_handler) + .set_stack_index(DOUBLE_FAULT_IST_INDEX as u16); + } ... }; } - {{< / highlight >}} -We also rewrite the `set_stack_index` method in `src/interrupts/idt.rs`: - -```rust -pub fn set_stack_index(&mut self, index: u16) -> &mut Self { - // The hardware IST index starts at 1, but our software IST index - // starts at 0. Therefore we need to add 1 here. - self.0.set_bits(0..3, index + 1); - self -} -``` -The only change is that we're now adding `1` to the passed `index`, because the hardware expects an index between _one_ and seven. A zero means “no stack switch”. +The `set_stack_index` method is unsafe because the the caller must ensure that the used index is valid and not already used for another exception. That's it! Now the CPU should switch to the double fault stack whenever a double fault occurs. Thus, we are able to catch _all_ double faults, including kernel stack overflows: @@ -912,61 +894,6 @@ That's it! Now the CPU should switch to the double fault stack whenever a double From now on we should never see a triple fault again! -## Safety Problems -In this post, we needed a few `unsafe` blocks to load the GDT and TSS structures. We always used `'static` references, so the passed addresses should be always valid. - -However, the IST entries (stored in the TSS) are used as stack pointers by the CPU. This can lead to various memory safety violations: - -- The CPU writes to any address that we store in the IST. This way, we can easily circumvent Rust's safety guarantees and e.g. overwrite a `&mut` reference on some random stack space. -- If we use the same stack index for multiple exceptions, memory safety might be violated too. For example, imagine that the double fault hander and the breakpoint handler use the same IST index. If the double fault handler causes a breakpoint exception, the breakpoint overwrites the stack frame of the double fault handler. When the breakpoint returns, the CPU jumps back to the double fault handler and undefined behavior occurs. -- If we accidentally use an empty IST entry, the CPU uses the stack pointer `0`. This is really bad, since we overwrite our [recursively mapped] page tables this way. - -[recursively mapped]: {{% relref "06-page-tables.md#recursive-mapping" %}} - -Let's try the last case (empty IST entry) as an example: - -```rust -// in src/interrups/mod.rs - -lazy_static! { - static ref IDT: idt::Idt = { - ... - idt.set_handler(8, handler_with_error_code!(double_fault_handler)) - .set_stack_index(5); - ... - } -} -``` -Instead of using the `DOUBLE_FAULT_IST_INDEX`, we use the IST index 5. However the entry at index 5 is still empty. - -In oder to see the effect, we print the exception stack frame pointer in our `double_fault_handler`: - -```rust -// in src/interrupts/mod.rs - -extern "C" fn double_fault_handler(stack_frame: &ExceptionStackFrame, - _error_code: u64) -{ - println!("\nEXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); - println!("exception stack frame at {:#p}", stack_frame); // new - loop {} -} -``` -When we start our kernel now, we see that the exception stack frame was written to `0xffffffffffffffd8`: - -![QEMU printing `exception stack frame at 0xffffffffffffffd8](images/qemu-empty-IST-entry.png) - -This address is part of the [recursive mapping][recursively mapped], so the CPU just overwrote some random page table entries. - -### Possible Solutions? -Normally, the type system allows us to make things safer. Unfortunately, there are many difficulties in this case. For example, we need to be able to create multiple task state segments since we have multiple CPUs (each CPU has its own TSS). Each IST index is only valid if the corresponding TSS is loaded in the CPU. This makes compile-time abstractions very difficult. - -So this might be a case where we have to tolerate the safety dangers [^fn-ideas]. At least, they are limited to the `interrupts` module, where we already have lots of dangerous inline assembly. From outside, only the safe `interrupts::init` function is visible, so it is similar to an [unsafe abstraction]. We only need to be aware of the safety dangers when we edit the `interrupts` module in the future. - -[unsafe abstraction]: http://smallcultfollowing.com/babysteps/blog/2016/05/23/unsafe-abstractions/ - -[^fn-ideas]: If somebody has a good solution for this problem, please tell me :). - ## What's next? Now that we mastered exceptions, it's time to explore another kind of interrupts: interrupts from external devices such as timers, keyboards, or network controllers. These hardware interrupts are very similar to exceptions, e.g. they are also dispatched through the IDT. From 67739e40200e296fd25725aecd0b7001ba132b9c Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 25 Mar 2017 18:14:38 +0100 Subject: [PATCH 12/12] Add post templates for old-posts --- blog/layouts/old-posts/single.html | 28 ++++++++++++++++++++++++++++ blog/layouts/old-posts/teaser.html | 10 ++++++++++ 2 files changed, 38 insertions(+) create mode 100644 blog/layouts/old-posts/single.html create mode 100644 blog/layouts/old-posts/teaser.html diff --git a/blog/layouts/old-posts/single.html b/blog/layouts/old-posts/single.html new file mode 100644 index 00000000..d8e2c7a8 --- /dev/null +++ b/blog/layouts/old-posts/single.html @@ -0,0 +1,28 @@ +{{ partial "header.html" . }} + +
+

{{ .Title }}

+ + + {{ .Content }} +
+ +
+ +
+ +{{ partial "disqus.html" . }} +{{ partial "anchorjs.html" . }} +{{ partial "footer.html" . }} diff --git a/blog/layouts/old-posts/teaser.html b/blog/layouts/old-posts/teaser.html new file mode 100644 index 00000000..795e27ad --- /dev/null +++ b/blog/layouts/old-posts/teaser.html @@ -0,0 +1,10 @@ +