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 some fantastic blog on 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.

Links

OSDev Wiki

Writing an OS in Rust

Barebones9 source code