Skip to content

Commit 7744a08

Browse files
committed
Move footnotes to the bottom
1 parent 7e63bf0 commit 7744a08

File tree

7 files changed

+26
-22
lines changed

7 files changed

+26
-22
lines changed

blog/content/posts/01-multiboot-kernel/index.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,6 @@ If you don't know x86 assembly, here is some quick guide:
8181
- `dd` stands for `define double` (32bit) and `dw` stands for `define word` (16bit). They just output the specified 32bit/16bit constant.
8282
- the additional `0x100000000` in the checksum calculation is a small hack[^fn-checksum_hack] to avoid a compiler warning
8383

84-
[^fn-checksum_hack]: The formula from the table, `-(magic + architecture + header_length)`, creates a negative value that doesn't fit into 32bit. By subtracting from `0x100000000` (= 2^(32)) instead, we keep the value positive without changing its truncated value. Without the additional sign bit(s) the result fits into 32bit and the compiler is happy :).
85-
8684
We can already _assemble_ this file (which I called `multiboot_header.asm`) using `nasm`. It produces a flat binary by default, so the resulting file just contains our 24 bytes (in little endian if you work on a x86 machine):
8785

8886
```
@@ -167,8 +165,6 @@ Let's translate it:
167165
- the `.text` output section contains all input sections named `.text`
168166
- Sections named `.multiboot_header` are added to the first output section (`.boot`) to ensure they are at the beginning of the executable. This is necessary because GRUB expects to find the Multiboot header very early in the file.
169167

170-
[^Linker 1M]: We don't want to load the kernel to e.g. `0x0` because there are many special memory areas below the 1MB mark (for example the so-called VGA buffer at `0xb8000`, that we use to print `OK` to the screen).
171-
172168
So let's create the ELF object files and link them using our new linker script:
173169

174170
```
@@ -324,3 +320,8 @@ In the [next post] we will create a page table and do some CPU configuration to
324320

325321
[next post]: {{% relref "02-entering-longmode.md" %}}
326322
[long mode]: https://en.wikipedia.org/wiki/Long_mode
323+
324+
## Footnotes
325+
[^fn-checksum_hack]: The formula from the table, `-(magic + architecture + header_length)`, creates a negative value that doesn't fit into 32bit. By subtracting from `0x100000000` (= 2^(32)) instead, we keep the value positive without changing its truncated value. Without the additional sign bit(s) the result fits into 32bit and the compiler is happy :).
326+
327+
[^Linker 1M]: We don't want to load the kernel to e.g. `0x0` because there are many special memory areas below the 1MB mark (for example the so-called VGA buffer at `0xb8000`, that we use to print `OK` to the screen).

blog/content/posts/02-entering-longmode/index.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,6 @@ As I don't like these names, I will call them P4, P3, P2, and P1 from now on.
215215

216216
Each page table contains 512 entries and one entry is 8 bytes, so they fit exactly in one page (`512*8 = 4096`). To translate a virtual address to a physical address the CPU[^hardware_lookup] will do the following[^virtual_physical_translation_source]:
217217

218-
[^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.
219-
220-
[^virtual_physical_translation_source]: Image source: [Wikipedia](https://commons.wikimedia.org/wiki/File:X86_Paging_64bit.svg), with modified font size, page table naming, and removed sign extended bits. The modified file is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
221-
222218
![translation of virtual to physical addresses in 64 bit mode](/images/X86_Paging_64bit.svg)
223219

224220
1. Get the address of the P4 table from the CR3 register
@@ -259,8 +255,6 @@ To identity map the first gigabyte of our kernel with 512 2MiB pages, we need on
259255

260256
We can add these two tables at the beginning[^page_table_alignment] of the `.bss` section:
261257

262-
[^page_table_alignment]: Page tables need to be page-aligned as the bits 0-11 are used for flags. By putting these tables at the beginning of `.bss`, the linker can just page align the whole section and we don't have unused padding bytes in between.
263-
264258
```nasm
265259
...
266260
@@ -524,3 +518,10 @@ It's time to finally leave assembly behind and switch to [Rust]. Rust is a syste
524518

525519
[Rust]: https://www.rust-lang.org/
526520
[next post]: {{% relref "03-set-up-rust.md" %}}
521+
522+
## Footnotes
523+
[^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.
524+
525+
[^virtual_physical_translation_source]: Image source: [Wikipedia](https://commons.wikimedia.org/wiki/File:X86_Paging_64bit.svg), with modified font size, page table naming, and removed sign extended bits. The modified file is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
526+
527+
[^page_table_alignment]: Page tables need to be page-aligned as the bits 0-11 are used for flags. By putting these tables at the beginning of `.bss`, the linker can just page align the whole section and we don't have unused padding bytes in between.

blog/content/posts/04-printing-to-screen/index.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,6 @@ _Note_: You need to [cross compile binutils] to build it (or you create some sym
652652
[Rust Bare-Bones Kernel]: https://github.com/thepowersgang/rust-barebones-kernel
653653
[higher half]: http://wiki.osdev.org/Higher_Half_Kernel
654654
[cross compile binutils]: {{% relref "cross-compile-binutils.md" %}}
655-
[^fn-symlink]: You will need to symlink `x86_64-none_elf-XXX` to `/usr/bin/XXX` where `XXX` is in {`as`, `ld`, `objcopy`, `objdump`, `strip`}. The `x86_64-none_elf-XXX` files must be in some folder that is in your `$PATH`. But then you can only build for your x86_64 host architecture, so use this hack only for testing.
656655

657656
- [RustOS]: More advanced kernel that supports allocation, keyboard inputs, and threads. It also has a scheduler and a basic network driver.
658657
[RustOS]: https://github.com/RustOS-Fork-Holding-Ground/RustOS
@@ -663,3 +662,6 @@ _Note_: You need to [cross compile binutils] to build it (or you create some sym
663662
- [Redox]: Probably the most complete Rust OS today. It has an active community and over 1000 Github stars. File systems, network, an audio player, a picture viewer, and much more. Just take a look at the [screenshots][redox screenshots].
664663
[Redox]: https://github.com/redox-os/redox
665664
[redox screenshots]: https://github.com/redox-os/redox#what-it-looks-like
665+
666+
## Footnotes
667+
[^fn-symlink]: You will need to symlink `x86_64-none_elf-XXX` to `/usr/bin/XXX` where `XXX` is in {`as`, `ld`, `objcopy`, `objdump`, `strip`}. The `x86_64-none_elf-XXX` files must be in some folder that is in your `$PATH`. But then you can only build for your x86_64 host architecture, so use this hack only for testing.

blog/content/posts/06-page-tables/index.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,6 @@ _What happens if we call them on a P1 table?_
346346

347347
Well, they would calculate the address of the next table (which does not exist) and treat it as a page table. Either they construct an invalid address (if `XXX < 400`)[^fn-invalid-address] or access the mapped page itself. That way, we could easily corrupt memory or cause CPU exceptions by accident. So these two functions are not _safe_ in Rust terms. Thus we need to make them `unsafe` functions unless we find some clever solution.
348348

349-
[^fn-invalid-address]: If the `XXX` part of the address is smaller than `0o400`, it's binary representation doesn't start with `1`. But the sign extension bits, which should be a copy of that bit, are `1` instead of `0`. Thus the address is not valid.
350-
351349
## Some Clever Solution
352350
We can use Rust's type system to statically guarantee that the `next_table` methods can only be called on P4, P3, and P2 tables, but not on a P1 table. The idea is to add a `Level` parameter to the `Table` type and implement the `next_table` methods only for level 4, 3, and 2.
353351

@@ -887,4 +885,7 @@ Afterwards, we will use this paging module to build a heap allocator. This will
887885

888886
<small>Image sources: [^virtual_physical_translation_source]</small>
889887

888+
## Footnotes
889+
[^fn-invalid-address]: If the `XXX` part of the address is smaller than `0o400`, it's binary representation doesn't start with `1`. But the sign extension bits, which should be a copy of that bit, are `1` instead of `0`. Thus the address is not valid.
890+
890891
[^virtual_physical_translation_source]: Image sources: Modified versions of an image from [Wikipedia](https://commons.wikimedia.org/wiki/File:X86_Paging_64bit.svg). The modified files are licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.

blog/content/posts/07-remap-the-kernel/index.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ Also, we will use the [information about kernel sections] to map the various sec
2727
## Preparation
2828
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].
2929
[set up gdb]: {{% relref "set-up-gdb.md" %}}
30-
[^fn-debug-notes]: For this post the most useful GDB command is probably `p/x *((long int*)0xfffffffffffff000)@512`. It prints all entries of the recursively mapped P4 table by interpreting it as an array of 512 long ints (the `@512` is GDB's array syntax). Of course you can also print other tables by adjusting the address.
3130

3231
We also update the `Page` and `Frame` types to make our lives easier. The `Page` struct gets some derived traits:
3332

@@ -64,9 +63,7 @@ We can't do that for a `Frame`. If we wanted to be sure that a given frame is un
6463
## Recap: The Paging Module
6564
This post builds upon the post about [page tables][previous post], so let's start by quickly recapitulating what we've done there.
6665

67-
We created a `memory::paging` module, which reads and modifies the hierarchical page table through recursive mapping. The owner of the active P4 table and thus all subtables is an `ActivePageTable`[^fn-apt-renamed] struct, which must be instantiated only once.
68-
69-
[^fn-apt-renamed]: It was renamed in [#88](https://github.com/phil-opp/blog_os/pull/88). The previous name was `RecursivePageTable`.
66+
We created a `memory::paging` module, which reads and modifies the hierarchical page table through recursive mapping. The owner of the active P4 table and thus all subtables is an `ActivePageTable` struct, which must be instantiated only once.
7067

7168
The `ActivePageTable` struct provides the following interface:
7269

@@ -1077,3 +1074,6 @@ Now that we have a (mostly) safe kernel stack and a working page table module, w
10771074
[Box]: https://doc.rust-lang.org/nightly/alloc/boxed/struct.Box.html
10781075
[Vec]: https://doc.rust-lang.org/nightly/collections/vec/struct.Vec.html
10791076
[BTreeMap]: https://doc.rust-lang.org/nightly/collections/btree_map/struct.BTreeMap.html
1077+
1078+
## Footnotes
1079+
[^fn-debug-notes]: For this post the most useful GDB command is probably `p/x *((long int*)0xfffffffffffff000)@512`. It prints all entries of the recursively mapped P4 table by interpreting it as an array of 512 long ints (the `@512` is GDB's array syntax). Of course you can also print other tables by adjusting the address.

blog/content/posts/08-kernel-heap/index.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -680,9 +680,7 @@ I created the [linked_list_allocator] crate to handle all of these cases. It con
680680
[Heap struct]: http://phil-opp.github.io/linked-list-allocator/linked_list_allocator/struct.Heap.html
681681
[linked_list_allocator source]: https://github.com/phil-opp/linked-list-allocator
682682

683-
So we just need to implement Rust's allocation modules and integrate it into our kernel. We start by creating a new `hole_list_allocator`[^1] crate inside the `libs` directory:
684-
685-
[^1]: The name `linked_list_allocator` is already taken, sorry :P.
683+
So we just need to implement Rust's allocation modules and integrate it into our kernel. We start by creating a new `hole_list_allocator` crate inside the `libs` directory:
686684

687685
```shell
688686
> cd libs

blog/content/posts/09-handling-exceptions/index.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,6 @@ The answer is that the stored instruction pointer only points to the causing ins
444444

445445
The reason for the diffent instruction pointer values is that the stored value is also the return address. So for faults, the instruction that caused the exception is restarted and might cause the same exception again if it's not resolved. This would not make much sense for traps, since invoking the breakpoint exception again would just cause another breakpoint exception[^fn-breakpoint-restart-use-cases]. Thus the instruction pointer points to the _next_ instruction for these exceptions.
446446

447-
[^fn-breakpoint-restart-use-cases]: There are valid use cases for restarting an instruction that caused a breakpoint. The most common use case is a debugger: When setting a breakpoint on some code line, the debugger overwrites the corresponding instruction with an `int3` instruction, so that the CPU traps when that line is executed. When the user continues execution, the debugger swaps in the original instruction and continues the program from the replaced instruction.
448-
449447
In some cases, the distinction between faults and traps is vague. For example, the [debug exception] behaves like a fault in some cases, but like a trap in others. So to find out the meaning of the saved instruction pointer, it is a good idea to read the official documentation for the exception, which can be found in the [AMD64 manual] in Section 8.2. For example, for the breakpoint exception it says:
450448

451449
[debug exception]: http://wiki.osdev.org/Exceptions#Debug
@@ -468,3 +466,6 @@ We've successfully caught our first exception and returned from it! The next ste
468466

469467
[triple fault]: http://wiki.osdev.org/Triple_Fault
470468
[double faults]: http://wiki.osdev.org/Double_Fault#Double_Fault
469+
470+
## Footnotes
471+
[^fn-breakpoint-restart-use-cases]: There are valid use cases for restarting an instruction that caused a breakpoint. The most common use case is a debugger: When setting a breakpoint on some code line, the debugger overwrites the corresponding instruction with an `int3` instruction, so that the CPU traps when that line is executed. When the user continues execution, the debugger swaps in the original instruction and continues the program from the replaced instruction.

0 commit comments

Comments
 (0)