Merge pull request #890 from phil-opp/rename-edition-folders
Rename edition folders
5
blog/content/edition-1/_index.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
+++
|
||||||
|
title = "First Edition"
|
||||||
|
template = "edition-1/index.html"
|
||||||
|
aliases = ["first-edition/index.html"]
|
||||||
|
+++
|
||||||
@@ -4,7 +4,7 @@ weight = 1
|
|||||||
path = "catching-exceptions"
|
path = "catching-exceptions"
|
||||||
aliases = ["catching-exceptions.html"]
|
aliases = ["catching-exceptions.html"]
|
||||||
date = 2016-05-28
|
date = 2016-05-28
|
||||||
template = "first-edition/page.html"
|
template = "edition-1/page.html"
|
||||||
[extra]
|
[extra]
|
||||||
updated = "2016-06-25"
|
updated = "2016-06-25"
|
||||||
+++
|
+++
|
||||||
@@ -20,8 +20,8 @@ As always, the complete source code is on [GitHub]. Please file [issues] for any
|
|||||||
|
|
||||||
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
|
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
|
||||||
|
|
||||||
[“Handling Exceptions with Naked Functions”]: @/first-edition/extra/naked-exceptions/_index.md
|
[“Handling Exceptions with Naked Functions”]: @/edition-1/extra/naked-exceptions/_index.md
|
||||||
[“Handling Exceptions”]: @/first-edition/posts/09-handling-exceptions/index.md
|
[“Handling Exceptions”]: @/edition-1/posts/09-handling-exceptions/index.md
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
An exception signals that something is wrong with the current instruction. For example, the CPU issues an exception if the current instruction tries to divide by 0. When an exception occurs, the CPU interrupts its current work and immediately calls a specific exception handler function, depending on the exception type.
|
An exception signals that something is wrong with the current instruction. For example, the CPU issues an exception if the current instruction tries to divide by 0. When an exception occurs, the CPU interrupts its current work and immediately calls a specific exception handler function, depending on the exception type.
|
||||||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@@ -4,7 +4,7 @@ weight = 2
|
|||||||
path = "better-exception-messages"
|
path = "better-exception-messages"
|
||||||
aliases = ["better-exception-messages.html"]
|
aliases = ["better-exception-messages.html"]
|
||||||
date = 2016-08-03
|
date = 2016-08-03
|
||||||
template = "first-edition/page.html"
|
template = "edition-1/page.html"
|
||||||
[extra]
|
[extra]
|
||||||
updated = "2016-11-01"
|
updated = "2016-11-01"
|
||||||
+++
|
+++
|
||||||
@@ -21,8 +21,8 @@ As always, the complete source code is on [GitHub]. Please file [issues] for any
|
|||||||
|
|
||||||
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
|
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
|
||||||
|
|
||||||
[“Handling Exceptions with Naked Functions”]: @/first-edition/extra/naked-exceptions/_index.md
|
[“Handling Exceptions with Naked Functions”]: @/edition-1/extra/naked-exceptions/_index.md
|
||||||
[“Handling Exceptions”]: @/first-edition/posts/09-handling-exceptions/index.md
|
[“Handling Exceptions”]: @/edition-1/posts/09-handling-exceptions/index.md
|
||||||
|
|
||||||
## Exceptions in Detail
|
## Exceptions in Detail
|
||||||
An exception signals that something is wrong with the currently-executed instruction. Whenever an exception occurs, the CPU interrupts its current work and starts an internal exception routine.
|
An exception signals that something is wrong with the currently-executed instruction. Whenever an exception occurs, the CPU interrupts its current work and starts an internal exception routine.
|
||||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
@@ -4,7 +4,7 @@ weight = 3
|
|||||||
path = "returning-from-exceptions"
|
path = "returning-from-exceptions"
|
||||||
aliases = ["returning-from-exceptions.html"]
|
aliases = ["returning-from-exceptions.html"]
|
||||||
date = 2016-09-21
|
date = 2016-09-21
|
||||||
template = "first-edition/page.html"
|
template = "edition-1/page.html"
|
||||||
[extra]
|
[extra]
|
||||||
updated = "2016-11-01"
|
updated = "2016-11-01"
|
||||||
+++
|
+++
|
||||||
@@ -21,8 +21,8 @@ As always, the complete source code is on [GitHub]. Please file [issues] for any
|
|||||||
|
|
||||||
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
|
> **Note**: This post describes how to handle exceptions using naked functions (see [“Handling Exceptions with Naked Functions”] for an overview). Our new way of handling exceptions can be found in the [“Handling Exceptions”] post.
|
||||||
|
|
||||||
[“Handling Exceptions with Naked Functions”]: @/first-edition/extra/naked-exceptions/_index.md
|
[“Handling Exceptions with Naked Functions”]: @/edition-1/extra/naked-exceptions/_index.md
|
||||||
[“Handling Exceptions”]: @/first-edition/posts/09-handling-exceptions/index.md
|
[“Handling Exceptions”]: @/edition-1/posts/09-handling-exceptions/index.md
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
Most exceptions are fatal and can't be resolved. For example, we can't return from a divide-by-zero exception in a reasonable way. However, there are some exceptions that we can resolve:
|
Most exceptions are fatal and can't be resolved. For example, we can't return from a divide-by-zero exception in a reasonable way. However, there are some exceptions that we can resolve:
|
||||||
@@ -42,7 +42,7 @@ The breakpoint exception is commonly used in debuggers: When the user sets a bre
|
|||||||
|
|
||||||
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.
|
||||||
|
|
||||||
[set the page table flags]: @/first-edition/posts/07-remap-the-kernel/index.md#using-the-correct-flags
|
[set the page table flags]: @/edition-1/posts/07-remap-the-kernel/index.md#using-the-correct-flags
|
||||||
|
|
||||||
### Catching Breakpoints
|
### Catching Breakpoints
|
||||||
Let's start by defining a handler function for the breakpoint exception:
|
Let's start by defining a handler function for the breakpoint exception:
|
||||||
@@ -216,7 +216,7 @@ Instead of the expected _“It did not crash”_ message after the breakpoint ex
|
|||||||
### Debugging
|
### Debugging
|
||||||
Let's debug it using GDB. For that we execute `make debug` in one terminal (which starts QEMU with the `-s -S` flags) and then `make gdb` (which starts and connects GDB) in a second terminal. For more information about GDB debugging, check out our [Set Up GDB] guide.
|
Let's debug it using GDB. For that we execute `make debug` in one terminal (which starts QEMU with the `-s -S` flags) and then `make gdb` (which starts and connects GDB) in a second terminal. For more information about GDB debugging, check out our [Set Up GDB] guide.
|
||||||
|
|
||||||
[Set Up GDB]: @/first-edition/extra/set-up-gdb/index.md
|
[Set Up GDB]: @/edition-1/extra/set-up-gdb/index.md
|
||||||
|
|
||||||
First we want to check if our `iretq` was successful. Therefore we set a breakpoint on the `println!("It did not crash line!")` statement in `src/lib.rs`. Let's assume that it's on line 61:
|
First we want to check if our `iretq` was successful. Therefore we set a breakpoint on the `println!("It did not crash line!")` statement in `src/lib.rs`. Let's assume that it's on line 61:
|
||||||
|
|
||||||
@@ -304,7 +304,7 @@ Unfortunately, Rust does not support such a calling convention. It was [proposed
|
|||||||
|
|
||||||
[interrupt calling conventions]: https://github.com/rust-lang/rfcs/pull/1275
|
[interrupt calling conventions]: https://github.com/rust-lang/rfcs/pull/1275
|
||||||
[Naked functions]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
|
[Naked functions]: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
|
||||||
[naked fn post]: @/first-edition/extra/naked-exceptions/02-better-exception-messages/index.md#naked-functions
|
[naked fn post]: @/edition-1/extra/naked-exceptions/02-better-exception-messages/index.md#naked-functions
|
||||||
|
|
||||||
### A naked wrapper function
|
### A naked wrapper function
|
||||||
|
|
||||||
@@ -574,7 +574,7 @@ It doesn't compile anymore. The error tells us that the Rust compiler no longer
|
|||||||
The [core library] is implicitly linked to all `no_std` crates and contains things such as `Result`, `Option`, and iterators. We've used that library without problems since [the very beginning], so why is it no longer available?
|
The [core library] is implicitly linked to all `no_std` crates and contains things such as `Result`, `Option`, and iterators. We've used that library without problems since [the very beginning], so why is it no longer available?
|
||||||
|
|
||||||
[core library]: https://doc.rust-lang.org/nightly/core/index.html
|
[core library]: https://doc.rust-lang.org/nightly/core/index.html
|
||||||
[the very beginning]: @/first-edition/posts/03-set-up-rust/index.md
|
[the very beginning]: @/edition-1/posts/03-set-up-rust/index.md
|
||||||
|
|
||||||
The problem is that the core library is distributed together with the Rust compiler as a _precompiled_ library. So it is only valid for the host triple, which is `x86_64-unknown-linux-gnu` in our case. If we want to compile code for other targets, we need to recompile `core` for these targets first.
|
The problem is that the core library is distributed together with the Rust compiler as a _precompiled_ library. So it is only valid for the host triple, which is `x86_64-unknown-linux-gnu` in our case. If we want to compile code for other targets, we need to recompile `core` for these targets first.
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
7
blog/content/edition-1/extra/naked-exceptions/_index.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
+++
|
||||||
|
title = "Handling Exceptions using naked Functions"
|
||||||
|
sort_by = "weight"
|
||||||
|
template = "edition-1/handling-exceptions-with-naked-fns.html"
|
||||||
|
insert_anchor_links = "left"
|
||||||
|
aliases = ["first-edition/extra/naked-exceptions/index.html"]
|
||||||
|
+++
|
||||||
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
@@ -4,7 +4,7 @@ weight = 1
|
|||||||
path = "multiboot-kernel"
|
path = "multiboot-kernel"
|
||||||
aliases = ["multiboot-kernel.html", "/2015/08/18/multiboot-kernel/", "/rust-os/multiboot-kernel.html"]
|
aliases = ["multiboot-kernel.html", "/2015/08/18/multiboot-kernel/", "/rust-os/multiboot-kernel.html"]
|
||||||
date = 2015-08-18
|
date = 2015-08-18
|
||||||
template = "first-edition/page.html"
|
template = "edition-1/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.
|
||||||
@@ -187,7 +187,7 @@ Idx Name Size VMA LMA File off Algn
|
|||||||
```
|
```
|
||||||
_Note_: The `ld` and `objdump` commands are platform specific. If you're _not_ working on x86_64 architecture, you will need to [cross compile binutils]. Then use `x86_64‑elf‑ld` and `x86_64‑elf‑objdump` instead of `ld` and `objdump`.
|
_Note_: The `ld` and `objdump` commands are platform specific. If you're _not_ working on x86_64 architecture, you will need to [cross compile binutils]. Then use `x86_64‑elf‑ld` and `x86_64‑elf‑objdump` instead of `ld` and `objdump`.
|
||||||
|
|
||||||
[cross compile binutils]: @/first-edition/extra/cross-compile-binutils.md
|
[cross compile binutils]: @/edition-1/extra/cross-compile-binutils.md
|
||||||
|
|
||||||
## Creating the ISO
|
## Creating the ISO
|
||||||
All PC BIOSes know how to boot from a CD-ROM, so we want to create a bootable CD-ROM image, containing our kernel and the GRUB bootloader's files, in a single file called an [ISO](https://en.wikipedia.org/wiki/ISO_image). Make the following directory structure and copy the `kernel.bin` to the right place:
|
All PC BIOSes know how to boot from a CD-ROM, so we want to create a bootable CD-ROM image, containing our kernel and the GRUB bootloader's files, in a single file called an [ISO](https://en.wikipedia.org/wiki/ISO_image). Make the following directory structure and copy the `kernel.bin` to the right place:
|
||||||
@@ -315,7 +315,7 @@ Now we can invoke `make` and all updated assembly files are compiled and linked.
|
|||||||
|
|
||||||
In the [next post] we will create a page table and do some CPU configuration to switch to the 64-bit [long mode].
|
In the [next post] we will create a page table and do some CPU configuration to switch to the 64-bit [long mode].
|
||||||
|
|
||||||
[next post]: @/first-edition/posts/02-entering-longmode/index.md
|
[next post]: @/edition-1/posts/02-entering-longmode/index.md
|
||||||
[long mode]: https://en.wikipedia.org/wiki/Long_mode
|
[long mode]: https://en.wikipedia.org/wiki/Long_mode
|
||||||
|
|
||||||
## Footnotes
|
## Footnotes
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
@@ -4,14 +4,14 @@ weight = 2
|
|||||||
path = "entering-longmode"
|
path = "entering-longmode"
|
||||||
aliases = ["entering-longmode.html", "/2015/08/25/entering-longmode/", "/rust-os/entering-longmode.html"]
|
aliases = ["entering-longmode.html", "/2015/08/25/entering-longmode/", "/rust-os/entering-longmode.html"]
|
||||||
date = 2015-08-25
|
date = 2015-08-25
|
||||||
template = "first-edition/page.html"
|
template = "edition-1/page.html"
|
||||||
[extra]
|
[extra]
|
||||||
updated = "2015-10-29"
|
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]: @/edition-1/posts/01-multiboot-kernel/index.md
|
||||||
[Rust]: https://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
|
||||||
@@ -39,7 +39,7 @@ error:
|
|||||||
At address `0xb8000` begins the so-called [VGA text buffer]. It's an array of screen characters that are displayed by the graphics card. A [future post] will cover the VGA buffer in detail and create a Rust interface to it. But for now, manual bit-fiddling is the easiest option.
|
At address `0xb8000` begins the so-called [VGA text buffer]. It's an array of screen characters that are displayed by the graphics card. A [future post] will cover the VGA buffer in detail and create a Rust interface to it. But for now, manual bit-fiddling is the easiest option.
|
||||||
|
|
||||||
[VGA text buffer]: https://en.wikipedia.org/wiki/VGA-compatible_text_mode
|
[VGA text buffer]: https://en.wikipedia.org/wiki/VGA-compatible_text_mode
|
||||||
[future post]: @/first-edition/posts/04-printing-to-screen/index.md
|
[future post]: @/edition-1/posts/04-printing-to-screen/index.md
|
||||||
|
|
||||||
A screen character consists of a 8 bit color code and a 8 bit [ASCII] character. We used the color code `4f` for all characters, which means white text on red background. `0x52` is an ASCII `R`, `0x45` is an `E`, `0x3a` is a `:`, and `0x20` is a space. The second space is overwritten by the given ASCII byte. Finally the CPU is stopped with the `hlt` instruction.
|
A screen character consists of a 8 bit color code and a 8 bit [ASCII] character. We used the color code `4f` for all characters, which means white text on red background. `0x52` is an ASCII `R`, `0x45` is an `E`, `0x3a` is a `:`, and `0x20` is a space. The second space is overwritten by the given ASCII byte. Finally the CPU is stopped with the `hlt` instruction.
|
||||||
|
|
||||||
@@ -492,8 +492,8 @@ _Congratulations_! You have successfully wrestled through this CPU configuration
|
|||||||
#### One Last Thing
|
#### One Last Thing
|
||||||
Above, we reloaded the code segment register `cs` with the new GDT offset. However, the data segment registers `ss`, `ds`, `es`, `fs`, and `gs` still contain the data segment offsets of the old GDT. This isn't necessarily bad, since they're ignored by almost all instructions in 64-bit mode. However, there are a few instructions that expect a valid data segment descriptor _or the null descriptor_ in those registers. An example is the the [iretq] instruction that we'll need in the [_Returning from Exceptions_] post.
|
Above, we reloaded the code segment register `cs` with the new GDT offset. However, the data segment registers `ss`, `ds`, `es`, `fs`, and `gs` still contain the data segment offsets of the old GDT. This isn't necessarily bad, since they're ignored by almost all instructions in 64-bit mode. However, there are a few instructions that expect a valid data segment descriptor _or the null descriptor_ in those registers. An example is the the [iretq] instruction that we'll need in the [_Returning from Exceptions_] post.
|
||||||
|
|
||||||
[iretq]: @/first-edition/extra/naked-exceptions/03-returning-from-exceptions/index.md#the-iretq-instruction
|
[iretq]: @/edition-1/extra/naked-exceptions/03-returning-from-exceptions/index.md#the-iretq-instruction
|
||||||
[_Returning from Exceptions_]: @/first-edition/extra/naked-exceptions/03-returning-from-exceptions/index.md
|
[_Returning from Exceptions_]: @/edition-1/extra/naked-exceptions/03-returning-from-exceptions/index.md
|
||||||
|
|
||||||
To avoid future problems, we reload all data segment registers with null:
|
To avoid future problems, we reload all data segment registers with null:
|
||||||
|
|
||||||
@@ -515,7 +515,7 @@ long_mode_start:
|
|||||||
It's time to finally leave assembly behind and switch to [Rust]. Rust is a systems language without garbage collections that guarantees memory safety. Through a real type system and many abstractions it feels like a high-level language but can still be low-level enough for OS development. The [next post] describes the Rust setup.
|
It's time to finally leave assembly behind and switch to [Rust]. Rust is a systems language without garbage collections that guarantees memory safety. Through a real type system and many abstractions it feels like a high-level language but can still be low-level enough for OS development. The [next post] describes the Rust setup.
|
||||||
|
|
||||||
[Rust]: https://www.rust-lang.org/
|
[Rust]: https://www.rust-lang.org/
|
||||||
[next post]: @/first-edition/posts/03-set-up-rust/index.md
|
[next post]: @/edition-1/posts/03-set-up-rust/index.md
|
||||||
|
|
||||||
## Footnotes
|
## Footnotes
|
||||||
[^hardware_lookup]: In the x86 architecture, the page tables are _hardware walked_, so the CPU will look at the table on its own when it needs a translation. Other architectures, for example MIPS, just throw an exception and let the OS translate the virtual address.
|
[^hardware_lookup]: In the x86 architecture, the page tables are _hardware walked_, so the CPU will look at the table on its own when it needs a translation. Other architectures, for example MIPS, just throw an exception and let the OS translate the virtual address.
|
||||||
@@ -4,15 +4,15 @@ weight = 3
|
|||||||
path = "set-up-rust"
|
path = "set-up-rust"
|
||||||
aliases = ["set-up-rust.html", "setup-rust.html", "/2015/09/02/setup-rust/", "/rust-os/setup-rust.html"]
|
aliases = ["set-up-rust.html", "setup-rust.html", "/2015/09/02/setup-rust/", "/rust-os/setup-rust.html"]
|
||||||
date = 2015-09-02
|
date = 2015-09-02
|
||||||
template = "first-edition/page.html"
|
template = "edition-1/page.html"
|
||||||
[extra]
|
[extra]
|
||||||
updated = "2017-04-12"
|
updated = "2017-04-12"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In the previous posts we created a [minimal Multiboot kernel][multiboot post] and [switched to Long Mode][long mode post]. Now we can finally switch to [Rust] code. Rust is a high-level language without runtime. It allows us to not link the standard library and write bare metal code. Unfortunately the setup is not quite hassle-free yet.
|
In the previous posts we created a [minimal Multiboot kernel][multiboot post] and [switched to Long Mode][long mode post]. Now we can finally switch to [Rust] code. Rust is a high-level language without runtime. It allows us to not link the standard library and write bare metal code. Unfortunately the setup is not quite hassle-free yet.
|
||||||
|
|
||||||
[multiboot post]: @/first-edition/posts/01-multiboot-kernel/index.md
|
[multiboot post]: @/edition-1/posts/01-multiboot-kernel/index.md
|
||||||
[long mode post]: @/first-edition/posts/02-entering-longmode/index.md
|
[long mode post]: @/edition-1/posts/02-entering-longmode/index.md
|
||||||
[Rust]: https://www.rust-lang.org/
|
[Rust]: https://www.rust-lang.org/
|
||||||
|
|
||||||
<!-- more -->
|
<!-- more -->
|
||||||
@@ -92,7 +92,7 @@ Let's define some properties of our target system:
|
|||||||
- **No SSE**: Our target might not have [SSE] support. Even if it does, we probably don't want to use SSE instructions in our kernel, because it makes interrupt handling much slower. We will explain this in detail in the [“Handling Exceptions”] post.
|
- **No SSE**: Our target might not have [SSE] support. Even if it does, we probably don't want to use SSE instructions in our kernel, because it makes interrupt handling much slower. We will explain this in detail in the [“Handling Exceptions”] post.
|
||||||
- **No hardware floats**: The `x86_64` architecture uses SSE instructions for floating point operations, which we don't want to use (see the previous point). So we also need to avoid hardware floating point operations in our kernel. Instead, we will use _soft floats_, which are basically software functions that emulate floating point operations using normal integers.
|
- **No hardware floats**: The `x86_64` architecture uses SSE instructions for floating point operations, which we don't want to use (see the previous point). So we also need to avoid hardware floating point operations in our kernel. Instead, we will use _soft floats_, which are basically software functions that emulate floating point operations using normal integers.
|
||||||
|
|
||||||
[“Handling Exceptions”]: @/first-edition/posts/09-handling-exceptions/index.md
|
[“Handling Exceptions”]: @/edition-1/posts/09-handling-exceptions/index.md
|
||||||
|
|
||||||
### Target Specifications
|
### Target Specifications
|
||||||
Rust allows us to define [custom targets] through a JSON configuration file. A minimal target specification equal to `x86_64-unknown-linux-gnu` (the default 64-bit Linux target) looks like this:
|
Rust allows us to define [custom targets] through a JSON configuration file. A minimal target specification equal to `x86_64-unknown-linux-gnu` (the default 64-bit Linux target) looks like this:
|
||||||
@@ -486,10 +486,10 @@ Some notes:
|
|||||||
### Stack Overflows
|
### Stack Overflows
|
||||||
Since we still use the small 64 byte [stack from the last post], we must be careful not to [overflow] it. Normally, Rust tries to avoid stack overflows through _guard pages_: The page below the stack isn't mapped and such a stack overflow triggers a page fault (instead of silently overwriting random memory). But we can't unmap the page below our stack right now since we currently use only a single big page. Fortunately the stack is located just above the page tables. So some important page table entry would probably get overwritten on stack overflow and then a page fault occurs, too.
|
Since we still use the small 64 byte [stack from the last post], we must be careful not to [overflow] it. Normally, Rust tries to avoid stack overflows through _guard pages_: The page below the stack isn't mapped and such a stack overflow triggers a page fault (instead of silently overwriting random memory). But we can't unmap the page below our stack right now since we currently use only a single big page. Fortunately the stack is located just above the page tables. So some important page table entry would probably get overwritten on stack overflow and then a page fault occurs, too.
|
||||||
|
|
||||||
[stack from the last post]: @/first-edition/posts/02-entering-longmode/index.md#creating-a-stack
|
[stack from the last post]: @/edition-1/posts/02-entering-longmode/index.md#creating-a-stack
|
||||||
[overflow]: https://en.wikipedia.org/wiki/Stack_overflow
|
[overflow]: https://en.wikipedia.org/wiki/Stack_overflow
|
||||||
|
|
||||||
## What's next?
|
## What's next?
|
||||||
Until now we write magic bits to some memory location when we want to print something to screen. In the [next post] we create a abstraction for the VGA text buffer that allows us to print strings in different colors and provides a simple interface.
|
Until now we write magic bits to some memory location when we want to print something to screen. In the [next post] we create a abstraction for the VGA text buffer that allows us to print strings in different colors and provides a simple interface.
|
||||||
|
|
||||||
[next post]: @/first-edition/posts/04-printing-to-screen/index.md
|
[next post]: @/edition-1/posts/04-printing-to-screen/index.md
|
||||||
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
@@ -4,14 +4,14 @@ weight = 4
|
|||||||
path = "printing-to-screen"
|
path = "printing-to-screen"
|
||||||
aliases = ["printing-to-screen.html", "/2015/10/23/printing-to-screen/", "/rust-os/printing-to-screen.html"]
|
aliases = ["printing-to-screen.html", "/2015/10/23/printing-to-screen/", "/rust-os/printing-to-screen.html"]
|
||||||
date = 2015-10-23
|
date = 2015-10-23
|
||||||
template = "first-edition/page.html"
|
template = "edition-1/page.html"
|
||||||
[extra]
|
[extra]
|
||||||
updated = "2016-10-31"
|
updated = "2016-10-31"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In the [previous post] we switched from assembly to [Rust], a systems programming language that provides great safety. But so far we are using unsafe features like [raw pointers] whenever we want to print to screen. In this post we will create a Rust module that provides a safe and easy-to-use interface for the VGA text buffer. It will support Rust's [formatting macros], too.
|
In the [previous post] we switched from assembly to [Rust], a systems programming language that provides great safety. But so far we are using unsafe features like [raw pointers] whenever we want to print to screen. In this post we will create a Rust module that provides a safe and easy-to-use interface for the VGA text buffer. It will support Rust's [formatting macros], too.
|
||||||
|
|
||||||
[previous post]: @/first-edition/posts/03-set-up-rust/index.md
|
[previous post]: @/edition-1/posts/03-set-up-rust/index.md
|
||||||
[Rust]: https://www.rust-lang.org/
|
[Rust]: https://www.rust-lang.org/
|
||||||
[raw pointers]: https://doc.rust-lang.org/book/raw-pointers.html
|
[raw pointers]: https://doc.rust-lang.org/book/raw-pointers.html
|
||||||
[formatting macros]: https://doc.rust-lang.org/std/fmt/#related-macros
|
[formatting macros]: https://doc.rust-lang.org/std/fmt/#related-macros
|
||||||
@@ -642,7 +642,7 @@ In the next posts we will map the kernel pages correctly so that accessing `0x0`
|
|||||||
|
|
||||||
The [next post] describes the Multiboot information structure and creates a frame allocator using the information about memory areas.
|
The [next post] describes the Multiboot information structure and creates a frame allocator using the information about memory areas.
|
||||||
|
|
||||||
[next post]: @/first-edition/posts/05-allocating-frames/index.md
|
[next post]: @/edition-1/posts/05-allocating-frames/index.md
|
||||||
|
|
||||||
## Other Rust OS Projects
|
## Other Rust OS Projects
|
||||||
Now that you know the very basics of OS development in Rust, you should also check out the following projects:
|
Now that you know the very basics of OS development in Rust, you should also check out the following projects:
|
||||||
@@ -658,7 +658,7 @@ _Note_: You need to [cross compile binutils] to build it (or you create some sym
|
|||||||
|
|
||||||
[Rust Bare-Bones Kernel]: https://github.com/thepowersgang/rust-barebones-kernel
|
[Rust Bare-Bones Kernel]: https://github.com/thepowersgang/rust-barebones-kernel
|
||||||
[higher half]: https://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]: @/edition-1/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
|
||||||
[Redox]: https://github.com/redox-os/redox
|
[Redox]: https://github.com/redox-os/redox
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
@@ -4,7 +4,7 @@ weight = 5
|
|||||||
path = "allocating-frames"
|
path = "allocating-frames"
|
||||||
aliases = ["allocating-frames.html"]
|
aliases = ["allocating-frames.html"]
|
||||||
date = 2015-11-15
|
date = 2015-11-15
|
||||||
template = "first-edition/page.html"
|
template = "edition-1/page.html"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In this post we create an allocator that provides free physical frames for a future paging module. To get the required information about available and used memory we use the Multiboot information structure. Additionally, we improve the `panic` handler to print the corresponding message and source line.
|
In this post we create an allocator that provides free physical frames for a future paging module. To get the required information about available and used memory we use the Multiboot information structure. Additionally, we improve the `panic` handler to print the corresponding message and source line.
|
||||||
@@ -430,10 +430,10 @@ Now we have a working frame allocator. It is a bit rudimentary and cannot free f
|
|||||||
## What's next?
|
## What's next?
|
||||||
The [next post] will be about paging again. We will use the frame allocator to create a safe module that allows us to switch page tables and map pages. Then we will use this module and the information from the Elf-sections tag to remap the kernel correctly.
|
The [next post] will be about paging again. We will use the frame allocator to create a safe module that allows us to switch page tables and map pages. Then we will use this module and the information from the Elf-sections tag to remap the kernel correctly.
|
||||||
|
|
||||||
[next post]: @/first-edition/posts/06-page-tables/index.md
|
[next post]: @/edition-1/posts/06-page-tables/index.md
|
||||||
|
|
||||||
## Recommended Posts
|
## Recommended Posts
|
||||||
Eric Kidd started the [Bare Metal Rust] series last week. Like this post, it builds upon the code from [Printing to Screen], but tries to support keyboard input instead of wrestling through memory management details.
|
Eric Kidd started the [Bare Metal Rust] series last week. Like this post, it builds upon the code from [Printing to Screen], but tries to support keyboard input instead of wrestling through memory management details.
|
||||||
|
|
||||||
[Bare Metal Rust]: http://www.randomhacks.net/bare-metal-rust/
|
[Bare Metal Rust]: http://www.randomhacks.net/bare-metal-rust/
|
||||||
[Printing to Screen]: @/first-edition/posts/04-printing-to-screen/index.md
|
[Printing to Screen]: @/edition-1/posts/04-printing-to-screen/index.md
|
||||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
@@ -4,7 +4,7 @@ weight = 6
|
|||||||
path = "page-tables"
|
path = "page-tables"
|
||||||
aliases = ["page-tables.html", "modifying-page-tables.html"]
|
aliases = ["page-tables.html", "modifying-page-tables.html"]
|
||||||
date = 2015-12-09
|
date = 2015-12-09
|
||||||
template = "first-edition/page.html"
|
template = "edition-1/page.html"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In this post we will create a paging module, which allows us to access and modify the 4-level page table. We will explore recursive page table mapping and use some Rust features to make it safe. Finally we will create functions to translate virtual addresses and to map and unmap pages.
|
In this post we will create a paging module, which allows us to access and modify the 4-level page table. We will explore recursive page table mapping and use some Rust features to make it safe. Finally we will create functions to translate virtual addresses and to map and unmap pages.
|
||||||
@@ -52,7 +52,7 @@ pub struct Page {
|
|||||||
```
|
```
|
||||||
We import the `PAGE_SIZE` and define a constant for the number of entries per table. To make future function signatures more expressive, we can use the type aliases `PhysicalAddress` and `VirtualAddress`. The `Page` struct is similar to the `Frame` struct in the [previous post], but represents a virtual page instead of a physical frame.
|
We import the `PAGE_SIZE` and define a constant for the number of entries per table. To make future function signatures more expressive, we can use the type aliases `PhysicalAddress` and `VirtualAddress`. The `Page` struct is similar to the `Frame` struct in the [previous post], but represents a virtual page instead of a physical frame.
|
||||||
|
|
||||||
[previous post]: @/first-edition/posts/05-allocating-frames/index.md#a-memory-module
|
[previous post]: @/edition-1/posts/05-allocating-frames/index.md#a-memory-module
|
||||||
|
|
||||||
### Page Table Entries
|
### Page Table Entries
|
||||||
To model page table entries, we create a new `entry` submodule:
|
To model page table entries, we create a new `entry` submodule:
|
||||||
@@ -652,7 +652,7 @@ pub struct ActivePageTable {
|
|||||||
```
|
```
|
||||||
We can't store the `Table<Level4>` directly because it needs to be at a special memory location (like the [VGA text buffer]). We could use a raw pointer or `&mut` instead of [Unique], but Unique indicates ownership better.
|
We can't store the `Table<Level4>` directly because it needs to be at a special memory location (like the [VGA text buffer]). We could use a raw pointer or `&mut` instead of [Unique], but Unique indicates ownership better.
|
||||||
|
|
||||||
[VGA text buffer]: @/first-edition/posts/04-printing-to-screen/index.md#the-text-buffer
|
[VGA text buffer]: @/edition-1/posts/04-printing-to-screen/index.md#the-text-buffer
|
||||||
[Unique]: https://doc.rust-lang.org/1.10.0/core/ptr/struct.Unique.html
|
[Unique]: https://doc.rust-lang.org/1.10.0/core/ptr/struct.Unique.html
|
||||||
|
|
||||||
Because the `ActivePageTable` owns the unique recursive mapped P4 table, there must be only one `ActivePageTable` instance. Thus we make the constructor function unsafe:
|
Because the `ActivePageTable` owns the unique recursive mapped P4 table, there must be only one `ActivePageTable` instance. Thus we make the constructor function unsafe:
|
||||||
@@ -881,7 +881,7 @@ This post has become pretty long. So let's summarize what we've done:
|
|||||||
## What's next?
|
## What's next?
|
||||||
In the [next post] we will extend this module and add a function to modify inactive page tables. Through that function, we will create a new page table hierarchy that maps the kernel correctly using 4KiB pages. Then we will switch to the new table to get a safer kernel environment.
|
In the [next post] we will extend this module and add a function to modify inactive page tables. Through that function, we will create a new page table hierarchy that maps the kernel correctly using 4KiB pages. Then we will switch to the new table to get a safer kernel environment.
|
||||||
|
|
||||||
[next post]: @/first-edition/posts/07-remap-the-kernel/index.md
|
[next post]: @/edition-1/posts/07-remap-the-kernel/index.md
|
||||||
|
|
||||||
Afterwards, we will use this paging module to build a heap allocator. This will allow us to use allocation and collection types such as `Box` and `Vec`.
|
Afterwards, we will use this paging module to build a heap allocator. This will allow us to use allocation and collection types such as `Box` and `Vec`.
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@@ -4,7 +4,7 @@ weight = 7
|
|||||||
path = "remap-the-kernel"
|
path = "remap-the-kernel"
|
||||||
aliases = ["remap-the-kernel.html"]
|
aliases = ["remap-the-kernel.html"]
|
||||||
date = 2016-01-01
|
date = 2016-01-01
|
||||||
template = "first-edition/page.html"
|
template = "edition-1/page.html"
|
||||||
[extra]
|
[extra]
|
||||||
updated = "2016-03-06"
|
updated = "2016-03-06"
|
||||||
+++
|
+++
|
||||||
@@ -21,18 +21,18 @@ As always, you can find the source code on [GitHub]. Don't hesitate to file issu
|
|||||||
|
|
||||||
In the [previous post], we had a strange bug in the `unmap` function. Its reason was a silent stack overflow, which corrupted the page tables. Fortunately, our kernel stack is right above the page tables so that we noticed the overflow relatively quickly. This won't be the case when we add threads with new stacks in the future. Then a silent stack overflow could overwrite some data without us noticing. But eventually some completely unrelated function fails because a variable changed its value.
|
In the [previous post], we had a strange bug in the `unmap` function. Its reason was a silent stack overflow, which corrupted the page tables. Fortunately, our kernel stack is right above the page tables so that we noticed the overflow relatively quickly. This won't be the case when we add threads with new stacks in the future. Then a silent stack overflow could overwrite some data without us noticing. But eventually some completely unrelated function fails because a variable changed its value.
|
||||||
|
|
||||||
[previous post]: @/first-edition/posts/06-page-tables/index.md
|
[previous post]: @/edition-1/posts/06-page-tables/index.md
|
||||||
|
|
||||||
As you can imagine, these kinds of bugs are horrendous to debug. For that reason we will create a new hierarchical page table in this post, which has _guard page_ below the stack. A guard page is basically an unmapped page that causes a page fault when accessed. Thus we can catch stack overflows right when they happen.
|
As you can imagine, these kinds of bugs are horrendous to debug. For that reason we will create a new hierarchical page table in this post, which has _guard page_ below the stack. A guard page is basically an unmapped page that causes a page fault when accessed. Thus we can catch stack overflows right when they happen.
|
||||||
|
|
||||||
Also, we will use the [information about kernel sections] to map the various sections individually instead of blindly mapping the first gigabyte. To improve safety even further, we will set the correct page table flags for the various sections. Thus it won't be possible to modify the contents of `.text` or to execute code from `.data` anymore.
|
Also, we will use the [information about kernel sections] to map the various sections individually instead of blindly mapping the first gigabyte. To improve safety even further, we will set the correct page table flags for the various sections. Thus it won't be possible to modify the contents of `.text` or to execute code from `.data` anymore.
|
||||||
|
|
||||||
[information about kernel sections]: @/first-edition/posts/05-allocating-frames/index.md#kernel-elf-sections
|
[information about kernel sections]: @/edition-1/posts/05-allocating-frames/index.md#kernel-elf-sections
|
||||||
|
|
||||||
## Preparation
|
## Preparation
|
||||||
There are many things that can go wrong when we switch to a new table. Therefore it's a good idea to [set up a debugger][set up gdb]. You should not need it when you follow this post, but it's good to know how to debug a problem when it occurs[^fn-debug-notes].
|
There are many things that can go wrong when we switch to a new table. Therefore it's a good idea to [set up a debugger][set up gdb]. You should not need it when you follow this post, but it's good to know how to debug a problem when it occurs[^fn-debug-notes].
|
||||||
|
|
||||||
[set up gdb]: @/first-edition/extra/set-up-gdb/index.md
|
[set up gdb]: @/edition-1/extra/set-up-gdb/index.md
|
||||||
|
|
||||||
We also update the `Page` and `Frame` types to make our lives easier. The `Page` struct gets some derived traits:
|
We also update the `Page` and `Frame` types to make our lives easier. The `Page` struct gets some derived traits:
|
||||||
|
|
||||||
@@ -281,7 +281,7 @@ pub fn map_table_frame(&mut self,
|
|||||||
```
|
```
|
||||||
This function interprets the given frame as a page table frame and returns a `Table` reference. We return a table of level 1 because it [forbids calling the `next_table` methods][some clever solution]. Calling `next_table` must not be possible since it's not a page of the recursive mapping. To be able to return a `Table<Level1>`, we need to make the `Level1` enum in `memory/paging/table.rs` public.
|
This function interprets the given frame as a page table frame and returns a `Table` reference. We return a table of level 1 because it [forbids calling the `next_table` methods][some clever solution]. Calling `next_table` must not be possible since it's not a page of the recursive mapping. To be able to return a `Table<Level1>`, we need to make the `Level1` enum in `memory/paging/table.rs` public.
|
||||||
|
|
||||||
[some clever solution]: @/first-edition/posts/06-page-tables/index.md#some-clever-solution
|
[some clever solution]: @/edition-1/posts/06-page-tables/index.md#some-clever-solution
|
||||||
|
|
||||||
|
|
||||||
The `unsafe` block is safe since the `VirtualAddress` returned by the `map` function is always valid and the type cast just reinterprets the frame's content.
|
The `unsafe` block is safe since the `VirtualAddress` returned by the `map` function is always valid and the type cast just reinterprets the frame's content.
|
||||||
@@ -554,7 +554,7 @@ First, we create a temporary page at page number `0xcafebabe`. We could use `0xd
|
|||||||
|
|
||||||
Then we use the `with` function to temporary change the recursive mapping and execute the closure as if the `new_table` were active. This allows us to map the sections in the new table without changing the active mapping. To get the kernel sections, we use the [Multiboot information structure].
|
Then we use the `with` function to temporary change the recursive mapping and execute the closure as if the `new_table` were active. This allows us to map the sections in the new table without changing the active mapping. To get the kernel sections, we use the [Multiboot information structure].
|
||||||
|
|
||||||
[Multiboot information structure]: @/first-edition/posts/05-allocating-frames/index.md#the-multiboot-information-structure
|
[Multiboot information structure]: @/edition-1/posts/05-allocating-frames/index.md#the-multiboot-information-structure
|
||||||
|
|
||||||
Let's resolve the above `TODO` by identity mapping the sections:
|
Let's resolve the above `TODO` by identity mapping the sections:
|
||||||
|
|
||||||
@@ -829,8 +829,8 @@ These lines are the important ones. We can read many useful information from the
|
|||||||
[osdev exception overview]: https://wiki.osdev.org/Exceptions
|
[osdev exception overview]: https://wiki.osdev.org/Exceptions
|
||||||
[page fault]: https://wiki.osdev.org/Exceptions#Page_Fault
|
[page fault]: https://wiki.osdev.org/Exceptions#Page_Fault
|
||||||
[page fault error code]: https://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]: @/edition-1/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]: @/edition-1/posts/04-printing-to-screen/index.md#the-vga-text-buffer
|
||||||
|
|
||||||
So let's find out which function caused the exception:
|
So let's find out which function caused the exception:
|
||||||
|
|
||||||
@@ -882,7 +882,7 @@ Now we should see the `NEW TABLE!!!` message (and also the `It did not crash!` l
|
|||||||
### Fixing the Frame Allocator
|
### Fixing the Frame Allocator
|
||||||
The same problem as above occurs when we try to use our [AreaFrameAllocator] again. Try to add the following to `rust_main` after switching to the new table:
|
The same problem as above occurs when we try to use our [AreaFrameAllocator] again. Try to add the following to `rust_main` after switching to the new table:
|
||||||
|
|
||||||
[AreaFrameAllocator]: @/first-edition/posts/05-allocating-frames/index.md#the-allocator
|
[AreaFrameAllocator]: @/edition-1/posts/05-allocating-frames/index.md#the-allocator
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// in src/lib.rs
|
// in src/lib.rs
|
||||||
@@ -1028,7 +1028,7 @@ The final step is to create a guard page for our kernel stack.
|
|||||||
|
|
||||||
The decision to place the kernel stack right above the page tables was already useful to detect a silent stack overflow in the [previous post][silent stack overflow]. Now we profit from it again. Let's look at our assembly `.bss` section again to understand why:
|
The decision to place the kernel stack right above the page tables was already useful to detect a silent stack overflow in the [previous post][silent stack overflow]. Now we profit from it again. Let's look at our assembly `.bss` section again to understand why:
|
||||||
|
|
||||||
[silent stack overflow]: @/first-edition/posts/06-page-tables/index.md
|
[silent stack overflow]: @/edition-1/posts/06-page-tables/index.md
|
||||||
|
|
||||||
```nasm
|
```nasm
|
||||||
; in src/arch/x86_64/boot.asm
|
; in src/arch/x86_64/boot.asm
|
||||||
@@ -1089,7 +1089,7 @@ Unfortunately stack probes require compiler support. They already work on Window
|
|||||||
## What's next?
|
## What's next?
|
||||||
Now that we have a (mostly) safe kernel stack and a working page table module, we can add a virtual memory allocator. The [next post] will explore Rust's allocator API and create a very basic allocator. At the end of that post, we will be able to use Rust's allocation and collections types such as [Box], [Vec], or even [BTreeMap].
|
Now that we have a (mostly) safe kernel stack and a working page table module, we can add a virtual memory allocator. The [next post] will explore Rust's allocator API and create a very basic allocator. At the end of that post, we will be able to use Rust's allocation and collections types such as [Box], [Vec], or even [BTreeMap].
|
||||||
|
|
||||||
[next post]: @/first-edition/posts/08-kernel-heap/index.md
|
[next post]: @/edition-1/posts/08-kernel-heap/index.md
|
||||||
[Box]: https://doc.rust-lang.org/nightly/alloc/boxed/struct.Box.html
|
[Box]: https://doc.rust-lang.org/nightly/alloc/boxed/struct.Box.html
|
||||||
[Vec]: https://doc.rust-lang.org/1.10.0/collections/vec/struct.Vec.html
|
[Vec]: https://doc.rust-lang.org/1.10.0/collections/vec/struct.Vec.html
|
||||||
[BTreeMap]: https://doc.rust-lang.org/1.10.0/collections/btree_map/struct.BTreeMap.html
|
[BTreeMap]: https://doc.rust-lang.org/1.10.0/collections/btree_map/struct.BTreeMap.html
|
||||||
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
@@ -4,15 +4,15 @@ weight = 8
|
|||||||
path = "kernel-heap"
|
path = "kernel-heap"
|
||||||
aliases = ["kernel-heap.html"]
|
aliases = ["kernel-heap.html"]
|
||||||
date = 2016-04-11
|
date = 2016-04-11
|
||||||
template = "first-edition/page.html"
|
template = "edition-1/page.html"
|
||||||
[extra]
|
[extra]
|
||||||
updated = "2017-11-19"
|
updated = "2017-11-19"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In the previous posts we created a [frame allocator] and a [page table module]. Now we are ready to create a kernel heap and a memory allocator. Thus, we will unlock `Box`, `Vec`, `BTreeMap`, and the rest of the [alloc] crate.
|
In the previous posts we created a [frame allocator] and a [page table module]. Now we are ready to create a kernel heap and a memory allocator. Thus, we will unlock `Box`, `Vec`, `BTreeMap`, and the rest of the [alloc] crate.
|
||||||
|
|
||||||
[frame allocator]: @/first-edition/posts/05-allocating-frames/index.md
|
[frame allocator]: @/edition-1/posts/05-allocating-frames/index.md
|
||||||
[page table module]: @/first-edition/posts/06-page-tables/index.md
|
[page table module]: @/edition-1/posts/06-page-tables/index.md
|
||||||
[alloc]: https://doc.rust-lang.org/nightly/alloc/index.html
|
[alloc]: https://doc.rust-lang.org/nightly/alloc/index.html
|
||||||
|
|
||||||
<!-- more -->
|
<!-- more -->
|
||||||
@@ -333,7 +333,7 @@ let heap_test = Box::new(42);
|
|||||||
|
|
||||||
When we run it, a triple fault occurs and causes permanent rebooting. Let's try debug it using QEMU and objdump as described [in the previous post][qemu debugging]:
|
When we run it, a triple fault occurs and causes permanent rebooting. Let's try debug it using QEMU and objdump as described [in the previous post][qemu debugging]:
|
||||||
|
|
||||||
[qemu debugging]: @/first-edition/posts/07-remap-the-kernel/index.md#debugging
|
[qemu debugging]: @/edition-1/posts/07-remap-the-kernel/index.md#debugging
|
||||||
|
|
||||||
```
|
```
|
||||||
> qemu-system-x86_64 -d int -no-reboot -cdrom build/os-x86_64.iso
|
> qemu-system-x86_64 -d int -no-reboot -cdrom build/os-x86_64.iso
|
||||||
@@ -461,8 +461,8 @@ That's it. Now our `memory::init` function can only be called once. The macro wo
|
|||||||
### Mapping the Heap
|
### Mapping the Heap
|
||||||
Now we're ready to map the heap pages. In order to do it, we need access to the `ActivePageTable` or `Mapper` instance (see the [page table] and [kernel remapping] posts). For that we return it from the `paging::remap_the_kernel` function:
|
Now we're ready to map the heap pages. In order to do it, we need access to the `ActivePageTable` or `Mapper` instance (see the [page table] and [kernel remapping] posts). For that we return it from the `paging::remap_the_kernel` function:
|
||||||
|
|
||||||
[page table]: @/first-edition/posts/06-page-tables/index.md
|
[page table]: @/edition-1/posts/06-page-tables/index.md
|
||||||
[kernel remapping]: @/first-edition/posts/07-remap-the-kernel/index.md
|
[kernel remapping]: @/edition-1/posts/07-remap-the-kernel/index.md
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// in src/memory/paging/mod.rs
|
// in src/memory/paging/mod.rs
|
||||||
@@ -737,4 +737,4 @@ Now we're able to use heap storage in our kernel without leaking memory. This al
|
|||||||
## What's next?
|
## What's next?
|
||||||
This post concludes the section about memory management for now. We will revisit this topic eventually, but now it's time to explore other topics. The upcoming posts will be about CPU exceptions and interrupts. We will catch all page, double, and triple faults and create a driver to read keyboard input. The [next post] starts by setting up a so-called _Interrupt Descriptor Table_.
|
This post concludes the section about memory management for now. We will revisit this topic eventually, but now it's time to explore other topics. The upcoming posts will be about CPU exceptions and interrupts. We will catch all page, double, and triple faults and create a driver to read keyboard input. The [next post] starts by setting up a so-called _Interrupt Descriptor Table_.
|
||||||
|
|
||||||
[next post]: @/first-edition/posts/09-handling-exceptions/index.md
|
[next post]: @/edition-1/posts/09-handling-exceptions/index.md
|
||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
@@ -4,7 +4,7 @@ weight = 9
|
|||||||
path = "handling-exceptions"
|
path = "handling-exceptions"
|
||||||
aliases = ["handling-exceptions.html"]
|
aliases = ["handling-exceptions.html"]
|
||||||
date = 2017-03-26
|
date = 2017-03-26
|
||||||
template = "first-edition/page.html"
|
template = "edition-1/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.
|
||||||
@@ -229,7 +229,7 @@ The breakpoint exception is commonly used in debuggers: When the user sets a bre
|
|||||||
|
|
||||||
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.
|
||||||
|
|
||||||
[set the page table flags]: @/first-edition/posts/07-remap-the-kernel/index.md#using-the-correct-flags
|
[set the page table flags]: @/edition-1/posts/07-remap-the-kernel/index.md#using-the-correct-flags
|
||||||
|
|
||||||
So let's create a simple `breakpoint_handler` function and add it to our IDT:
|
So let's create a simple `breakpoint_handler` function and add it to our IDT:
|
||||||
|
|
||||||
@@ -461,7 +461,7 @@ The documentation of the [`Idt`] struct and the [OSDev Wiki][osdev wiki exceptio
|
|||||||
## 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.
|
||||||
|
|
||||||
[“Handling Exceptions with Naked Functions”]: @/first-edition/extra/naked-exceptions/_index.md
|
[“Handling Exceptions with Naked Functions”]: @/edition-1/extra/naked-exceptions/_index.md
|
||||||
|
|
||||||
## 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].
|
||||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
@@ -4,7 +4,7 @@ weight = 10
|
|||||||
path = "double-faults"
|
path = "double-faults"
|
||||||
aliases = ["double-faults.html"]
|
aliases = ["double-faults.html"]
|
||||||
date = 2017-01-02
|
date = 2017-01-02
|
||||||
template = "first-edition/page.html"
|
template = "edition-1/page.html"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
In this post we explore double faults in detail. We also set up an _Interrupt Stack Table_ to catch double faults on a separate kernel stack. This way, we can completely prevent triple faults, even on kernel stack overflow.
|
In this post we explore double faults in detail. We also set up an _Interrupt Stack Table_ to catch double faults on a separate kernel stack. This way, we can completely prevent triple faults, even on kernel stack overflow.
|
||||||
@@ -20,7 +20,7 @@ As always, the complete source code is available on [GitHub]. Please file [issue
|
|||||||
## What is a Double Fault?
|
## What is a Double Fault?
|
||||||
In simplified terms, a double fault is a special exception that occurs when the CPU fails to invoke an exception handler. For example, it occurs when a page fault is triggered but there is no page fault handler registered in the [Interrupt Descriptor Table][IDT] (IDT). So it's kind of similar to catch-all blocks in programming languages with exceptions, e.g. `catch(...)` in C++ or `catch(Exception e)` in Java or C#.
|
In simplified terms, a double fault is a special exception that occurs when the CPU fails to invoke an exception handler. For example, it occurs when a page fault is triggered but there is no page fault handler registered in the [Interrupt Descriptor Table][IDT] (IDT). So it's kind of similar to catch-all blocks in programming languages with exceptions, e.g. `catch(...)` in C++ or `catch(Exception e)` in Java or C#.
|
||||||
|
|
||||||
[IDT]: @/first-edition/posts/09-handling-exceptions/index.md#the-interrupt-descriptor-table
|
[IDT]: @/edition-1/posts/09-handling-exceptions/index.md#the-interrupt-descriptor-table
|
||||||
|
|
||||||
A double fault behaves like a normal exception. It has the vector number `8` and we can define a normal handler function for it in the IDT. It is really important to provide a double fault handler, because if a double fault is unhandled a fatal _triple fault_ occurs. Triple faults can't be caught and most hardware reacts with a system reset.
|
A double fault behaves like a normal exception. It has the vector number `8` and we can define a normal handler function for it in the IDT. It is really important to provide a double fault handler, because if a double fault is unhandled a fatal _triple fault_ occurs. Triple faults can't be caught and most hardware reacts with a system reset.
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ For example, what happens if… :
|
|||||||
3. a divide-by-zero handler causes a breakpoint exception, but the breakpoint handler is swapped out?
|
3. a divide-by-zero handler causes a breakpoint exception, but the breakpoint handler is swapped out?
|
||||||
4. our kernel overflows its stack and the [guard page] is hit?
|
4. our kernel overflows its stack and the [guard page] is hit?
|
||||||
|
|
||||||
[guard page]: @/first-edition/posts/07-remap-the-kernel/index.md#creating-a-guard-page
|
[guard page]: @/edition-1/posts/07-remap-the-kernel/index.md#creating-a-guard-page
|
||||||
|
|
||||||
Fortunately, the AMD64 manual ([PDF][AMD64 manual]) has an exact definition (in Section 8.2.9). According to it, a “double fault exception _can_ occur when a second exception occurs during the handling of a prior (first) exception handler”. The _“can”_ is important: Only very specific combinations of exceptions lead to a double fault. These combinations are:
|
Fortunately, the AMD64 manual ([PDF][AMD64 manual]) has an exact definition (in Section 8.2.9). According to it, a “double fault exception _can_ occur when a second exception occurs during the handling of a prior (first) exception handler”. The _“can”_ is important: Only very specific combinations of exceptions lead to a double fault. These combinations are:
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ Let's look at the fourth question:
|
|||||||
|
|
||||||
When our kernel overflows its stack and hits the guard page, a _page fault_ occurs. The CPU looks up the page fault handler in the IDT and tries to push the [exception stack frame] onto the stack. However, our current stack pointer still points to the non-present guard page. Thus, a second page fault occurs, which causes a double fault (according to the above table).
|
When our kernel overflows its stack and hits the guard page, a _page fault_ occurs. The CPU looks up the page fault handler in the IDT and tries to push the [exception stack frame] onto the stack. However, our current stack pointer still points to the non-present guard page. Thus, a second page fault occurs, which causes a double fault (according to the above table).
|
||||||
|
|
||||||
[exception stack frame]: @/first-edition/posts/09-handling-exceptions/index.md#the-exception-stack-frame
|
[exception stack frame]: @/edition-1/posts/09-handling-exceptions/index.md#the-exception-stack-frame
|
||||||
|
|
||||||
So the CPU tries to call our _double fault handler_ now. However, on a double fault the CPU tries to push the exception stack frame, too. Our stack pointer still points to the guard page, so a _third_ page fault occurs, which causes a _triple fault_ and a system reboot. So our current double fault handler can't avoid a triple fault in this case.
|
So the CPU tries to call our _double fault handler_ now. However, on a double fault the CPU tries to push the exception stack frame, too. Our stack pointer still points to the guard page, so a _third_ page fault occurs, which causes a _triple fault_ and a system reboot. So our current double fault handler can't avoid a triple fault in this case.
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ struct InterruptStackTable {
|
|||||||
|
|
||||||
For each exception handler, we can choose a stack from the IST through the `options` field in the corresponding [IDT entry]. For example, we could use the first stack in the IST for our double fault handler. Then the CPU would automatically switch to this stack whenever a double fault occurs. This switch would happen before anything is pushed, so it would prevent the triple fault.
|
For each exception handler, we can choose a stack from the IST through the `options` field in the corresponding [IDT entry]. For example, we could use the first stack in the IST for our double fault handler. Then the CPU would automatically switch to this stack whenever a double fault occurs. This switch would happen before anything is pushed, so it would prevent the triple fault.
|
||||||
|
|
||||||
[IDT entry]: @/first-edition/posts/09-handling-exceptions/index.md#the-interrupt-descriptor-table
|
[IDT entry]: @/edition-1/posts/09-handling-exceptions/index.md#the-interrupt-descriptor-table
|
||||||
|
|
||||||
### Allocating a new Stack
|
### Allocating a new Stack
|
||||||
In order to fill an Interrupt Stack Table later, we need a way to allocate new stacks. Therefore we extend our `memory` module with a new `stack_allocator` submodule:
|
In order to fill an Interrupt Stack Table later, we need a way to allocate new stacks. Therefore we extend our `memory` module with a new `stack_allocator` submodule:
|
||||||
@@ -230,7 +230,7 @@ impl StackAllocator {
|
|||||||
```
|
```
|
||||||
We create a simple `StackAllocator` that allocates stacks from a given range of pages (`PageIter` is an Iterator over a range of pages; we introduced it [in the kernel heap post].).
|
We create a simple `StackAllocator` that allocates stacks from a given range of pages (`PageIter` is an Iterator over a range of pages; we introduced it [in the kernel heap post].).
|
||||||
|
|
||||||
[in the kernel heap post]: @/first-edition/posts/08-kernel-heap/index.md#mapping-the-heap
|
[in the kernel heap post]: @/edition-1/posts/08-kernel-heap/index.md#mapping-the-heap
|
||||||
|
|
||||||
We add a `alloc_stack` method that allocates a new stack:
|
We add a `alloc_stack` method that allocates a new stack:
|
||||||
|
|
||||||
@@ -285,8 +285,8 @@ impl StackAllocator {
|
|||||||
```
|
```
|
||||||
The method takes mutable references to the [ActivePageTable] and a [FrameAllocator], since it needs to map the new virtual stack pages to physical frames. We define that the stack size is a multiple of the page size.
|
The method takes mutable references to the [ActivePageTable] and a [FrameAllocator], since it needs to map the new virtual stack pages to physical frames. We define that the stack size is a multiple of the page size.
|
||||||
|
|
||||||
[ActivePageTable]: @/first-edition/posts/06-page-tables/index.md#page-table-ownership
|
[ActivePageTable]: @/edition-1/posts/06-page-tables/index.md#page-table-ownership
|
||||||
[FrameAllocator]: @/first-edition/posts/05-allocating-frames/index.md#a-frame-allocator
|
[FrameAllocator]: @/edition-1/posts/05-allocating-frames/index.md#a-frame-allocator
|
||||||
|
|
||||||
Instead of operating directly on `self.range`, we [clone] it and only write it back on success. This way, subsequent stack allocations can still succeed if there are pages left (e.g., a call with `size_in_pages = 3` can still succeed after a failed call with `size_in_pages = 100`).
|
Instead of operating directly on `self.range`, we [clone] it and only write it back on success. This way, subsequent stack allocations can still succeed if there are pages left (e.g., a call with `size_in_pages = 3` can still succeed after a failed call with `size_in_pages = 100`).
|
||||||
|
|
||||||
@@ -297,7 +297,7 @@ In order to be able to clone `PageIter`, we add a `#[derive(Clone)]` to its defi
|
|||||||
The actual allocation is straightforward: First, we choose the next page as [guard page]. Then we choose the next `size_in_pages` pages as stack pages using [Iterator::nth]. If all three variables are `Some`, the allocation succeeded and we map the stack pages to physical frames using [ActivePageTable::map]. The guard page remains unmapped.
|
The actual allocation is straightforward: First, we choose the next page as [guard page]. Then we choose the next `size_in_pages` pages as stack pages using [Iterator::nth]. If all three variables are `Some`, the allocation succeeded and we map the stack pages to physical frames using [ActivePageTable::map]. The guard page remains unmapped.
|
||||||
|
|
||||||
[Iterator::nth]: https://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.nth
|
[Iterator::nth]: https://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.nth
|
||||||
[ActivePageTable::map]: @/first-edition/posts/06-page-tables/index.md#more-mapping-functions
|
[ActivePageTable::map]: @/edition-1/posts/06-page-tables/index.md#more-mapping-functions
|
||||||
|
|
||||||
Finally, we create and return a new `Stack`, which we define as follows:
|
Finally, we create and return a new `Stack`, which we define as follows:
|
||||||
|
|
||||||
@@ -505,7 +505,7 @@ The Global Descriptor Table (GDT) is a relict that was used for [memory segmenta
|
|||||||
|
|
||||||
We already created a GDT [when switching to long mode]. Back then, we used assembly to create valid code and data segment descriptors, which were required to enter 64-bit mode. We could just edit that assembly file and add an additional TSS descriptor. However, we now have the expressiveness of Rust, so let's do it in Rust instead.
|
We already created a GDT [when switching to long mode]. Back then, we used assembly to create valid code and data segment descriptors, which were required to enter 64-bit mode. We could just edit that assembly file and add an additional TSS descriptor. However, we now have the expressiveness of Rust, so let's do it in Rust instead.
|
||||||
|
|
||||||
[when switching to long mode]: @/first-edition/posts/02-entering-longmode/index.md#the-global-descriptor-table
|
[when switching to long mode]: @/edition-1/posts/02-entering-longmode/index.md#the-global-descriptor-table
|
||||||
|
|
||||||
We start by creating a new `interrupts::gdt` submodule. For that we need to rename the `src/interrupts.rs` file to `src/interrupts/mod.rs`. Then we can create a new submodule:
|
We start by creating a new `interrupts::gdt` submodule. For that we need to rename the `src/interrupts.rs` file to `src/interrupts/mod.rs`. Then we can create a new submodule:
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
@@ -1,4 +1,5 @@
|
|||||||
+++
|
+++
|
||||||
title = "Second Edition"
|
title = "Second Edition"
|
||||||
template = "redirect-to-frontpage.html"
|
template = "redirect-to-frontpage.html"
|
||||||
|
aliases = ["second-edition/index.html"]
|
||||||
+++
|
+++
|
||||||
@@ -3,5 +3,5 @@ title = "Extra Content"
|
|||||||
insert_anchor_links = "left"
|
insert_anchor_links = "left"
|
||||||
render = false
|
render = false
|
||||||
sort_by = "weight"
|
sort_by = "weight"
|
||||||
page_template = "second-edition/extra.html"
|
page_template = "edition-2/extra.html"
|
||||||
+++
|
+++
|
||||||
|
Before Width: | Height: | Size: 287 KiB After Width: | Height: | Size: 287 KiB |
@@ -1,7 +1,7 @@
|
|||||||
+++
|
+++
|
||||||
title = "Building on Android"
|
title = "Building on Android"
|
||||||
weight = 3
|
weight = 3
|
||||||
|
aliases = ["second-edition/extra/building-on-android/index.html"]
|
||||||
+++
|
+++
|
||||||
|
|
||||||
I finally managed to get `blog_os` building on my Android phone using [termux](https://termux.com/). This post explains the necessary steps to set it up.
|
I finally managed to get `blog_os` building on my Android phone using [termux](https://termux.com/). This post explains the necessary steps to set it up.
|
||||||
@@ -523,4 +523,4 @@ cargo rustc -- -C link-args="-e __start -static -nostartfiles"
|
|||||||
|
|
||||||
[پست بعدی] مراحل مورد نیاز برای تبدیل باینری مستقل به حداقل هسته سیستمعامل را توضیح میدهد. که شامل ایجاد یک هدف سفارشی، ترکیب اجرایی ما با بوتلودر و یادگیری نحوه چاپ چیزی در صفحه است.
|
[پست بعدی] مراحل مورد نیاز برای تبدیل باینری مستقل به حداقل هسته سیستمعامل را توضیح میدهد. که شامل ایجاد یک هدف سفارشی، ترکیب اجرایی ما با بوتلودر و یادگیری نحوه چاپ چیزی در صفحه است.
|
||||||
|
|
||||||
[پست بعدی]: @/second-edition/posts/02-minimal-rust-kernel/index.fa.md
|
[پست بعدی]: @/edition-2/posts/02-minimal-rust-kernel/index.fa.md
|
||||||
@@ -528,4 +528,4 @@ cargo rustc -- -C link-args="-e __start -static -nostartfiles"
|
|||||||
|
|
||||||
[次の記事][next post]では、この独立したバイナリを最小限の OS カーネルにするために必要なステップを説明しています。カスタムターゲットの作成、実行可能ファイルとブートローダの組み合わせ、画面に何か文字を表示する方法について説明しています。
|
[次の記事][next post]では、この独立したバイナリを最小限の OS カーネルにするために必要なステップを説明しています。カスタムターゲットの作成、実行可能ファイルとブートローダの組み合わせ、画面に何か文字を表示する方法について説明しています。
|
||||||
|
|
||||||
[next post]: @/second-edition/posts/02-minimal-rust-kernel/index.md
|
[next post]: @/edition-2/posts/02-minimal-rust-kernel/index.md
|
||||||
@@ -516,4 +516,4 @@ Note that this is just a minimal example of a freestanding Rust binary. This bin
|
|||||||
|
|
||||||
The [next post] explains the steps needed for turning our freestanding binary into a minimal operating system kernel. This includes creating a custom target, combining our executable with a bootloader, and learning how to print something to the screen.
|
The [next post] explains the steps needed for turning our freestanding binary into a minimal operating system kernel. This includes creating a custom target, combining our executable with a bootloader, and learning how to print something to the screen.
|
||||||
|
|
||||||
[next post]: @/second-edition/posts/02-minimal-rust-kernel/index.md
|
[next post]: @/edition-2/posts/02-minimal-rust-kernel/index.md
|
||||||
@@ -296,4 +296,4 @@ cargo build --target thumbv7em-none-eabihf
|
|||||||
|
|
||||||
下一篇文章要做的事情基于我们这篇文章的成果,它将详细讲述编写一个最小的操作系统内核需要的步骤:如何配置特定的编译目标,如何将可执行程序与引导程序拼接,以及如何把一些特定的字符串打印到屏幕上。
|
下一篇文章要做的事情基于我们这篇文章的成果,它将详细讲述编写一个最小的操作系统内核需要的步骤:如何配置特定的编译目标,如何将可执行程序与引导程序拼接,以及如何把一些特定的字符串打印到屏幕上。
|
||||||
|
|
||||||
[next post]: @/second-edition/posts/02-minimal-rust-kernel/index.md
|
[next post]: @/edition-2/posts/02-minimal-rust-kernel/index.md
|
||||||
@@ -516,4 +516,4 @@ cargo rustc -- -C link-args="-e __start -static -nostartfiles"
|
|||||||
|
|
||||||
[下一篇文章][next post] 將會講解如何將我們的獨立執行檔轉成最小的作業系統核心。這包含建立自訂目標、用啟動程式組合我們的執行檔,還有學習如何輸出一些東西到螢幕上。
|
[下一篇文章][next post] 將會講解如何將我們的獨立執行檔轉成最小的作業系統核心。這包含建立自訂目標、用啟動程式組合我們的執行檔,還有學習如何輸出一些東西到螢幕上。
|
||||||
|
|
||||||
[next post]: @/second-edition/posts/02-minimal-rust-kernel/index.md
|
[next post]: @/edition-2/posts/02-minimal-rust-kernel/index.md
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
title = "Disable the Red Zone"
|
title = "Disable the Red Zone"
|
||||||
weight = 1
|
weight = 1
|
||||||
path = "red-zone"
|
path = "red-zone"
|
||||||
template = "second-edition/extra.html"
|
template = "edition-2/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:
|
||||||
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
@@ -2,7 +2,7 @@
|
|||||||
title = "Disable SIMD"
|
title = "Disable SIMD"
|
||||||
weight = 2
|
weight = 2
|
||||||
path = "disable-simd"
|
path = "disable-simd"
|
||||||
template = "second-edition/extra.html"
|
template = "edition-2/extra.html"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
[Single Instruction Multiple Data (SIMD)] instructions are able to perform an operation (e.g. addition) simultaneously on multiple data words, which can speed up programs significantly. The `x86_64` architecture supports various SIMD standards:
|
[Single Instruction Multiple Data (SIMD)] instructions are able to perform an operation (e.g. addition) simultaneously on multiple data words, which can speed up programs significantly. The `x86_64` architecture supports various SIMD standards:
|
||||||
@@ -15,7 +15,7 @@ rtl = true
|
|||||||
|
|
||||||
در این پست ما برای معماری x86 یک هسته مینیمال ۶۴ بیتی به زبان راست میسازیم. با استفاده از باینری مستقل Rust از پست قبل، یک دیسک ایمیج قابل بوت میسازیم، که متنی را در صفحه چاپ کند.
|
در این پست ما برای معماری x86 یک هسته مینیمال ۶۴ بیتی به زبان راست میسازیم. با استفاده از باینری مستقل Rust از پست قبل، یک دیسک ایمیج قابل بوت میسازیم، که متنی را در صفحه چاپ کند.
|
||||||
|
|
||||||
[باینری مستقل Rust]: @/second-edition/posts/01-freestanding-rust-binary/index.md
|
[باینری مستقل Rust]: @/edition-2/posts/01-freestanding-rust-binary/index.md
|
||||||
|
|
||||||
<!-- more -->
|
<!-- more -->
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ rtl = true
|
|||||||
|
|
||||||
به دلیل این اشکالات ما تصمیم گرفتیم از GRUB یا استاندارد Multiboot استفاده نکنیم. با این حال، ما قصد داریم پشتیبانی Multiboot را به ابزار [bootimage] خود اضافه کنیم، به طوری که امکان بارگذاری هسته شما بر روی یک سیستم با بوتلودر GRUB نیز وجود داشته باشد. اگر علاقهمند به نوشتن هسته سازگار با Multiboot هستید، [نسخه اول] مجموعه پستهای این وبلاگ را بررسی کنید.
|
به دلیل این اشکالات ما تصمیم گرفتیم از GRUB یا استاندارد Multiboot استفاده نکنیم. با این حال، ما قصد داریم پشتیبانی Multiboot را به ابزار [bootimage] خود اضافه کنیم، به طوری که امکان بارگذاری هسته شما بر روی یک سیستم با بوتلودر GRUB نیز وجود داشته باشد. اگر علاقهمند به نوشتن هسته سازگار با Multiboot هستید، [نسخه اول] مجموعه پستهای این وبلاگ را بررسی کنید.
|
||||||
|
|
||||||
[نسخه اول]: @/first-edition/_index.md
|
[نسخه اول]: @/edition-1/_index.md
|
||||||
|
|
||||||
### UEFI
|
### UEFI
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ rtl = true
|
|||||||
|
|
||||||
ما در حال نوشتن یک هسته هستیم، بنابراین بالاخره باید وقفهها را مدیریت کنیم. برای انجام ایمن آن، باید بهینهسازی اشارهگر پشتهای خاصی به نام _“red zone”_ (ترجمه: منطقه قرمز) را غیرفعال کنیم، زیرا در غیر این صورت باعث خراب شدن پشته میشود. برای اطلاعات بیشتر، به پست جداگانه ما در مورد [غیرفعال کردن منطقه قرمز] مراجعه کنید.
|
ما در حال نوشتن یک هسته هستیم، بنابراین بالاخره باید وقفهها را مدیریت کنیم. برای انجام ایمن آن، باید بهینهسازی اشارهگر پشتهای خاصی به نام _“red zone”_ (ترجمه: منطقه قرمز) را غیرفعال کنیم، زیرا در غیر این صورت باعث خراب شدن پشته میشود. برای اطلاعات بیشتر، به پست جداگانه ما در مورد [غیرفعال کردن منطقه قرمز] مراجعه کنید.
|
||||||
|
|
||||||
[غیرفعال کردن منطقه قرمز]: @/second-edition/posts/02-minimal-rust-kernel/disable-red-zone/index.md
|
[غیرفعال کردن منطقه قرمز]: @/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.md
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"features": "-mmx,-sse,+soft-float",
|
"features": "-mmx,-sse,+soft-float",
|
||||||
@@ -195,7 +195,7 @@ rtl = true
|
|||||||
|
|
||||||
یک مشکل در غیرفعال کردن SIMD این است که عملیاتهای مُمَیزِ شناور (ترجمه: floating point) در `x86_64` به طور پیش فرض به ثباتهای SIMD نیاز دارد. برای حل این مشکل، ویژگی `soft-float` را اضافه میکنیم، که از طریق عملکردهای نرمافزاری مبتنی بر اعداد صحیح عادی، تمام عملیات مُمَیزِ شناور را شبیهسازی میکند.
|
یک مشکل در غیرفعال کردن SIMD این است که عملیاتهای مُمَیزِ شناور (ترجمه: floating point) در `x86_64` به طور پیش فرض به ثباتهای SIMD نیاز دارد. برای حل این مشکل، ویژگی `soft-float` را اضافه میکنیم، که از طریق عملکردهای نرمافزاری مبتنی بر اعداد صحیح عادی، تمام عملیات مُمَیزِ شناور را شبیهسازی میکند.
|
||||||
|
|
||||||
For more information, see our post on [disabling SIMD](@/second-edition/posts/02-minimal-rust-kernel/disable-simd/index.md).
|
For more information, see our post on [disabling SIMD](@/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.md).
|
||||||
|
|
||||||
#### کنار هم قرار دادن
|
#### کنار هم قرار دادن
|
||||||
فایل مشخصات هدف ما اکنون به این شکل است:
|
فایل مشخصات هدف ما اکنون به این شکل است:
|
||||||
@@ -222,7 +222,7 @@ For more information, see our post on [disabling SIMD](@/second-edition/posts/02
|
|||||||
|
|
||||||
عملیات کامپایل کردن برای هدف جدید ما از قراردادهای لینوکس استفاده خواهد کرد (کاملاً مطمئن نیستم که چرا، تصور میکنم این فقط پیش فرض LLVM باشد). این بدان معنی است که ما به یک نقطه ورود به نام `start_` نیاز داریم همانطور که در [پست قبلی] توضیح داده شد:
|
عملیات کامپایل کردن برای هدف جدید ما از قراردادهای لینوکس استفاده خواهد کرد (کاملاً مطمئن نیستم که چرا، تصور میکنم این فقط پیش فرض LLVM باشد). این بدان معنی است که ما به یک نقطه ورود به نام `start_` نیاز داریم همانطور که در [پست قبلی] توضیح داده شد:
|
||||||
|
|
||||||
[پست قبلی]: @/second-edition/posts/01-freestanding-rust-binary/index.md
|
[پست قبلی]: @/edition-2/posts/01-freestanding-rust-binary/index.md
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// src/main.rs
|
// src/main.rs
|
||||||
@@ -14,7 +14,7 @@ translators = ["woodyZootopia", "JohnTitor"]
|
|||||||
|
|
||||||
この記事では、Rustで最小限の64bitカーネルを作ります。前の記事で作った[フリースタンディングなRustバイナリ][freestanding Rust binary]を下敷きにして、何かを画面に出力する、ブータブルディスクイメージを作ります。
|
この記事では、Rustで最小限の64bitカーネルを作ります。前の記事で作った[フリースタンディングなRustバイナリ][freestanding Rust binary]を下敷きにして、何かを画面に出力する、ブータブルディスクイメージを作ります。
|
||||||
|
|
||||||
[freestanding Rust binary]: @/second-edition/posts/01-freestanding-rust-binary/index.ja.md
|
[freestanding Rust binary]: @/edition-2/posts/01-freestanding-rust-binary/index.ja.md
|
||||||
|
|
||||||
<!-- more -->
|
<!-- more -->
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ x86には2つのファームウェアの標準規格があります:"Basic Inp
|
|||||||
|
|
||||||
これらの欠点を考慮し、私達はGRUBとMultiboot標準規格を使わないことに決めました。しかし、あなたのカーネルをGRUBシステム上で読み込めるように、私達の[bootimage]ツールにMultibootのサポートを追加することも計画しています。Multiboot準拠なカーネルを書きたい場合は、このブログシリーズの[第1版][first edition]をご覧ください。
|
これらの欠点を考慮し、私達はGRUBとMultiboot標準規格を使わないことに決めました。しかし、あなたのカーネルをGRUBシステム上で読み込めるように、私達の[bootimage]ツールにMultibootのサポートを追加することも計画しています。Multiboot準拠なカーネルを書きたい場合は、このブログシリーズの[第1版][first edition]をご覧ください。
|
||||||
|
|
||||||
[first edition]: @/first-edition/_index.md
|
[first edition]: @/edition-1/_index.md
|
||||||
|
|
||||||
### UEFI
|
### UEFI
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ Cargoは`--target`パラメータを使ってさまざまなターゲットを
|
|||||||
|
|
||||||
カーネルを書いている以上、ある時点で<ruby>割り込み<rp> (</rp><rt>interrupt</rt><rp>) </rp></ruby>を処理しなければならなくなるでしょう。これを安全に行うために、 **"red zone"** と呼ばれる、ある種のスタックポインタ最適化を無効化する必要があります。こうしないと、スタックの<ruby>破損<rp> (</rp><rt>corruption</rt><rp>) </rp></ruby>を引き起こしてしまう恐れがあるためです。より詳しくは、[red zoneの無効化][disabling the red zone]という別記事をご覧ください。
|
カーネルを書いている以上、ある時点で<ruby>割り込み<rp> (</rp><rt>interrupt</rt><rp>) </rp></ruby>を処理しなければならなくなるでしょう。これを安全に行うために、 **"red zone"** と呼ばれる、ある種のスタックポインタ最適化を無効化する必要があります。こうしないと、スタックの<ruby>破損<rp> (</rp><rt>corruption</rt><rp>) </rp></ruby>を引き起こしてしまう恐れがあるためです。より詳しくは、[red zoneの無効化][disabling the red zone]という別記事をご覧ください。
|
||||||
|
|
||||||
[disabling the red zone]: @/second-edition/posts/02-minimal-rust-kernel/disable-red-zone/index.md
|
[disabling the red zone]: @/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.md
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"features": "-mmx,-sse,+soft-float",
|
"features": "-mmx,-sse,+soft-float",
|
||||||
@@ -189,7 +189,7 @@ Cargoは`--target`パラメータを使ってさまざまなターゲットを
|
|||||||
|
|
||||||
SIMDを無効化することによる問題に、`x86_64`における浮動小数点演算は標準ではSIMDレジスタを必要とするということがあります。この問題を解決するため、`soft-float`機能を追加します。これは、すべての浮動小数点演算を通常の整数に基づいたソフトウェア上の関数を使ってエミュレートするというものです。
|
SIMDを無効化することによる問題に、`x86_64`における浮動小数点演算は標準ではSIMDレジスタを必要とするということがあります。この問題を解決するため、`soft-float`機能を追加します。これは、すべての浮動小数点演算を通常の整数に基づいたソフトウェア上の関数を使ってエミュレートするというものです。
|
||||||
|
|
||||||
より詳しくは、[SIMDを無効化する](@/second-edition/posts/02-minimal-rust-kernel/disable-simd/index.md)ことに関する私達の記事を読んでください。
|
より詳しくは、[SIMDを無効化する](@/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.md)ことに関する私達の記事を読んでください。
|
||||||
|
|
||||||
#### まとめると
|
#### まとめると
|
||||||
私達のターゲット仕様ファイルは今このようになっているはずです。
|
私達のターゲット仕様ファイルは今このようになっているはずです。
|
||||||
@@ -215,7 +215,7 @@ SIMDを無効化することによる問題に、`x86_64`における浮動小
|
|||||||
### カーネルをビルドする
|
### カーネルをビルドする
|
||||||
私達の新しいターゲットのコンパイルにはLinuxの慣習に倣います(理由は知りません、LLVMのデフォルトであるというだけではないでしょうか)。つまり、[前の記事][previous post]で説明したように`_start`という名前のエントリポイントが要るということです。
|
私達の新しいターゲットのコンパイルにはLinuxの慣習に倣います(理由は知りません、LLVMのデフォルトであるというだけではないでしょうか)。つまり、[前の記事][previous post]で説明したように`_start`という名前のエントリポイントが要るということです。
|
||||||
|
|
||||||
[previous post]: @/second-edition/posts/01-freestanding-rust-binary/index.ja.md
|
[previous post]: @/edition-2/posts/01-freestanding-rust-binary/index.ja.md
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// src/main.rs
|
// src/main.rs
|
||||||
@@ -10,7 +10,7 @@ chapter = "Bare Bones"
|
|||||||
|
|
||||||
In this post we create a minimal 64-bit Rust kernel for the x86 architecture. We build upon the [freestanding Rust binary] from the previous post to create a bootable disk image, that prints something to the screen.
|
In this post we create a minimal 64-bit Rust kernel for the x86 architecture. We build upon the [freestanding Rust binary] from the previous post to create a bootable disk image, that prints something to the screen.
|
||||||
|
|
||||||
[freestanding Rust binary]: @/second-edition/posts/01-freestanding-rust-binary/index.md
|
[freestanding Rust binary]: @/edition-2/posts/01-freestanding-rust-binary/index.md
|
||||||
|
|
||||||
<!-- more -->
|
<!-- more -->
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ To make a kernel Multiboot compliant, one just needs to insert a so-called [Mult
|
|||||||
|
|
||||||
Because of these drawbacks we decided to not use GRUB or the Multiboot standard. However, we plan to add Multiboot support to our [bootimage] tool, so that it's possible to load your kernel on a GRUB system too. If you're interested in writing a Multiboot compliant kernel, check out the [first edition] of this blog series.
|
Because of these drawbacks we decided to not use GRUB or the Multiboot standard. However, we plan to add Multiboot support to our [bootimage] tool, so that it's possible to load your kernel on a GRUB system too. If you're interested in writing a Multiboot compliant kernel, check out the [first edition] of this blog series.
|
||||||
|
|
||||||
[first edition]: @/first-edition/_index.md
|
[first edition]: @/edition-1/_index.md
|
||||||
|
|
||||||
### UEFI
|
### UEFI
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ This setting specifies that the target doesn't support [stack unwinding] on pani
|
|||||||
|
|
||||||
We're writing a kernel, so we'll need to handle interrupts at some point. To do that safely, we have to disable a certain stack pointer optimization called the _“red zone”_, because it would cause stack corruptions otherwise. For more information, see our separate post about [disabling the red zone].
|
We're writing a kernel, so we'll need to handle interrupts at some point. To do that safely, we have to disable a certain stack pointer optimization called the _“red zone”_, because it would cause stack corruptions otherwise. For more information, see our separate post about [disabling the red zone].
|
||||||
|
|
||||||
[disabling the red zone]: @/second-edition/posts/02-minimal-rust-kernel/disable-red-zone/index.md
|
[disabling the red zone]: @/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.md
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"features": "-mmx,-sse,+soft-float",
|
"features": "-mmx,-sse,+soft-float",
|
||||||
@@ -186,7 +186,7 @@ The `mmx` and `sse` features determine support for [Single Instruction Multiple
|
|||||||
|
|
||||||
A problem with disabling SIMD is that floating point operations on `x86_64` require SIMD registers by default. To solve this problem, we add the `soft-float` feature, which emulates all floating point operations through software functions based on normal integers.
|
A problem with disabling SIMD is that floating point operations on `x86_64` require SIMD registers by default. To solve this problem, we add the `soft-float` feature, which emulates all floating point operations through software functions based on normal integers.
|
||||||
|
|
||||||
For more information, see our post on [disabling SIMD](@/second-edition/posts/02-minimal-rust-kernel/disable-simd/index.md).
|
For more information, see our post on [disabling SIMD](@/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.md).
|
||||||
|
|
||||||
#### Putting it Together
|
#### Putting it Together
|
||||||
Our target specification file now looks like this:
|
Our target specification file now looks like this:
|
||||||
@@ -212,7 +212,7 @@ Our target specification file now looks like this:
|
|||||||
### Building our Kernel
|
### Building our Kernel
|
||||||
Compiling for our new target will use Linux conventions (I'm not quite sure why, I assume that it's just LLVM's default). This means that we need an entry point named `_start` as described in the [previous post]:
|
Compiling for our new target will use Linux conventions (I'm not quite sure why, I assume that it's just LLVM's default). This means that we need an entry point named `_start` as described in the [previous post]:
|
||||||
|
|
||||||
[previous post]: @/second-edition/posts/01-freestanding-rust-binary/index.md
|
[previous post]: @/edition-2/posts/01-freestanding-rust-binary/index.md
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// src/main.rs
|
// src/main.rs
|
||||||
@@ -13,7 +13,7 @@ translators = ["luojia65", "Rustin-Liu"]
|
|||||||
|
|
||||||
在这篇文章中,我们将基于 **x86架构**(the x86 architecture),使用 Rust 语言,编写一个最小化的 64 位内核。我们将从上一章中构建的独立式可执行程序开始,构建自己的内核;它将向显示器打印字符串,并能被打包为一个能够引导启动的**磁盘映像**(disk image)。
|
在这篇文章中,我们将基于 **x86架构**(the x86 architecture),使用 Rust 语言,编写一个最小化的 64 位内核。我们将从上一章中构建的独立式可执行程序开始,构建自己的内核;它将向显示器打印字符串,并能被打包为一个能够引导启动的**磁盘映像**(disk image)。
|
||||||
|
|
||||||
[freestanding Rust binary]: @/second-edition/posts/01-freestanding-rust-binary/index.md
|
[freestanding Rust binary]: @/edition-2/posts/01-freestanding-rust-binary/index.md
|
||||||
|
|
||||||
<!-- more -->
|
<!-- more -->
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
@@ -29,11 +29,11 @@ rtl = true
|
|||||||
|
|
||||||
این پست جایگزین (حالا منسوخ شده) پستهای [_Unit Testing_] و [_Integration Tests_] میشود. فرض بر این است که شما پست [_یک کرنل مینیمال با Rust_] را پس از 27-09-2019 دنبال کردهاید. اساساً نیاز است که شما یک فایل `.cargo/config.toml` داشته باشید که [یک هدف پیشفرض مشخص میکند] و [یک اجرا کننده قابل اجرا تعریف میکند].
|
این پست جایگزین (حالا منسوخ شده) پستهای [_Unit Testing_] و [_Integration Tests_] میشود. فرض بر این است که شما پست [_یک کرنل مینیمال با Rust_] را پس از 27-09-2019 دنبال کردهاید. اساساً نیاز است که شما یک فایل `.cargo/config.toml` داشته باشید که [یک هدف پیشفرض مشخص میکند] و [یک اجرا کننده قابل اجرا تعریف میکند].
|
||||||
|
|
||||||
[_Unit Testing_]: @/second-edition/posts/deprecated/04-unit-testing/index.md
|
[_Unit Testing_]: @/edition-2/posts/deprecated/04-unit-testing/index.md
|
||||||
[_Integration Tests_]: @/second-edition/posts/deprecated/05-integration-tests/index.md
|
[_Integration Tests_]: @/edition-2/posts/deprecated/05-integration-tests/index.md
|
||||||
[_یک کرنل مینیمال با Rust_]: @/second-edition/posts/02-minimal-rust-kernel/index.md
|
[_یک کرنل مینیمال با Rust_]: @/edition-2/posts/02-minimal-rust-kernel/index.md
|
||||||
[یک هدف پیشفرض مشخص میکند]: @/second-edition/posts/02-minimal-rust-kernel/index.md#set-a-default-target
|
[یک هدف پیشفرض مشخص میکند]: @/edition-2/posts/02-minimal-rust-kernel/index.md#set-a-default-target
|
||||||
[یک اجرا کننده قابل اجرا تعریف میکند]: @/second-edition/posts/02-minimal-rust-kernel/index.md#using-cargo-run
|
[یک اجرا کننده قابل اجرا تعریف میکند]: @/edition-2/posts/02-minimal-rust-kernel/index.md#using-cargo-run
|
||||||
|
|
||||||
## تست کردن در Rust
|
## تست کردن در Rust
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ test-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04"]
|
|||||||
|
|
||||||
برای برقراری ارتباط بین پردازنده و سخت افزار جانبی در x86، دو رویکرد مختلف وجود دارد،**memory-mapped I/O** و **port-mapped I/O**. ما قبلاً برای دسترسی به [بافر متن VGA] از طریق آدرس حافظه `0xb8000` از memory-mapped I/O استفاده کردهایم. این آدرس به RAM مپ (ترسیم) نشده است، بلکه به برخی از حافظههای دستگاه VGA مپ شده است.
|
برای برقراری ارتباط بین پردازنده و سخت افزار جانبی در x86، دو رویکرد مختلف وجود دارد،**memory-mapped I/O** و **port-mapped I/O**. ما قبلاً برای دسترسی به [بافر متن VGA] از طریق آدرس حافظه `0xb8000` از memory-mapped I/O استفاده کردهایم. این آدرس به RAM مپ (ترسیم) نشده است، بلکه به برخی از حافظههای دستگاه VGA مپ شده است.
|
||||||
|
|
||||||
[بافر متن VGA]: @/second-edition/posts/03-vga-text-buffer/index.md
|
[بافر متن VGA]: @/edition-2/posts/03-vga-text-buffer/index.md
|
||||||
|
|
||||||
در مقابل، port-mapped I/O از یک گذرگاه I/O جداگانه برای ارتباط استفاده میکند. هر قسمت جانبی متصل دارای یک یا چند شماره پورت است. برای برقراری ارتباط با چنین پورت I/O، دستورالعملهای CPU خاصی وجود دارد که `in` و `out` نامیده میشوند، که یک عدد پورت و یک بایت داده را میگیرند (همچنین این دستورات تغییراتی دارند که اجازه می دهد یک `u16` یا `u32` ارسال کنید).
|
در مقابل، port-mapped I/O از یک گذرگاه I/O جداگانه برای ارتباط استفاده میکند. هر قسمت جانبی متصل دارای یک یا چند شماره پورت است. برای برقراری ارتباط با چنین پورت I/O، دستورالعملهای CPU خاصی وجود دارد که `in` و `out` نامیده میشوند، که یک عدد پورت و یک بایت داده را میگیرند (همچنین این دستورات تغییراتی دارند که اجازه می دهد یک `u16` یا `u32` ارسال کنید).
|
||||||
|
|
||||||
@@ -315,7 +315,7 @@ lazy_static! {
|
|||||||
|
|
||||||
مانند دستگاه `isa-debug-exit`، UART با استفاده از پورت I/O برنامه نویسی میشود. از آنجا که UART پیچیدهتر است، از چندین پورت I/O برای برنامه نویسی رجیسترهای مختلف دستگاه استفاده میکند. تابع ناامن `SerialPort::new` انتظار دارد که آدرس اولین پورت I/O از UART به عنوان آرگومان باشد، که از آن میتواند آدرس تمام پورتهای مورد نیاز را محاسبه کند. ما در حال عبور دادنِ آدرس پورت `0x3F8` هستیم که شماره پورت استاندارد برای اولین رابط سریال است.
|
مانند دستگاه `isa-debug-exit`، UART با استفاده از پورت I/O برنامه نویسی میشود. از آنجا که UART پیچیدهتر است، از چندین پورت I/O برای برنامه نویسی رجیسترهای مختلف دستگاه استفاده میکند. تابع ناامن `SerialPort::new` انتظار دارد که آدرس اولین پورت I/O از UART به عنوان آرگومان باشد، که از آن میتواند آدرس تمام پورتهای مورد نیاز را محاسبه کند. ما در حال عبور دادنِ آدرس پورت `0x3F8` هستیم که شماره پورت استاندارد برای اولین رابط سریال است.
|
||||||
|
|
||||||
[vga lazy-static]: @/second-edition/posts/03-vga-text-buffer/index.md#lazy-statics
|
[vga lazy-static]: @/edition-2/posts/03-vga-text-buffer/index.md#lazy-statics
|
||||||
|
|
||||||
برای اینکه پورت سریال به راحتی قابل استفاده باشد، ماکروهای `serial_print!` و `serial_println!` را اضافه میکنیم:
|
برای اینکه پورت سریال به راحتی قابل استفاده باشد، ماکروهای `serial_print!` و `serial_println!` را اضافه میکنیم:
|
||||||
|
|
||||||
@@ -24,11 +24,11 @@ This blog is openly developed on [GitHub]. If you have any problems or questions
|
|||||||
|
|
||||||
This post replaces the (now deprecated) [_Unit Testing_] and [_Integration Tests_] posts. It assumes that you have followed the [_A Minimal Rust Kernel_] post after 2019-04-27. Mainly, it requires that you have a `.cargo/config.toml` file that [sets a default target] and [defines a runner executable].
|
This post replaces the (now deprecated) [_Unit Testing_] and [_Integration Tests_] posts. It assumes that you have followed the [_A Minimal Rust Kernel_] post after 2019-04-27. Mainly, it requires that you have a `.cargo/config.toml` file that [sets a default target] and [defines a runner executable].
|
||||||
|
|
||||||
[_Unit Testing_]: @/second-edition/posts/deprecated/04-unit-testing/index.md
|
[_Unit Testing_]: @/edition-2/posts/deprecated/04-unit-testing/index.md
|
||||||
[_Integration Tests_]: @/second-edition/posts/deprecated/05-integration-tests/index.md
|
[_Integration Tests_]: @/edition-2/posts/deprecated/05-integration-tests/index.md
|
||||||
[_A Minimal Rust Kernel_]: @/second-edition/posts/02-minimal-rust-kernel/index.md
|
[_A Minimal Rust Kernel_]: @/edition-2/posts/02-minimal-rust-kernel/index.md
|
||||||
[sets a default target]: @/second-edition/posts/02-minimal-rust-kernel/index.md#set-a-default-target
|
[sets a default target]: @/edition-2/posts/02-minimal-rust-kernel/index.md#set-a-default-target
|
||||||
[defines a runner executable]: @/second-edition/posts/02-minimal-rust-kernel/index.md#using-cargo-run
|
[defines a runner executable]: @/edition-2/posts/02-minimal-rust-kernel/index.md#using-cargo-run
|
||||||
|
|
||||||
## Testing in Rust
|
## Testing in Rust
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ Together with the device name (`isa-debug-exit`), we pass the two parameters `io
|
|||||||
|
|
||||||
There are two different approaches for communicating between the CPU and peripheral hardware on x86, **memory-mapped I/O** and **port-mapped I/O**. We already used memory-mapped I/O for accessing the [VGA text buffer] through the memory address `0xb8000`. This address is not mapped to RAM, but to some memory on the VGA device.
|
There are two different approaches for communicating between the CPU and peripheral hardware on x86, **memory-mapped I/O** and **port-mapped I/O**. We already used memory-mapped I/O for accessing the [VGA text buffer] through the memory address `0xb8000`. This address is not mapped to RAM, but to some memory on the VGA device.
|
||||||
|
|
||||||
[VGA text buffer]: @/second-edition/posts/03-vga-text-buffer/index.md
|
[VGA text buffer]: @/edition-2/posts/03-vga-text-buffer/index.md
|
||||||
|
|
||||||
In contrast, port-mapped I/O uses a separate I/O bus for communication. Each connected peripheral has one or more port numbers. To communicate with such an I/O port there are special CPU instructions called `in` and `out`, which take a port number and a data byte (there are also variations of these commands that allow sending an `u16` or `u32`).
|
In contrast, port-mapped I/O uses a separate I/O bus for communication. Each connected peripheral has one or more port numbers. To communicate with such an I/O port there are special CPU instructions called `in` and `out`, which take a port number and a data byte (there are also variations of these commands that allow sending an `u16` or `u32`).
|
||||||
|
|
||||||
@@ -310,7 +310,7 @@ Like with the [VGA text buffer][vga lazy-static], we use `lazy_static` and a spi
|
|||||||
|
|
||||||
Like the `isa-debug-exit` device, the UART is programmed using port I/O. Since the UART is more complex, it uses multiple I/O ports for programming different device registers. The unsafe `SerialPort::new` function expects the address of the first I/O port of the UART as argument, from which it can calculate the addresses of all needed ports. We're passing the port address `0x3F8`, which is the standard port number for the first serial interface.
|
Like the `isa-debug-exit` device, the UART is programmed using port I/O. Since the UART is more complex, it uses multiple I/O ports for programming different device registers. The unsafe `SerialPort::new` function expects the address of the first I/O port of the UART as argument, from which it can calculate the addresses of all needed ports. We're passing the port address `0x3F8`, which is the standard port number for the first serial interface.
|
||||||
|
|
||||||
[vga lazy-static]: @/second-edition/posts/03-vga-text-buffer/index.md#lazy-statics
|
[vga lazy-static]: @/edition-2/posts/03-vga-text-buffer/index.md#lazy-statics
|
||||||
|
|
||||||
To make the serial port easily usable, we add `serial_print!` and `serial_println!` macros:
|
To make the serial port easily usable, we add `serial_print!` and `serial_println!` macros:
|
||||||
|
|
||||||
@@ -27,11 +27,11 @@ translators = ["luojia65", "Rustin-Liu"]
|
|||||||
|
|
||||||
这篇文章替换了此前的(现在已经过时了) [_单元测试(Unit Testing)_] 和 [_集成测试(Integration Tests)_] 两篇文章。这里我将假定你是在2019-04-27日后阅读的[_最小Rust内核_]一文。总而言之,本文要求你已经有一个[设置默认目标]的 `.cargo/config` 文件且[定义了一个runner可执行文件]。
|
这篇文章替换了此前的(现在已经过时了) [_单元测试(Unit Testing)_] 和 [_集成测试(Integration Tests)_] 两篇文章。这里我将假定你是在2019-04-27日后阅读的[_最小Rust内核_]一文。总而言之,本文要求你已经有一个[设置默认目标]的 `.cargo/config` 文件且[定义了一个runner可执行文件]。
|
||||||
|
|
||||||
[_单元测试(Unit Testing)_]: @/second-edition/posts/deprecated/04-unit-testing/index.md
|
[_单元测试(Unit Testing)_]: @/edition-2/posts/deprecated/04-unit-testing/index.md
|
||||||
[_集成测试(Integration Tests)_]: @/second-edition/posts/deprecated/05-integration-tests/index.md
|
[_集成测试(Integration Tests)_]: @/edition-2/posts/deprecated/05-integration-tests/index.md
|
||||||
[_最小Rust内核_]: @/second-edition/posts/02-minimal-rust-kernel/index.md
|
[_最小Rust内核_]: @/edition-2/posts/02-minimal-rust-kernel/index.md
|
||||||
[设置默认目标]: @/second-edition/posts/02-minimal-rust-kernel/index.md#set-a-default-target
|
[设置默认目标]: @/edition-2/posts/02-minimal-rust-kernel/index.md#set-a-default-target
|
||||||
[定义了一个runner可执行文件]: @/second-edition/posts/02-minimal-rust-kernel/index.md#using-cargo-run
|
[定义了一个runner可执行文件]: @/edition-2/posts/02-minimal-rust-kernel/index.md#using-cargo-run
|
||||||
|
|
||||||
## Rust中的测试
|
## Rust中的测试
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ test-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04"]
|
|||||||
### I/O 端口
|
### I/O 端口
|
||||||
在x86平台上,CPU和外围硬件通信通常有两种方式,**内存映射I/O**和**端口映射I/O**。之前,我们已经使用内存映射的方式,通过内存地址`0xb8000`访问了[VGA文本缓冲区]。该地址并没有映射到RAM,而是映射到了VGA设备的一部分内存上。
|
在x86平台上,CPU和外围硬件通信通常有两种方式,**内存映射I/O**和**端口映射I/O**。之前,我们已经使用内存映射的方式,通过内存地址`0xb8000`访问了[VGA文本缓冲区]。该地址并没有映射到RAM,而是映射到了VGA设备的一部分内存上。
|
||||||
|
|
||||||
[VGA text buffer]: @/second-edition/posts/03-vga-text-buffer/index.md
|
[VGA text buffer]: @/edition-2/posts/03-vga-text-buffer/index.md
|
||||||
|
|
||||||
与内存映射不同,端口映射I/O使用独立的I/O总线来进行通信。每个外围设备都有一个或数个端口号。CPU采用了特殊的`in`和`out`指令来和端口通信,这些指令要求一个端口号和一个字节的数据作为参数(有些这种指令的变体也允许发送`u16`或是`u32`长度的数据)。
|
与内存映射不同,端口映射I/O使用独立的I/O总线来进行通信。每个外围设备都有一个或数个端口号。CPU采用了特殊的`in`和`out`指令来和端口通信,这些指令要求一个端口号和一个字节的数据作为参数(有些这种指令的变体也允许发送`u16`或是`u32`长度的数据)。
|
||||||
|
|
||||||
@@ -309,7 +309,7 @@ lazy_static! {
|
|||||||
|
|
||||||
和 `isa-debug-exit`设备一样,UART也是用过I/O端口进行编程的。由于UART相对来讲更加复杂,它使用多个I/O端口来对不同的设备寄存器进行编程。不安全的`SerialPort::new`函数需要UART的第一个I/O端口的地址作为参数,从该地址中可以计算出所有所需端口的地址。我们传递的端口地址为`0x3F8` ,该地址是第一个串行接口的标准端口号。
|
和 `isa-debug-exit`设备一样,UART也是用过I/O端口进行编程的。由于UART相对来讲更加复杂,它使用多个I/O端口来对不同的设备寄存器进行编程。不安全的`SerialPort::new`函数需要UART的第一个I/O端口的地址作为参数,从该地址中可以计算出所有所需端口的地址。我们传递的端口地址为`0x3F8` ,该地址是第一个串行接口的标准端口号。
|
||||||
|
|
||||||
[vga lazy-static]: @/second-edition/posts/03-vga-text-buffer/index.md#lazy-statics
|
[vga lazy-static]: @/edition-2/posts/03-vga-text-buffer/index.md#lazy-statics
|
||||||
|
|
||||||
为了使串口更加易用,我们添加了 `serial_print!` 和 `serial_println!`宏:
|
为了使串口更加易用,我们添加了 `serial_print!` 和 `serial_println!`宏:
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |