RustForge

rust-kernel

Rustbelt: a hobby x86_64 kernel written in Rust

rust-kernel / README.md
7749 bytes · raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# rust-kernel

A tiny hobby x86_64 kernel written in Rust, following the 2nd edition of
Philipp Oppermann's [Writing an OS in Rust]https://os.phil-opp.com/.
Right now it boots in QEMU, prints a greeting on the VGA text screen and on
the serial port (COM1), and then halts.

![boot screenshot]boot.png

## What's installed (on this box)

| Tool | Version | Why |
|---|---|---|
| rustup | 1.29.2 | toolchain manager (in `~/.cargo/bin`, ahead of Debian's `/usr/bin/cargo`) |
| Rust nightly | rustc 1.101.0-nightly (2026-10-29) | custom JSON targets + `build-std` are nightly-only |
| `rust-src` component | – | source of `core`, recompiled for our bare-metal target |
| `llvm-tools-preview` component | – | `llvm-objcopy` etc., used by `bootimage` |
| `bootimage` (cargo subcommand) | 0.10.5 | links kernel + bootloader into a bootable disk image |
| QEMU (`qemu-system-x86`) | 10.0.13 | the emulator we boot in |

`rust-toolchain.toml` pins the project to nightly with the two components, so
`cargo` inside this directory picks them up automatically.

Crates: `bootloader` 0.9.35 (BIOS bootloader used by the 2nd edition),
`volatile` 0.2, `spin` 0.5, `x86_64` 0.15, `uart_16550` 0.3, `lazy_static` 1.5.

Fresh setup on another machine:

```sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh   # rustup
rustup toolchain install nightly -c rust-src -c llvm-tools-preview
cargo install bootimage
sudo apt install qemu-system-x86
```

## Build and run

```sh
cargo bootimage                     # build -> target/x86_64-rust_kernel/debug/bootimage-rust_kernel.bin
cargo run                           # build + boot in a QEMU window; serial output also prints in the terminal
                                    # (kernel halts forever, close the window or press Ctrl+C to quit)
```

Headless (no window, good for SSH / scripts):

```sh
./scripts/run-headless.sh           # builds with --features headless, boots with -display none,
                                    # kernel exits QEMU via isa-debug-exit; script exits 0 on success
cargo run --features headless -- -display none   # same thing via cargo
                                    # (QEMU's exit status is 33 = success; cargo just reports it as non-zero)
cargo run -- -display none          # headless but kernel keeps running; Ctrl+C to stop
./scripts/screenshot.sh [out.png]   # boots, saves the VGA screen to boot.png via QEMU monitor `screendump`
```

Expected serial output:

```
Hello from Tyler's Rust kernel! (via serial COM1)
Kernel booted successfully.
```

Extra args after `--` in `cargo run -- ...` are appended to the QEMU command
line, e.g. `cargo run -- -d int -no-reboot` to debug triple faults, or
`cargo run -- -s -S` to wait for `gdb` on port 1234.

## Project layout

```
.cargo/config.toml       build-std + custom target + "bootimage runner" as the cargo runner
rust-toolchain.toml      pin nightly + rust-src + llvm-tools-preview
x86_64-rust_kernel.json  custom target: no OS, no red zone, soft-float, abort on panic, rust-lld
Cargo.toml               deps, `headless` feature, QEMU run-args for `cargo run`
src/main.rs              entry point `_start`, panic handler, QEMU exit helper
src/vga_buffer.rs        VGA text-mode writer + print!/println! macros
src/serial.rs            COM1 UART writer + serial_print!/serial_println! macros
scripts/run-headless.sh  headless boot with timeout + exit-code check
scripts/screenshot.sh    headless boot + PNG screenshot via QEMU monitor
boot.png                 screenshot of the VGA screen
```

## Roadmap

Already done (roughly chapters 1–3, plus the serial/QEMU-exit bits of chapter 4):

- [x] Freestanding binary — [phil-opp]https://os.phil-opp.com/freestanding-rust-binary/
- [x] Minimal kernel booting in QEMU — [phil-opp]https://os.phil-opp.com/minimal-rust-kernel/, [OSDev: Bare Bones]https://wiki.osdev.org/Bare_Bones
- [x] VGA text mode printing — [phil-opp]https://os.phil-opp.com/vga-text-mode/, [OSDev: Printing to Screen]https://wiki.osdev.org/Printing_To_Screen
- [x] Serial output + QEMU exit — [phil-opp: Testing]https://os.phil-opp.com/testing/, [OSDev: Serial Ports]https://wiki.osdev.org/Serial_Ports

Next stages:

1. **Testing** — custom test framework (`cargo test` in QEMU). [phil-opp: Testing]https://os.phil-opp.com/testing/
2. **CPU exceptions / IDT** — breakpoint & page fault handlers. [phil-opp: CPU Exceptions]https://os.phil-opp.com/cpu-exceptions/, [OSDev: IDT]https://wiki.osdev.org/Interrupt_Descriptor_Table, [OSDev: Exceptions]https://wiki.osdev.org/Exceptions
3. **GDT / TSS / double faults** — separate stack so stack overflows don't triple-fault. [phil-opp: Double Faults]https://os.phil-opp.com/double-fault-exceptions/, [OSDev: GDT]https://wiki.osdev.org/Global_Descriptor_Table, [OSDev: TSS]https://wiki.osdev.org/Task_State_Segment
4. **Hardware interrupts** — 8259 PIC, timer, PS/2 keyboard. [phil-opp: Hardware Interrupts]https://os.phil-opp.com/hardware-interrupts/, [OSDev: 8259 PIC]https://wiki.osdev.org/8259_PIC, [OSDev: PIT]https://wiki.osdev.org/Programmable_Interval_Timer, [OSDev: PS/2 Keyboard]https://wiki.osdev.org/PS/2_Keyboard (later: [APIC]https://wiki.osdev.org/APIC)
5. **Paging** — 4-level page tables, mapping frames. [phil-opp: Introduction to Paging]https://os.phil-opp.com/paging-introduction/, [phil-opp: Paging Implementation]https://os.phil-opp.com/paging-implementation/, [OSDev: Paging]https://wiki.osdev.org/Paging
6. **Heap allocation** — `alloc` crate, `Box`/`Vec`, allocator designs. [phil-opp: Heap Allocation]https://os.phil-opp.com/heap-allocation/, [phil-opp: Allocator Designs]https://os.phil-opp.com/allocator-designs/, [OSDev: Memory Allocation]https://wiki.osdev.org/Memory_Allocation
7. **Multitasking** — cooperative async/await executor first, then preemptive threads. [phil-opp: Async/Await]https://os.phil-opp.com/async-await/, [OSDev: Multitasking Systems]https://wiki.osdev.org/Multitasking_Systems, [OSDev: Brendan's Multi-tasking Tutorial]https://wiki.osdev.org/Brendan%27s_Multi-tasking_Tutorial
8. **User mode & syscalls** — ring 3, `syscall`/`sysret`, loading ELF programs. (Beyond the phil-opp series.) [OSDev: Getting to Ring 3]https://wiki.osdev.org/Getting_to_Ring_3, [OSDev: System Calls]https://wiki.osdev.org/System_Calls, [OSDev: SYSENTER/SYSCALL]https://wiki.osdev.org/SYSENTER, [OSDev: ELF]https://wiki.osdev.org/ELF
9. **Filesystem & shell** — VFS, a RAM disk / FAT, a tiny shell reading the keyboard. [OSDev: File Systems]https://wiki.osdev.org/File_Systems, [OSDev: VFS]https://wiki.osdev.org/VFS, [OSDev: FAT]https://wiki.osdev.org/FAT, [OSDev: Initrd]https://wiki.osdev.org/Initrd

Other good references: [OSDev wiki main page]https://wiki.osdev.org/Expanded_Main_Page,
[Intel SDM]https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdms.html,
the [blog_os repo]https://github.com/phil-opp/blog_os (one branch per post, e.g. `post-04`),
and for a UEFI/framebuffer setup later, the newer [`bootloader` 0.11]https://github.com/rust-osdev/bootloader crate.

## Notes / gotchas

- The target JSON changes occasionally with new nightlies (e.g. `target-pointer-width`
  became an integer, `rustc-abi` was added). If a nightly update breaks the build,
  compare with the latest `x86_64-blog_os.json` in the blog_os repo, or pin a date in
  `rust-toolchain.toml` (`channel = "nightly-2026-10-29"`).
- The "packages contain code that will be rejected by a future version of Rust: core"
  warning comes from building `core` with build-std and is harmless.
- `bootimage` prints "`CARGO_MANIFEST_DIR` env variable not set" when run outside
  `cargo run`; also harmless.