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.