Translate

Archives

Building a Bootable GRUB Legacy Image for QEMU

Recently, as a moderator on a Unix and GNU/Linux-related forum, I assisted a member who was having a problem creating a bootable image for use with QEMU. As a result of this experience, I decided to write this post to demonstrate to others how easy the process is.

Without further ado, here is a simple Bash shell script to create a bootable image containing only the GRUB Legacy boot loader which is bootable using QEMU.

#!/bin/bash
#
#  Finnbarr P. Murphy    01/03/2011
#
#  Create a bootable image containing just Legacy Grub
#  and use QEMU to test boot it if requested
#

if (( $(id -u) != 0 ))
then
    echo "ERROR: This script must be run as root" 1>&2
    exit 1
fi

GRUBFILEDIR=/usr/share/grub/x86_64-redhat
IMAGE=myimage
LOOPDEV=$(/sbin/losetup -f)

dd if=/dev/zero of=${IMAGE}.img bs=512 count=2880

if [[ -d ${IMAGE} ]]
then
    rm -rf ${IMAGE}
fi
mkdir -p ${IMAGE}/boot/grub

cp ${GRUBFILEDIR}/stage1 ${IMAGE}/boot/grub
cp ${GRUBFILEDIR}/stage2 ${IMAGE}/boot/grub
/sbin/losetup ${LOOPDEV} ${IMAGE}.img
/sbin/mke2fs ${LOOPDEV}
mount ${LOOPDEV} -o loop /mnt
chmod 777 /mnt
cp -aR ${IMAGE}/* /mnt
umount /mnt

cat <<EOF | /sbin/grub --batch --device-map=/dev/null
device (fd0) ${LOOPDEV}
root (fd0)
setup (fd0)
quit
EOF

/sbin/losetup -d ${LOOPDEV}

read -n1 -p "Use QEMU to boot the image (Y/N) "
echo
if [[ $REPLY = [yY] ]]
then
    qemu  -fda ${IMAGE}.img
fi

exit 0


Here is a screenshot of the above image running in QEMU.

QEMU screenshot1

One problem with the previous script is that is requires you to run QEMU in graphical mode. If you want to run QEMU in non-graphical mode, you have to configure GRUB to work with a serial device. To do this we create a GRUB configuration file, /boot/grub/grub.conf, and include the following commands:

serial --unit=0 --speed=9600 --word=8 --parity=no --stop=1
terminal serial timeout=10 console


where unit is the number of the serial port, counting from zero. Note that the values for parity must speled out in full, i.e. even, odd or no.

If both a serial port and a console are configured on a platform, both devices will ask for a key to be pressed until timeout expires. If a key is pressed on a particular device, then the boot menu is displayed to that device and the other device sees nothing. If no key is pressed during the timeout period then the boot menu is displayed on whichever of serial or console is listed first on the terminal command line. In the above case it is the serial port. By the way, the terminal that GRUB Legacy expects to be attached to the serial port is a VT100. You can add a –dumb argument to the terminal command to force GRUB Legacy to use its command line interface.

Here is the modified script. It will display the GRUB menu on either a graphical console or a serial line. It includes a dummy menu entry called Fedora (Dummy Release).

#!/bin/bash
#
#  Finnbarr P. Murphy    01/03/2011
#
#  Create a bootable image containing just Legacy Grub
#  and use QEMU in either grahical or non-graphical 
#  mode to test boot it if requested
#

if (( $(id -u) != 0 ))
then
    echo "ERROR: This script must be run as root" 1>&2
    exit 1
fi

GRUBFILEDIR=/usr/share/grub/x86_64-redhat
IMAGE=myimage
LOOPDEV=$(/sbin/losetup -f)
dd if=/dev/zero of=${IMAGE}.img bs=512 count=2880

if [[ -d ${IMAGE} ]]
then
    rm -rf ${IMAGE}
fi
mkdir -p ${IMAGE}/boot/grub

cp ${GRUBFILEDIR}/stage1 ${IMAGE}/boot/grub
cp ${GRUBFILEDIR}/stage2 ${IMAGE}/boot/grub

cat <<EOF > ${IMAGE}/boot/grub/grub.conf
serial --unit=0 --speed=9600 --word=8 --parity=no --stop=1
terminal --timeout=10 serial console
timeout=30
default=0
title Fedora (Dummy Release)
root (fd0,1)
kernel /vmlinuz-dummy rhgb quiet
initrd /initrd-dummy.img 
EOF

touch ${IMAGE}/boot/vmlinuz-dummy
touch ${IMAGE}/boot/initrd-dummy.img

/sbin/losetup ${LOOPDEV} ${IMAGE}.img
/sbin/mke2fs ${LOOPDEV}
mount ${LOOPDEV} -o loop /mnt
chmod 777 /mnt
cp -aR ${IMAGE}/* /mnt
umount /mnt

cat <<EOF | /sbin/grub --batch --device-map=/dev/null 
device (fd0) ${LOOPDEV}
root (fd0)
setup (fd0)
quit
EOF

/sbin/losetup -d ${LOOPDEV}

read -n1 -p "Use QEMU to boot the image (Y/N) "
echo
if [[ $REPLY = [yY] ]]
then
    read -n1 -p "Use non-graphical mode (Y/N) "
    echo
    if [[ $REPLY = [yY] ]]
    then
        qemu -nographic -fda ${IMAGE}.img
    else
        qemu -fda ${IMAGE}.img
    fi
fi

exit 0


An explanation is in order. Normally, QEMU uses SDL graphical routines to display it’s output on a popup graphical screen. However when QEMU is invoked with the -nographic option, graphical output is disabled entirely and QEMU becomes a command line application with its emulated serial port being redirected to the console.

However without the additional lines in grub.conf directing GRUB to use a serial port, you would see nothing. QEMU simply looks like it is hung. With the additional of the serial and terminal lines to grub.conf, you see the output from GRUB even though we have told GRUB to use a serial port. This is because QEMU redirects it’s emulated serial port (which we told GRUB to use) to your console.

Here are two screenshots of QEMU running above image running in nographic mode. The first screenshot shows you how GRUB asks you to press any key so that it can decide how to interact with you, i.e. via a console or via a serial port.

QEMU screenshot2

This screenshot was taken after a key was pressed and GRUB is using QEMU’s serial port.

QEMU screenshot3

This is about all there is to creating a simple bootable image containing just GRUB Legacy that will work with QEMU. These scripts can easily be modified and extended to include a real GNU/Linux kernel and utilities in the created image. However, I will leave that as an exercise for you the reader!

2 comments to Building a Bootable GRUB Legacy Image for QEMU

  • Ravikiran D Borse

    Hi

    I am new to grub and linux kernel.
    I am trying to understand the Grub Implementation. I am making Grub USB bootable.
    For testing everytime I have to restart the PC. B

    But because of your tutorial I can do it without turning it off.
    and I can test my app very easily.

    Thanks & regards

  • Ravikiran D Borse

    It is one of the best work, I have ever seen.
    I have learned a lot from your script.

    Thank you very much.