Boot
OS in Rust
Recall
Where we left off.
- We had add a
.cargo/config.tomland updated our existing files. - We were able to build an executable for a bare metal target.
- We have not yet run the executable, and will have to do so using
qemu.
qemu
- Let’s break out
qemu
$ qemu-system-x86_64 -kernel target/x86_64-osirs/debug/osirs
Command 'qemu-system-x86_64' not found, but can be installed with:
sudo apt install qemu-system-x86 # version 1:6.2+dfsg-2ubuntu6.27, or
sudo apt install qemu-system-x86-xen # version 1:6.2+dfsg-2ubuntu6.27- Oh right, we only installed “misc”
qemu- Real ones will use
apt
- Real ones will use
Boot it
$ qemu-system-x86_64 -kernel target/x86_64-osirs/debug/osirs
qemu-system-x86_64: Error loading uncompressed kernel without PVH ELF Note- Oh right, we did precisely nothing with the BIOS and the bootloader and all that and still need to do those things.
- I bet that would be a fun topic for a lab.
Main File
- Unaltered except loops for stability.
Config File
- All new, only works with nightly.
Target File
- All new, this is the nightly version.
x86_64-osirs.json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": 64,
"target-c-int-width": 32,
"os": "none",
"executables": true,
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"panic-strategy": "abort",
"disable-redzone": true
}Cargo File
- Move panic specification to target.
The Lab
Running our Kernel
- Now that we have an executable that does something perceptible, it is time to run it.
- First, we need to turn our compiled kernel into a bootable disk image by linking it with a bootloader.
- Then we can run the disk image in
qemuvirtual machine… - …or boot it on real hardware using a USB stick.
- If you do that, you are responsible for what happens.
Creating a Bootimage
- To turn our compiled kernel into a bootable disk image, we need to link it with a bootloader.
- Recall the bootloader is responsible for initializing the CPU and loading our kernel.
- MU/TH/UR dumping a bucket of electrons atop a CPU.
Work Smart Not Hard
- We do not write our own bootloader1.
- We use the
bootloadercrate. - This crate implements a basic BIOS bootloader without any C dependencies.
- Just Rust and
- inline assembly (
asm!, handwaved for the compilers class)
- To use it for booting our kernel, we need to add a dependency on it:
Cargo.toml
- Note: We use
bootloader v0.9. - Newer versions allegedly use a different build system and will result in build errors.
- I didn’t check.
Onward!
- Adding the bootloader as a dependency is not enough to actually create a bootable disk image.
- The problem is that we need to link our kernel with the bootloader after compilation.
- But Cargo has no support for post-build scripts.
- Typical Cargo L.
Install Bootimage
- To solve this problem, use a tool named
bootimage - It first compiles the kernel and bootloader.
- Then it links them together to create a bootable disk image.
- To install the tool, we will use… cargo:
Create Bootimage
- Once installed it is a simple matter to use:
- This will obviously work on the first try, unless it doesn’t.
- In point of fact, I expect you to be able to fix two or three sequential errors you should see after using this command.
- Stop here and get a bootimage working with no errors.
- You can consult lecture materials and also review the recommendations of Cargo when it reports an error.
- If you get stuck here’s a spoiler.
How does it work?
The bootimage tool performs the following steps behind the scenes:
- It compiles our kernel to an ELF file.
- It compiles the bootloader dependency as a standalone executable.
- It links the bytes of the kernel ELF file to the bootloader.
- Read about the rust-osdev/bootloader
On Boot
- When booted, the bootloader reads and parses the appended ELF file.
- It then maps the program segments to virtual addresses.
- Zeroes the
.bsssection, and sets up a stack.- .bss holds static variables.
- Like
static mut BUSfrom the Malloc assignment.
- It reads the entry point address (
_start). - It “jumps” to
_startand begins executing the code there.
Booting it in QEMU
- We can now boot the disk image in a virtual machine.
- To boot it in
qemu, execute the following command:
- Naturally this won’t work if you have other names for your files, but hopefully you get the gist.
- This opens a separate window “QEMU” window and currently display nothing.
- We want to continue to open this graphics window so we have somewhere to look at text when we finally get it to work!
Real Machine
Don’t do this
Don’t do this.
- It is also possible to write it to a USB stick and boot it on a real machine, but be careful to choose the correct device name, because everything on that device is overwritten:
- Where
sdXis the device name of your USB stick. - After writing the image to the USB stick, you can run it on real hardware by booting from it.
- You probably need to use a special boot menu or change the boot order in your BIOS configuration to boot from the USB stick.
- Note that it currently doesn’t work for UEFI machines, since the
bootloadercrate has no UEFI support yet.
Using cargo run
To make it easier to run our kernel in QEMU, we can set the runner configuration key for cargo:
.cargo/config.toml
target.'cfg(target_os = "none")'matches any case where"os"is"none".- Like our custom target.
- The
runnerkey specifies the command that should be invoked forcargo run. - The command is run after a successful build with the executable path passed as the first argument.
Runners
- The
bootimage runnercommand is specifically designed to be usable as arunnerexecutable. - It links the given executable with the project’s bootloader dependency and then launches QEMU.
- See the Readme of
bootimagefor more details and possible configuration options. - Now we can use
cargo runto compile our kernel and boot it in QEMU!
Fin
$ cargo +nightly r
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.06s
Running `bootimage runner target/x86_64-osirs/debug/osirs`
Building bootloader
Finished `release` profile [optimized + debuginfo] target(s) in 0.06s
Running: `qemu-system-x86_64 -drive format=raw,file=target/x86_64-osirs/debug/bootimage-osirs.bin`Footnotes
This would be cool and fun and we should do it sometime, we just have other things to do!↩︎