The following is a short introduction to writing kernels for 32 bit x86 computers. I have taken up the topic over the weekend and have found that most of the introduction and documentation I have been able to get my hands on and lacking on the most basic things — it seems they expect you to know everything before you start learning it. Some of the details in here might be a bit too low for some — they were added for completeness and to make sure I don’t leave anything out that might be vital to someone.
This part will guide you through setting up the environment which allows you to test the kernel.
I expect the following things from you and your operating system.
You:
- You know some basic C — you don’t have to be an expert, but it helps.
If you know x86 assembler, it helps too, but since we won’t be doing a whole lot of x86 assembler it won’t hurt a lot if you don’t know any. Besides, we’re doing this in GNU as, so chances are you’ll be confused if you are used to use {M,N}ASM or anything else that’s not AT&T syntax.
The following software is installed on your computer
- GCC (I’ve only tested this with GCC 4.4.2 on Fedora 12)
- Make
- Qemu
- binutils (a linker and an assembler)
- parted
- mount and umount
Your tool chain must, ofcourse, be able to produce 32 bit x86 ELF files. I have no idea if you can get all of that installed on a Windows machine, so if you don’t want to run a GNU distribution (on a real or virtual machine) then you may not be able to build and test the kernel.
Parts you need
To build and test the kernel you need the following things
- A boot loader
- A kernel
- A disk to boot from
The bootloader of choise in this case is GRUB legacy (I plan to move to GRUB 2 at some later point — if I ever get around to it, this article will be updated). You can’t install it before you have the disk image however, so that’s the first thing to do.
Creating the disk image
The disk image doesn’t have to be large — for the kernel I am describing here, a few hundred kilobytes will suffice. I’ll be making a 20 MiB image however. The command to do so is
qemu-img create disk.img 20M
This command will create a 20MiB file filled with zero bytes — in order for it to be usefull a partition table has to be added and a partition with a usefull filesystem has to be created. To do both, parted is used. When parted is running it will open a shell where the user can enter commands — the commands (in order) to enter are:
mklabel msdos
mkpartfs primary ext2 0 20
set 1 boot on
quit
The first command (mklabel msdos) creates a label on the disk. The second command (mkpartfs primary ext2 0 20) first creates a partition and then installs an ext2 filesystem on the parition. GRUB can now be installed, but to be able to boot on it, the third command has to be run — this makes the disk bootable. The last command simply exits parted.
Installing GRUB Legacy
To install GRUB you need to copy a few files to the disk image — in order to do that, you must first mount it. On non-UNIX like operating systems, this is likely to be very tricky — I know of no way of mounting the filesystem if mount is not present. As always you will need root access (or an entry in /etc/fstab) to mount the disk image. The command for mounting the filesystem — which does not reside directly at the beginning of the file, you need to specify an offset, for where the filesystem begins. In this case (because it’s simple) the filesystem begins after the first 16KiB. The command for mounting the filesystem is
mount -o loop,offset=16384 -t ext2 disk.img mnt
where mnt is the directory you wish to mount it on (I just use a local directory relative to the disk image). Umounting is a simple matter of running umount the usual way:
umount mnt
On the disk image, you need the directory grub to store the files GRUB need to boot.
mkdir mnt/grub
Now copy the files to the image — you need ext2 support and stage 1 and 2 files.
cp /boot/grub/stage{1,2} /boot/grub/e2fs_stage1_5 mnt/grub
The ext2 file may have a slightly different name on some systems — likely something that makes more sense (try ext2fs_stage1_5). The next step is making grub the bootloader on the image. To do so, run the following command
grub --device=/dev/null
When grub is running it will (like parted) open a shell for you to enter commands in. The commands you need to enter are:
device (hd0) disk.img
root (hd0,0)
setup (hd0)
quit
The first command maps the first BIOS disk to be disk.img — this is required for grub since it depends on the BIOS mapping of disks. The second command sets the root to be the first partition of the first disk (the partition you created earlier). The third command installs grub to the first BIOS disk (that is the image) and the final command stops grub.
Configuring GRUB
You should now have grub installed on the image. If you boot it with qemu, grub should start up and offer you a shell. In order to make grub boot the kernel (which has yet to be made and copied to the image) create the file mnt/grub/grub.conf and let it have the following content
timeout 10
default 0
title kernel
root (hd0,0)
kernel /kernel
This file will instruct grub to wait for 10 seconds and the boot entry 0 in the list of operating systems. In this case entry 0 is the only entry. This entry instructs grub to use the first partition of the first disk as the root disk and to use the file kernel, found at the root of the root disk as the kernel and then boot that kernel. Once you have that file on the image you’re done setting it up and is then ready to create the kernel.
55.396241
10.390635