My Bare Metal RPi journey

Evan Brass
4 min readJul 30, 2021

I bought a Raspberry Pi 3 B+ a few years ago about the time I started learning Rust which was also about when I took OS dev. I wanted to learn OS development and I’d heard about CS140e so I thought it would be relatively easy. And it has been relatively easy when compared to interchanging the OS on my laptop which is the alternative I was considering. The Raspberry Pi has a lot of tutorials and examples, it’s hard to brick, it can netboot, it has user and kernel mode, and it has GPIO that would be fun to play with.

When I got the board, I first loaded Rasbian and played around with it for a little while. Then I started trying to do bare metal. I was following a Rust tutorial which was very helpful for setting up cross compiling, turning the elf file into a binary, disassembling to debug, etc. But I couldn’t get passed the first tutorial (getting the on-board green ACT led to blink) until I released that I had a Raspberry Pi 3B+ not a Raspberry Pi 3B which the tutorial was based on. The tutorial had said that the ACT led wasn’t on the normal GPIO and that I needed to use a special undocumented VideoCore mailbox to control it. However, they put the ACT led back on normal GPIO for the B+ so what my board did instead was turn off the power led. I guess that could be useful if you were using RPi’s for hacking purposes or something. Anyway, I got the led to blink eventually.

My second challenge was getting the UART working. I think I had to refer to the BCM manual for memory addresses and do lots of troubleshooting. I don’t have a USB-to-serial cable so I’ve been using my Arduino with the microcontroller removed instead. If I remember correctly, I did get the MiniUART working before I dropped out of university.

OS dev is one of the classes I failed and never retook. I learned that OS development is hard — though that isn’t the entire reason I failed. It was harder because — whether due to my mis-planning or a glitch in my university program — my assembly and computer architecture class had taught me AVR assembly and architecture, but I was expected to know x86 assembly and architecture. I could barely get the inline assembly we needed working for the few assignments I completed. If I remember correctly, the last assignment I completed was setting up virtual memory. What I’m saying is that I didn’t get into the realm of normal OS stuff like task scheduling, driving devices like network controllers and screens, or file systems.

But… classes aren’t the only place to learn things and I’m so very grateful that there are resources online to teach bare metal programming. For me, learning off articles, blogs, and repos isn’t as efficient as lectures and assignments. But, the freedom to choose exactly what to learn may compensate for that. I suppose I even did that in university though. I rewrote the project skeletons for my graphics class in Rust, because I wanted to learn it. For a web dev class I used MySQL X — a document store api for MySQL that I wanted to learn because it was new and I was curious — instead of MongoDB which is what the class taught.

So, where is my bare metal project currently? I just brought it back out and I’m still at the UART stage. I’ve setup netbooting which makes my code-test cycle faster, I can output strings but I can’t use println! yet. I think this is due to the stack pointer not being setup properly as any binary’s with uninlined functions have failed to work. I’m not sure though because not even the code before the function call will run and… I don’t have a JTAG debugger.

My code is here:

One of the aspects I’ve really liked about using Rust for this is that it’s almost as easy as running cargo build . I can specify the default toolchain to use when building: aarch64-unknown-none which has tier 2 support — might need to switch to aarch64-unknown-none-softfloat if I find out what that means ;). I can also specify the toolchain which includes specifying that you’ll need the llvm-tools-preview component which provides the tools for turning the elf file into a binary and disassembling. I also think there might be a way of building directly into a binary using the linker script though I need to research that more. What I’m trying to say is that Rust is great because my embedded project didn’t need any kind of docker image with all the tools preinstalled or anything. It’s just there — all you need is rustup and cargo. Which I love so much. Rust is the best.

I still have a lot to do to get into the real operating system stuff like hosting other programs, but I think I can make it. I don’t think I would be able to make it on my own if I was trying to do this in C. Even though I’m writing a fair bit of unsafe code, Rust gives me a confidence boost. I trust the Rust compiler — even if I am getting a segfault on nightly when I don’t compile with --release — enough that if there’s a problem I’m confident it’s something I’m doing wrong and not some expected behavior of the language.



Evan Brass

I write a lot of ECMAScript… enough to have plenty of mistakes to learn from.