Setup for linux kernel dev using qemu
I could not think of a better title, I'm sorry.
These are notes how to create a kernel which can be booted in qemu and pass a drive to it which has loadable kernel modules.
The idea
I wanted to play around with the linux kernel and write a minimal filesystem inside the kernel tree. I wanted to boot the kernel in a QEMU, as I do not want to mess with my running kernel.
Here are the steps I did (on a debian VM I run on my universitys infrastructure).
Setup
sudo apt-get install build-essential bc git gcc
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
make x86_64_defconfig
make kvmconfig
make menuconfig
The last step brings up the kernel configuration interface, which I used to
enable SATA
(Device Drivers -> Serial ATA and Parallel ATA drivers (libata) -> Generic ATA support
)
support which is needed to be able to mount a device into the qemu instance,
where we can put things to play around with in the VM.
I then did a make -j 16
(if you have not 8 cores, use a lower number).
While the kernel compiled, I created a busybox initramfs. I used this blog article for how to do it. I'll dump in the commands from the blog article, for detailed information, go and read it, it's a really good article. I didn't do the speed-optimizing things, as the QEMU boots fast enough for me (~3 seconds).
(I used the latest busybox, but I don't see why it should work with the one from the article, which is about two versions old)
curl http://busybox.net/downloads/busybox-1.23.2.tar.bz2 | tar xjf -
cd busybox-1.23.2
mkdir -pv /tmp/obj/busybox-x86
make O=/tmp/obj/busybox-x86 defconfig
make O=/tmp/obj/busybox-x86 menuconfig # See below why
cd /tmp/obj/busybox-x86
make -j 16
# it does not install in your system, so you can execute this without fear
make install
mkdir -p /tmp/initramfs/x86-busybox
cd /tmp/initramfs/x86-busybox
mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}}
cp -av /tmp/obj/busybox-x86/_install/* .
# now edit the init script
vim init
You have to enable busybox build as static binary (with the menuconfig
part).
The init
script I use:
#!/bin/sh
echo "INIT SCRIPT"
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mkdir /tmp
mount -t tmpfs none /tmp
mdev -s # We need this to find /dev/sda later
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
exec /bin/sh
The init
script boots into a /bin/sh
. We can now build the initramfs with
it:
chmod +x init
find . -print0 \
| cpio --null -ov --format=newc \
| gzip -9 > /tmp/initramfs-busybox-x86.cpio.gz
Try it out (1)
Now we can try it out, whether the kernel boots and we get out shell:
# In the kernel tree:
qemu-system-x86_64 \
-kernel arch/x86_64/boot/bzImage \
-initrd /tmp/initramfs-busybox-x86.cpio.gz \
-nographic -append "console=ttyS0"
We should see some output like this:
[...]
[ 7.170702] Freeing unused kernel memory: 1200K (ffffffff81f55000 - ffffffff82081000)
[ 7.171539] Write protecting the kernel read-only data: 14336k
[ 7.177328] Freeing unused kernel memory: 816K (ffff880001934000 - ffff880001a00000)
[ 7.219360] Freeing unused kernel memory: 1116K (ffff880001ce9000 - ffff880001e00000)
INIT SCRIPT
mount: mounting none on /sys/kernel/config failed: No such file or directory
mount: mounting none on /tmp failed: No such file or directory
Boot took 7.33 seconds
/bin/sh: can't access tty; job control turned off
/ # [ 7.630681] input: ImExPS/2 BYD TouchPad as /devices/platform/i8042/serio1/input/input3
/ #
(Yes, the output is slightly messed up... but hey! We just booted a linux kernel in a QEMU!
Load the kernel module
We can now create a kernel module in the linux kernel tree:
# In the kernel tree
git checkout -b my-module
mkdir fs/iamcoolfs/ # because I like filesystems
cat <<EOS > ./fs/iamcoolfs/Kconfig
config COOL_FS
tristate "COOLFS support"
help
Private FS for playing and learning FS programming
This will definitively eat everything you put into it.
EOS
cat <<EOS > ./fs/iamcoolfs/Makefile
#
# Makefile for the linux mb-filesystem routines.
#
obj-$(CONFIG_MB_FS) += coolfs.o
EOS
cat <<EOS > ./fs/iamcoolfs/coolfs.c
#include <linux/init.h>
#include <linux/module.h>
static int __init coolfs_init(void)
{
pr_debug("cool module loaded\n");
return 0;
}
static void __exit coolfs_fini(void)
{
pr_debug("cool module unloaded\n");
}
module_init(coolfs_init);
module_exit(coolfs_fini);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("<YOUR NAME GOES HERE>");
EOS
This creates all the files we need for a minimal module. Because I like
filesystems and I really want to implement a minimal filesystem, I chose to
create the files in ./fs
, but I guess it shouldn't matter.
Make sure you adapt the fs/Kconfig
file to find your new module! Put
source "fs/coolfs/Kconfig"
into it.
Now you can execute make menuconfig
and find the configuration option for
your new module.
We can also simply make M=fs/coolfs
to build the module.
Now we create a (ext2) disk where we can cp
the module to, so we can mount
that disk into our QEMU and use the module there:
dd if=/dev/zero of=/tmp/disk bs=4M count=100
sudo mkfs.ext2 /tmp/disk # you could also try ext4, for example
mkdir /tmp/disk-mount
sudo mount /tmp/disk /tmp/disk-mount
cp fs/coolfs/coolfs.ko /tmp/disk-mount/
sudo umount /tmp/disk-mount
Try it out (2)
And when we boot our QEMU now with -hda /tmp/disk
, we can find /dev/sda
in
the booted instance and mount that:
# In the booted VM
mkdir sda
mount /dev/sda ./sda
ls sda
Happy FS dev!
tags: #c #linux #open source #programming #software #testing #kernel