Want to build your own kernel in Rust? See the Bare Metal Rust page for more resources and more posts in this series.
Rust is a really fun language: It allows me to work on low-level kernel code, but it also allows me to wrap my code up in clean, high-level APIs. If you this sounds interesting, you should really check out Philipp Oppermann's blog posts about writing a basic x86_64 operating system kernel in Rust. He walks you through booting the kernel, entering long mode, getting Rust running, and printing text to the screen.
Once you get a basic kernel running, you'll probably want to start working
on basic I/O, which requires interrupts. And this point, you'll find that
pretty much every tutorial dives right into the in
and out
instructions. For example, if you look at the
OSDev.org introduction to interrupts, the very first code
you'll see is (comments added):
moval,20h;Moveinterruptacknowledgmentcodeintoal.out20h,al;WritealtoPIConport0x20.
Here, we're talking to the PIC ("Programmable Interrupt Controller"), and we're telling it that we've finished handling a processor interrupt. To do this, we need to write an 8-bit status code to the I/O port at address 0x20.
Traditionally, we would wrap this up in an outb
("out byte") function,
which might look something like this in Rust:
// The asm! macro requires a nightly build of Rust, and// we need to opt-in explicitly.#![feature(asm)]unsafefnoutb(value:u8,port:u16){asm!("outb %al, %dx"::"{dx}"(port),"{al}"(value)::"volatile");}
This writes an 8-byte value to the specified port. It uses the unstable
Rust extension asm!
, which allows us to use GCC/LLVM-style inline
assembly. We'd invoke it like this:
outb(0x20,0x20);
But let's see if we can wrap a higher-level API around an I/O port.