I’m currently reading Linux Kernel Development. To really understand the book, you have to modify, compile, and install new kernels. One way to do this is to update the kernel on the machine you are developing on. This method seems…scary and dangerous. Sweeping the rug out from under my Ubuntu installation seems wrought with peril.

A safer way is to build and run the Linux kernel inside of a simulated sandbox. The steps in this post will show you how to get your kernel running in a lightweight environment (Qemu) that boots in seconds, with no chance of corrupting your development machine installation.

Install required build tools on host machine

Fedora

sudo dnf install ncurses-devel kernel-devel kernel-headers openssl-devel
sudo dnf install gcc gcc-c++ glibc-static
sudo dnf install git
sudo dnf install qemu

Ubuntu

sudo apt install ncurses-dev build-essential libssl-dev libelf-dev
sudo apt install git
sudo apt install qemu

Prepare a working space for kernel development

cd $HOME
mkdir kdev
TOP=$HOME/kdev

Download source code

Download stable busybox, which will provide a basic set of tools for the kernel-under-test:

wget http://busybox.net/downloads/busybox-1.24.2.tar.bz2
tar xvf busybox-1.24.2.tar.bz2
rm busybox-1.24.2.tar.bz2

Clone Linux kernel:

git clone https://github.com/torvalds/linux.git

Configure busybox

cd $TOP/busybox-1.24.2
mkdir -p $TOP/build/busybox-x86
make O=$TOP/build/busybox-x86 defconfig
make O=$TOP/build/busybox-x86 menuconfig

Enable building BusyBox as a static binary:

> Busybox Settings
> Build Options
[*] Build BusyBox as a static binary (no shared libs)

Build and install busybox (local install, no sudo required)

cd $TOP/build/busybox-x86
make -j2
make install

Create minimal filesystem

mkdir -p $TOP/build/initramfs/busybox-x86
cd $TOP/build/initramfs/busybox-x86
mkdir -pv {bin,sbin,etc,proc,sys,usr}
cp -av $TOP/build/busybox-x86/_install/*.

Create simple init script

Create a file called init with the following contents:

#!/bin/sh

mount -t proc none /proc
mount -t sysfs none /sys

echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"

exec /bin/sh</code></pre>

Make it executable:

chmod +x init

Generate initramfs

find . -print0 \
  | cpio --null -ov --format=newc \
  | gzip -9 > $TOP/build/initramfs-busybox-x86.cpio.gz

Configure Linux using simple defconfig and kvm options

cd $TOP/linux
make O=$TOP/build/linux-x86-basic x86_64_defconfig
make O=$TOP/build/linux-x86-basic kvmconfig

Build Linux

make O=$TOP/build/linux-x86-basic -j2

Boot with Qemu in seconds!

cd $TOP
qemu-system-x86_64 \
  -kernel build/linux-x86-basic/arch/x86_64/boot/bzImage \
  -initrd build/initramfs-busybox-x86.cpio.gz \
  -nographic -append "console=ttyS0" \
  -enable-kvm

If all goes well, this will boot the kernel and drop you into a prompt where you can start interacting with your minimal busybox system.

kernel boots!

If you want to exit Qemu, use Ctrl-A X.

References