A barebones kernel in Nim

I’ve been fascinated by operating systems for a very long time. For someone who genuinely loves low-level programming, they’re the lowest you can get in our modern age, barring a few microcontroller applications. So I’ve spent the occasional weeks over the past 20 or so years looking into the field, wondering if there’s a way to make my mark on it. At the same time, I’ve been looking at ways to write those low-level programs.

Combining those two threads of research has led me to create Nim Limine Barebones.

What is it?

It’s pretty simple. This is a port of the Limine Bare Bones tutorial kernel to the Nim programming language. It doesn’t do much; it’s literally the “Hello World” of OS development. But it can be used as the start of something much greater.

Why Nim?

I’ve looked at a lot of different languages that purport to be suitable for low-level “systems” programming. I settled on Nim because I wanted to learn something new, but also because every other option has a flaw.

  • C is the gold standard for OS work, but it’s really a horrible language. Especially when you don’t have the luxury of an operating system to protect you from, say, filling every byte of memory with garbage.

  • C++ is one of my favorite languages anyway, but using it on bare metal is harder than you might think. A freestanding implementation has to throw out most of the standard library (i.e., the bits that make C++ worth using), so you’re mostly left with C plus classes.

  • Rust might be a decent language, but its syntax is as ugly as the politics of its developers. I’d never use it for an unpaid project.

  • Go, from all I’ve read, requires a hefty runtime to get started, and Google has no inclination to change that. It’s also a language I find annoying for some irrational reason.

  • D is pretty much the opposite of Rust in both politics and syntax. It would be a great choice if not for a bit of runtime you just can’t get rid of. Oh, and the fact that nobody can seem to agree on what, exactly, the language should be.

  • Zig looks okay, and I found it extremely interesting when I delved into it a few months back. Alas, it’s just too immature for production use, and the latest revisions of the compiler have completely removed some necessary options for bare-metal development.

Nim isn’t perfect by any means, but what I’ve seen so far makes it look like a good “better C” that doesn’t require too many hoops to get the runtime out of the way. For application development, it wouldn’t beat out C++ for me—things like multiple inheritance are just too useful—but at the OS level? Sure beats trying to write my own std::vector. (Seriously. Where are the minimal STL implementations to go along with mlibc?)

Why Limine?

Most OS tutorials are centered on Multiboot. After all, it is kind of a standard. Here, though, I went with Limine. It’s a little more obscure, and much newer; the project is only a few months old here at the start of 2023, as it is intended to replace the older Stivale bootloader.

Limine has a lot of advantages, in my opinion. It’s entirely 64-bit. It sets up a call stack for you, which mostly cuts out the need for assembly in the boot phase. Framebuffers are sensible, there’s an integrated terminal that can work until your own is ready, and it’s just nice in general.

That said, it does have its annoyances. It requires a “higher half” kernel, and that makes paging a necessity sooner than it should be. But the page tables Limine gives you are intentionally sparse. And for this project in particular, dealing with an array of function pointers is just awful. Surely there’s a better way.

Conclusion

All told, I’m happy with what I wrote. It’s a good start, and it fills a niche that nobody else was really looking at. Yes, there’s another barebones Nim kernel out there, and I took inspiration from it. I like to think I’ve provided a better starting point for myself and anyone who would like to follow in my footsteps.