Posixcafe Fresh shipments of beans and standards every Tuesday

9front Bare Bones Kernel

I have recently been interested in reading and understanding the processes of kernel development. In that effort I have been spending some time reading the OSDev wiki as well as this fantastic set of blogs for writing a kernel in rust. However I quickly ran in to two problems:

As such I thought it might be worth while to take a peek on getting a barebones kernel setup using the common tools that are available on the OS that I do most of my development in, 9front. As such I set out to first learn how 9front manages its kernel, and then see if I could strip out just the minimum to get myself a little "hello world".

Knowing where to look

Let's start by looking at how 9front organizes it's kernel code. All of the kernels are located in /sys/src/9/$objtype/ with port referring to portable code between them. For our purposes we're only going to look at the amd64 kernel. There are three files that are good to look at first

Also worth noting is a couple additional directories:

Start putting stuff together

Copying over the l.s file we see tons of stuff that we wont need, so lets trim it down a bit. Reading it quickly we find that we call our main funciton in _start64v, so let's delete everything after that. We also can see that l.s requires a mem.h so let's grab that as well. Then let's write our own very tiny kern.c with a void main(void) entry point. For now simply enterying a infinite loop will suffice.

#include <u.h>

u32int MemMin; //Filled by l.s, thus the symbol must be somewhere

void main(void) { for(;;); }

Now let's get each of these compiled/assembled.

; 6c kern.c && 6a l.s

Now if we check the 9pc64 mkfile for how to link them we see something a bit out of the norm. First we see a KTZERO variable declared and then it being passed to the linker through the -T flag.

Looking at the man page for the linker, we see that the -T flag tells the linker where to start placing the .TEXT section for the binary. To understand why this is needed, let's remind ourselves of what goes on in the average boot(in relation to our kernel).

When we first get to our kernel we have not set up virtual memory, so our first sets of jumps and addressing must use the physical addresses. Looking at mem.h we can see that KZERO (kernel zero) is set to 0xffffffff80000000, so this must be where 9boot puts the start of our kernel binary. However, the start of the binary is not the start of executable code, that would be the .TEXT section. So we must have a common definition of where our executable code starts between our linker and our l.s code. This allows l.s to tell 9boot where exactly in physical memory to jump to.

To acomplish this we pick a common starting point, define it in our source code, and make sure to pass it to the linker so things lign up. So now that we know what is going on, let's link our kernel:

6l -o kern -T0xffffffff80110000 -l l.6 kern.6

It's worth noting that l.6 must come first or else our dance to get the .TEXT section aligned will be pointless, as kern.6 will be placed first in to the section.

Now let's verify that we indeed set things up right by using file(1). The output should look like:

kern: amd64 plan 9 boot image

Booting our new kernel

We have one more step before we can actually get our fresh kernel booted in something like QEMU. We need to create a cdrom iso image that contains both our kernel as well as 9boot. For this we will take a look at the existing script for iso generation on 9front: /sys/lib/dist/mkfile.

Lets start by creating a new plan9.ini for 9boot to point to our new kernel:

echo 'bootfile=/amd64/9pc64' > plan9.ini

We'll also want a local copy of /sys/lib/sysconfig/proto/9bootproto so that we can add our kernel path to it.

Now that we have those, let's create our iso using disk/mk9660 like so:

; @{rfork n
# Setup our root
bind /root /n/src9
bind plan9.ini /n/src9/cfg/plan9.ini
bind kern /n/src9/amd64/9pc64
disk/mk9660 -c9j -B 386/9bootiso \
-p 9bootproto \
-s /n/src9 -v 'Plan 9 BareBones' kern.iso

With that, you should have a bootable iso image fit for use in something like QEMU.


The source code is available on my github. It adds a small print message in kern.c as well as a mkfile from what is shown here.