Writing Your Second Operating System

With any skill for which there exist Internet tutorials, there are bound to be hundreds of tutorials covering the very basics. This is especially true for computer programming. The problem, though, is that once you get past the basics, the amount of resources available tends to thin out dramatically. The world is full of “your first OS kernel” articles, but what happens when you decide your first design was novice-made garbage, and want to create a better system from the ground up?

stallion-prototype-1 (GitHub) is/was my second attempt (not counting myriad mini-attempts along the way) at creating an operating system kernel. Clearly, I didn’t create the next Linux, and there are no user space programs, but I still consider it a success in terms of material learned. I don’t intend to start an OS project for some time, as I’m working on other things right now, but these are the things I’ll be focusing on:

What Is My Plan, And What Order Will I Do Things In?

At least, in my (albeit limited) experience, it’s pretty difficult to decide on the order you’ll add things to your system, because not only are there several components required before you can add any meaningful functionality to your kernel, but it’s also very difficult for novices to foresee what they’ll need to add when they are probably unaware of the existence of most of what they’ll soon be interacting with. For example, how would you know when to add ACPI code before you even know what ACPI is?

For the next version of Stallion, though, I’ve identified basic groups of functionality that should be accomplished at roughly at the same time:

  • Bootloader, higher-half kernel setup, GDT, IDT, mapping the kernel into pages
  • Setting up memory management – a kmalloc ported from liballoc (https://github.com/blanham/liballoc) will do fine

How Will I Boot My System?

OSDev intermediates have probably used GRUB before. However, I’m not talking about the bootloader here (I’m using GRUB2). Above, I mentioned that I’ll be building a higher-half kernel. This involves loading the kernel into memory at offset 0xC1000000 (or higher). The most common way to do this is load some “early” code that maps the kernel into the necessary memory.

My approach will be to have GRUB boot this early code, providing the kernel itself as an ELF module.

How Will I Test My System?

Unit testing is crucial for things that cannot (easily) be formally verified. The more exhaustive, the better, for something like this.

Testing an operating system in an emulator is probably not the easiest thing to do. But also, it’s important to note that not every component of the kernel directly needs to be run in an emulator. If you can abstract away the underlying system, then you can possibly create a simple test driver, and test the logical components using GoogleTest, or another unit testing library.

How Will I Make My System Secure?

To be honest, I don’t know. But some terms you want to familiarize yourself with are capabilities and ASLR. My system will be microkernel-based, so as long as I know what I’m doing, and at least prevent Heartbleed/Spectre, hopefully things will be alright. My OS will somewhat be like the “iOS of desktop,” so programs will have limited privileges to begin with.

How Will I Organize My Code?

Ultimately, I’ll probably split up the higher-half loader and kernel, and then split up the source code and actual user space items. More important than just organizing the code is also writing English-language documentation about each folder, including which functionality the files in that directory provides.

How Can I Isolate Platform-Specific Code?

Conversely to how you might test the system by abstracting away the underlying system, you can place all the platform-specific code into the individual implementations of the interface. For example, if you have a base InterruptHandler class, you can have an X86InterruptHandler and ArmInterruptHandler.

How Can I Take Advantage Of An IDE?

Setting up a build system via CMake alone is painful, and a bad idea because of all the shell stuff you have to do. Maybe I’ll have a make target that calls CMake? This way, I can use CLion to develop the kernel. I’ve tried bear and ran into issues generating a compile_commands.json, but since CMake can generate them, I can even edit with language server support in Vim.

How Can I Make Sure My Code Is Readable And Maintainable?

Not only by using clang format, and a style guide, but also documenting the organization of the project in English. If I don’t take care to follow good practices early, then the project will quickly become a chore to work on.

How Can I Get To User-Space ASAP?

All the cool stuff exists in user space. The goals of my theoretical operating system involve mostly having a slick UI, so I’d love to hack on that ASAP.

However, having a user interface requires not only drivers, but also all of the underlying infrastructure necessary to implement them. Maybe I can possibly load test programs without existing drivers by providing in-memory mock implementations?

How Can Other People Build My System?

Getting the toolchains necessary to build operating systems is a project in itself. For example, it’s very difficult, if not impossible, to build Linux on Mac, let alone Windows.

CMake itself is cross-platform, but make isn’t. Plus, the tools to build ISO’s, like GRUB, need to be built locally.

If other people can’t feasibly build your system, it’ll be impossible for them to contribute, which means your project will likely never reach the critical mass of users necessary to be important in any way.

When, And How, Am I Going To Build A GUI?

A GUI is my eventual goal. There’s no shame in wanting to have windows and nice things. After I have user-space (see above), I can start on that. My first idea for a nice UI is to build the system UI using embedded Chromium; this way, I could build applications using, maybe, React.

An alternative is to embed something Flutter, or yet, write the system UI manually in C/C++ (I would like to avoid this).

Why Would Someone Want To Use My System?

What unique does my system bring to the table? Do I execute on those goals well? How am I marketing the project? Do I even expect it to be a long term success? Am I treating the system as a project, or product? Is it user-friendly? Or is it just a side project? Why would someone bother using it?

Most Importantly – How Can I Measure Progress?

GitHub issues are a great idea. Combined with their “Projects” triage feature, it’s very possible to keep track of goals and milestones. The triage boards, when made public, also effectively serve as a project roadmap.

If we’re being honest here, I’m probably never going to actually start the project, let alone complete it. But maybe the reader will. Take the experience you used from failing your first project, and take it to finish your second.