Skip to content

Commit 530b26f

Browse files
authored
Added Design documentation (#134)
1 parent 0898aa9 commit 530b26f

File tree

1 file changed

+87
-2
lines changed

1 file changed

+87
-2
lines changed

README.md

+87-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,77 @@ 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+
The assembler files get imported through the [global_asm feature](https://doc.rust-lang.org/unstable-book/library-features/global-asm.html).
20+
The assembler syntax definition used is the one llvm uses: [GNU Assembly](http://microelectronics.esa.int/erc32/doc/as.pdf).
21+
22+
* stage_1.s
23+
This stage initializes the stack, enables the A20 line, loads the rest of
24+
the bootloader from disk, and jumps to stage_2.
25+
26+
* stage_2.s
27+
This stage sets the target operating mode, loads the kernel from disk,
28+
creates an e820 memory map, enters protected mode, and jumps to the
29+
third stage.
30+
31+
* stage_3.s
32+
This stage performs some checks on the CPU (cpuid, long mode), sets up an
33+
initial page table mapping (identity map the bootloader, map the P4
34+
recursively, map the kernel blob to 4MB), enables paging, switches to long
35+
mode, and jumps to stage_4.
36+
37+
38+
## Build chain
39+
The file `.cargo/config` defines an llvm target file called `x86_64-bootloader.json`.
40+
This file defines the architecture sets freestanding flags and tells llvm to use the linker script `linker.ld`.
41+
42+
The linker script tells the linker at which offsets the sections should be mapped to. In our case it tells the linker
43+
that the bootloader asm files stage_0-3.s should be mapped to the very beginning of the executable. Read more about linker scripts
44+
[here](https://www.sourceware.org/binutils/docs/ld/Scripts.html)
45+
46+
Another important role plays the file `build.rs`.
47+
Placing a file named `build.rs` in the root of a package will cause
48+
Cargo to compile that script and execute it just before building the package.
49+
You can read more about it [here](https://doc.rust-lang.org/cargo/reference/build-scripts.html).
50+
The `build.rs` file execute the llvm tools you installed with `rustup component add llvm-tools-preview`
51+
in this order:
52+
53+
* Check size of .text section of the kernel if it's too small throw an error
54+
```bash
55+
llvm-size "../../target/x86_64-os/debug/svm_kernel"
56+
```
57+
58+
* Strip debug symbols from kernel to make loading faster
59+
```bash
60+
llvm-objcopy "--strip-debug" "../../target/x86_64-os/debug/svm_kernel" "target/x86_64-bootloader/debug/build/bootloader-c8df27c930d8f65a/out/kernel_stripped-svm_kernel"
61+
```
62+
* Rename the .data section to .kernel in the stripped kernel.
63+
Objcopy when using `--binary-architecture` flag creates three synthetic symbols
64+
`_binary_objfile_start`, `_binary_objfile_end`, `_binary_objfile_size.`.
65+
These symbols use the project / binary name which is why we have to rename them to something more generic
66+
to be able to reference them.
67+
```bash
68+
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"
69+
```
70+
* Now create a static library out of it
71+
```bash
72+
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"
73+
```
74+
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
75+
resulting elf file.
76+
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:
77+
```bash
78+
cargo objcopy -- -I elf64-x86-64 -O binary --binary-architecture=i386:x86-64 \
79+
target/x86_64-bootloader/release/bootloader target/x86_64-bootloader/release/bootloader.bin
80+
```
81+
1282

1383
## Configuration
1484

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

88158
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.
89159

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

93176
- `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`.
94177
- `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`.
95178
- `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).
179+
- `sse` enables sse instruction support
180+
- 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).
181+
97182

98183
## Advanced Documentation
99184
See these guides for advanced usage of this crate:

0 commit comments

Comments
 (0)