Hello OS

06 Aug 2013

In the spirit of learning how stuff works I have put together a tutorial on how to create you own little Linux distribution. Just follow these few steps.

When building an operating system like linux you need to decide what hardware architecture the system will run on. This can be things like x86, arm, powerpc. In my case I will run the operating system through an emulator called qemu which is able to emulate the 64 bit Intel architecture called x86_64 which is used in most PC's today.

In later notes I will build systems for other more exotic architectures.

What do we need

So what parts are needed to create a linux distribution. We will need two things:

  • kernel
  • root filesystem

kernel

In our case this is the linux kernel. So we need to download the sources for the newest stable kernel and build it for our hardware.

The linux kernel can be found at https://www.kernel.org and today (2013-08-08) the latest stable kernel is version 3.10.5. So we download the kernel sources and unpack them.

wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.10.5.tar.xz
tar xvf linux-3.10.5.tar.xz

Next we need to configure the kernel for our hardware architecture. This is quite easy since we don't have any special requirements. We will use the default configuration for x86_64. And finally we will build our kernel.

cd linux-3.10.5/
make ARCH=x86_64 defconfig
make ARCH=x86_64 -j 4

When all this is finished we will have our working x86_64 kernel in the file arch/x86/boot/bzImage. We can even test our new kernel to see if we can load it. It will crash since we don't have the rest of our system yet but it's always nice to see that we have something running at least. So we fire up qemu and give it our new kernel.

qemu-system-x86_64 -kernel arch/x86/boot/bzImage

Why does it crash?

When the kernel is loaded, it starts to configure the hardware to be able to use it. This includes memory, processors, I/O systems and other connected devices. After that the kernel tries to find a filesystem that contains an executable file that it can run. This executable will be the first process running on the system. Such a filesystem is called a root filesystem or rootfs.

root filesystem

There are as many ways of creating and loading a root filesystem as there are cows in Ås. But I have choose one method which is quite easy and require few additional tools. I will be using something called an initramfs. An initramfs is an cpio archive that is unpacked by the kernel into ram. After unpacking it the kernel looks for a file called init at the root of the archive and it executes that file.

But first we need to create the contents of the root filesystem. So we start off by creating a folder named rootfs

mkdir rootfs

Next we need to create a single executable that can be the first process of our system. I will be using C as the programming language this time since it gives me a warm fuzzy feeling inside.

touch init.c 
emacs init.c

Now time to create some content. To keep it super simple we will just output a message and then pause the program forever. This program will do just that.

#include <stdio.h>
#include <unistd.h>

int main()
{
    printf("Velkommen til kjetilos\n");
    while (1)
        pause();
    return 0;
}

Now it's time to compile it, and I am using the gcc compiler. It is pretty straight forward, but we have to remember to link this binary statically. This is because our root filesystem will not contain any c runtime library (libc) that we can link with at runtime.

gcc -static init.c -o init

At last we just copy the init program into the rootfs folder.

cp init rootfs/

Creating the archive

We now how a directory with the necessary content of the root filesystem and we need to package it up in a format that the linux kernel can read and understand. One such format is the cpio archive format.

cd rootfs
find . | cpio -H newc -o > ../initramfs.cpio

Run!

Now we just fire up qemu with our kernel and root filesystem and we should be seeing our message on screen.

cp linux-3.10.5/arch/x86/boot/bzImage .
qemu-system-x86_64 -kernel bzImage -initrd initramfs.cpio

The -initrd option is just a way of telling qemu and the linux kernel where to search for the initramfs. An alternative to doing this is to actually embed the initramfs into the kernel image, this can be done by small configuration to the kernel configuration. Beware that if you want to embed the root filesystem then you also need to create a device node for the console, if you don't do that then you will not get any output.