Debugging Linux Kernel using QEMU

An old lost nifty toolkit for kernel debugging, I used long ago… Of course, the host OS is Linux — some Debian clone (Ubuntu).

Commands to run qemu and debug Linux kernel (either one will do):

qemu -kernel bzImage -append "root=/dev/sda console=ttyS0" -m 2G -hda wheezy.img -serial stdio -nographic -nodefaults -s -S
qemu -kernel bzImage -append "root=/dev/sda console=tty0" -m 2G -hda wheezy.img -s -S

Two important options here are:

# -S  Do not start CPU at startup (you must type 'continue' on the gdb prompt).
# -s  Shorthand for -gdb tcp::1234, i.e. open a gdbserver on TCP port 1234.

On the debugging side:

$ gdb bzImage
(gdb) target remote localhost:1234

This might be needed:

sudo apt-get install debootstrap

Below is the script that automates the creation of the Linux image (wheezy.img) that contains the kernel image from the distro. Some parameters need user-discretion!

DEBIAN_DISTRO="wheezy"
FS="ext4"
ARCH="i386"
MOUNT_POINT="/mnt/wheezy/"
IMG="$HOME/$DEBIAN_DISTRO-$ARCH.img"

This does the major work. Only need to run it ones.

#!/bin/bash

# Copyright (C) Tejas Wanjari (twanjari@andrew.cmu.edu)

# GNU/GPL
# -------
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

# Fill in the macros
# ------------------------------------------------------------------------

DEBIAN_DISTRO="wheezy"
FS="ext4"
ARCH="i386"
MOUNT_POINT="/mnt/wheezy/"
IMG="$HOME/$DEBIAN_DISTRO-$ARCH.img"

# ------------------------------------------------------------------------

# Make sure only root can run our script
if [ "$(id -u)" != "0" ]
then
echo "This script must be run as root" 1>&2
exit 1
fi

# Following binaries are mandetory
BINS="dd mount umount mkfs.$FS debootstrap"
for i in $BINS
do
which $i > /dev/null && continue || \
{ echo "$i command not found."; exit 1; }
done

# Check if the disk image is already mounted
mountpoint -q $MOUNT_POINT && umount -f $MOUNT_POINT

# Create raw disk image [size = bs * count]
echo -e "\nCreating disk image $IMG"
dd if=/dev/zero of=$IMG bs=1K count=1M || exit 1
du -h $IMG

# Format the hda (ext3 is perferred -- better for large no. of small files
# than ext2, and smaller footprint than ext4 since there is no journaling
# overhead)
echo -e "\nFormatting $IMG"
mkfs.$FS -F $IMG || exit 1
file $IMG

# Mount the disk image
echo -e "\nCreating mount-point $MOUNT_POINT"
mkdir -p $MOUNT_POINT
echo "Mounting $IMG, mount-point: $MOUNT_POINT"
mount -o loop $IMG $MOUNT_POINT || exit 1

# Bootstrapping the rootfs
echo -e "\nCreating the root file system"
echo "This may take a while..."
debootstrap --arch=$ARCH $DEBIAN_DISTRO $MOUNT_POINT \
"http://ftp.us.debian.org/debian" || exit 1
echo "======== rootfs created ========"

#Create serial port and sda in /dev of the image
mknod $MOUNT_POINT/dev/ttyS0 c 80 10
mknod $MOUNT_POINT/dev/sda1 c 70 10

# Unmount
echo -e "\nCleaning up"
umount $MOUNT_POINT
rm -rf $MOUNT_POINT

# hda ready
echo -e "\nhda: $IMG ready."

Happy hacking!

2 thoughts on “Debugging Linux Kernel using QEMU

    1. Just set these in the script as per your preference, and run it:
      DEBIAN_DISTRO="wheezy"
      FS="ext4"
      ARCH="i386"
      MOUNT_POINT="/mnt/wheezy/"
      IMG="$HOME/$DEBIAN_DISTRO-$ARCH.img"

      Some additional insights:
      For debugging, with the -kernel switch, we explicitly tell Qemu to use given image instead of the vmlinuz in this rootfs. The bzImage, that we are referring to, is same as vmlinuz that you see in the boot directory. During kernel’s 'make install', it does a copy, "cp /usr/src/linux/arch/i386/linux/boot/bzImage /boot/vmlinuz". Let’s refer to these as K_IMG.

      To mount the rootfs image IMG to take a peek into it, do the following:
      sudo mount -o loop $IMG /mnt/loop/

      With this you will see the complete rootfs under /mnt/loop/. The kernel image (K_IMG) will be located at: /mnt/loop/boot/vmlinuz. K_IMG can be set to this kernel or any other.

      To boot this OS in qemu run this in one terminal:
      qemu -kernel $K_IMG -append "root=/dev/sda console=tty0" -m 2G -hda $IMG -s -S
      The -s switch makes Qemu to listen on port 1234 and wait for a gdb connection to it.

      When it boots up, in another terminal, start GDB:
      gdb $K_IMG
      (gdb) target remote localhost:1234

      Hope that helps.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s