mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 06:17:49 +00:00
Convert all external links to https (if supported)
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
http://www.apache.org/licenses/
|
https://www.apache.org/licenses/
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
|||||||
@@ -110,8 +110,8 @@ The current version of the blog is already the second edition. The first edition
|
|||||||
This project, with exception of the `blog/content` folder, is licensed under either of
|
This project, with exception of the `blog/content` folder, is licensed under either of
|
||||||
|
|
||||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
http://www.apache.org/licenses/LICENSE-2.0)
|
https://www.apache.org/licenses/LICENSE-2.0)
|
||||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ This folder contains the content for the _"Writing an OS in Rust"_ blog.
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This folder is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License, available in [LICENSE-CC-BY-NC](LICENSE-CC-BY-NC) or under <http://creativecommons.org/licenses/by-nc/4.0/>.
|
This folder is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License, available in [LICENSE-CC-BY-NC](LICENSE-CC-BY-NC) or under <https://creativecommons.org/licenses/by-nc/4.0/>.
|
||||||
|
|
||||||
All _code examples_ between markdown code blocks denoted by three backticks (<code>\`\`\`</code>) are additionally licensed under either of
|
All _code examples_ between markdown code blocks denoted by three backticks (<code>\`\`\`</code>) are additionally licensed under either of
|
||||||
|
|
||||||
- Apache License, Version 2.0 ([LICENSE-APACHE](../../LICENSE-APACHE) or
|
- Apache License, Version 2.0 ([LICENSE-APACHE](../../LICENSE-APACHE) or
|
||||||
http://www.apache.org/licenses/LICENSE-2.0)
|
https://www.apache.org/licenses/LICENSE-2.0)
|
||||||
- MIT license ([LICENSE-MIT](../../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
- MIT license ([LICENSE-MIT](../../LICENSE-MIT) or https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ We've already seen several types of exceptions in our kernel:
|
|||||||
|
|
||||||
For the full list of exceptions check out the [OSDev wiki][exceptions].
|
For the full list of exceptions check out the [OSDev wiki][exceptions].
|
||||||
|
|
||||||
[exceptions]: http://wiki.osdev.org/Exceptions
|
[exceptions]: https://wiki.osdev.org/Exceptions
|
||||||
|
|
||||||
### The Interrupt Descriptor Table
|
### 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:
|
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:
|
||||||
@@ -311,8 +311,8 @@ u64 | Offset | Virtual start address of the table.
|
|||||||
|
|
||||||
This structure is already contained [in the x86_64 crate], so we don't need to create it ourselves. The same is true for the [lidt function]. So we just need to put the pieces together to create a `load` method:
|
This structure is already contained [in the x86_64 crate], so we don't need to create it ourselves. The same is true for the [lidt function]. So we just need to put the pieces together to create a `load` method:
|
||||||
|
|
||||||
[in the x86_64 crate]: http://docs.rs/x86_64/0.1.0/x86_64/instructions/tables/struct.DescriptorTablePointer.html
|
[in the x86_64 crate]: https://docs.rs/x86_64/0.1.0/x86_64/instructions/tables/struct.DescriptorTablePointer.html
|
||||||
[lidt function]: http://docs.rs/x86_64/0.1.0/x86_64/instructions/tables/fn.lidt.html
|
[lidt function]: https://docs.rs/x86_64/0.1.0/x86_64/instructions/tables/fn.lidt.html
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
impl Idt {
|
impl Idt {
|
||||||
@@ -422,7 +422,7 @@ extern "C" fn divide_by_zero_handler() -> ! {
|
|||||||
```
|
```
|
||||||
We register a single handler function for a [divide by zero error] \(index 0). Like the name says, this exception occurs when dividing a number by 0. Thus we have an easy way to test our new exception handler.
|
We register a single handler function for a [divide by zero error] \(index 0). Like the name says, this exception occurs when dividing a number by 0. Thus we have an easy way to test our new exception handler.
|
||||||
|
|
||||||
[divide by zero error]: http://wiki.osdev.org/Exceptions#Divide-by-zero_Error
|
[divide by zero error]: https://wiki.osdev.org/Exceptions#Divide-by-zero_Error
|
||||||
|
|
||||||
However, it doesn't work this way:
|
However, it doesn't work this way:
|
||||||
|
|
||||||
|
|||||||
@@ -394,7 +394,7 @@ The base pointer is initialized directly from the stack pointer (`rsp`) after pu
|
|||||||
The reason is that our exception handler is defined as `extern "C" function`, which specifies that it's using the C [calling convention]. On x86_64 Linux, the C calling convention is specified by the System V AMD64 ABI ([PDF][system v abi]). Section 3.2.2 defines the following:
|
The reason is that our exception handler is defined as `extern "C" function`, which specifies that it's using the C [calling convention]. On x86_64 Linux, the C calling convention is specified by the System V AMD64 ABI ([PDF][system v abi]). Section 3.2.2 defines the following:
|
||||||
|
|
||||||
[calling convention]: https://en.wikipedia.org/wiki/X86_calling_conventions
|
[calling convention]: https://en.wikipedia.org/wiki/X86_calling_conventions
|
||||||
[system v abi]: http://web.archive.org/web/20160801075139/http://www.x86-64.org/documentation/abi.pdf
|
[system v abi]: https://web.archive.org/web/20160801075139/https://www.x86-64.org/documentation/abi.pdf
|
||||||
|
|
||||||
> The end of the input argument area shall be aligned on a 16 byte boundary. In other words, the value (%rsp + 8) is always a multiple of 16 when control is transferred to the function entry point.
|
> The end of the input argument area shall be aligned on a 16 byte boundary. In other words, the value (%rsp + 8) is always a multiple of 16 when control is transferred to the function entry point.
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ The breakpoint exception is the perfect exception to test our upcoming return-fr
|
|||||||
|
|
||||||
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.
|
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
|
[How debuggers work]: https://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.
|
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.
|
||||||
|
|
||||||
@@ -250,7 +250,7 @@ However, there is a major difference between exceptions and function calls: A fu
|
|||||||
[Calling conventions] specify the details of a function call. For example, they specify where function parameters are placed (e.g. in registers or on the stack) and how results are returned. On x86_64 Linux, the following rules apply for C functions (specified in the [System V ABI]):
|
[Calling conventions] 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
|
[Calling conventions]: https://en.wikipedia.org/wiki/Calling_convention
|
||||||
[System V ABI]: http://refspecs.linuxbase.org/elf/gabi41.pdf
|
[System V ABI]: https://refspecs.linuxbase.org/elf/gabi41.pdf
|
||||||
|
|
||||||
- the first six integer arguments are passed in registers `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`
|
- the first six integer arguments are passed in registers `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`
|
||||||
- additional arguments are passed on the stack
|
- additional arguments are passed on the stack
|
||||||
@@ -518,7 +518,7 @@ The `llvm-target` field specifies the target triple that is passed to LLVM. We w
|
|||||||
|
|
||||||
The other fields are used for conditional compilation. This allows crate authors to use `cfg` variables to write special code for depending on the OS or the architecture. There isn't any up-to-date documentation about these fields but the [corresponding source code][target specification] is quite readable.
|
The other fields are used for conditional compilation. This allows crate authors to use `cfg` variables to write special code for depending on the OS or the architecture. There isn't any up-to-date documentation about these fields but the [corresponding source code][target specification] is quite readable.
|
||||||
|
|
||||||
[data layout]: http://llvm.org/docs/LangRef.html#data-layout
|
[data layout]: https://llvm.org/docs/LangRef.html#data-layout
|
||||||
[target specification]: https://github.com/rust-lang/rust/blob/c772948b687488a087356cb91432425662e034b9/src/librustc_back/target/mod.rs#L194-L214
|
[target specification]: https://github.com/rust-lang/rust/blob/c772948b687488a087356cb91432425662e034b9/src/librustc_back/target/mod.rs#L194-L214
|
||||||
|
|
||||||
#### Disabling MMX and SSE
|
#### Disabling MMX and SSE
|
||||||
@@ -695,7 +695,7 @@ So now our return-from-exception logic works without problems in _most_ cases. H
|
|||||||
## The Red Zone
|
## The Red Zone
|
||||||
The [red zone] is an optimization of the [System V ABI] that allows functions to temporary use the 128 bytes below its stack frame without adjusting the stack pointer:
|
The [red zone] is an optimization of the [System V ABI] that allows functions to temporary use the 128 bytes below its stack frame without adjusting the stack pointer:
|
||||||
|
|
||||||
[red zone]: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
|
[red zone]: https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -709,7 +709,7 @@ However, this optimization leads to huge problems with exceptions. Let's assume
|
|||||||
|
|
||||||
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. It might fail or cause another exception, but it could also lead to strange bugs that [take weeks to debug].
|
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. It might fail or cause another exception, but it could also lead to strange bugs that [take weeks to debug].
|
||||||
|
|
||||||
[take weeks to debug]: http://forum.osdev.org/viewtopic.php?t=21720
|
[take weeks to debug]: https://forum.osdev.org/viewtopic.php?t=21720
|
||||||
|
|
||||||
### Adjusting our Exception Handler?
|
### Adjusting our Exception Handler?
|
||||||
The problem is that the [System V ABI] demands that the red zone _“shall not be modified by signal or interrupt handlers.”_ Our current exception handlers do not respect this. We could try to fix it by subtracting 128 from the stack pointer before pushing anything:
|
The problem is that the [System V ABI] demands that the red zone _“shall not be modified by signal or interrupt handlers.”_ Our current exception handlers do not respect this. We could try to fix it by subtracting 128 from the stack pointer before pushing anything:
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ Remote 'g' packet reply is too long: [a very long number]
|
|||||||
```
|
```
|
||||||
This issue is known [since 2012][gdb issue patch] but it is still not fixed. Maybe we find the reason in the [issue thread][gdb issue thread]:
|
This issue is known [since 2012][gdb issue patch] but it is still not fixed. Maybe we find the reason in the [issue thread][gdb issue thread]:
|
||||||
|
|
||||||
[gdb issue patch]: https://web.archive.org/web/20190114181420/http://www.cygwin.com/ml/gdb-patches/2012-03/msg00116.html
|
[gdb issue patch]: https://web.archive.org/web/20190114181420/https://www.cygwin.com/ml/gdb-patches/2012-03/msg00116.html
|
||||||
[gdb issue thread]: https://sourceware.org/bugzilla/show_bug.cgi?id=13984#c11
|
[gdb issue thread]: https://sourceware.org/bugzilla/show_bug.cgi?id=13984#c11
|
||||||
|
|
||||||
> from my (limited) experience, unless you ping the gdb-patches list weekly, this patch is more likely to remain forgotten :-)
|
> from my (limited) experience, unless you ping the gdb-patches list weekly, this patch is more likely to remain forgotten :-)
|
||||||
@@ -73,5 +73,5 @@ After connecting to QEMU, you can use various gdb commands to control execution
|
|||||||
|
|
||||||
Of course there are many more commands. Feel free to send a PR if you think this list is missing something important. For a more complete GDB overview, check out [Beej's Quick Guide][bggdb] or the [website for Harvard's CS161 course][CS161].
|
Of course there are many more commands. Feel free to send a PR if you think this list is missing something important. For a more complete GDB overview, check out [Beej's Quick Guide][bggdb] or the [website for Harvard's CS161 course][CS161].
|
||||||
|
|
||||||
[bggdb]: http://beej.us/guide/bggdb/
|
[bggdb]: https://beej.us/guide/bggdb/
|
||||||
[CS161]: http://www.eecs.harvard.edu/~cs161/resources/gdb.html
|
[CS161]: https://www.eecs.harvard.edu/~cs161/resources/gdb.html
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ template = "first-edition/page.html"
|
|||||||
|
|
||||||
This post explains how to create a minimal x86 operating system kernel using the Multiboot standard. In fact, it will just boot and print `OK` to the screen. In subsequent blog posts we will extend it using the [Rust] programming language.
|
This post explains how to create a minimal x86 operating system kernel using the Multiboot standard. In fact, it will just boot and print `OK` to the screen. In subsequent blog posts we will extend it using the [Rust] programming language.
|
||||||
|
|
||||||
[Rust]: http://www.rust-lang.org/
|
[Rust]: https://www.rust-lang.org/
|
||||||
|
|
||||||
<!-- more -->
|
<!-- more -->
|
||||||
|
|
||||||
@@ -28,19 +28,19 @@ When you turn on a computer, it loads the [BIOS] from some special flash memory.
|
|||||||
|
|
||||||
[BIOS]: https://en.wikipedia.org/wiki/BIOS
|
[BIOS]: https://en.wikipedia.org/wiki/BIOS
|
||||||
[protected mode]: https://en.wikipedia.org/wiki/Protected_mode
|
[protected mode]: https://en.wikipedia.org/wiki/Protected_mode
|
||||||
[real mode]: http://wiki.osdev.org/Real_Mode
|
[real mode]: https://wiki.osdev.org/Real_Mode
|
||||||
|
|
||||||
We won't write a bootloader because that would be a complex project on its own (if you really want to do it, check out [_Rolling Your Own Bootloader_]). Instead we will use one of the [many well-tested bootloaders][bootloader comparison] out there to boot our kernel from a CD-ROM. But which one?
|
We won't write a bootloader because that would be a complex project on its own (if you really want to do it, check out [_Rolling Your Own Bootloader_]). Instead we will use one of the [many well-tested bootloaders][bootloader comparison] out there to boot our kernel from a CD-ROM. But which one?
|
||||||
|
|
||||||
[_Rolling Your Own Bootloader_]: http://wiki.osdev.org/Rolling_Your_Own_Bootloader
|
[_Rolling Your Own Bootloader_]: https://wiki.osdev.org/Rolling_Your_Own_Bootloader
|
||||||
[bootloader comparison]: https://en.wikipedia.org/wiki/Comparison_of_boot_loaders
|
[bootloader comparison]: https://en.wikipedia.org/wiki/Comparison_of_boot_loaders
|
||||||
|
|
||||||
## Multiboot
|
## Multiboot
|
||||||
Fortunately there is a bootloader standard: the [Multiboot Specification][multiboot]. Our kernel just needs to indicate that it supports Multiboot and every Multiboot-compliant bootloader can boot it. We will use the Multiboot 2 specification ([PDF][Multiboot 2]) together with the well-known [GRUB 2] bootloader.
|
Fortunately there is a bootloader standard: the [Multiboot Specification][multiboot]. Our kernel just needs to indicate that it supports Multiboot and every Multiboot-compliant bootloader can boot it. We will use the Multiboot 2 specification ([PDF][Multiboot 2]) together with the well-known [GRUB 2] bootloader.
|
||||||
|
|
||||||
[multiboot]: https://en.wikipedia.org/wiki/Multiboot_Specification
|
[multiboot]: https://en.wikipedia.org/wiki/Multiboot_Specification
|
||||||
[multiboot 2]: http://nongnu.askapache.com/grub/phcoder/multiboot.pdf
|
[multiboot 2]: https://nongnu.askapache.com/grub/phcoder/multiboot.pdf
|
||||||
[grub 2]: http://wiki.osdev.org/GRUB_2
|
[grub 2]: https://wiki.osdev.org/GRUB_2
|
||||||
|
|
||||||
To indicate our Multiboot 2 support to the bootloader, our kernel must start with a _Multiboot Header_, which has the following format:
|
To indicate our Multiboot 2 support to the bootloader, our kernel must start with a _Multiboot Header_, which has the following format:
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ Through assembling, viewing and disassembling we can see the CPU [Opcodes] in ac
|
|||||||
To boot our executable later through GRUB, it should be an [ELF] executable. So we want `nasm` to create ELF [object files] instead of plain binaries. To do that, we simply pass the `‑f elf64` argument to it.
|
To boot our executable later through GRUB, it should be an [ELF] executable. So we want `nasm` to create ELF [object files] instead of plain binaries. To do that, we simply pass the `‑f elf64` argument to it.
|
||||||
|
|
||||||
[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
|
[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
|
||||||
[object files]: http://wiki.osdev.org/Object_Files
|
[object files]: https://wiki.osdev.org/Object_Files
|
||||||
|
|
||||||
To create the ELF _executable_, we need to [link] the object files together. We use a custom [linker script] named `linker.ld`:
|
To create the ELF _executable_, we need to [link] the object files together. We use a custom [linker script] named `linker.ld`:
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ updated = "2015-10-29"
|
|||||||
In the [previous post] we created a minimal multiboot kernel. It just prints `OK` and hangs. The goal is to extend it and call 64-bit [Rust] code. But the CPU is currently in [protected mode] and allows only 32-bit instructions and up to 4GiB memory. So we need to set up _Paging_ and switch to the 64-bit [long mode] first.
|
In the [previous post] we created a minimal multiboot kernel. It just prints `OK` and hangs. The goal is to extend it and call 64-bit [Rust] code. But the CPU is currently in [protected mode] and allows only 32-bit instructions and up to 4GiB memory. So we need to set up _Paging_ and switch to the 64-bit [long mode] first.
|
||||||
|
|
||||||
[previous post]: @/first-edition/posts/01-multiboot-kernel/index.md
|
[previous post]: @/first-edition/posts/01-multiboot-kernel/index.md
|
||||||
[Rust]: http://www.rust-lang.org/
|
[Rust]: https://www.rust-lang.org/
|
||||||
[protected mode]: https://en.wikipedia.org/wiki/Protected_mode
|
[protected mode]: https://en.wikipedia.org/wiki/Protected_mode
|
||||||
[long mode]: https://en.wikipedia.org/wiki/Long_mode
|
[long mode]: https://en.wikipedia.org/wiki/Long_mode
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ A screen character consists of a 8 bit color code and a 8 bit [ASCII] character.
|
|||||||
|
|
||||||
Now we can add some check _functions_. A function is just a normal label with an `ret` (return) instruction at the end. The `call` instruction can be used to call it. Unlike the `jmp` instruction that just jumps to a memory address, the `call` instruction will push a return address to the stack (and the `ret` will jump to this address). But we don't have a stack yet. The [stack pointer] in the esp register could point to some important data or even invalid memory. So we need to update it and point it to some valid stack memory.
|
Now we can add some check _functions_. A function is just a normal label with an `ret` (return) instruction at the end. The `call` instruction can be used to call it. Unlike the `jmp` instruction that just jumps to a memory address, the `call` instruction will push a return address to the stack (and the `ret` will jump to this address). But we don't have a stack yet. The [stack pointer] in the esp register could point to some important data or even invalid memory. So we need to update it and point it to some valid stack memory.
|
||||||
|
|
||||||
[stack pointer]: http://stackoverflow.com/a/1464052/866447
|
[stack pointer]: https://stackoverflow.com/a/1464052/866447
|
||||||
|
|
||||||
### Creating a Stack
|
### Creating a Stack
|
||||||
To create stack memory we reserve some bytes at the end of our `boot.asm`:
|
To create stack memory we reserve some bytes at the end of our `boot.asm`:
|
||||||
@@ -96,14 +96,14 @@ We use the `cmp` instruction to compare the value in `eax` to the magic value. I
|
|||||||
|
|
||||||
In `no_multiboot`, we use the `jmp` (“jump”) instruction to jump to our error function. We could just as well use the `call` instruction, which additionally pushes the return address. But the return address is not needed because `error` never returns. To pass `0` as error code to the `error` function, we move it into `al` before the jump (`error` will read it from there).
|
In `no_multiboot`, we use the `jmp` (“jump”) instruction to jump to our error function. We could just as well use the `call` instruction, which additionally pushes the return address. But the return address is not needed because `error` never returns. To pass `0` as error code to the `error` function, we move it into `al` before the jump (`error` will read it from there).
|
||||||
|
|
||||||
[Multiboot specification]: http://nongnu.askapache.com/grub/phcoder/multiboot.pdf
|
[Multiboot specification]: https://nongnu.askapache.com/grub/phcoder/multiboot.pdf
|
||||||
[FLAGS register]: https://en.wikipedia.org/wiki/FLAGS_register
|
[FLAGS register]: https://en.wikipedia.org/wiki/FLAGS_register
|
||||||
|
|
||||||
### CPUID check
|
### CPUID check
|
||||||
[CPUID] is a CPU instruction that can be used to get various information about the CPU. But not every processor supports it. CPUID detection is quite laborious, so we just copy a detection function from the [OSDev wiki][CPUID detection]:
|
[CPUID] is a CPU instruction that can be used to get various information about the CPU. But not every processor supports it. CPUID detection is quite laborious, so we just copy a detection function from the [OSDev wiki][CPUID detection]:
|
||||||
|
|
||||||
[CPUID]: http://wiki.osdev.org/CPUID
|
[CPUID]: https://wiki.osdev.org/CPUID
|
||||||
[CPUID detection]: http://wiki.osdev.org/Setting_Up_Long_Mode#Detection_of_CPUID
|
[CPUID detection]: https://wiki.osdev.org/Setting_Up_Long_Mode#Detection_of_CPUID
|
||||||
|
|
||||||
```nasm
|
```nasm
|
||||||
check_cpuid:
|
check_cpuid:
|
||||||
@@ -151,7 +151,7 @@ Don't worry, you don't need to understand the details.
|
|||||||
### Long Mode check
|
### Long Mode check
|
||||||
Now we can use CPUID to detect whether long mode can be used. I use code from [OSDev][long mode detection] again:
|
Now we can use CPUID to detect whether long mode can be used. I use code from [OSDev][long mode detection] again:
|
||||||
|
|
||||||
[long mode detection]: http://wiki.osdev.org/Setting_Up_Long_Mode#x86_or_x86-64
|
[long mode detection]: https://wiki.osdev.org/Setting_Up_Long_Mode#x86_or_x86-64
|
||||||
|
|
||||||
```nasm
|
```nasm
|
||||||
check_long_mode:
|
check_long_mode:
|
||||||
@@ -400,7 +400,7 @@ Bit(s) | Name | Meaning
|
|||||||
54 | 32-bit | must be 0 for 64-bit segments
|
54 | 32-bit | must be 0 for 64-bit segments
|
||||||
55-63 | ignored | ignored in 64-bit mode
|
55-63 | ignored | ignored in 64-bit mode
|
||||||
|
|
||||||
[ring level]: http://wiki.osdev.org/Security#Rings
|
[ring level]: https://wiki.osdev.org/Security#Rings
|
||||||
|
|
||||||
We need one code segment, a data segment is not necessary in 64-bit mode. Code segments have the following bits set: _descriptor type_, _present_, _executable_ and the _64-bit_ flag. Translated to assembly the long mode GDT looks like this:
|
We need one code segment, a data segment is not necessary in 64-bit mode. Code segments have the following bits set: _descriptor type_, _present_, _executable_ and the _64-bit_ flag. Translated to assembly the long mode GDT looks like this:
|
||||||
|
|
||||||
@@ -451,7 +451,7 @@ gdt64:
|
|||||||
```
|
```
|
||||||
We can't just use a normal label here, since we need the table _offset_. We calculate this offset using the current address `$` and set the label to this value using [equ]. Now we can use `gdt64.code` instead of 8 and this label will still work if we modify the GDT.
|
We can't just use a normal label here, since we need the table _offset_. We calculate this offset using the current address `$` and set the label to this value using [equ]. Now we can use `gdt64.code` instead of 8 and this label will still work if we modify the GDT.
|
||||||
|
|
||||||
[equ]: http://www.nasm.us/doc/nasmdoc3.html#section-3.2.4
|
[equ]: https://www.nasm.us/doc/nasmdoc3.html#section-3.2.4
|
||||||
|
|
||||||
In order to finally enter the true 64-bit mode, we need to load `cs` with `gdt64.code`. But we can't do it through `mov`. The only way to reload the code selector is a _far jump_ or a _far return_. These instructions work like a normal jump/return but change the code selector. We use a far jump to a long mode label:
|
In order to finally enter the true 64-bit mode, we need to load `cs` with `gdt64.code`. But we can't do it through `mov`. The only way to reload the code selector is a _far jump_ or a _far return_. These instructions work like a normal jump/return but change the code selector. We use a far jump to a long mode label:
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ The code from this post (and all following) is [automatically tested](https://tr
|
|||||||
## Creating a Cargo project
|
## Creating a Cargo project
|
||||||
[Cargo] is Rust's excellent package manager. Normally you would call `cargo new` when you want to create a new project folder. We can't use it because our folder already exists, so we need to do it manually. Fortunately we only need to add a cargo configuration file named `Cargo.toml`:
|
[Cargo] is Rust's excellent package manager. Normally you would call `cargo new` when you want to create a new project folder. We can't use it because our folder already exists, so we need to do it manually. Fortunately we only need to add a cargo configuration file named `Cargo.toml`:
|
||||||
|
|
||||||
[Cargo]: http://doc.crates.io/guide.html
|
[Cargo]: https://doc.crates.io/guide.html
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[package]
|
[package]
|
||||||
@@ -114,7 +114,7 @@ Rust allows us to define [custom targets] through a JSON configuration file. A m
|
|||||||
|
|
||||||
The `llvm-target` field specifies the target triple that is passed to LLVM. [Target triples] are a naming convention that define the CPU architecture (e.g., `x86_64` or `arm`), the vendor (e.g., `apple` or `unknown`), the operating system (e.g., `windows` or `linux`), and the [ABI] \(e.g., `gnu` or `msvc`). For example, the target triple for 64-bit Linux is `x86_64-unknown-linux-gnu` and for 32-bit Windows the target triple is `i686-pc-windows-msvc`.
|
The `llvm-target` field specifies the target triple that is passed to LLVM. [Target triples] are a naming convention that define the CPU architecture (e.g., `x86_64` or `arm`), the vendor (e.g., `apple` or `unknown`), the operating system (e.g., `windows` or `linux`), and the [ABI] \(e.g., `gnu` or `msvc`). For example, the target triple for 64-bit Linux is `x86_64-unknown-linux-gnu` and for 32-bit Windows the target triple is `i686-pc-windows-msvc`.
|
||||||
|
|
||||||
[Target triples]: http://llvm.org/docs/LangRef.html#target-triple
|
[Target triples]: https://llvm.org/docs/LangRef.html#target-triple
|
||||||
[ABI]: https://en.wikipedia.org/wiki/Application_binary_interface
|
[ABI]: https://en.wikipedia.org/wiki/Application_binary_interface
|
||||||
|
|
||||||
The `data-layout` field is also passed to LLVM and specifies how data should be laid out in memory. It consists of various specifications separated by a `-` character. For example, the `e` means little endian and `S128` specifies that the stack should be 128 bits (= 16 byte) aligned. The format is described in detail in the [LLVM documentation][data layout] but there shouldn't be a reason to change this string.
|
The `data-layout` field is also passed to LLVM and specifies how data should be laid out in memory. It consists of various specifications separated by a `-` character. For example, the `e` means little endian and `S128` specifies that the stack should be 128 bits (= 16 byte) aligned. The format is described in detail in the [LLVM documentation][data layout] but there shouldn't be a reason to change this string.
|
||||||
@@ -126,7 +126,7 @@ The `linker-flavor` field was recently introduced in [#40018] with the intention
|
|||||||
|
|
||||||
The other fields are used for conditional compilation. This allows crate authors to use `cfg` variables to write special code for depending on the OS or the architecture. There isn't any up-to-date documentation about these fields but the [corresponding source code][target specification] is quite readable.
|
The other fields are used for conditional compilation. This allows crate authors to use `cfg` variables to write special code for depending on the OS or the architecture. There isn't any up-to-date documentation about these fields but the [corresponding source code][target specification] is quite readable.
|
||||||
|
|
||||||
[data layout]: http://llvm.org/docs/LangRef.html#data-layout
|
[data layout]: https://llvm.org/docs/LangRef.html#data-layout
|
||||||
[target specification]: https://github.com/rust-lang/rust/blob/c772948b687488a087356cb91432425662e034b9/src/librustc_back/target/mod.rs#L194-L214
|
[target specification]: https://github.com/rust-lang/rust/blob/c772948b687488a087356cb91432425662e034b9/src/librustc_back/target/mod.rs#L194-L214
|
||||||
|
|
||||||
### A Kernel Target Specification
|
### A Kernel Target Specification
|
||||||
@@ -152,8 +152,8 @@ As `llvm-target` we use `x86_64-unknown-none`, which defines the `x86_64` archit
|
|||||||
#### The Red Zone
|
#### The Red Zone
|
||||||
The [red zone] is an optimization of the [System V ABI] that allows functions to temporary use the 128 bytes below its stack frame without adjusting the stack pointer:
|
The [red zone] is an optimization of the [System V ABI] that allows functions to temporary use the 128 bytes below its stack frame without adjusting the stack pointer:
|
||||||
|
|
||||||
[red zone]: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
|
[red zone]: https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
|
||||||
[System V ABI]: http://wiki.osdev.org/System_V_ABI
|
[System V ABI]: https://wiki.osdev.org/System_V_ABI
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ However, this optimization leads to huge problems with exceptions or hardware in
|
|||||||
|
|
||||||
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. This might lead to strange bugs that [take weeks to debug].
|
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. This might lead to strange bugs that [take weeks to debug].
|
||||||
|
|
||||||
[take weeks to debug]: http://forum.osdev.org/viewtopic.php?t=21720
|
[take weeks to debug]: https://forum.osdev.org/viewtopic.php?t=21720
|
||||||
|
|
||||||
To avoid such bugs when we implement exception handling in the future, we disable the red zone right from the beginning. This is achieved by adding the `"disable-redzone": true` line to our target configuration file.
|
To avoid such bugs when we implement exception handling in the future, we disable the red zone right from the beginning. This is achieved by adding the `"disable-redzone": true` line to our target configuration file.
|
||||||
|
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ error[E0507]: cannot move out of borrowed content
|
|||||||
The reason it that Rust _moves_ values by default instead of copying them like other languages. And we cannot move `color_code` out of `self` because we only borrowed `self`. For more information check out the [ownership section] in the Rust book.
|
The reason it that Rust _moves_ values by default instead of copying them like other languages. And we cannot move `color_code` out of `self` because we only borrowed `self`. For more information check out the [ownership section] in the Rust book.
|
||||||
|
|
||||||
[ownership section]: https://doc.rust-lang.org/book/ownership.html
|
[ownership section]: https://doc.rust-lang.org/book/ownership.html
|
||||||
[by reference]: http://rust-lang.github.io/book/ch04-02-references-and-borrowing.html
|
[by reference]: https://rust-lang.github.io/book/ch04-02-references-and-borrowing.html
|
||||||
|
|
||||||
To fix it, we can implement the [Copy] trait for the `ColorCode` type. The easiest way to do this is to use the built-in [derive macro]:
|
To fix it, we can implement the [Copy] trait for the `ColorCode` type. The easiest way to do this is to use the built-in [derive macro]:
|
||||||
|
|
||||||
@@ -287,8 +287,8 @@ volatile = "0.1.0"
|
|||||||
|
|
||||||
The `0.1.0` is the [semantic] version number. For more information, see the [Specifying Dependencies] guide of the cargo documentation.
|
The `0.1.0` is the [semantic] version number. For more information, see the [Specifying Dependencies] guide of the cargo documentation.
|
||||||
|
|
||||||
[semantic]: http://semver.org/
|
[semantic]: https://semver.org/
|
||||||
[Specifying Dependencies]: http://doc.crates.io/specifying-dependencies.html
|
[Specifying Dependencies]: https://doc.crates.io/specifying-dependencies.html
|
||||||
|
|
||||||
Now we've declared that our project depends on the `volatile` crate and are able to import it in `src/lib.rs`:
|
Now we've declared that our project depends on the `volatile` crate and are able to import it in `src/lib.rs`:
|
||||||
|
|
||||||
@@ -354,7 +354,7 @@ You can try it yourself in the `print_something` function.
|
|||||||
When you print strings with some special characters like `ä` or `λ`, you'll notice that they cause weird symbols on screen. That's because they are represented by multiple bytes in [UTF-8]. By converting them to bytes, we of course get strange results. But since the VGA buffer doesn't support UTF-8, it's not possible to display these characters anyway.
|
When you print strings with some special characters like `ä` or `λ`, you'll notice that they cause weird symbols on screen. That's because they are represented by multiple bytes in [UTF-8]. By converting them to bytes, we of course get strange results. But since the VGA buffer doesn't support UTF-8, it's not possible to display these characters anyway.
|
||||||
|
|
||||||
[core tracking issue]: https://github.com/rust-lang/rust/issues/27701
|
[core tracking issue]: https://github.com/rust-lang/rust/issues/27701
|
||||||
[UTF-8]: http://www.fileformat.info/info/unicode/utf8.htm
|
[UTF-8]: https://www.fileformat.info/info/unicode/utf8.htm
|
||||||
|
|
||||||
### Support Formatting Macros
|
### Support Formatting Macros
|
||||||
It would be nice to support Rust's formatting macros, too. That way, we can easily print different types like integers or floats. To support them, we need to implement the [core::fmt::Write] trait. The only required method of this trait is `write_str` that looks quite similar to our `write_str` method. To implement the trait, we just need to move it into an `impl fmt::Write for Writer` block and add a return type:
|
It would be nice to support Rust's formatting macros, too. That way, we can easily print different types like integers or floats. To support them, we need to implement the [core::fmt::Write] trait. The only required method of this trait is `write_str` that looks quite similar to our `write_str` method. To implement the trait, we just need to move it into an `impl fmt::Write for Writer` block and add a return type:
|
||||||
@@ -657,7 +657,7 @@ _Note_: You need to [cross compile binutils] to build it (or you create some sym
|
|||||||
- [Redox]: Probably the most complete Rust OS today. It has an active community and over 1000 Github stars. File systems, network, an audio player, a picture viewer, and much more. Just take a look at the [screenshots][redox screenshots].
|
- [Redox]: Probably the most complete Rust OS today. It has an active community and over 1000 Github stars. File systems, network, an audio player, a picture viewer, and much more. Just take a look at the [screenshots][redox screenshots].
|
||||||
|
|
||||||
[Rust Bare-Bones Kernel]: https://github.com/thepowersgang/rust-barebones-kernel
|
[Rust Bare-Bones Kernel]: https://github.com/thepowersgang/rust-barebones-kernel
|
||||||
[higher half]: http://wiki.osdev.org/Higher_Half_Kernel
|
[higher half]: https://wiki.osdev.org/Higher_Half_Kernel
|
||||||
[cross compile binutils]: @/first-edition/extra/cross-compile-binutils.md
|
[cross compile binutils]: @/first-edition/extra/cross-compile-binutils.md
|
||||||
[RustOS]: https://github.com/RustOS-Fork-Holding-Ground/RustOS
|
[RustOS]: https://github.com/RustOS-Fork-Holding-Ground/RustOS
|
||||||
["Tifflin" Experimental Kernel]:https://github.com/thepowersgang/rust_os
|
["Tifflin" Experimental Kernel]:https://github.com/thepowersgang/rust_os
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ Now we can use it to print available memory areas.
|
|||||||
### Available Memory
|
### Available Memory
|
||||||
The boot information structure consists of various _tags_. See section 3.4 of the Multiboot specification ([PDF][multiboot specification]) for a complete list. The _memory map_ tag contains a list of all available RAM areas. Special areas such as the VGA text buffer at `0xb8000` are not available. Note that some of the available memory is already used by our kernel and by the multiboot information structure itself.
|
The boot information structure consists of various _tags_. See section 3.4 of the Multiboot specification ([PDF][multiboot specification]) for a complete list. The _memory map_ tag contains a list of all available RAM areas. Special areas such as the VGA text buffer at `0xb8000` are not available. Note that some of the available memory is already used by our kernel and by the multiboot information structure itself.
|
||||||
|
|
||||||
[multiboot specification]: http://nongnu.askapache.com/grub/phcoder/multiboot.pdf
|
[multiboot specification]: https://nongnu.askapache.com/grub/phcoder/multiboot.pdf
|
||||||
|
|
||||||
To print all available memory areas, we can use the `multiboot2` crate in our `rust_main` as follows:
|
To print all available memory areas, we can use the `multiboot2` crate in our `rust_main` as follows:
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ So we have one area from `0x0` to `0x9fc00`, which is a bit below the 1MiB mark.
|
|||||||
|
|
||||||
If you give QEMU more than 4GiB of memory by passing `-m 5G`, you get another unusable area below the 4GiB mark. This memory is normally mapped to some hardware devices. See the [OSDev Wiki][Memory_map] for more information.
|
If you give QEMU more than 4GiB of memory by passing `-m 5G`, you get another unusable area below the 4GiB mark. This memory is normally mapped to some hardware devices. See the [OSDev Wiki][Memory_map] for more information.
|
||||||
|
|
||||||
[Memory_map]: http://wiki.osdev.org/Memory_Map_(x86)
|
[Memory_map]: https://wiki.osdev.org/Memory_Map_(x86)
|
||||||
|
|
||||||
### Handling Panics
|
### Handling Panics
|
||||||
We used `expect` in the code above, which will panic if there is no memory map tag. But our current panic handler just loops without printing any error message. Of course we could replace `expect` by a `match`, but we should fix the panic handler nonetheless:
|
We used `expect` in the code above, which will panic if there is no memory map tag. But our current panic handler just loops without printing any error message. Of course we could replace `expect` by a `match`, but we should fix the panic handler nonetheless:
|
||||||
@@ -217,7 +217,7 @@ We could create some kind of linked list from the free frames. For example, each
|
|||||||
|
|
||||||
Another approach is to create some kind of data structure such as a [bitmap or a stack] to manage free frames. We could place it in the already identity mapped area right behind the kernel or multiboot structure. That way we would not need to (temporary) map each free frame. But it has the same problem of the slow initial creating/filling. In fact, we will use this approach in a future post to manage frames that are freed again. But for the initial management of free frames, we use a different method.
|
Another approach is to create some kind of data structure such as a [bitmap or a stack] to manage free frames. We could place it in the already identity mapped area right behind the kernel or multiboot structure. That way we would not need to (temporary) map each free frame. But it has the same problem of the slow initial creating/filling. In fact, we will use this approach in a future post to manage frames that are freed again. But for the initial management of free frames, we use a different method.
|
||||||
|
|
||||||
[bitmap or a stack]: http://wiki.osdev.org/Page_Frame_Allocation#Physical_Memory_Allocators
|
[bitmap or a stack]: https://wiki.osdev.org/Page_Frame_Allocation#Physical_Memory_Allocators
|
||||||
|
|
||||||
In the following, we will use Multiboot's memory map directly. The idea is to maintain a simple counter that starts at frame 0 and is increased constantly. If the current frame is available (part of an available area in the memory map) and not used by the kernel or the multiboot structure (we know their start and end addresses), we know that it's free and return it. Else, we increase the counter to the next possibly free frame. That way, we don't need to create a data structure when booting and the physical frames can remain unmapped. The only problem is that we cannot reasonably free frames again, but we will solve that problem in a future post (by adding an intermediate frame stack that saves freed frames).
|
In the following, we will use Multiboot's memory map directly. The idea is to maintain a simple counter that starts at frame 0 and is increased constantly. If the current frame is available (part of an available area in the memory map) and not used by the kernel or the multiboot structure (we know their start and end addresses), we know that it's free and return it. Else, we increase the counter to the next possibly free frame. That way, we don't need to create a data structure when booting and the physical frames can remain unmapped. The only problem is that we cannot reasonably free frames again, but we will solve that problem in a future post (by adding an intermediate frame stack that saves freed frames).
|
||||||
|
|
||||||
@@ -421,7 +421,7 @@ for i in 0.. {
|
|||||||
```
|
```
|
||||||
You can try different amounts of memory by passing e.g. `-m 500M` to QEMU. To compare these numbers, [WolframAlpha] can be very helpful.
|
You can try different amounts of memory by passing e.g. `-m 500M` to QEMU. To compare these numbers, [WolframAlpha] can be very helpful.
|
||||||
|
|
||||||
[WolframAlpha]: http://www.wolframalpha.com/input/?i=%2832605+*+4096%29+bytes+in+MiB
|
[WolframAlpha]: https://www.wolframalpha.com/input/?i=%2832605+*+4096%29+bytes+in+MiB
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
|
|||||||
@@ -368,7 +368,7 @@ pub fn with<F>(&mut self,
|
|||||||
```
|
```
|
||||||
It overwrites the 511th P4 entry and points it to the inactive table frame. Then it flushes the [translation lookaside buffer (TLB)][TLB], which still contains some old translations. We need to flush all pages that are part of the recursive mapping, so the easiest way is to flush the TLB completely.
|
It overwrites the 511th P4 entry and points it to the inactive table frame. Then it flushes the [translation lookaside buffer (TLB)][TLB], which still contains some old translations. We need to flush all pages that are part of the recursive mapping, so the easiest way is to flush the TLB completely.
|
||||||
|
|
||||||
[TLB]: http://wiki.osdev.org/TLB
|
[TLB]: https://wiki.osdev.org/TLB
|
||||||
|
|
||||||
|
|
||||||
Now that the recursive mapping points to the given inactive table, we execute the closure in the new context. The closure can call all active table methods such as `translate` or `map_to`. It could even call `with` again and chain another inactive table! Wait… that would not work:
|
Now that the recursive mapping points to the given inactive table, we execute the closure in the new context. The closure can call all active table methods such as `translate` or `map_to`. It could even call `with` again and chain another inactive table! Wait… that would not work:
|
||||||
@@ -466,7 +466,7 @@ let backup = Frame::containing_address(
|
|||||||
```
|
```
|
||||||
Why is it unsafe? Because reading the CR3 register leads to a CPU exception if the processor is not running in kernel mode ([Ring 0]). But this code will always run in kernel mode, so the `unsafe` block is completely safe here.
|
Why is it unsafe? Because reading the CR3 register leads to a CPU exception if the processor is not running in kernel mode ([Ring 0]). But this code will always run in kernel mode, so the `unsafe` block is completely safe here.
|
||||||
|
|
||||||
[Ring 0]: http://wiki.osdev.org/Security#Low-level_Protection_Mechanisms
|
[Ring 0]: https://wiki.osdev.org/Security#Low-level_Protection_Mechanisms
|
||||||
|
|
||||||
Now that we have a backup of the original P4 frame, we need a way to restore it after the closure has run. So we need to somehow modify the 511th entry of the original P4 frame, which is still the active table in the CPU. But we can't access it because the recursive mapping now points to the inactive table:
|
Now that we have a backup of the original P4 frame, we need a way to restore it after the closure has run. So we need to somehow modify the 511th entry of the original P4 frame, which is still the active table in the CPU. But we can't access it because the recursive mapping now points to the inactive table:
|
||||||
|
|
||||||
@@ -826,9 +826,9 @@ These lines are the important ones. We can read many useful information from the
|
|||||||
|
|
||||||
- `CR2=00000000000b8f00`: Finally the most useful register. It tells us which virtual address caused the page fault. In our case it's `0xb8f00`, which is part of the [VGA text buffer].
|
- `CR2=00000000000b8f00`: Finally the most useful register. It tells us which virtual address caused the page fault. In our case it's `0xb8f00`, which is part of the [VGA text buffer].
|
||||||
|
|
||||||
[osdev exception overview]: http://wiki.osdev.org/Exceptions
|
[osdev exception overview]: https://wiki.osdev.org/Exceptions
|
||||||
[page fault]: http://wiki.osdev.org/Exceptions#Page_Fault
|
[page fault]: https://wiki.osdev.org/Exceptions#Page_Fault
|
||||||
[page fault error code]: http://wiki.osdev.org/Exceptions#Error_code
|
[page fault error code]: https://wiki.osdev.org/Exceptions#Error_code
|
||||||
[GDT segment]: @/first-edition/posts/02-entering-longmode/index.md#loading-the-gdt
|
[GDT segment]: @/first-edition/posts/02-entering-longmode/index.md#loading-the-gdt
|
||||||
[VGA text buffer]: @/first-edition/posts/04-printing-to-screen/index.md#the-vga-text-buffer
|
[VGA text buffer]: @/first-edition/posts/04-printing-to-screen/index.md#the-vga-text-buffer
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ A good allocator is fast and reliable. It also effectively utilizes the availabl
|
|||||||
|
|
||||||
[cache locality]: http://docs.cray.com/books/S-2315-50/html-S-2315-50/qmeblljm.html
|
[cache locality]: http://docs.cray.com/books/S-2315-50/html-S-2315-50/qmeblljm.html
|
||||||
[fragmentation]: https://en.wikipedia.org/wiki/Fragmentation_(computing)
|
[fragmentation]: https://en.wikipedia.org/wiki/Fragmentation_(computing)
|
||||||
[false sharing]: http://mechanical-sympathy.blogspot.de/2011/07/false-sharing.html
|
[false sharing]: https://mechanical-sympathy.blogspot.de/2011/07/false-sharing.html
|
||||||
|
|
||||||
These requirements make good allocators pretty complex. For example, [jemalloc] has over 30.000 lines of code. This complexity is out of scope for our kernel, so we will create a much simpler allocator. Nevertheless, it should suffice for the foreseeable future, since we'll allocate only when it's absolutely necessary.
|
These requirements make good allocators pretty complex. For example, [jemalloc] has over 30.000 lines of code. This complexity is out of scope for our kernel, so we will create a much simpler allocator. Nevertheless, it should suffice for the foreseeable future, since we'll allocate only when it's absolutely necessary.
|
||||||
|
|
||||||
@@ -344,7 +344,7 @@ check_exception old: 0xffffffff new 0xe
|
|||||||
```
|
```
|
||||||
Aha! It's a [page fault] \(`v=0e`) and was caused by the code at `0x102860`. The code tried to write (`e=0002`) to address `0x40000000`. This address is `0o_000_001_000_000_0000` in octal, which is the `HEAP_START` address defined above. Of course it page-faults: We have forgotten to map the heap memory to some physical memory.
|
Aha! It's a [page fault] \(`v=0e`) and was caused by the code at `0x102860`. The code tried to write (`e=0002`) to address `0x40000000`. This address is `0o_000_001_000_000_0000` in octal, which is the `HEAP_START` address defined above. Of course it page-faults: We have forgotten to map the heap memory to some physical memory.
|
||||||
|
|
||||||
[page fault]: http://wiki.osdev.org/Exceptions#Page_Fault
|
[page fault]: https://wiki.osdev.org/Exceptions#Page_Fault
|
||||||
|
|
||||||
### Some Refactoring
|
### Some Refactoring
|
||||||
In order to map the heap cleanly, we do a bit of refactoring first. We move all memory initialization from our `rust_main` to a new `memory::init` function. Now our `rust_main` looks like this:
|
In order to map the heap cleanly, we do a bit of refactoring first. We move all memory initialization from our `rust_main` to a new `memory::init` function. Now our `rust_main` looks like this:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ template = "first-edition/page.html"
|
|||||||
|
|
||||||
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.
|
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
|
[breakpoint exceptions]: https://wiki.osdev.org/Exceptions#Breakpoint
|
||||||
|
|
||||||
<!-- more -->
|
<!-- more -->
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ We've already seen several types of exceptions in our kernel:
|
|||||||
|
|
||||||
For the full list of exceptions check out the [OSDev wiki][exceptions].
|
For the full list of exceptions check out the [OSDev wiki][exceptions].
|
||||||
|
|
||||||
[exceptions]: http://wiki.osdev.org/Exceptions
|
[exceptions]: https://wiki.osdev.org/Exceptions
|
||||||
|
|
||||||
### The Interrupt Descriptor Table
|
### 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:
|
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:
|
||||||
@@ -128,7 +128,7 @@ However, there is a major difference between exceptions and function calls: A fu
|
|||||||
[Calling conventions] specify the details of a function call. For example, they specify where function parameters are placed (e.g. in registers or on the stack) and how results are returned. On x86_64 Linux, the following rules apply for C functions (specified in the [System V ABI]):
|
[Calling conventions] 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
|
[Calling conventions]: https://en.wikipedia.org/wiki/Calling_convention
|
||||||
[System V ABI]: http://refspecs.linuxbase.org/elf/gabi41.pdf
|
[System V ABI]: https://refspecs.linuxbase.org/elf/gabi41.pdf
|
||||||
|
|
||||||
- the first six integer arguments are passed in registers `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`
|
- the first six integer arguments are passed in registers `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`
|
||||||
- additional arguments are passed on the stack
|
- additional arguments are passed on the stack
|
||||||
@@ -221,11 +221,11 @@ pub fn init() {
|
|||||||
|
|
||||||
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.
|
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
|
[breakpoint exception]: https://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.
|
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
|
["_How debuggers work_"]: https://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.
|
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.
|
||||||
|
|
||||||
@@ -439,16 +439,16 @@ The answer is that the stored instruction pointer only points to the causing ins
|
|||||||
- **Aborts** are fatal exceptions that can't be recovered. Examples are [machine check exception] or the [double fault].
|
- **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].
|
- **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
|
[page fault]: https://wiki.osdev.org/Exceptions#Page_Fault
|
||||||
[machine check exception]: http://wiki.osdev.org/Exceptions#Machine_Check
|
[machine check exception]: https://wiki.osdev.org/Exceptions#Machine_Check
|
||||||
[double fault]: http://wiki.osdev.org/Exceptions#Double_Fault
|
[double fault]: https://wiki.osdev.org/Exceptions#Double_Fault
|
||||||
[overflow exception]: http://wiki.osdev.org/Exceptions#Overflow
|
[overflow exception]: https://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.
|
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.
|
||||||
|
|
||||||
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:
|
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
|
[debug exception]: https://wiki.osdev.org/Exceptions#Debug
|
||||||
[AMD64 manual]: https://www.amd.com/system/files/TechDocs/24593.pdf
|
[AMD64 manual]: https://www.amd.com/system/files/TechDocs/24593.pdf
|
||||||
|
|
||||||
> `#BP` is a trap-type exception. The saved instruction pointer points to the byte after the `INT3` instruction.
|
> `#BP` is a trap-type exception. The saved instruction pointer points to the byte after the `INT3` instruction.
|
||||||
@@ -456,7 +456,7 @@ In some cases, the distinction between faults and traps is vague. For example, t
|
|||||||
The documentation of the [`Idt`] struct and the [OSDev Wiki][osdev wiki exceptions] also contain this information.
|
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
|
[`Idt`]: https://docs.rs/x86_64/0.1.1/x86_64/structures/idt/struct.Idt.html
|
||||||
[osdev wiki exceptions]: http://wiki.osdev.org/Exceptions
|
[osdev wiki exceptions]: https://wiki.osdev.org/Exceptions
|
||||||
|
|
||||||
## Too much Magic?
|
## 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.
|
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.
|
||||||
@@ -466,8 +466,8 @@ The `x86-interrupt` calling convention and the [`Idt`] type made the exception h
|
|||||||
## What's next?
|
## 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].
|
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
|
[triple fault]: https://wiki.osdev.org/Triple_Fault
|
||||||
[double faults]: http://wiki.osdev.org/Double_Fault#Double_Fault
|
[double faults]: https://wiki.osdev.org/Double_Fault#Double_Fault
|
||||||
|
|
||||||
## Footnotes
|
## Footnotes
|
||||||
[^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.
|
[^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.
|
||||||
|
|||||||
@@ -128,12 +128,12 @@ First Exception | Second Exception
|
|||||||
[Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
|
[Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
|
||||||
[Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
|
[Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
|
||||||
|
|
||||||
[Divide-by-zero]: http://wiki.osdev.org/Exceptions#Divide-by-zero_Error
|
[Divide-by-zero]: https://wiki.osdev.org/Exceptions#Divide-by-zero_Error
|
||||||
[Invalid TSS]: http://wiki.osdev.org/Exceptions#Invalid_TSS
|
[Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS
|
||||||
[Segment Not Present]: http://wiki.osdev.org/Exceptions#Segment_Not_Present
|
[Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present
|
||||||
[Stack-Segment Fault]: http://wiki.osdev.org/Exceptions#Stack-Segment_Fault
|
[Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault
|
||||||
[General Protection Fault]: http://wiki.osdev.org/Exceptions#General_Protection_Fault
|
[General Protection Fault]: https://wiki.osdev.org/Exceptions#General_Protection_Fault
|
||||||
[Page Fault]: http://wiki.osdev.org/Exceptions#Page_Fault
|
[Page Fault]: https://wiki.osdev.org/Exceptions#Page_Fault
|
||||||
|
|
||||||
|
|
||||||
[AMD64 manual]: https://www.amd.com/system/files/TechDocs/24593.pdf
|
[AMD64 manual]: https://www.amd.com/system/files/TechDocs/24593.pdf
|
||||||
@@ -438,7 +438,7 @@ We allocate a 4096 bytes stack (one page) for our double fault handler. Now we j
|
|||||||
The Interrupt Stack Table (IST) is part of an old legacy structure called _[Task State Segment]_ \(TSS). The TSS used to hold various information (e.g. processor register state) about a task in 32-bit mode and was for example used for [hardware context switching]. However, hardware context switching is no longer supported in 64-bit mode and the format of the TSS changed completely.
|
The Interrupt Stack Table (IST) is part of an old legacy structure called _[Task State Segment]_ \(TSS). The TSS used to hold various information (e.g. processor register state) about a task in 32-bit mode and was for example used for [hardware context switching]. However, hardware context switching is no longer supported in 64-bit mode and the format of the TSS changed completely.
|
||||||
|
|
||||||
[Task State Segment]: https://en.wikipedia.org/wiki/Task_state_segment
|
[Task State Segment]: https://en.wikipedia.org/wiki/Task_state_segment
|
||||||
[hardware context switching]: http://wiki.osdev.org/Context_Switching#Hardware_Context_Switching
|
[hardware context switching]: https://wiki.osdev.org/Context_Switching#Hardware_Context_Switching
|
||||||
|
|
||||||
On x86_64, the TSS no longer holds any task specific information at all. Instead, it holds two stack tables (the IST is one of them). The only common field between the 32-bit and 64-bit TSS is the pointer to the [I/O port permissions bitmap].
|
On x86_64, the TSS no longer holds any task specific information at all. Instead, it holds two stack tables (the IST is one of them). The only common field between the 32-bit and 64-bit TSS is the pointer to the [I/O port permissions bitmap].
|
||||||
|
|
||||||
@@ -608,7 +608,7 @@ Bit(s) | Name | Meaning
|
|||||||
64-95 | **base 32-63** | the last four bytes of the base address
|
64-95 | **base 32-63** | the last four bytes of the base address
|
||||||
96-127 | ignored/must be zero | bits 104-108 must be zero, the rest is ignored
|
96-127 | ignored/must be zero | bits 104-108 must be zero, the rest is ignored
|
||||||
|
|
||||||
[ring level]: http://wiki.osdev.org/Security#Rings
|
[ring level]: https://wiki.osdev.org/Security#Rings
|
||||||
|
|
||||||
We only need the bold fields for our TSS descriptor. For example, we don't need the `limit 16-19` field since a TSS has a fixed size that is smaller than `2^16`.
|
We only need the bold fields for our TSS descriptor. For example, we don't need the `limit 16-19` field since a TSS has a fixed size that is smaller than `2^16`.
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ The [first edition] required several C-tools for building:
|
|||||||
[`grub-mkrescue`]: https://www.gnu.org/software/grub/manual/grub/html_node/Invoking-grub_002dmkrescue.html
|
[`grub-mkrescue`]: https://www.gnu.org/software/grub/manual/grub/html_node/Invoking-grub_002dmkrescue.html
|
||||||
[`xorriso`]: https://www.gnu.org/software/xorriso/
|
[`xorriso`]: https://www.gnu.org/software/xorriso/
|
||||||
[entering long mode]: @/first-edition/posts/02-entering-longmode/index.md
|
[entering long mode]: @/first-edition/posts/02-entering-longmode/index.md
|
||||||
[`nasm`]: http://www.nasm.us/xdoc/2.13.03/html/nasmdoc1.html
|
[`nasm`]: https://www.nasm.us/xdoc/2.13.03/html/nasmdoc1.html
|
||||||
[`ld`]: https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_3.html
|
[`ld`]: https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_3.html
|
||||||
[linker script]: http://www.scoberlin.de/content/media/http/informatik/gcc_docs/ld_3.html
|
[linker script]: http://www.scoberlin.de/content/media/http/informatik/gcc_docs/ld_3.html
|
||||||
[`make`]: https://www.gnu.org/software/make/
|
[`make`]: https://www.gnu.org/software/make/
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ blog_os
|
|||||||
|
|
||||||
The `Cargo.toml` contains the crate configuration, for example the crate name, the author, the [semantic version] number, and dependencies. The `src/main.rs` file contains the root module of our crate and our `main` function. You can compile your crate through `cargo build` and then run the compiled `blog_os` binary in the `target/debug` subfolder.
|
The `Cargo.toml` contains the crate configuration, for example the crate name, the author, the [semantic version] number, and dependencies. The `src/main.rs` file contains the root module of our crate and our `main` function. You can compile your crate through `cargo build` and then run the compiled `blog_os` binary in the `target/debug` subfolder.
|
||||||
|
|
||||||
[semantic version]: http://semver.org/
|
[semantic version]: https://semver.org/
|
||||||
|
|
||||||
### The `no_std` Attribute
|
### The `no_std` Attribute
|
||||||
|
|
||||||
@@ -152,8 +152,8 @@ While providing custom implementations of language items is possible, it should
|
|||||||
The [`eh_personality` language item] marks a function that is used for implementing [stack unwinding]. By default, Rust uses unwinding to run the destructors of all live stack variables in case of a [panic]. This ensures that all used memory is freed and allows the parent thread to catch the panic and continue execution. Unwinding, however, is a complicated process and requires some OS specific libraries (e.g. [libunwind] on Linux or [structured exception handling] on Windows), so we don't want to use it for our operating system.
|
The [`eh_personality` language item] marks a function that is used for implementing [stack unwinding]. By default, Rust uses unwinding to run the destructors of all live stack variables in case of a [panic]. This ensures that all used memory is freed and allows the parent thread to catch the panic and continue execution. Unwinding, however, is a complicated process and requires some OS specific libraries (e.g. [libunwind] on Linux or [structured exception handling] on Windows), so we don't want to use it for our operating system.
|
||||||
|
|
||||||
[`eh_personality` language item]: https://github.com/rust-lang/rust/blob/edb368491551a77d77a48446d4ee88b35490c565/src/libpanic_unwind/gcc.rs#L11-L45
|
[`eh_personality` language item]: https://github.com/rust-lang/rust/blob/edb368491551a77d77a48446d4ee88b35490c565/src/libpanic_unwind/gcc.rs#L11-L45
|
||||||
[stack unwinding]: http://www.bogotobogo.com/cplusplus/stackunwinding.php
|
[stack unwinding]: https://www.bogotobogo.com/cplusplus/stackunwinding.php
|
||||||
[libunwind]: http://www.nongnu.org/libunwind/
|
[libunwind]: https://www.nongnu.org/libunwind/
|
||||||
[structured exception handling]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680657(v=vs.85).aspx
|
[structured exception handling]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680657(v=vs.85).aspx
|
||||||
|
|
||||||
### Disabling Unwinding
|
### Disabling Unwinding
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ blog_os
|
|||||||
└── main.rs
|
└── main.rs
|
||||||
```
|
```
|
||||||
|
|
||||||
在这里,`Cargo.toml` 文件包含了包的**配置**(configuration),比如包的名称、作者、[semver版本](http://semver.org/) 和项目依赖项;`src/main.rs` 文件包含包的**根模块**(root module)和 main 函数。我们可以使用 `cargo build` 来编译这个包,然后在 `target/debug` 文件夹内找到编译好的 `blog_os` 二进制文件。
|
在这里,`Cargo.toml` 文件包含了包的**配置**(configuration),比如包的名称、作者、[semver版本](https://semver.org/) 和项目依赖项;`src/main.rs` 文件包含包的**根模块**(root module)和 main 函数。我们可以使用 `cargo build` 来编译这个包,然后在 `target/debug` 文件夹内找到编译好的 `blog_os` 二进制文件。
|
||||||
|
|
||||||
### no_std 属性
|
### no_std 属性
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ fn panic(_info: &PanicInfo) -> ! {
|
|||||||
|
|
||||||
我们可以自己实现语言项,但这是下下策:目前来看,语言项是高度不稳定的语言细节实现,它们不会经过编译期类型检查(所以编译器甚至不确保它们的参数类型是否正确)。幸运的是,我们有更稳定的方式,来修复上面的语言项错误。
|
我们可以自己实现语言项,但这是下下策:目前来看,语言项是高度不稳定的语言细节实现,它们不会经过编译期类型检查(所以编译器甚至不确保它们的参数类型是否正确)。幸运的是,我们有更稳定的方式,来修复上面的语言项错误。
|
||||||
|
|
||||||
`eh_personality` 语言项标记的函数,将被用于实现**栈展开**([stack unwinding](http://www.bogotobogo.com/cplusplus/stackunwinding.php))。在使用标准库的情况下,当 panic 发生时,Rust 将使用栈展开,来运行在栈上所有活跃的变量的**析构函数**(destructor)——这确保了所有使用的内存都被释放,允许调用程序的**父进程**(parent thread)捕获 panic,处理并继续运行。但是,栈展开是一个复杂的过程,如 Linux 的 [libunwind](http://www.nongnu.org/libunwind/) 或 Windows 的**结构化异常处理**([structured exception handling, SEH](https://msdn.microsoft.com/en-us/library/windows/desktop/ms680657(v=vs.85).aspx)),通常需要依赖于操作系统的库;所以我们不在自己编写的操作系统中使用它。
|
`eh_personality` 语言项标记的函数,将被用于实现**栈展开**([stack unwinding](https://www.bogotobogo.com/cplusplus/stackunwinding.php))。在使用标准库的情况下,当 panic 发生时,Rust 将使用栈展开,来运行在栈上所有活跃的变量的**析构函数**(destructor)——这确保了所有使用的内存都被释放,允许调用程序的**父进程**(parent thread)捕获 panic,处理并继续运行。但是,栈展开是一个复杂的过程,如 Linux 的 [libunwind](https://www.nongnu.org/libunwind/) 或 Windows 的**结构化异常处理**([structured exception handling, SEH](https://msdn.microsoft.com/en-us/library/windows/desktop/ms680657(v=vs.85).aspx)),通常需要依赖于操作系统的库;所以我们不在自己编写的操作系统中使用它。
|
||||||
|
|
||||||
### 禁用栈展开
|
### 禁用栈展开
|
||||||
|
|
||||||
|
|||||||
@@ -152,8 +152,8 @@ Language item 是一些編譯器需求的特殊函式或類型。舉例來說,
|
|||||||
|
|
||||||
`eh_personality` language item 標記的函式將被用於實作[堆疊回溯][stack unwinding]。在預設情況下當 panic 發生時,Rust 會使用堆疊回溯來執行所有存在堆疊上變數的解構子(destructor)。這確保所有使用的記憶體都被釋放,並讓 parent thread 獲取 panic 資訊並繼續運行。但是堆疊回溯是一個複雜的過程,通常會需要一些 OS 的函式庫如 Linux 的 [libunwind] 或 Windows 的 [structured exception handling]。所以我們並不希望在我們的作業系統中使用它。
|
`eh_personality` language item 標記的函式將被用於實作[堆疊回溯][stack unwinding]。在預設情況下當 panic 發生時,Rust 會使用堆疊回溯來執行所有存在堆疊上變數的解構子(destructor)。這確保所有使用的記憶體都被釋放,並讓 parent thread 獲取 panic 資訊並繼續運行。但是堆疊回溯是一個複雜的過程,通常會需要一些 OS 的函式庫如 Linux 的 [libunwind] 或 Windows 的 [structured exception handling]。所以我們並不希望在我們的作業系統中使用它。
|
||||||
|
|
||||||
[stack unwinding]: http://www.bogotobogo.com/cplusplus/stackunwinding.php
|
[stack unwinding]: https://www.bogotobogo.com/cplusplus/stackunwinding.php
|
||||||
[libunwind]: http://www.nongnu.org/libunwind/
|
[libunwind]: https://www.nongnu.org/libunwind/
|
||||||
[structured exception handling]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680657(v=vs.85).aspx
|
[structured exception handling]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680657(v=vs.85).aspx
|
||||||
|
|
||||||
### 停用回溯
|
### 停用回溯
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ template = "second-edition/extra.html"
|
|||||||
|
|
||||||
The [red zone] is an optimization of the [System V ABI] that allows functions to temporarily use the 128 bytes below its stack frame without adjusting the stack pointer:
|
The [red zone] is an optimization of the [System V ABI] that allows functions to temporarily use the 128 bytes below its stack frame without adjusting the stack pointer:
|
||||||
|
|
||||||
[red zone]: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
|
[red zone]: https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
|
||||||
[System V ABI]: http://wiki.osdev.org/System_V_ABI
|
[System V ABI]: https://wiki.osdev.org/System_V_ABI
|
||||||
|
|
||||||
<!-- more -->
|
<!-- more -->
|
||||||
|
|
||||||
@@ -24,6 +24,6 @@ However, this optimization leads to huge problems with exceptions or hardware in
|
|||||||
|
|
||||||
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. This might lead to strange bugs that [take weeks to debug].
|
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. This might lead to strange bugs that [take weeks to debug].
|
||||||
|
|
||||||
[take weeks to debug]: http://forum.osdev.org/viewtopic.php?t=21720
|
[take weeks to debug]: https://forum.osdev.org/viewtopic.php?t=21720
|
||||||
|
|
||||||
To avoid such bugs when we implement exception handling in the future, we disable the red zone right from the beginning. This is achieved by adding the `"disable-redzone": true` line to our target configuration file.
|
To avoid such bugs when we implement exception handling in the future, we disable the red zone right from the beginning. This is achieved by adding the `"disable-redzone": true` line to our target configuration file.
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ Instead of using the platform's default linker (which might not support Linux ta
|
|||||||
|
|
||||||
This setting specifies that the target doesn't support [stack unwinding] on panic, so instead the program should abort directly. This has the same effect as the `panic = "abort"` option in our Cargo.toml, so we can remove it from there. (Note that in contrast to the Cargo.toml option, this target option also applies when we recompile the `core` library later in this post. So be sure to add this option, even if you prefer to keep the Cargo.toml option.)
|
This setting specifies that the target doesn't support [stack unwinding] on panic, so instead the program should abort directly. This has the same effect as the `panic = "abort"` option in our Cargo.toml, so we can remove it from there. (Note that in contrast to the Cargo.toml option, this target option also applies when we recompile the `core` library later in this post. So be sure to add this option, even if you prefer to keep the Cargo.toml option.)
|
||||||
|
|
||||||
[stack unwinding]: http://www.bogotobogo.com/cplusplus/stackunwinding.php
|
[stack unwinding]: https://www.bogotobogo.com/cplusplus/stackunwinding.php
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"disable-redzone": true,
|
"disable-redzone": true,
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ Nightly 版本的编译器允许我们在源码的开头插入**特性标签**
|
|||||||
"panic-strategy": "abort",
|
"panic-strategy": "abort",
|
||||||
```
|
```
|
||||||
|
|
||||||
这个配置项的意思是,我们的编译目标不支持 panic 时的**栈展开**([stack unwinding](http://www.bogotobogo.com/cplusplus/stackunwinding.php)),所以我们选择直接**在 panic 时中止**(abort on panic)。这和在 `Cargo.toml` 文件中添加 `panic = "abort"` 选项的作用是相同的,所以我们可以不在这里的配置清单中填写这一项。
|
这个配置项的意思是,我们的编译目标不支持 panic 时的**栈展开**([stack unwinding](https://www.bogotobogo.com/cplusplus/stackunwinding.php)),所以我们选择直接**在 panic 时中止**(abort on panic)。这和在 `Cargo.toml` 文件中添加 `panic = "abort"` 选项的作用是相同的,所以我们可以不在这里的配置清单中填写这一项。
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"disable-redzone": true,
|
"disable-redzone": true,
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ impl Writer {
|
|||||||
The VGA text buffer only supports ASCII and the additional bytes of [code page 437]. Rust strings are [UTF-8] by default, so they might contain bytes that are not supported by the VGA text buffer. We use a match to differentiate printable ASCII bytes (a newline or anything in between a space character and a `~` character) and unprintable bytes. For unprintable bytes, we print a `■` character, which has the hex code `0xfe` on the VGA hardware.
|
The VGA text buffer only supports ASCII and the additional bytes of [code page 437]. Rust strings are [UTF-8] by default, so they might contain bytes that are not supported by the VGA text buffer. We use a match to differentiate printable ASCII bytes (a newline or anything in between a space character and a `~` character) and unprintable bytes. For unprintable bytes, we print a `■` character, which has the hex code `0xfe` on the VGA hardware.
|
||||||
|
|
||||||
[code page 437]: https://en.wikipedia.org/wiki/Code_page_437
|
[code page 437]: https://en.wikipedia.org/wiki/Code_page_437
|
||||||
[UTF-8]: http://www.fileformat.info/info/unicode/utf8.htm
|
[UTF-8]: https://www.fileformat.info/info/unicode/utf8.htm
|
||||||
|
|
||||||
#### Try it out!
|
#### Try it out!
|
||||||
To write some characters to the screen, you can create a temporary function:
|
To write some characters to the screen, you can create a temporary function:
|
||||||
@@ -307,8 +307,8 @@ volatile = "0.2.6"
|
|||||||
|
|
||||||
The `0.2.6` is the [semantic] version number. For more information, see the [Specifying Dependencies] guide of the cargo documentation.
|
The `0.2.6` is the [semantic] version number. For more information, see the [Specifying Dependencies] guide of the cargo documentation.
|
||||||
|
|
||||||
[semantic]: http://semver.org/
|
[semantic]: https://semver.org/
|
||||||
[Specifying Dependencies]: http://doc.crates.io/specifying-dependencies.html
|
[Specifying Dependencies]: https://doc.crates.io/specifying-dependencies.html
|
||||||
|
|
||||||
Let's use it to make writes to the VGA buffer volatile. We update our `Buffer` type as follows:
|
Let's use it to make writes to the VGA buffer volatile. We update our `Buffer` type as follows:
|
||||||
|
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ impl Writer {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
VGA 字符缓冲区只支持 ASCII 码字节和**代码页 437**([Code page 437](https://en.wikipedia.org/wiki/Code_page_437))定义的字节。Rust 语言的字符串默认编码为 [UTF-8](http://www.fileformat.info/info/unicode/utf8.htm),也因此可能包含一些 VGA 字符缓冲区不支持的字节:我们使用 `match` 语句,来区别可打印的 ASCII 码或换行字节,和其它不可打印的字节。对每个不可打印的字节,我们打印一个 `■` 符号;这个符号在 VGA 硬件中被编码为十六进制的 `0xfe`。
|
VGA 字符缓冲区只支持 ASCII 码字节和**代码页 437**([Code page 437](https://en.wikipedia.org/wiki/Code_page_437))定义的字节。Rust 语言的字符串默认编码为 [UTF-8](https://www.fileformat.info/info/unicode/utf8.htm),也因此可能包含一些 VGA 字符缓冲区不支持的字节:我们使用 `match` 语句,来区别可打印的 ASCII 码或换行字节,和其它不可打印的字节。对每个不可打印的字节,我们打印一个 `■` 符号;这个符号在 VGA 硬件中被编码为十六进制的 `0xfe`。
|
||||||
|
|
||||||
我们可以亲自试一试已经编写的代码。为了这样做,我们可以临时编写一个函数:
|
我们可以亲自试一试已经编写的代码。为了这样做,我们可以临时编写一个函数:
|
||||||
|
|
||||||
@@ -259,7 +259,7 @@ pub extern "C" fn _start() -> ! {
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
需要注意的是,`ö` 字符被打印为两个 `■` 字符。这是因为在 [UTF-8](http://www.fileformat.info/info/unicode/utf8.htm) 编码下,字符 `ö` 是由两个字节表述的——而这两个字节并不处在可打印的 ASCII 码字节范围之内。事实上,这是 UTF-8 编码的基本特点之一:**如果一个字符占用多个字节,那么每个组成它的独立字节都不是有效的 ASCII 码字节**(the individual bytes of multi-byte values are never valid ASCII)。
|
需要注意的是,`ö` 字符被打印为两个 `■` 字符。这是因为在 [UTF-8](https://www.fileformat.info/info/unicode/utf8.htm) 编码下,字符 `ö` 是由两个字节表述的——而这两个字节并不处在可打印的 ASCII 码字节范围之内。事实上,这是 UTF-8 编码的基本特点之一:**如果一个字符占用多个字节,那么每个组成它的独立字节都不是有效的 ASCII 码字节**(the individual bytes of multi-byte values are never valid ASCII)。
|
||||||
|
|
||||||
### 易失操作
|
### 易失操作
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ pub extern "C" fn _start() -> ! {
|
|||||||
volatile = "0.2.3"
|
volatile = "0.2.3"
|
||||||
```
|
```
|
||||||
|
|
||||||
`0.2.3` 表示一个**语义版本号**([semantic version number](http://semver.org/)),在 cargo 文档的[《指定依赖项》章节](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html)可以找到与它相关的使用指南。
|
`0.2.3` 表示一个**语义版本号**([semantic version number](https://semver.org/)),在 cargo 文档的[《指定依赖项》章节](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html)可以找到与它相关的使用指南。
|
||||||
|
|
||||||
现在,我们使用它来完成 VGA 缓冲区的 volatile 写入操作。我们将 `Buffer` 类型的定义修改为下列代码:
|
现在,我们使用它来完成 VGA 缓冲区的 volatile 写入操作。我们将 `Buffer` 类型的定义修改为下列代码:
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ chapter = "Interrupts"
|
|||||||
|
|
||||||
CPU exceptions occur in various erroneous situations, for example when accessing an invalid memory address or when dividing by zero. To react to 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.
|
CPU exceptions occur in various erroneous situations, for example when accessing an invalid memory address or when dividing by zero. To react to 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
|
[breakpoint exceptions]: https://wiki.osdev.org/Exceptions#Breakpoint
|
||||||
|
|
||||||
<!-- more -->
|
<!-- more -->
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ On x86 there are about 20 different CPU exception types. The most important are:
|
|||||||
|
|
||||||
For the full list of exceptions check out the [OSDev wiki][exceptions].
|
For the full list of exceptions check out the [OSDev wiki][exceptions].
|
||||||
|
|
||||||
[exceptions]: http://wiki.osdev.org/Exceptions
|
[exceptions]: https://wiki.osdev.org/Exceptions
|
||||||
|
|
||||||
### The Interrupt Descriptor Table
|
### 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:
|
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:
|
||||||
@@ -139,7 +139,7 @@ However, there is a major difference between exceptions and function calls: A fu
|
|||||||
[Calling conventions] specify the details of a function call. For example, they specify where function parameters are placed (e.g. in registers or on the stack) and how results are returned. On x86_64 Linux, the following rules apply for C functions (specified in the [System V ABI]):
|
[Calling conventions] 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
|
[Calling conventions]: https://en.wikipedia.org/wiki/Calling_convention
|
||||||
[System V ABI]: http://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf
|
[System V ABI]: https://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`
|
- the first six integer arguments are passed in registers `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`
|
||||||
- additional arguments are passed on the stack
|
- additional arguments are passed on the stack
|
||||||
@@ -229,11 +229,11 @@ pub fn init_idt() {
|
|||||||
|
|
||||||
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 temporarily pause a program when the breakpoint instruction `int3` is executed.
|
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 temporarily pause a program when the breakpoint instruction `int3` is executed.
|
||||||
|
|
||||||
[breakpoint exception]: http://wiki.osdev.org/Exceptions#Breakpoint
|
[breakpoint exception]: https://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.
|
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
|
["_How debuggers work_"]: https://eli.thegreenplace.net/2011/01/27/how-debuggers-work-part-2-breakpoints
|
||||||
|
|
||||||
For our use case, we don't need to overwrite any instructions. Instead, we just want to print a message when the breakpoint instruction is executed and then continue the program. So let's create a simple `breakpoint_handler` function and add it to our IDT:
|
For our use case, we don't need to overwrite any instructions. Instead, we just want to print a message when the breakpoint instruction is executed and then continue the program. So let's create a simple `breakpoint_handler` function and add it to our IDT:
|
||||||
|
|
||||||
@@ -466,5 +466,5 @@ The `x86-interrupt` calling convention and the [`InterruptDescriptorTable`] type
|
|||||||
## What's next?
|
## What's next?
|
||||||
We've successfully caught our first exception and returned from it! The next step is to ensure that we catch all exceptions, because an uncaught exception causes a fatal [triple fault], which leads to a system reset. The next post explains how we can avoid this by correctly catching [double faults].
|
We've successfully caught our first exception and returned from it! The next step is to ensure that we catch all exceptions, because an uncaught exception causes a fatal [triple fault], which leads to a system reset. The next post explains how we can avoid this by correctly catching [double faults].
|
||||||
|
|
||||||
[triple fault]: http://wiki.osdev.org/Triple_Fault
|
[triple fault]: https://wiki.osdev.org/Triple_Fault
|
||||||
[double faults]: http://wiki.osdev.org/Double_Fault#Double_Fault
|
[double faults]: https://wiki.osdev.org/Double_Fault#Double_Fault
|
||||||
|
|||||||
@@ -128,12 +128,12 @@ First Exception | Second Exception
|
|||||||
[Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
|
[Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
|
||||||
[Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
|
[Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
|
||||||
|
|
||||||
[Divide-by-zero]: http://wiki.osdev.org/Exceptions#Divide-by-zero_Error
|
[Divide-by-zero]: https://wiki.osdev.org/Exceptions#Divide-by-zero_Error
|
||||||
[Invalid TSS]: http://wiki.osdev.org/Exceptions#Invalid_TSS
|
[Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS
|
||||||
[Segment Not Present]: http://wiki.osdev.org/Exceptions#Segment_Not_Present
|
[Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present
|
||||||
[Stack-Segment Fault]: http://wiki.osdev.org/Exceptions#Stack-Segment_Fault
|
[Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault
|
||||||
[General Protection Fault]: http://wiki.osdev.org/Exceptions#General_Protection_Fault
|
[General Protection Fault]: https://wiki.osdev.org/Exceptions#General_Protection_Fault
|
||||||
[Page Fault]: http://wiki.osdev.org/Exceptions#Page_Fault
|
[Page Fault]: https://wiki.osdev.org/Exceptions#Page_Fault
|
||||||
|
|
||||||
|
|
||||||
[AMD64 manual]: https://www.amd.com/system/files/TechDocs/24593.pdf
|
[AMD64 manual]: https://www.amd.com/system/files/TechDocs/24593.pdf
|
||||||
@@ -206,7 +206,7 @@ For each exception handler, we can choose a stack from the IST through the `stac
|
|||||||
The Interrupt Stack Table (IST) is part of an old legacy structure called _[Task State Segment]_ \(TSS). The TSS used to hold various information (e.g. processor register state) about a task in 32-bit mode and was for example used for [hardware context switching]. However, hardware context switching is no longer supported in 64-bit mode and the format of the TSS changed completely.
|
The Interrupt Stack Table (IST) is part of an old legacy structure called _[Task State Segment]_ \(TSS). The TSS used to hold various information (e.g. processor register state) about a task in 32-bit mode and was for example used for [hardware context switching]. However, hardware context switching is no longer supported in 64-bit mode and the format of the TSS changed completely.
|
||||||
|
|
||||||
[Task State Segment]: https://en.wikipedia.org/wiki/Task_state_segment
|
[Task State Segment]: https://en.wikipedia.org/wiki/Task_state_segment
|
||||||
[hardware context switching]: http://wiki.osdev.org/Context_Switching#Hardware_Context_Switching
|
[hardware context switching]: https://wiki.osdev.org/Context_Switching#Hardware_Context_Switching
|
||||||
|
|
||||||
On x86_64, the TSS no longer holds any task specific information at all. Instead, it holds two stack tables (the IST is one of them). The only common field between the 32-bit and 64-bit TSS is the pointer to the [I/O port permissions bitmap].
|
On x86_64, the TSS no longer holds any task specific information at all. Instead, it holds two stack tables (the IST is one of them). The only common field between the 32-bit and 64-bit TSS is the pointer to the [I/O port permissions bitmap].
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ Apart from correctness, there are many secondary design goals. For example, the
|
|||||||
|
|
||||||
[cache locality]: http://docs.cray.com/books/S-2315-50/html-S-2315-50/qmeblljm.html
|
[cache locality]: http://docs.cray.com/books/S-2315-50/html-S-2315-50/qmeblljm.html
|
||||||
[_fragmentation_]: https://en.wikipedia.org/wiki/Fragmentation_(computing)
|
[_fragmentation_]: https://en.wikipedia.org/wiki/Fragmentation_(computing)
|
||||||
[false sharing]: http://mechanical-sympathy.blogspot.de/2011/07/false-sharing.html
|
[false sharing]: https://mechanical-sympathy.blogspot.de/2011/07/false-sharing.html
|
||||||
|
|
||||||
These requirements can make good allocators very complex. For example, [jemalloc] has over 30.000 lines of code. This complexity is often undesired in kernel code where a single bug can lead to severe security vulnerabilities. Fortunately, the allocation patterns of kernel code are often much simpler compared to userspace code, so that relatively simple allocator designs often suffice.
|
These requirements can make good allocators very complex. For example, [jemalloc] has over 30.000 lines of code. This complexity is often undesired in kernel code where a single bug can lead to severe security vulnerabilities. Fortunately, the allocation patterns of kernel code are often much simpler compared to userspace code, so that relatively simple allocator designs often suffice.
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ This post gives an overview of the recent updates to the _Writing an OS in Rust_
|
|||||||
- [Use cast crate instead of usize_conversions crate](https://github.com/rust-osdev/x86_64/pull/70) (released as version 0.5.5).
|
- [Use cast crate instead of usize_conversions crate](https://github.com/rust-osdev/x86_64/pull/70) (released as version 0.5.5).
|
||||||
- [Make FrameAllocator an unsafe trait](https://github.com/rust-osdev/x86_64/pull/71) (released as version 0.6.0).
|
- [Make FrameAllocator an unsafe trait](https://github.com/rust-osdev/x86_64/pull/71) (released as version 0.6.0).
|
||||||
- [Change Port::read and PortReadOnly::read to take &mut self](https://github.com/rust-osdev/x86_64/pull/76) (released as version 0.7.0).
|
- [Change Port::read and PortReadOnly::read to take &mut self](https://github.com/rust-osdev/x86_64/pull/76) (released as version 0.7.0).
|
||||||
- [@npmccallum](https://github.com/npmccallum) started working on [moving the type declarations to a separate crate](https://github.com/rust-osdev/x86_64/issues/72) to make them usable for more projects. We created the experimental [x86_64_types](http://github.com/rust-osdev/x86_64_types/) crate for this.
|
- [@npmccallum](https://github.com/npmccallum) started working on [moving the type declarations to a separate crate](https://github.com/rust-osdev/x86_64/issues/72) to make them usable for more projects. We created the experimental [x86_64_types](https://github.com/rust-osdev/x86_64_types/) crate for this.
|
||||||
|
|
||||||
## Cargo-Xbuild
|
## Cargo-Xbuild
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@
|
|||||||
</path>
|
</path>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"></div></div></div><div id="isso-198" class="isso-comment isso-no-votes"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="666df3217240"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="20" y="12" width="8" height="8" style="fill: #9abf88"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header"><span class="author">Philipp Oppermann</span><span class="spacer">•</span><a rel="nofollow" href="#isso-198" class="permalink"><time title="Sun May 29 2016 12:59:05 GMT+0200 (Central European Summer Time)" datetime="2016-04-00T10:59:05Z">vor 3 Jahren</time></a><span class="note"></span></div><div class="text"><p>I'm a computer science student and I've taken some great OS courses. It's also a hobby of mine and I've experimented with a lot with toy x86 kernels and Rust. Most of the x86 information is from the <a rel="nofollow" href="http://wiki.osdev.org/Main_Page">OSDev wiki</a> and the <a rel="nofollow" href="http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html">Intel</a>/<a rel="nofollow" href="https://developer.amd.com/resources/developer-guides-manuals/">AMD</a> manuals.</p><p>I also have a great research assistant job since November, where I try to bring Rust to an ARM Cortex-M7 board.</p></div><div class="isso-comment-footer"><span class="votes">0</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"></div></div></div><div id="isso-198" class="isso-comment isso-no-votes"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="666df3217240"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="20" y="12" width="8" height="8" style="fill: #9abf88"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header"><span class="author">Philipp Oppermann</span><span class="spacer">•</span><a rel="nofollow" href="#isso-198" class="permalink"><time title="Sun May 29 2016 12:59:05 GMT+0200 (Central European Summer Time)" datetime="2016-04-00T10:59:05Z">vor 3 Jahren</time></a><span class="note"></span></div><div class="text"><p>I'm a computer science student and I've taken some great OS courses. It's also a hobby of mine and I've experimented with a lot with toy x86 kernels and Rust. Most of the x86 information is from the <a rel="nofollow" href="https://wiki.osdev.org/Main_Page">OSDev wiki</a> and the <a rel="nofollow" href="http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html">Intel</a>/<a rel="nofollow" href="https://developer.amd.com/resources/developer-guides-manuals/">AMD</a> manuals.</p><p>I also have a great research assistant job since November, where I try to bring Rust to an ARM Cortex-M7 board.</p></div><div class="isso-comment-footer"><span class="votes">0</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
||||||
<g>
|
<g>
|
||||||
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
||||||
</path>
|
</path>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@
|
|||||||
</path>
|
</path>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"></div></div></div><div id="isso-234" class="isso-comment isso-no-votes"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="666df3217240"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="20" y="12" width="8" height="8" style="fill: #9abf88"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header"><span class="author">Philipp Oppermann</span><span class="spacer">•</span><a rel="nofollow" href="#isso-234" class="permalink"><time title="Thu Oct 29 2015 20:32:02 GMT+0100 (Central European Standard Time)" datetime="2015-09-04T19:32:02Z">vor 4 Jahren</time></a><span class="note"></span></div><div class="text"><p></p><blockquote>Are there any OS development books that you recommend for ideas on further enhancing this basic system?</blockquote><p></p><p>Well, there is the <a rel="nofollow" href="http://pages.cs.wisc.edu/~remzi/OSTEP/">Three Easy Pieces</a> book I linked in the post, which gives a theoretical overview over different OS concepts. Then there's <a rel="nofollow" href="http://littleosbook.github.io">the little book about OS development</a>, which is more practical and contains C example code. Of course there are <a rel="nofollow" href="http://wiki.osdev.org/Books#Operating_Systems">many paid books</a>, too.</p><p>Besides books, the <a rel="nofollow" href="http://wiki.osdev.org/Main_Page">OSDev Wiki</a> is also a good resource for many topics. Looking at the source of e.g. <a rel="nofollow" href="https://github.com/redox-os/redox">Redox</a> can be helpful, too.</p><p>For exotical ideas, I really like the concept of <a rel="nofollow" href="https://en.wikipedia.org/wiki/Phantom_OS">Phantom OS</a> and Rust's memory safety might allow something similar… We'll see ;)</p><p></p></div><div class="isso-comment-footer"><span class="votes">0</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"></div></div></div><div id="isso-234" class="isso-comment isso-no-votes"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="666df3217240"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="20" y="12" width="8" height="8" style="fill: #9abf88"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header"><span class="author">Philipp Oppermann</span><span class="spacer">•</span><a rel="nofollow" href="#isso-234" class="permalink"><time title="Thu Oct 29 2015 20:32:02 GMT+0100 (Central European Standard Time)" datetime="2015-09-04T19:32:02Z">vor 4 Jahren</time></a><span class="note"></span></div><div class="text"><p></p><blockquote>Are there any OS development books that you recommend for ideas on further enhancing this basic system?</blockquote><p></p><p>Well, there is the <a rel="nofollow" href="http://pages.cs.wisc.edu/~remzi/OSTEP/">Three Easy Pieces</a> book I linked in the post, which gives a theoretical overview over different OS concepts. Then there's <a rel="nofollow" href="http://littleosbook.github.io">the little book about OS development</a>, which is more practical and contains C example code. Of course there are <a rel="nofollow" href="https://wiki.osdev.org/Books#Operating_Systems">many paid books</a>, too.</p><p>Besides books, the <a rel="nofollow" href="https://wiki.osdev.org/Main_Page">OSDev Wiki</a> is also a good resource for many topics. Looking at the source of e.g. <a rel="nofollow" href="https://github.com/redox-os/redox">Redox</a> can be helpful, too.</p><p>For exotical ideas, I really like the concept of <a rel="nofollow" href="https://en.wikipedia.org/wiki/Phantom_OS">Phantom OS</a> and Rust's memory safety might allow something similar… We'll see ;)</p><p></p></div><div class="isso-comment-footer"><span class="votes">0</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
||||||
<g>
|
<g>
|
||||||
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
||||||
</path>
|
</path>
|
||||||
@@ -359,7 +359,7 @@
|
|||||||
</path>
|
</path>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"></div></div></div><div id="isso-253" class="isso-comment isso-no-votes"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="666df3217240"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="20" y="12" width="8" height="8" style="fill: #9abf88"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header"><span class="author">Philipp Oppermann</span><span class="spacer">•</span><a rel="nofollow" href="#isso-253" class="permalink"><time title="Wed Mar 02 2016 16:30:12 GMT+0100 (Central European Standard Time)" datetime="2016-02-03T15:30:12Z">vor 3 Jahren</time></a><span class="note"></span></div><div class="text"><p>Thanks for the overview and the simplified example! I haven't had the time to look at it in detail, but the problem in your simplified example could be the <a rel="nofollow" href="http://wiki.osdev.org/Programmable_Interval_Timer">Programmable Interval timer</a>. From the “Outputs” section:</p><p></p><blockquote>The output from PIT channel 0 is connected to the PIC chip, so that it generates an "IRQ 0". Typically during boot the BIOS sets channel 0 with a count of 65535 or 0 (which translates to 65536), which gives an output frequency of 18.2065 Hz (or an IRQ every 54.9254 ms).</blockquote><p></p><p>So it seems like the BIOS turns it on by default so that it causes an interrupts every ~55ms. This causes a double fault, since there is no interrupt handler for IRQ 0.</p></div><div class="isso-comment-footer"><span class="votes">0</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"></div></div></div><div id="isso-253" class="isso-comment isso-no-votes"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="666df3217240"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="20" y="12" width="8" height="8" style="fill: #9abf88"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header"><span class="author">Philipp Oppermann</span><span class="spacer">•</span><a rel="nofollow" href="#isso-253" class="permalink"><time title="Wed Mar 02 2016 16:30:12 GMT+0100 (Central European Standard Time)" datetime="2016-02-03T15:30:12Z">vor 3 Jahren</time></a><span class="note"></span></div><div class="text"><p>Thanks for the overview and the simplified example! I haven't had the time to look at it in detail, but the problem in your simplified example could be the <a rel="nofollow" href="https://wiki.osdev.org/Programmable_Interval_Timer">Programmable Interval timer</a>. From the “Outputs” section:</p><p></p><blockquote>The output from PIT channel 0 is connected to the PIC chip, so that it generates an "IRQ 0". Typically during boot the BIOS sets channel 0 with a count of 65535 or 0 (which translates to 65536), which gives an output frequency of 18.2065 Hz (or an IRQ every 54.9254 ms).</blockquote><p></p><p>So it seems like the BIOS turns it on by default so that it causes an interrupts every ~55ms. This causes a double fault, since there is no interrupt handler for IRQ 0.</p></div><div class="isso-comment-footer"><span class="votes">0</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
||||||
<g>
|
<g>
|
||||||
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
||||||
</path>
|
</path>
|
||||||
@@ -383,7 +383,7 @@
|
|||||||
</path>
|
</path>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"></div></div></div><div id="isso-255" class="isso-comment isso-no-votes"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="3773460ec1e4"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="4" width="8" height="8" style="fill: #be5168"></rect><rect x="36" y="4" width="8" height="8" style="fill: #be5168"></rect><rect x="4" y="20" width="8" height="8" style="fill: #be5168"></rect><rect x="36" y="20" width="8" height="8" style="fill: #be5168"></rect><rect x="4" y="28" width="8" height="8" style="fill: #be5168"></rect><rect x="36" y="28" width="8" height="8" style="fill: #be5168"></rect><rect x="12" y="36" width="8" height="8" style="fill: #be5168"></rect><rect x="28" y="36" width="8" height="8" style="fill: #be5168"></rect><rect x="20" y="4" width="8" height="8" style="fill: #be5168"></rect><rect x="20" y="12" width="8" height="8" style="fill: #be5168"></rect><rect x="20" y="20" width="8" height="8" style="fill: #be5168"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header"><span class="author">Wink Saville</span><span class="spacer">•</span><a rel="nofollow" href="#isso-255" class="permalink"><time title="Fri Mar 04 2016 01:53:38 GMT+0100 (Central European Standard Time)" datetime="2016-02-05T00:53:38Z">vor 3 Jahren</time></a><span class="note"></span></div><div class="text"><p>Here is a solution. There doesn't seem to be a way to disable the PIT, but you can disable all IRQ's from the PIC, adding the following code to my test_enable_interrupts branch allows the code to work even with the enabling interrupts:</p><p>```<br> ; Disable PIC interrupts so we don't get interrupts if the PIC<br> ; was being used by grub or BIOS. See Disabling section of<br> ; <a rel="nofollow" href="http://wiki.osdev.org/PIC">http://wiki.osdev.org/PIC</a>. If the application wants to use devices<br>; connected to the PIC, such at the PIT, it will probably want<br> ; to remap the PIC interrupts to be above 0 .. 31 which are<br> ; used or reserved by Intel. See the Initialisation section of<br> ; the same page for the PIC_remap subroutine.</p><p> mov al,0xff<br> out 0xa1, al<br> out 0x21, al<br>```</p><p>Thanks again for your help.</p></div><div class="isso-comment-footer"><span class="votes">0</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"></div></div></div><div id="isso-255" class="isso-comment isso-no-votes"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="3773460ec1e4"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="4" width="8" height="8" style="fill: #be5168"></rect><rect x="36" y="4" width="8" height="8" style="fill: #be5168"></rect><rect x="4" y="20" width="8" height="8" style="fill: #be5168"></rect><rect x="36" y="20" width="8" height="8" style="fill: #be5168"></rect><rect x="4" y="28" width="8" height="8" style="fill: #be5168"></rect><rect x="36" y="28" width="8" height="8" style="fill: #be5168"></rect><rect x="12" y="36" width="8" height="8" style="fill: #be5168"></rect><rect x="28" y="36" width="8" height="8" style="fill: #be5168"></rect><rect x="20" y="4" width="8" height="8" style="fill: #be5168"></rect><rect x="20" y="12" width="8" height="8" style="fill: #be5168"></rect><rect x="20" y="20" width="8" height="8" style="fill: #be5168"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header"><span class="author">Wink Saville</span><span class="spacer">•</span><a rel="nofollow" href="#isso-255" class="permalink"><time title="Fri Mar 04 2016 01:53:38 GMT+0100 (Central European Standard Time)" datetime="2016-02-05T00:53:38Z">vor 3 Jahren</time></a><span class="note"></span></div><div class="text"><p>Here is a solution. There doesn't seem to be a way to disable the PIT, but you can disable all IRQ's from the PIC, adding the following code to my test_enable_interrupts branch allows the code to work even with the enabling interrupts:</p><p>```<br> ; Disable PIC interrupts so we don't get interrupts if the PIC<br> ; was being used by grub or BIOS. See Disabling section of<br> ; <a rel="nofollow" href="https://wiki.osdev.org/PIC">https://wiki.osdev.org/PIC</a>. If the application wants to use devices<br>; connected to the PIC, such at the PIT, it will probably want<br> ; to remap the PIC interrupts to be above 0 .. 31 which are<br> ; used or reserved by Intel. See the Initialisation section of<br> ; the same page for the PIC_remap subroutine.</p><p> mov al,0xff<br> out 0xa1, al<br> out 0x21, al<br>```</p><p>Thanks again for your help.</p></div><div class="isso-comment-footer"><span class="votes">0</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
||||||
<g>
|
<g>
|
||||||
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
||||||
</path>
|
</path>
|
||||||
|
|||||||
@@ -215,7 +215,7 @@
|
|||||||
</path>
|
</path>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"></div></div></div><div id="isso-135" class="isso-comment"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="4ac55151f530"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="20" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="20" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="20" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="20" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="20" y="20" width="8" height="8" style="fill: #9abf88"></rect><rect x="20" y="28" width="8" height="8" style="fill: #9abf88"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header"><span class="author">Arnaud Bailly</span><span class="spacer">•</span><a rel="nofollow" href="#isso-135" class="permalink"><time title="Thu Nov 12 2015 10:04:06 GMT+0100 (Central European Standard Time)" datetime="2015-10-04T09:04:06Z">vor 4 Jahren</time></a><span class="note"></span></div><div class="text"><p>This is definitely fun! I tried to do this from my Mac OS X (Yosemite) and could not properly boot my fresh ISO disk. Compilation works fine, I have installed a cross-compiler for x86_64-elf architecture, compiled grub following instructions here <a rel="nofollow" href="http://wiki.osdev.org/GRUB_2#Installing_GRUB2_on_Mac_OS_X">http://wiki.osdev.org/GRUB_...</a>... I generate a correct ISO file (checked it by mounting using Disk Utility) but it does not boot and I cannot see the GRUB message.</p><p>Not sure how to troubleshoot this issue.... I suspect this might be a problem with incorrect format in grub as the last stage of compilation shows this message:</p><p> ../grub/configure --build=x86_64-elf --target=x86_64-elf --disable-werror TARGET_CC=x86_64-elf-gcc TARGET_OBJCOPY=x86_64-elf-objcopy TARGET_STRIP=x86_64-elf-strip TARGET_NM=x86_64-elf-nm TARGET_RANLIB=x86_64-elf-ranlib LD_FLAGS=/usr/local/opt/flex/ CPP_FLAGS=/usr/local/opt/flex/include/</p><p>[..]</p><p>config.status: linking ../grub/include/grub/i386 to include/grub/cpu<br>config.status: linking ../grub/include/grub/i386/pc to include/grub/machine<br>config.status: executing depfiles commands<br>config.status: executing po-directories commands<br>config.status: creating po/POTFILES<br>config.status: creating po/Makefile<br>*******************************************************<br>GRUB2 will be compiled with following components:<br>Platform: i386-pc<br>With devmapper support: No (need libdevmapper header)<br>With memory debugging: No<br>With disk cache statistics: No<br>With boot time statistics: No<br>efiemu runtime: Yes<br>grub-mkfont: Yes<br>grub-mount: No (need FUSE headers)<br>starfield theme: No (No DejaVu found)<br>With libzfs support: No (need zfs library)<br>Build-time grub-mkfont: No (no fonts)<br>Without unifont (no build-time grub-mkfont)<br>With liblzma from -llzma (support for XZ-compressed mips images)<br>*******************************************************</p><p>I don't know what the i386-pc refer too, but if this is the target platform then it's probably incorrect. Note that I tried to boot using qemu-system-i386 but to no avail.</p><p>Regards,</p></div><div class="isso-comment-footer"><span class="votes">-1</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"></div></div></div><div id="isso-135" class="isso-comment"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="4ac55151f530"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="20" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="20" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="20" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="20" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="20" y="20" width="8" height="8" style="fill: #9abf88"></rect><rect x="20" y="28" width="8" height="8" style="fill: #9abf88"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header"><span class="author">Arnaud Bailly</span><span class="spacer">•</span><a rel="nofollow" href="#isso-135" class="permalink"><time title="Thu Nov 12 2015 10:04:06 GMT+0100 (Central European Standard Time)" datetime="2015-10-04T09:04:06Z">vor 4 Jahren</time></a><span class="note"></span></div><div class="text"><p>This is definitely fun! I tried to do this from my Mac OS X (Yosemite) and could not properly boot my fresh ISO disk. Compilation works fine, I have installed a cross-compiler for x86_64-elf architecture, compiled grub following instructions here <a rel="nofollow" href="https://wiki.osdev.org/GRUB_2#Installing_GRUB2_on_Mac_OS_X">https://wiki.osdev.org/GRUB_...</a>... I generate a correct ISO file (checked it by mounting using Disk Utility) but it does not boot and I cannot see the GRUB message.</p><p>Not sure how to troubleshoot this issue.... I suspect this might be a problem with incorrect format in grub as the last stage of compilation shows this message:</p><p> ../grub/configure --build=x86_64-elf --target=x86_64-elf --disable-werror TARGET_CC=x86_64-elf-gcc TARGET_OBJCOPY=x86_64-elf-objcopy TARGET_STRIP=x86_64-elf-strip TARGET_NM=x86_64-elf-nm TARGET_RANLIB=x86_64-elf-ranlib LD_FLAGS=/usr/local/opt/flex/ CPP_FLAGS=/usr/local/opt/flex/include/</p><p>[..]</p><p>config.status: linking ../grub/include/grub/i386 to include/grub/cpu<br>config.status: linking ../grub/include/grub/i386/pc to include/grub/machine<br>config.status: executing depfiles commands<br>config.status: executing po-directories commands<br>config.status: creating po/POTFILES<br>config.status: creating po/Makefile<br>*******************************************************<br>GRUB2 will be compiled with following components:<br>Platform: i386-pc<br>With devmapper support: No (need libdevmapper header)<br>With memory debugging: No<br>With disk cache statistics: No<br>With boot time statistics: No<br>efiemu runtime: Yes<br>grub-mkfont: Yes<br>grub-mount: No (need FUSE headers)<br>starfield theme: No (No DejaVu found)<br>With libzfs support: No (need zfs library)<br>Build-time grub-mkfont: No (no fonts)<br>Without unifont (no build-time grub-mkfont)<br>With liblzma from -llzma (support for XZ-compressed mips images)<br>*******************************************************</p><p>I don't know what the i386-pc refer too, but if this is the target platform then it's probably incorrect. Note that I tried to boot using qemu-system-i386 but to no avail.</p><p>Regards,</p></div><div class="isso-comment-footer"><span class="votes">-1</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
||||||
<g>
|
<g>
|
||||||
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
||||||
</path>
|
</path>
|
||||||
@@ -287,7 +287,7 @@
|
|||||||
</path>
|
</path>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"><div id="isso-140" class="isso-comment isso-no-votes"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="666df3217240"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="20" y="12" width="8" height="8" style="fill: #9abf88"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header"><span class="author">Philipp Oppermann</span><span class="spacer">•</span><a rel="nofollow" href="#isso-140" class="permalink"><time title="Wed Nov 18 2015 22:46:08 GMT+0100 (Central European Standard Time)" datetime="2015-10-03T21:46:08Z">vor 4 Jahren</time></a><span class="note"></span></div><div class="text"><p>Yes, that exactly what happens. The CPU simply tries to read the next instruction, even if it doesn't exist, until it causes some <a rel="nofollow" href="http://wiki.osdev.org/Exceptions">exception</a>. QEMU can print these exceptions, the "Setup Rust" post explains <a rel="nofollow" href="http://os.phil-opp.com/setup-rust.html#debugging">how</a>. I just tried it and it hits an Invalid Opcode exception at some point because some memory is no valid instruction.</p><p>Bonus: You can use GDB to disassemble the “code” behind the start label. You need to start `qemu-system-x86_64 -hda build/os-x86_64.iso -s -S` in one console and `gdb build/kernel-x86_64.bin` in another. Then you need the following gdb commands:</p><p>- `set architecture i386` because we are still in 32-bit mode<br>- `target remote :1234` to connect to QEMU<br>(- `disas /r start,+250` to disassemble the 250 bytes after the `start` label. Everything will be 0 as GRUB did not load our kernel yet)<br>- `break start` to set a breakpoint at `start`<br>- `continue` to continue execution until start is reached. Now the kernel is loaded and we can use<br>- `disas /r start,+250` to disassemble the 250 bytes after the `start` label</p><p>Then you can look at the faulting address you got from the QEMU debugging to see your invalid instruction. For me it seems to be an `add (%eax),%al` with the Opcode `02 00`.</p></div><div class="isso-comment-footer"><span class="votes">0</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"><div id="isso-140" class="isso-comment isso-no-votes"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="666df3217240"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="20" y="12" width="8" height="8" style="fill: #9abf88"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header"><span class="author">Philipp Oppermann</span><span class="spacer">•</span><a rel="nofollow" href="#isso-140" class="permalink"><time title="Wed Nov 18 2015 22:46:08 GMT+0100 (Central European Standard Time)" datetime="2015-10-03T21:46:08Z">vor 4 Jahren</time></a><span class="note"></span></div><div class="text"><p>Yes, that exactly what happens. The CPU simply tries to read the next instruction, even if it doesn't exist, until it causes some <a rel="nofollow" href="https://wiki.osdev.org/Exceptions">exception</a>. QEMU can print these exceptions, the "Setup Rust" post explains <a rel="nofollow" href="http://os.phil-opp.com/setup-rust.html#debugging">how</a>. I just tried it and it hits an Invalid Opcode exception at some point because some memory is no valid instruction.</p><p>Bonus: You can use GDB to disassemble the “code” behind the start label. You need to start `qemu-system-x86_64 -hda build/os-x86_64.iso -s -S` in one console and `gdb build/kernel-x86_64.bin` in another. Then you need the following gdb commands:</p><p>- `set architecture i386` because we are still in 32-bit mode<br>- `target remote :1234` to connect to QEMU<br>(- `disas /r start,+250` to disassemble the 250 bytes after the `start` label. Everything will be 0 as GRUB did not load our kernel yet)<br>- `break start` to set a breakpoint at `start`<br>- `continue` to continue execution until start is reached. Now the kernel is loaded and we can use<br>- `disas /r start,+250` to disassemble the 250 bytes after the `start` label</p><p>Then you can look at the faulting address you got from the QEMU debugging to see your invalid instruction. For me it seems to be an `add (%eax),%al` with the Opcode `02 00`.</p></div><div class="isso-comment-footer"><span class="votes">0</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
||||||
<g>
|
<g>
|
||||||
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
||||||
</path>
|
</path>
|
||||||
@@ -743,7 +743,7 @@
|
|||||||
</path>
|
</path>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"><div id="isso-298" class="isso-comment isso-no-votes"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="5db23f819f9f"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="12" width="8" height="8" style="fill: #447c69"></rect><rect x="36" y="12" width="8" height="8" style="fill: #447c69"></rect><rect x="4" y="20" width="8" height="8" style="fill: #447c69"></rect><rect x="36" y="20" width="8" height="8" style="fill: #447c69"></rect><rect x="12" y="4" width="8" height="8" style="fill: #447c69"></rect><rect x="28" y="4" width="8" height="8" style="fill: #447c69"></rect><rect x="12" y="12" width="8" height="8" style="fill: #447c69"></rect><rect x="28" y="12" width="8" height="8" style="fill: #447c69"></rect><rect x="12" y="20" width="8" height="8" style="fill: #447c69"></rect><rect x="28" y="20" width="8" height="8" style="fill: #447c69"></rect><rect x="12" y="28" width="8" height="8" style="fill: #447c69"></rect><rect x="28" y="28" width="8" height="8" style="fill: #447c69"></rect><rect x="12" y="36" width="8" height="8" style="fill: #447c69"></rect><rect x="28" y="36" width="8" height="8" style="fill: #447c69"></rect><rect x="20" y="4" width="8" height="8" style="fill: #447c69"></rect><rect x="20" y="28" width="8" height="8" style="fill: #447c69"></rect><rect x="20" y="36" width="8" height="8" style="fill: #447c69"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header">Philipp Oppermann<span class="spacer">•</span><a rel="nofollow" href="#isso-298" class="permalink"><time title="Mon Jun 19 2017 13:39:01 GMT+0200 (Central European Summer Time)" datetime="2017-05-01T11:39:01Z">letztes Jahr</time></a><span class="note"></span></div><div class="text"><p>See <a rel="nofollow" href="http://wiki.osdev.org/Shutdown">http://wiki.osdev.org/Shutdown</a></p></div><div class="isso-comment-footer"><span class="votes">0</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"><div id="isso-298" class="isso-comment isso-no-votes"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="5db23f819f9f"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="12" width="8" height="8" style="fill: #447c69"></rect><rect x="36" y="12" width="8" height="8" style="fill: #447c69"></rect><rect x="4" y="20" width="8" height="8" style="fill: #447c69"></rect><rect x="36" y="20" width="8" height="8" style="fill: #447c69"></rect><rect x="12" y="4" width="8" height="8" style="fill: #447c69"></rect><rect x="28" y="4" width="8" height="8" style="fill: #447c69"></rect><rect x="12" y="12" width="8" height="8" style="fill: #447c69"></rect><rect x="28" y="12" width="8" height="8" style="fill: #447c69"></rect><rect x="12" y="20" width="8" height="8" style="fill: #447c69"></rect><rect x="28" y="20" width="8" height="8" style="fill: #447c69"></rect><rect x="12" y="28" width="8" height="8" style="fill: #447c69"></rect><rect x="28" y="28" width="8" height="8" style="fill: #447c69"></rect><rect x="12" y="36" width="8" height="8" style="fill: #447c69"></rect><rect x="28" y="36" width="8" height="8" style="fill: #447c69"></rect><rect x="20" y="4" width="8" height="8" style="fill: #447c69"></rect><rect x="20" y="28" width="8" height="8" style="fill: #447c69"></rect><rect x="20" y="36" width="8" height="8" style="fill: #447c69"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header">Philipp Oppermann<span class="spacer">•</span><a rel="nofollow" href="#isso-298" class="permalink"><time title="Mon Jun 19 2017 13:39:01 GMT+0200 (Central European Summer Time)" datetime="2017-05-01T11:39:01Z">letztes Jahr</time></a><span class="note"></span></div><div class="text"><p>See <a rel="nofollow" href="https://wiki.osdev.org/Shutdown">https://wiki.osdev.org/Shutdown</a></p></div><div class="isso-comment-footer"><span class="votes">0</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
||||||
<g>
|
<g>
|
||||||
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
||||||
</path>
|
</path>
|
||||||
|
|||||||
@@ -300,7 +300,7 @@ May be you used cross compiled 'ld' ?</p></div><div class="isso-comment-footer">
|
|||||||
</path>
|
</path>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"><div id="isso-106" class="isso-comment isso-no-votes"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="666df3217240"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="20" y="12" width="8" height="8" style="fill: #9abf88"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header"><span class="author">Philipp Oppermann</span><span class="spacer">•</span><a rel="nofollow" href="#isso-106" class="permalink"><time title="Thu Jul 14 2016 10:04:05 GMT+0200 (Central European Summer Time)" datetime="2016-06-04T08:04:05Z">vor 3 Jahren</time></a><span class="note"></span></div><div class="text"><p>Good catch! However, SSE2 should always be available if the long mode is available. Citing the <a rel="nofollow" href="http://wiki.osdev.org/SSE">OSDev wiki</a>:</p><p></p><blockquote>When the X86-64 architecture was introduced, AMD demanded a minimum level of SSE support to simplify OS code. Any system capable of long mode should support at least SSE and SSE2</blockquote><p></p><p>So SSE and SSE2 should always be available in our case (if the wiki is correct). So we could even remove the SSE check. However, I think it's better to leave it in, because we enable SSE before switching to long mode.</p></div><div class="isso-comment-footer"><span class="votes">0</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
</a><a rel="nofollow" href="#" class="reply">Antworten</a></div><div class="isso-follow-up"><div id="isso-106" class="isso-comment isso-no-votes"><div class="avatar"><svg version="1.1" viewBox="0 0 48 48" preserveAspectRatio="xMinYMin meet" shape-rendering="crispEdges" data-hash="666df3217240"><rect x="0" y="0" width="56" height="56" style="fill: #f0f0f0"></rect><rect x="4" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="12" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="4" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="36" y="36" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="4" width="8" height="8" style="fill: #9abf88"></rect><rect x="12" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="28" y="28" width="8" height="8" style="fill: #9abf88"></rect><rect x="20" y="12" width="8" height="8" style="fill: #9abf88"></rect></svg></div><div class="text-wrapper"><div role="meta" class="isso-comment-header"><span class="author">Philipp Oppermann</span><span class="spacer">•</span><a rel="nofollow" href="#isso-106" class="permalink"><time title="Thu Jul 14 2016 10:04:05 GMT+0200 (Central European Summer Time)" datetime="2016-06-04T08:04:05Z">vor 3 Jahren</time></a><span class="note"></span></div><div class="text"><p>Good catch! However, SSE2 should always be available if the long mode is available. Citing the <a rel="nofollow" href="https://wiki.osdev.org/SSE">OSDev wiki</a>:</p><p></p><blockquote>When the X86-64 architecture was introduced, AMD demanded a minimum level of SSE support to simplify OS code. Any system capable of long mode should support at least SSE and SSE2</blockquote><p></p><p>So SSE and SSE2 should always be available in our case (if the wiki is correct). So we could even remove the SSE check. However, I think it's better to leave it in, because we enable SSE before switching to long mode.</p></div><div class="isso-comment-footer"><span class="votes">0</span><a rel="nofollow" href="#" class="upvote"><!-- Generator: IcoMoon.io --><svg width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="gray">
|
||||||
<g>
|
<g>
|
||||||
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
<path d="M 24.773,18.299c-0.651-0.669-7.512-7.203-7.512-7.203C 16.912,10.739, 16.456,10.56, 16,10.56c-0.458,0-0.914,0.179-1.261,0.536 c0,0-6.861,6.534-7.514,7.203c-0.651,0.669-0.696,1.872,0,2.586c 0.698,0.712, 1.669,0.77, 2.522,0L 16,14.89l 6.251,5.995 c 0.854,0.77, 1.827,0.712, 2.522,0C 25.47,20.17, 25.427,18.966, 24.773,18.299z">
|
||||||
</path>
|
</path>
|
||||||
|
|||||||
Reference in New Issue
Block a user