Creating a bootable hard-disk image with Grub2

Hard Disk Platter
Image courtesy of Wikimedia Commons.

Creating a Qemu hard-disk image that boots with Grub2 is, as it turns out, not too difficult. Unfortunately though, the steps you need to get it working are not completely obvious, and documentation is still pretty sparse (probably because Grub2 is still quite new). In this post I’ll run through how I got it working, and how I integrated it into my build.

I mentioned before how I had decided to start playing with OS development with Mink (which is now on Github, by the way). I’ve got a very simple kernel that does almost exactly nothing, and obviously I need to boot it up so I can test it as I add things. The obvious (and accepted) way to do this is to use some kind of virtual machine software to emulate the target computer (in my case, at the moment, that’s an x86 PC). I’m using Qemu.

Originally, I was using grub-mkrescue to create CDROM ISO images, and this worked fine for a while. Now, though, I’m getting to the point where I want to start implementing a disk driver, and filesystem support. To get started, I just want to implement simple IDE (i.e. PATA) support in the kernel – I don’t want to do ATAPI (which I’d need for the CDROM based setup) and I really don’t want to push off with an ISO9660 filesystem driver. I want read-write capability, so I’ve chosen to switch from the ISO build to a hard-disk image file, on which I’m using an ext2 filesystem.

So I spent much of today trying to get it to work. Googling the subject turned up a few links, but sadly nothing worked immediately, possibly because Grub2 is still being developed and has changed since the various tutorials were written. In the end I hacked up a solution based on bits from many different places, using the loopback device to mount the disk image, and then grub-install to actually install the bootloader. The configuration file I’d already put together for the ISO boot still works fine, and the final script I came up with is so short and simple, you’d never guess it took me the best part of a day to write!

Firstly, here’s mkimage.sh, which is the script that does the actual image creation:

# Create the actual disk image - 20MB
dd if=/dev/zero of=mink.img count=20 bs=1048576

# Make the partition table, partition and set it bootable.
parted --script mink.img mklabel msdos mkpart p ext2 1 20 set 1 boot on

# Map the partitions from the image file
kpartx -a mink.img

# sleep a sec, wait for kpartx to create the device nodes
sleep 1

# Make an ext2 filesystem on the first partition.
mkfs.ext2 /dev/mapper/loop0p1

# Make the mount-point
mkdir -p build/tmp/p1

# Mount the filesystem via loopback
mount /dev/mapper/loop0p1 build/tmp/p1

# Copy in the files from the staging directory
cp -r build/img/* build/tmp/p1

# Create a device map for grub
echo "(hd0) /dev/loop0" > build/tmp/device.map

# Use grub2-install to actually install Grub. The options are:
#   * No floppy polling.
#   * Use the device map we generated in the previous step.
#   * Include the basic set of modules we need in the Grub image.
#   * Install grub into the filesystem at our loopback mountpoint.
#   * Install the MBR to the loopback device itself.
grub2-install --no-floppy                                                      \
              --grub-mkdevicemap=build/tmp/device.map                          \
              --modules="biosdisk part_msdos ext2 configfile normal multiboot" \
              --root-directory=build/tmp/p1                                    \
              /dev/loop0

# Unmount the loopback
umount build/tmp/p1

# Unmap the image
kpartx -d mink.img

# hack to make everything owned by the original user, since it will currently be
# owned by root...
LOGNAME=`who am i | awk '{print $1}'`
LOGGROUP=`groups $LOGNAME | awk '{print $3}'`
chown $LOGNAME:$LOGGROUP -R build mink.img

Take away the comments, and that’s 15 lines… Just under two lines per hour – good thing I don’t work for IBM!

The comments pretty well explain what’s going on line-by-line, so I won’t go into much more depth here. The only things to note are that I use Fedora – on other system’s grub2-install is just called grub-install – and that on most systems, this will need to be run as root, so I added a small hack at the end to set the file ownership back to the original user once the image is created.

To integrate this into the build, I simply call it from the Makefile (I’ve included the older ISO build target here as well so you can see how it all works together):

# Generates the image staging area under build/img. This is
# the directory layout that is used to make the ISO or hard-disk
# images for emulators.
.PHONY: image-staging
image-staging: mink.bin grub2/grub.cfg
	$(MKDIR) build/img/boot/grub2
	$(CP) --parents $^ build/img/boot

# Generates the Grub2-based ISO - see bochsrc.txt for info.
# Requires grub2-mkrescue and xorriso be installed!
mink.iso: image-staging
	grub2-mkrescue -o mink.iso build/img# Generates a hard-disk image. See mkimage.sh.
mink.img: image-staging
	sudo sh mkimage.sh

(Thanks to tohtml.com for the syntax highlighting).

This should be pretty self-explanatory, so I won’t bore you with further details.

So there you have it – an easy way to make a Grub2-bootable hard-disk image as part of your build process. Of course you can do it manually by booting your virtual machine from a Linux live CD and installing grub to the hard drive, but if you want to automate it, feel free to use this (it’s under the MIT license, so please give credit where it’s due – see here).

Finally, in the event that Grub changes as it continues to evolve, please let me know in the comments and I’ll try to keep this post up-to-date.

Advertisements

6 thoughts on “Creating a bootable hard-disk image with Grub2

  1. Thanks for the article.

    I did not yet try it; but if you use ‘kpartx -as mink.img’, you need not do the ‘sleep 1’

    Regards, Mike Jonkmans

      • Hi roscopeco, it’s nice article , thnkx for sharing this greate information ;).
        I have some problems to build the mink OS, On the Github Source tree :
        i think there’s a file missed from the mink directory “tick.c” !! is it true ?
        because i got a make error : “tick.o there’s no dependency ” !!!!

      • Hi! It would appear that tick.c has gone missing somewhere – I’ll get out the old computer that has the original source tree on it and check it out. Thanks for the heads up!

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