mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-18 07:17:49 +00:00
Replace unsupported highlight syntax with normal code blocks
This commit is contained in:
@@ -139,7 +139,7 @@ To create the ELF _executable_, we need to [link] the object files together. We
|
||||
[link]: https://en.wikipedia.org/wiki/Linker_(computing)
|
||||
[linker script]: https://sourceware.org/binutils/docs/ld/Scripts.html
|
||||
|
||||
```
|
||||
```ld
|
||||
ENTRY(start)
|
||||
|
||||
SECTIONS {
|
||||
|
||||
@@ -214,10 +214,10 @@ To fix it, we can implement the [Copy] trait for the `ColorCode` type. The easie
|
||||
[Copy]: https://doc.rust-lang.org/nightly/core/marker/trait.Copy.html
|
||||
[derive macro]: http://rustbyexample.com/trait/derive.html
|
||||
|
||||
{{< highlight rust "hl_lines=1" >}}
|
||||
```rust
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct ColorCode(u8);
|
||||
{{< / highlight >}}
|
||||
```
|
||||
|
||||
We also derive the [Clone] trait, since it's a requirement for `Copy`, and the [Debug] trait, which allows us to print this field for debugging purposes.
|
||||
|
||||
@@ -230,7 +230,7 @@ However, the [documentation for Copy] says: _“if your type can implement Copy,
|
||||
|
||||
[documentation for Copy]: https://doc.rust-lang.org/core/marker/trait.Copy.html#when-should-my-type-be-copy
|
||||
|
||||
{{< highlight rust "hl_lines=2 6" >}}
|
||||
```rust
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
@@ -239,7 +239,7 @@ pub enum Color {...}
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
struct ScreenChar {...}
|
||||
{{< / highlight >}}
|
||||
```
|
||||
|
||||
### Try it out!
|
||||
To write some characters to the screen, you can create a temporary function:
|
||||
@@ -315,7 +315,7 @@ Instead of a `ScreenChar`, we're now using a `Volatile<ScreenChar>`. (The `Volat
|
||||
|
||||
This means that we have to update our `Writer::write_byte` method:
|
||||
|
||||
{{< highlight rust "hl_lines=8 11" >}}
|
||||
```rust
|
||||
impl Writer {
|
||||
pub fn write_byte(&mut self, byte: u8) {
|
||||
match byte {
|
||||
@@ -333,7 +333,7 @@ impl Writer {
|
||||
}
|
||||
...
|
||||
}
|
||||
{{< / highlight >}}
|
||||
```
|
||||
|
||||
Instead of a normal assignment using `=`, we're now using the `write` method. This guarantees that the compiler will never optimize away this write.
|
||||
|
||||
@@ -375,14 +375,14 @@ The `Ok(())` is just a `Ok` Result containing the `()` type. We can drop the `pu
|
||||
|
||||
Now we can use Rust's built-in `write!`/`writeln!` formatting macros:
|
||||
|
||||
{{< highlight rust "hl_lines=2 4 5 6" >}}
|
||||
```rust
|
||||
// in the `print_something` function
|
||||
use core::fmt::Write;
|
||||
let mut writer = Writer {...};
|
||||
writer.write_byte(b'H');
|
||||
writer.write_str("ello! ");
|
||||
write!(writer, "The numbers are {} and {}", 42, 1.0/3.0);
|
||||
{{< / highlight >}}
|
||||
```
|
||||
|
||||
Now you should see a `Hello! The numbers are 42 and 0.3333333333333333` at the bottom of the screen.
|
||||
|
||||
@@ -563,7 +563,7 @@ pub fn clear_screen() {
|
||||
### Hello World using `println`
|
||||
To use `println` in `lib.rs`, we need to import the macros of the VGA buffer module first. Therefore we add a `#[macro_use]` attribute to the module declaration:
|
||||
|
||||
{{< highlight rust "hl_lines=3 9 10" >}}
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
#[macro_use]
|
||||
@@ -577,7 +577,7 @@ pub extern fn rust_main() {
|
||||
|
||||
loop{}
|
||||
}
|
||||
{{< / highlight >}}
|
||||
```
|
||||
|
||||
Since we imported the macros at crate level, they are available in all modules and thus provide an easy and safe interface to the VGA buffer.
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ A double fault behaves like a normal exception. It has the vector number `8` and
|
||||
### Triggering a Double Fault
|
||||
Let's provoke a double fault by triggering an exception for that we didn't define a handler function:
|
||||
|
||||
{{< highlight rust "hl_lines=10" >}}
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
#[no_mangle]
|
||||
@@ -43,7 +43,7 @@ pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
||||
println!("It did not crash!");
|
||||
loop {}
|
||||
}
|
||||
{{< / highlight >}}
|
||||
```
|
||||
|
||||
We try to write to address `0xdeadbeaf`, but the corresponding page is not present in the page tables. Thus, a page fault occurs. We haven't registered a page fault handler in our [IDT], so a double fault occurs.
|
||||
|
||||
@@ -63,7 +63,7 @@ So in order to prevent this triple fault, we need to either provide a handler fu
|
||||
### A Double Fault Handler
|
||||
A double fault is a normal exception with an error code, so we can use our `handler_with_error_code` macro to create a wrapper function:
|
||||
|
||||
{{< highlight rust "hl_lines=8 14" >}}
|
||||
```rust
|
||||
// in src/interrupts.rs
|
||||
|
||||
lazy_static! {
|
||||
@@ -84,7 +84,7 @@ extern "x86-interrupt" fn double_fault_handler(
|
||||
println!("\nEXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
|
||||
loop {}
|
||||
}
|
||||
{{< / highlight >}}<!--end_-->
|
||||
```
|
||||
|
||||
Our handler prints a short error message and dumps the exception stack frame. The error code of the double fault handler is always zero, so there's no reason to print it.
|
||||
|
||||
@@ -160,7 +160,7 @@ So the CPU tries to call our _double fault handler_ now. However, on a double fa
|
||||
|
||||
Let's try it ourselves! We can easily provoke a kernel stack overflow by calling a function that recurses endlessly:
|
||||
|
||||
{{< highlight rust "hl_lines=9 10 11 14" >}}
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
#[no_mangle]
|
||||
@@ -179,7 +179,7 @@ pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
||||
println!("It did not crash!");
|
||||
loop {}
|
||||
}
|
||||
{{< / highlight >}}
|
||||
```
|
||||
|
||||
When we try this code in QEMU, we see that the system enters a boot-loop again.
|
||||
|
||||
@@ -402,7 +402,7 @@ impl Add<usize> for Page {
|
||||
#### Allocating a Double Fault Stack
|
||||
Now we can allocate a new double fault stack by passing the memory controller to our `interrupts::init` function:
|
||||
|
||||
{{< highlight rust "hl_lines=8 11 12 21 22 23" >}}
|
||||
```rust
|
||||
// in src/lib.rs
|
||||
|
||||
#[no_mangle]
|
||||
@@ -429,7 +429,7 @@ pub fn init(memory_controller: &mut MemoryController) {
|
||||
|
||||
IDT.load();
|
||||
}
|
||||
{{< / highlight >}}
|
||||
```
|
||||
|
||||
We allocate a 4096 bytes stack (one page) for our double fault handler. Now we just need some way to tell the CPU that it should use this stack for handling double faults.
|
||||
|
||||
@@ -470,7 +470,7 @@ use x86_64::structures::tss::TaskStateSegment;
|
||||
|
||||
Let's create a new TSS in our `interrupts::init` function:
|
||||
|
||||
{{< highlight rust "hl_lines=3 9 10" >}}
|
||||
```rust
|
||||
// in src/interrupts.rs
|
||||
|
||||
use x86_64::VirtualAddress;
|
||||
@@ -487,7 +487,7 @@ pub fn init(memory_controller: &mut MemoryController) {
|
||||
|
||||
IDT.load();
|
||||
}
|
||||
{{< / highlight >}}
|
||||
```
|
||||
|
||||
We define that the 0th IST entry is the double fault stack (any other IST index would work too). We create a new TSS through the `TaskStateSegment::new` function and load the top address (stacks grow downwards) of the double fault stack into the 0th entry.
|
||||
|
||||
@@ -737,7 +737,7 @@ We now have a double fault stack and are able to create and load a TSS (which co
|
||||
|
||||
We already created a new TSS in our `interrupts::init` function. Now we can load this TSS by creating a new GDT:
|
||||
|
||||
{{< highlight rust "hl_lines=10 11 12 13" >}}
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
|
||||
pub fn init(memory_controller: &mut MemoryController) {
|
||||
@@ -755,7 +755,7 @@ pub fn init(memory_controller: &mut MemoryController) {
|
||||
|
||||
IDT.load();
|
||||
}
|
||||
{{< / highlight >}}
|
||||
```
|
||||
|
||||
However, when we try to compile it, the following errors occur:
|
||||
|
||||
@@ -814,7 +814,7 @@ The `Once` type allows us to initialize a `static` at runtime. It is safe becaus
|
||||
|
||||
So let's rewrite our `interrupts::init` function to use the static `TSS` and `GDT`:
|
||||
|
||||
{{< highlight rust "hl_lines=5 9 10 12 17 18" >}}
|
||||
```rust
|
||||
pub fn init(memory_controller: &mut MemoryController) {
|
||||
let double_fault_stack = memory_controller.alloc_stack(1)
|
||||
.expect("could not allocate double fault stack");
|
||||
@@ -837,7 +837,7 @@ pub fn init(memory_controller: &mut MemoryController) {
|
||||
|
||||
IDT.load();
|
||||
}
|
||||
{{< / highlight >}}
|
||||
```
|
||||
|
||||
Now it should compile again!
|
||||
|
||||
@@ -850,7 +850,7 @@ We're almost done. We successfully loaded our new GDT, which contains a TSS desc
|
||||
|
||||
For the first two steps, we need access to the `code_selector` and `tss_selector` variables outside of the closure. We can achieve this by moving the `let` declarations out of the closure:
|
||||
|
||||
{{< highlight rust "hl_lines=3 4 5 8 9 12 13 20 22" >}}
|
||||
```rust
|
||||
// in src/interrupts/mod.rs
|
||||
pub fn init(memory_controller: &mut MemoryController) {
|
||||
use x86_64::structures::gdt::SegmentSelector;
|
||||
@@ -877,7 +877,7 @@ pub fn init(memory_controller: &mut MemoryController) {
|
||||
|
||||
IDT.load();
|
||||
}
|
||||
{{< / highlight >}}
|
||||
```
|
||||
|
||||
We first set the descriptors to `empty` and then update them from inside the closure (which implicitly borrows them as `&mut`). Now we're able to reload the code segment register using [`set_cs`] and to load the TSS using [`load_tss`].
|
||||
|
||||
@@ -886,7 +886,7 @@ We first set the descriptors to `empty` and then update them from inside the clo
|
||||
|
||||
Now that we loaded a valid TSS and interrupt stack table, we can set the stack index for our double fault handler in the IDT:
|
||||
|
||||
{{< highlight rust "hl_lines=7 9" >}}
|
||||
```rust
|
||||
// in src/interrupt/mod.rs
|
||||
|
||||
lazy_static! {
|
||||
@@ -900,7 +900,7 @@ lazy_static! {
|
||||
...
|
||||
};
|
||||
}
|
||||
{{< / highlight >}}
|
||||
```
|
||||
|
||||
The `set_stack_index` method is unsafe because the the caller must ensure that the used index is valid and not already used for another exception.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user