Skip to content

Commit 0b4d2f7

Browse files
committed
Added Design documentation
1 parent 0898aa9 commit 0b4d2f7

File tree

1 file changed

+85
-2
lines changed

1 file changed

+85
-2
lines changed

README.md

+85-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,75 @@ Written for the [second edition](https://github.com/phil-opp/blog_os/issues/360)
88

99
## Design
1010

11-
TODO
11+
When you press the power button the computer loads the BIOS from some flash memory stored on the motherboard.
12+
The BIOS initializes and self tests the hardware then loads the first 512 bytes into memory from the media device
13+
(i.e. the cdrom or floppy disk). If the last two bytes equal 0xAA55 then the BIOS will jump to location 0x7C00 effectively
14+
transferring control to the bootloader. At this point the CPU is running in 16 bit mode,
15+
meaning only the 16 bit registers are available. Also since the BIOS only loads the first 512 bytes this means our bootloader
16+
code has to stay below that limit, otherwise we’ll hit uninitialised memory!
17+
Using [Bios interrupt calls](https://en.wikipedia.org/wiki/BIOS_interrupt_call) the bootloader prints debug information to the screen.
18+
For more information on how to write a bootloader click [here](http://3zanders.co.uk/2017/10/13/writing-a-bootloader/)
19+
20+
* stage_1.s
21+
This stage initializes the stack, enables the A20 line, loads the rest of
22+
the bootloader from disk, and jumps to stage_2.
23+
24+
* stage_2.s
25+
This stage sets the target operating mode, loads the kernel from disk,
26+
creates an e820 memory map, enters protected mode, and jumps to the
27+
third stage.
28+
29+
* stage_3.s
30+
This stage performs some checks on the CPU (cpuid, long mode), sets up an
31+
initial page table mapping (identity map the bootloader, map the P4
32+
recursively, map the kernel blob to 4MB), enables paging, switches to long
33+
mode, and jumps to stage_4.
34+
35+
36+
## Build chain
37+
The file `.cargo/config` defines an llvm target file called `x86_64-bootloader.json`.
38+
This file defines the architecture sets freestanding flags and tells llvm to use the linker script `linker.ld`.
39+
40+
The linker script tells the linker at which offsets the sections should be mapped to. In our case it tells the linker
41+
that the bootloader asm files stage_0-3.s should be mapped to the very beginning of the executable. Read more about linker scripts
42+
[here](https://www.sourceware.org/binutils/docs/ld/Scripts.html)
43+
44+
Another important role plays the file `build.rs`.
45+
Placing a file named `build.rs` in the root of a package will cause
46+
Cargo to compile that script and execute it just before building the package.
47+
You can read more about it [here](https://doc.rust-lang.org/cargo/reference/build-scripts.html).
48+
The `build.rs` file execute the llvm tools you installed with `rustup component add llvm-tools-preview`
49+
in this order:
50+
51+
* Check size of .text section of the kernel if it's too small throw an error
52+
```bash
53+
llvm-size "../../target/x86_64-os/debug/svm_kernel"
54+
```
55+
56+
* Strip debug symbols from kernel to make loading faster
57+
```bash
58+
llvm-objcopy "--strip-debug" "../../target/x86_64-os/debug/svm_kernel" "target/x86_64-bootloader/debug/build/bootloader-c8df27c930d8f65a/out/kernel_stripped-svm_kernel"
59+
```
60+
* Rename the .data section to .kernel in the stripped kernel.
61+
Objcopy when using `--binary-architecture` flag creates three synthetic symbols
62+
`_binary_objfile_start`, `_binary_objfile_end`, `_binary_objfile_size.`.
63+
These symbols use the project / binary name which is why we have to rename them to something more generic
64+
to be able to reference them.
65+
```bash
66+
llvm-objcopy "-I" "binary" "-O" "elf64-x86-64" "--binary-architecture=i386:x86-64" "--rename-section" ".data=.kernel" "--redefine-sym" "_binary_kernel_stripped_svm_kernel_start=_kernel_start_addr" "--redefine-sym" "_binary_kernel_stripped_svm_kernel_end=_kernel_end_addr" "--redefine-sym" "_binary_kernel_stripped_svm_kernel_size=_kernel_size" "target/x86_64-bootloader/debug/build/bootloader-c8df27c930d8f65a/out/kernel_stripped-svm_kernel" "target/x86_64-bootloader/debug/build/bootloader-c8df27c930d8f65a/out/kernel_bin-svm_kernel.o"
67+
```
68+
* Now create a static library out of it
69+
```bash
70+
llvm-ar "crs" "bootloader/target/x86_64-bootloader/debug/build/bootloader-c8df27c930d8f65a/out/libkernel_bin-svm_kernel.a" "target/x86_64-bootloader/debug/build/bootloader-c8df27c930d8f65a/out/kernel_bin-svm_kernel.o"
71+
```
72+
Afterwards `build.rs` tells cargo to use the newly created static library to link against your kernel, with the help of the linker script everything gets placed correctly in the
73+
resulting elf file.
74+
The last step is to strip away the elf header from the resulting elf binary so that the bios can jump directly to the bootloader `stage_1.s`. This is done with:
75+
```bash
76+
cargo objcopy -- -I elf64-x86-64 -O binary --binary-architecture=i386:x86-64 \
77+
target/x86_64-bootloader/release/bootloader target/x86_64-bootloader/release/bootloader.bin
78+
```
79+
1280

1381
## Configuration
1482

@@ -87,13 +155,28 @@ dd if=target/x86_64-bootloader/release/bootloader.bin of=/dev/sdX && sync
87155

88156
Where sdX is the device name of your USB stick. **Be careful** to choose the correct device name, because everything on that device is overwritten.
89157

158+
## Debugging
159+
Set a breakpoint at address `0x7c00`. Disassemble instructions with gdb:
160+
```bash
161+
qemu-system-x86_64 -drive format=raw,file=target/x86_64-bootloader/release/bootloader.bin -s -S
162+
```
163+
```
164+
(gdb) target remote: 1234
165+
(gdb) b *0x7c00
166+
(gdb) x/i $rip
167+
```
168+
169+
If you use the `-enable-kvm` flag you need to use hardware breakpoints `hb`.
170+
90171
## Features
91172
The bootloader crate can be configured through some cargo features:
92173

93174
- `vga_320x200`: This feature switches the VGA hardware to mode 0x13, a graphics mode with resolution 320x200 and 256 colors per pixel. The framebuffer is linear and lives at address `0xa0000`.
94175
- `recursive_page_table`: Maps the level 4 page table recursively and adds the [`recursive_page_table_address`](https://docs.rs/bootloader/0.4.0/bootloader/bootinfo/struct.BootInfo.html#structfield.recursive_page_table_addr) field to the passed `BootInfo`.
95176
- `map_physical_memory`: Maps the complete physical memory in the virtual address space and passes a [`physical_memory_offset`](https://docs.rs/bootloader/0.4.0/bootloader/bootinfo/struct.BootInfo.html#structfield.physical_memory_offset) field in the `BootInfo`.
96-
- The virtual address where the physical memory should be mapped is configurable by setting the `physical-memory-offset` field in the kernel's `Cargo.toml`, as explained in [Configuration](#Configuration).
177+
- `sse` enables sse instruction support
178+
- The virtual address where the physical memory should be mapped is configurable by setting the `physical-memory-offset` field in the kernel's `Cargo.toml`, as explained in [Configuration](#Configuration).
179+
97180

98181
## Advanced Documentation
99182
See these guides for advanced usage of this crate:

0 commit comments

Comments
 (0)