Debian amd64 virtual machine using qemu

Make virtual machine running Debian sid with x86_64 (amd64) architecture for testing or fun. From software only...

This process does involve some black magic and cludging. However, there is disk image for download which might get you starting much faster.

Let's see those steps one by one.

Building QEMU

I'm using qemu from cvs source.

$ ./configure --cc=gcc-3.4 --host-gcc=gcc-3.4 --disable-gfx-check
$ sudo make install
$ sudo modprobe kqemu
Hint: I'm using Debian unstable, which has gcc 4.0 as default compiler. That's why I had to specify gcc-3.4 because qemu has problems with newer version.
Hint: If you want, you can also use Qemu hand written code generator to get additional 30% speed increase in processor intensive operations. This version (until it gets merged with main tree) is available at
Hint: Just for a reference, if you get qemu error set_multmode: status set 0x41 DriveReady error you will have to apply following patch (needed for 0.7.2 version, not needed for 0.8.0)
Index: hw/ide.c
RCS file: /cvsroot/qemu/qemu/hw/ide.c,v
retrieving revision 1.38
diff -u -w -r1.38 ide.c
--- hw/ide.c	6 Aug 2005 09:14:32 -0000	1.38
+++ hw/ide.c	29 Dec 2005 21:39:50 -0000
@@ -185,7 +185,7 @@
 /* set to 1 set disable mult support */
-#define MAX_MULT_SECTORS 16
 /* ATAPI defines */

Creating disk image

$ qemu-img create c.img 2G
Formating 'c.img', fmt=raw, size=2097152 kB
Create partition using fdisk, something like this:
$ sudo fdisk -C 261 -u c.img

Disk c-new.img: 0 MB, 0 bytes
255 heads, 63 sectors/track, 261 cylinders, total 0 sectors
Units = sectors of 1 * 512 = 512 bytes

    Device Boot      Start         End      Blocks   Id  System
c-new.img1              63     3694949     1847443+  83  Linux
c-new.img2         3694950     4192964      249007+  82  Linux swap / Solaris
Hint: cylindars are calculated using following expression
$ expr 2147483648 \/ 63 \/ 255 \/ 512
Now we need to calculate offset in our disk image to future hda1 (which is displayed as c.img1 in fdisk)
$ expr 63 \* 512
and mount it using loopback, to make filesystem on it.
$ sudo losetup -o 32256 -f c.img
$ sudo mke2fs -m 0 -j /dev/loop0 461860
$ sudo mount -o loop,offset=32256 -t ext2 c.img /mnt
Hint: block count for mke2fs was calculated with
$ expr 1847443 \/ 4
each block is 4k. Since loopback device /dev/loop0 is larger than hda1 (swap is after that), we need to specify it's size.

Installing Debian

Now we will use debootstrap to install sid on amd64

$ debootstrap --arch amd64 --foreign sid /mnt/
we will also need to create console file so that we can boot into our machine
$ sudo mknod /mnt/dev/console c 5 1
and umount our filesystem
$ sudo umount /mnt

Getting kernel for x86_64

Unfortunately, I wasn't able to get -initrd qemu option to work, so I couldn't use Debian installer kernel and initrd to work.

I took kernel and initrd from gentoo's install-amd64-minimal-2005.0.iso and extracted kernel (gentoo) and initrd (gentoo.igz) from it to be able to boot into newly installed system.

Hint: Procedure was simple. I just downloaded image and mounted iso image using -o loop and copied files.
$ sudo mount install-amd64-minimal-2005.0.iso /mnt2 -o loop
$ cp /mnt2/isolinux/gentoo .
$ cp /mnt2/isolinux/gentoo.igz .
You will also need livecd.squashfs but on newly created filesystem (so that original gentoo kernel can read it -- we need network modules from it).

First boot with qemu-system-x86_64

Now you can boot into your half-installed system
Hint: Since we used --foreign flag to debootstrap, we got half-installed system. Second stage debootstrap must be run on target hardware. It might work to install whole debootstrap on x86 (32-bit), but I didn't try.
$ qemu-system-x86_64 -kernel gentoo -initrd gentoo.igz -hda c.img -append "root=/dev/hda1 rw init=/bin/bash -ls"

Continue installation on "native" hardware

Once you get shell prompt (yee) continue debootstrap with
$ cd /debootstrap
$ export PATH
$ ./debootstrap --second-stage
this will take a while (it's emulated CPU).
Hint: export PATH is required so that dpkgwon't fail after chroot / which is used by debootstrap.
Now, let's run base-config:
$ mount proc /proc -t proc
$ base-config
Hint: I had to twiddle with base-config to make it work. After first start, it just returned Terminated message really fast. I tried running it with sh -x /usr/sbin/base-config and then started it manually using
root@(none):/# export BASE_CONFIG_IN_SCRIPT=1
root@(none):/# /usr/sbin/base-config -q -a /var/log/base-config.log -t
Configuring the base system...

Twaking new system

I still needed to make few tweaks to the system:
root@(none):/# cd /dev
root@(none):/dev# ./MAKEDEV generic
root@(none):/dev# mount /dev/hda1 / -o rw,remount
root@(none):/dev# mkswap /dev/hda2
Setting up swapspace version 1, size = 1052831 kB
no label, UUID=11144491-3940-4cd9-84e6-79083511d8bc
root@(none):/dev# swapon /dev/hda2
Adding 1028152k swap on /dev/hda2.  Priority:-1 extents:1
and create /etc/fstab:
/dev/hda1	/	ext3	defaults,errors=remount-ro	0 1
/dev/hda2	none	swap	sw				0 0
/proc		/proc	proc	defaults			0 0

Adding networking

Shut down your new machine,
$ sync
$ sync
$ halt
and mount again disk image to add modules (so we can enable initial networking)
$ sudo mount -o loop,offset=32256 -t ext3 c.img /mnt
$ sudo cp /mnt2/livecd.squashfs /mnt
$ sudo umount /mnt
now, we need to install modules, boot virtual machine with
$ qemu-system-x86_64 -kernel gentoo -initrd gentoo.igz -hda c.img -nographic -append "console=ttyS0 root=/dev/hda1 init=/bin/bash rw"
and copy modules to disk
root@(none):/# mount livecd.squashfs /mnt -o loop
root@(none):/# cp -r /mnt/lib/modules/2.6.11-gentoo-r3-k8 /lib/modules/
root@(none):/# depmod -a
root@(none):/# modprobe ne2k-pci
ne2k-pci.c:v1.03 9/22/2003 D. Becker/P. Gortmaker
PCI: Found IRQ 11 for device 0000:00:03.0
eth0: RealTek RTL-8029 found at 0xc100, IRQ 11, 52:54:00:12:34:56.
wow, we have network. We should now add ne2k-pci to /etc/modules and create /etc/network/interfaces:
auto lo eth0
iface lo inet loopback
iface eth0 inet dhcp
While you are at it, change your hostname in /etc/hostname to something sane, and add sources to /etc/apt/sources.list:
deb sid main contrib non-free

Install Debian kernel

We will now install also boot loader (grub in this case) and latest version of kernel image (or any other that you want):
root# apt-get update
root# apt-get install grub linux-image-2.6.14-2-amd64-generic
this will again take a while... (emulated CPU, rembember?)
Install grub to hard disk:
root# grub-install /dev/hda
root# update-grub
Searching for GRUB installation directory ... found: /boot/grub .
Testing for an existing GRUB menu.list file...

Could not find /boot/grub/menu.lst file. Would you like /boot/grub/menu.lst generated for you? (y/N) y
Searching for splash image... none found, skipping...
Found kernel: /boot/vmlinuz-2.6.14-2-amd64-generic
Updating /boot/grub/menu.lst ... done
we would also like to keep our serial console, so make following changes in /boot/grub/menu.lst:
 # Put static boot stanzas before and/or after AUTOMAGIC KERNEL LIST
 # serial console
 serial --unit=0 --speed=9600
 terminal --timeout=2 serial console
 ## lines between the AUTOMAGIC KERNELS LIST markers will be modified
 ## by the debian update-grub script except for the default options below
 ## DO NOT UNCOMMENT THEM, Just edit them to your needs
 ## ## Start Default Options ##
 ## default kernel options
 ## default kernel options for automagic boot options
 ## If you want special options for specific kernels use kopt_x_y_z
 ## where x.y.z is kernel version. Minor versions can be omitted.
 ## e.g. kopt=root=/dev/hda1 ro
 # kopt=root=/dev/hda1 ro console=tty1 console=ttyS0,9600
And changes in /etc/inittab to enable serial console:
 # Example how to put a getty on a serial line (for a terminal)
 T0:23:respawn:/sbin/getty -L ttyS0 9600 vt100
 #T1:23:respawn:/sbin/getty -L ttyS1 9600 vt100
Hint: This is also the right moment to change root password. It shouldn't be neceserry (especially if you won't access this machine over network, but ssh won't let you connect as root without password.

Booting configured system

now, we will now boot our emulated amd64 with serial console attached to it:
$ qemu-system-x86_64 -hda c.img -redir tcp:4222: -nographic -serial stdio -monitor stdio
Hint: With gentoo kernel you will get occasional serial8250: too much work for irq4 (especially with dialogs which try to refresh large portions of screen). That's why we used console=ttyS0,9600.

Additional software installation

You also might want to install several additional packages now which will make our life easier down the road:
root# apt-get install vim less screen ssh sudo locales

Cleanup host machine

Finally, remove loop device which we don't need any more

$ sudo losetup -d /dev/loop0

Disk image

If you don't have few days or patience to follow this steps, I have prepared ready-to-run disk image . It's 227 Mb, so download will take a while, but after download, you just need to compile qemu (or use binary provided by your distribution) and you are ready to go!

$ gzip -d debian-sid-x86_64.bin.gz
$ qemu-system-x86_64 -hda debian-sid-x86_64.bin -redir tcp:4222: -nographic -serial stdio -monitor stdio
Hint: qemu will disable graphic output even if it's compiled with SDL. However, image is configured to support normal tty console also, so if you omit last three options (in second line) you will get graphical terminal console.
Hint: Disk image has only one user, root without any password. You won't be able to connect via ssh until you change root password.
Enjoy your new virtual machine...
amd64:~# uname -a
Linux amd64 2.6.14-2-amd64-generic #2 Fri Dec 30 06:20:57 CET 2005 x86_64 GNU/Linux