Toy operating system
Ever since I learned programming I had wanted to write my own operating system. At first, I was simply too young to understand the sheer difficulty of this task and did not have a clue as to what this would have taken. So back in 2014, after I had gained a few years of experience writing C I decided to finally take on this project.
As a disclaimer: the goal of this project was to understand operating systems on a technical level and to learn systems programming. This means that I have not chosen to spend much time on choosing the proper algorithms and that the quality of the code and design choices varies wildly.
I started out by writing some of the basic memory management code around March 2014 and by May I had implemented enough infrastructure to run a trimmed down version of busybox and GCC using newlib. I had stubbed out a lot of the less critical syscalls used by these programs and as such not all of the busybox applets worked, so I first wrote a bunch of utilities such as login
and getty
.
Once I was satisfied with this limited userland I decided to work on hardware support. Up to this point I had been using a tmpfs like filesystem to test my kernel and hadn’t yet gotten around to implementing a disk driver or even PCI support. The ATA driver I initially wrote did not use DMA and was quite slow. As a first real filesystem I chose to use ext2
as it was relatively simple, but this did pose some issues as my filesystem layer was not quite compatible with ext2 yet. Adding this second filesystem driver helped me spot quite a few bugs in the VFS, block device and ELF loader code.
By July 20th 2014, the ext2 driver was mostly working and I had also fixed a load of other bugs in the kernel. I still did not support booting from anything else than ramfs
so the ext2 support was mostly used for large files such as the GCC port. All the work on the kernel was getting a bit annoying and I decided that adding a GUI would be fun, so I implemented a framebuffer driver, framebuffer console and added the mmap
syscall to expose it to userland.
I had not implemented networking so in order to provide IPC for the display server I chose SystemV IPC, which had a pretty simple interface and implementation.
At first I wrote a very simple compositor from scratch, but it simply could not keep up with the volume of data it had to move around. I did not feel like writing a SIMD version of this by hand so I rewrote it to use libcairo
and libpixman
for the heavy lifting. This was reasonably simple and by September 1st 2014 I had a simple desktop environment with a working terminal emulator running.
At this point, I had to put in some more work for high school and development slowed down, I did some more work on the ext2
driver and added some error handling as well as refactoring the codebase.
By March 2015 I was still having a bunch of memory corruption issues and was hoping that a port to a different architecture might help track these down, so I started working on an ARMv7 port targetting my old Nokia N900 phone, this took me about a month: By March 29th I had it booting to /sbin/init
on ARM:
Unfortunately, I never got around to run this build on a real device, but I do have a photo of an earlier build partially booting on real hardware:
After this I did some refactoring and implemented real POSIX signal support as well as allowing arbitrary root file systems instead of the ramfs
based boot used earlier.
This and some other improvements allowed running real applications such as VIM and Python:
The corrupted directory listing seen in the Python screenshot is caused by running a busybox
binary linked against a far too old libc
as the ABI had some minor changes since I last built busybox.
The some more improvements to the ext2 driver meant that it was now stable enough to run in read/write mode and test things that required persistent files, such as rebuilding the kernel and booting it:
After this I wanted to add networking and as such added multithreading and replaced a large part of the scheduling code but other projects started taking up my time and development stopped there.
The toolchain is stuck at an old GCC version and does not build on recent distros.
The source code for the kernel is available at it’s GitHub repository.
The source code for the applications, libraries and build system is at the GitHub organization for this project.