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.

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.

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

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!






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
It is one of the best work, I have ever seen.
I have learned a lot from your script.
Thank you very much.