29

I did the following:

  1. created an empty .img file with dd
  2. associated it to /dev/loop0 with losetup
  3. created a partition in it with fdisk
  4. formatted such partition with mke2fs
  5. copied a custom GNU/Linux system into that partition

Now I'd like to make the .img file bootable by installing grub into its MBR and /boot directory. My goal is to load the .img file with qemu. It would be better if grub2 is used instead of grub legacy.

Thanks.

1

3 Answers 3

26

This is with grub-pc version 1.98+20100804-5ubuntu3 (Maverick Meerkat).

The grub2 installer can install to loopback devices, but if you mount using the device mapper it will get confused and believe that you have an LVM scheme, failing mysteriously with a complaint about a missing abstraction.

Instead, you should setup the loopback device for the partition yourself, with a name that must match the pattern "/dev/loop[0-9]", i.e. without any partition designator at the end:

kpartx -v -a /dev/loop0
losetup /dev/loop1 /dev/mapper/loop0p1
mount /dev/loop1 /mnt

(Note that if you want grub-mkconfig/update-grub to operate on this volume, then the partition loopback must be connected to the disk loopback under /dev, and not directly to the image file).

Since you used fdisk to partition the image, you have an msdos-style partition table (aka label), and boot using a BIOS. In addition to putting the stage1/boot.img in the MBR, the stage1.5/core.img will be put in an embedding area in unpartitioned space (!) following right after, and there must be space for this.

The trick is now to tell the grub2 installer through a device map how your loopback setup will map to BIOS drives in the virtual machine. (In grub1 legacy this was done directly in the shell). You are probably planning to boot this image as the first disk, so I guess the appropriate mapping would be:

mkdir -p /mnt/boot/grub
cat > /mnt/boot/grub/device.map <<EOF
(hd0)   /dev/loop0
(hd0,1) /dev/loop1
EOF

Here I have put the device map inside the guest disk image, so that you can generate the boot configuration file grub.cfg:

mount --bind /dev /mnt/dev
chroot /mnt grub-mkconfig -o /boot/grub/grub.cfg

(Beware that the post-installer of the grub-pc package will run a probe that overwrites the device map(!), so you'll have to write it after installation and run grub-mkconfig/update-grub yourself).

Now run the installer from the host, pointing to the guest installation:

grub-install --no-floppy --grub-mkdevicemap=/mnt/boot/grub/device.map --root-directory=/mnt /dev/loop0

Finally, unmount everything set up here before starting qemu on your image:

umount /mnt/dev
umount /mnt
losetup -d /dev/loop1
kpartx -v -d /dev/loop0
6
  • Wow! I just ran into your answer while trying to understand why I couldn't update-grub a mount-looped, chrooted image file. I'm going to see if this can apply to my problem, but that's definitly an interesting track and very worth my upvote. Thanks!
    – filofel
    Dec 9, 2010 at 16:01
  • 1
    Nice answer, unfortunately chroot /mnt grub-mkconfig -o /boot/grub/grub.cfg fails because there's no grub-mkconfig or any binary for that matter in the .img disk, and /mnt is already mounted. It would be great if you'd take it step by step and write all the details/commands.
    – Flavius
    Aug 30, 2012 at 23:45
  • @RolKau: thank you for this excellent documentation! Together with toh's modifications below it solved many issues I had for years with grub when embedding from within chrooted environments.
    – sparkie
    Jan 9, 2013 at 15:17
  • 1
    losetup -P is another good way of mounting a single partition: stackoverflow.com/a/15200862/895245 Sep 11, 2015 at 11:10
  • I'm getting: /usr/sbin/grub-probe: warning: the device.map entry 'hd0,1' is invalid. Ignoring it. Please correct or delete your device.map. So this answer is useless.
    – Calmarius
    Mar 15, 2018 at 15:09
11

thanks a lot for these explanations. I integrated your solution into my own scripts with following modifications (translated to your notation/variables):

modprobe dm_mod
kpartx -va /root/rootfs.img # *.img is setup elsewhere
# normally you now would mount /dev/loop0p1 directly. BUT
# grub specialists didn't manage to work with loop partitions other than /dev/loop[0-9]
losetup -v -f --show /dev/mapper/loop0p1
mount /dev/loop1 /mnt
mkdir -p /mnt/boot/grub

# change into chrooted environment. all remaining work will be done from here. this differs from the howto above.
LANG=C chroot /mnt /bin/bash
set -o vi
mount -t sysfs sysfs /sys
mount -t proc  proc  /proc
# avoid grub asking questions
cat << ! | debconf-set-selections -v
grub2   grub2/linux_cmdline                select   
grub2   grub2/linux_cmdline_default        select   
grub-pc grub-pc/install_devices_empty      select yes
grub-pc grub-pc/install_devices            select   
!
apt-get -y install grub-pc
# don't setup device.map prior to this point. It will be overwritten by grub-pc install
#corrected the /mnt/boot/grub/device.map to /boot/grub/device.map
cat > /boot/grub/device.map << !
(hd0)   /dev/loop0
(hd0,1) /dev/loop1
!
# install here to fill /boot/grub for grub-mkconfig (update-grub)
grub-install /dev/loop0
# generate /boot/grub/grub.cfg
update-grub

this works at least on debian squeeze. Check '/boot/grub/grub.cfg' for correctness.

1
  • 1
    Should cat > /mnt/boot/grub/device.map be done in the chrooted environment? If so, the path should be /boot/grub/device.map.
    – cbliard
    Feb 12, 2014 at 9:03
2

Here's a quick walk-through on how to install and boot GRUB manually into a QEMU disk image. I haven't taken it to the next step with a grub.cfg, but I assume that's pretty straightforward once this main setup is completed.

Assumptions:

  • 'grub-install --version' is "grub-install (GRUB) 2.02~beta2-36ubuntu3.2"
  • 'qemu-system-x86_64 --version' is "QEMU emulator version 2.5.0 (Debian 1:2.5+dfsg-5ubuntu10.6), Copyright (c) 2003-2008 Fabrice Bellard"
  • A disk image named "disk1" in the current directory
  • /dev/loop0 has "Disklabel type: dos" (i.e. with fdisk)
  • /dev/loop0p1 is a bootable partition, already formated with ext4

This is how I booted qemu into the GRUB menu:

    # losetup -fP disk1
    # ls /dev/loop0*
    /dev/loop0  /dev/loop0p1  /dev/loop0p2  /dev/loop0p3
    # mount /dev/loop0p1 /mnt
    # cat > loop0device.map <<EOF
    (hd0) /dev/loop0
    EOF
    # grub-install --no-floppy --grub-mkdevicemap=loop0device.map \
    --modules="part_msdos" --boot-directory=/mnt /dev/loop0 -v
    # umount /mnt
    # losetup -d /dev/loop0
    # qemu-system-x86_64 -m 512 -curses -hda disk1 -enable-kvm

Then the GRUB shell comes up:

                        GNU GRUB  version 2.02~beta2-36ubuntu3.2

       Minimal BASH-like line editing is supported. For the first word, TAB
       lists possible command completions. Anywhere else TAB lists possible
       device or file completions.


    grub> ls
    (hd0) (hd0,msdos3) (hd0,msdos2) (hd0,msdos1) (fd0)
    grub>

If you had copied a kernel and ram disk to /dev/loop0p1, you could boot it:

    grub> linux (hd0,msdos1)/vmlinuz
    grub> initrd (hd0,msdos1)/initrd
    grub> boot

And here's the default Linux shell (because no /sbin/init was available in this case)

    BusyBox v1.22.1 (Ubuntu 1:1.22.0-15ubuntu1) built-in shell (ash)
    Enter 'help' for a list of built-in commands.

    (initramfs)
1
  • 1
    This one worked for me with archlinux. i just replaced --boot-directory=/mnt by --boot-directory=/mnt/boot Thanks Jun 17, 2021 at 19:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .