The qemu project is really cool, and their goal is to support lots of computers, not just PCs.
The PC emulation works well and doesn't suffer from major incompatibility problems from qemu release to qemu release. It is really simple to get started. Create 4GiB disk image:
dd if=/dev/zero of=disk.img bs=1048576 count=4096Install:
qemu-system-i386 -hda disk.img -cdrom your-favourite-os-install.iso -boot dBoot the installed system:
qemu-system-i386 -hda disk.img
To instead emulate a 64-bit PC, use qemu-system-x86_64
. See also
below. Note that the install suggestions below will result in faster systems
than these basic examples.
The downside of qemu is lack of documentation, and in particular usage examples. Also, if you try some random GNU/Linux or BSD release for a non-PC with some random qemu release, it will very likely not work as easily as above. Here I try to show one working variant of each base OS. Hopefully these examples will get you started!
If you upgrade a system, do not forget to first bring the system down and make a copy of the old disk image. It might be hard to go back once you've made your disk image unbootable. (The habit of copying disk images is a good one when working with any emulation, and is in fact a key feature of such environments!)
The qemu project relies on code inspection, but they perform limited regression testing. My experience is that regressions are not uncommon with qemu. If you are going to use qemu for something else than fun, you need to keep several builds and choose the one that works reliably for a particular system.
Unfortunately, reporting bugs or posting bug fixes to the qemu project is usually a completely futile exercise. I have never gotten a bug report taken seriously, and as a result some bugs live year after year. I have gotten some fixes in, but only after much haggling and pointing out that their variations on the initial (correct) fix are broken. In the end, my initial fix gets applied, but now with credit taken by some qemu project member.
My motive for this entire exercise is software testing; qemu allows me to test things for systems to which I have no other access. I just need ssh access, and that's exactly what these example installs provide.
Q: Do these examples really work? Have they been tested?
A: I have tested exactly what I suggest below in each case. They do work.
Q: What about networking? What's this "tap" thing?
A: If your host is set up to allow network bridging (think of that a simulated Ethernet switch) and if you either configure an IP address manually during guest OS installation, or have a dhcp server, then network should just work. If you don't want to configure the host to allow tap to work, check qemu's documentation about "user" networking and perhaps the hostfwd feature; many people find that easier to get going.
Q: The amount of memory (as specified by -m
) varies between these examples, why?
A: There isn't too much science behind that. I tend to give slightly more memory to a 64-bit guest than a 32-bit guest.
Q: The qemu version varies between these examples. Any good reasons for that?
A: Some past versions of qemu have had regressions affecting particular targets. Therefore, I recommend qemu versions that I have made sure works. By all means, try other versions, but my recommendation is to start with exactly my setup and then go from there.
Q: Disks, network, and kernel are specified in varying ways. Why not be more consistent?
A: I believe the variation is mostly well-motivated by limitations in the
emulated OS and/or qemu. It is possible that things could be made more
consistent, but I have tried to minimize unmotivated variation. Note that
Debian is highly inconsistent with kernel naming or downloads
and /boot
contents ("linux"/"vmlinux"/"vmlinuz", with or without
explicit version numbers, with version-number free symlinks or without such
symlinks).
Q: Qemu offers several disk formats. In these examples, "raw" format seem to be
either understood or explicitly enforced with the format=raw
specifier. Reasons?
A: I actually use either lvm partitions or sparse plain files for my guest
disks (the latter created with dd's seek
operator). These imply
minimal host CPU overhead. If you want to use qcow2 or some other setup, that
should work fine (but make sure to remove (or adapt) the format=raw
specifier).
Q: I want to emulate machine M with CPU C, a combination which I believe qemu supports. How should I do that?
A: The goal of my page here is to allow for testing on various CPU architectures. I realise that not all sub-architectures are covered (e.g., armv4, sparc millennium, mips r6). Also, the machine type is largely ignored. I don't currently have plans to try to make it much more complete, as that would be hard to maintain in the long run. So, I'm afraid I can only wish you luck with machine M emulation.
Q: There is something called qemu user-level emulation, skirting this thing about OS installation. Why don't you give examples of that instead?
A: I intend to do that. In fact, I have a shell script which installs all Debian versions which I can get to work. The main problem here is that the binfmt GNU/Linux feature is poorly designed and that the qemu pattern files which come with most (perhaps all) GNU/Linux dists are pretty buggy.
x86-32 | x86-64 | mips32 | mips64 | sparc32 | sparc64 | ppc32 | ppc64 | arm32 | arm64 | s390x | alpha | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
FreeBSD | yes1 | yes4 | ? | ? | n/a | yes10 | hangs | yes12 | ? | yes16 | n/a | |
NetBSD | yes2 | yes5 | ? | ? | yes9 | yes10 | no | n/a | ? | yes16 | n/a | |
OpenBSD | ? | ? | ? | ? | yes9 | yes10 | hangs | n/a | ? | yes16 | n/a | |
GNU/Linux | yes3 | yes6 | yes7 | yes8 | n/a | yes10 | yes11 | yes13 | yes14,15 | yes16 | yes17 | yes18,19 |
GNU/Hurd | yesTBD | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a |
These are rudimentary qemu guest install instructions. They assume the reader knows how to install each OS on real hardware.
You probably want to enable networking both during the install and boot
process. You can either use
like below, or
for some
adequate PORT number. (What "tap" means and how to do that is beyond the scope
of this page, the qemu side of it is described in the qemu man page, the rest
is quite system specific. It can be evil to get it right, since advanced
networking is not too well designed on all systems.)
I would recommend that you initially do exactly like in these examples, since the system is fragile and even seemingly innocent deviations might lead to failure. When you have something that works, you can always go from there.
If you start several qemu guests at the same time, you might get networking
problems since they by default use the same MAC address. The solution is to
pass
, for some suitable (e.g.,
random) values of the X'es, different for each running qemu. Each example
below uses a different MAC address.
These examples' index numbers correspond to the table above.
xz -d FreeBSD-12.2-RELEASE-i386-disc1.iso.xz
common_args="-drive file=disk.img,if=virtio,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:01,model=virtio -net tap"Install:
qemu-system-i386 $common_args -cdrom FreeBSD-12.2-RELEASE-i386-disc1.iso -boot dBoot:
qemu-system-i386 $common_args
common_args="-drive file=disk.img,if=virtio,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:02,model=virtio -net tap"Install:
qemu-system-i386 $common_args -cdrom NetBSD-8.2-i386.iso -boot dBoot:
qemu-system-i386 $common_args
common_args="-drive file=disk.img,if=virtio,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:03,model=virtio -net tap"Install:
qemu-system-i386 $common_args -cdrom debian-9.12.0-i386-netinst.iso -boot dBoot:
qemu-system-i386 $common_args
xz -d FreeBSD-12.2-RELEASE-amd64-disc1.iso.xz
common_args="-drive file=disk.img,if=virtio,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:04,model=virtio -net tap"Install:
qemu-system-x86_64 $common_args -cdrom FreeBSD-12.2-RELEASE-amd64-disc1.iso -boot dBoot:
qemu-system-x86_64 $common_args
common_args="-drive file=disk.img,if=virtio,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:05,model=virtio -net tap"Install:
qemu-system-x86_64 $common_args -cdrom NetBSD-8.2-amd64.iso -boot dBoot:
qemu-system-x86_64 $common_args
common_args="-drive file=disk.img,if=virtio,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:06,model=virtio -net tap"Install:
qemu-system-x86_64 $common_args -cdrom debian-9.12.0-amd64-netinst.iso -boot dBoot:
qemu-system-x86_64 $common_args
common_args="-M malta -m 256 -drive file=disk.img,if=virtio,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:07,model=virtio -net tap -nographic"Install:
kernel=[...fill in name of downloaded kernel...]Follow the instructions in the section Copying out Linux kernel and initrd below.
qemu-system-mipsel $common_args -kernel $kernel -initrd initrd.gz -append "console=ttyS0"
kernel=[...fill in name of copied-out kernel...]Notes:
qemu-system-mipsel $common_args -kernel boot/$kernel -initrd boot/initrd.img -append "root=/dev/vda1 console=ttyS0" -serial null -monitor null
"root=..."
argument accordingly.
qemu-system-mips
, is an
alternative. The install process is somewhat different, and Debian dropped
big-endian MIPS some years ago. We don't provide detailed instructions
here. (We used to provide big-endian as the default instructions,
see the
internet archive.)
common_args="-M malta -cpu 5KEc -m 1024 -drive file=disk.img,if=virtio,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:08,model=virtio -net tap -nographic"Install:
kernel=[...fill in name of downloaded kernel...]Follow the instructions in the section Copying out Linux kernel and initrd below.
qemu-system-mips64el $common_args -kernel $kernel -initrd initrd.gz -append "console=ttyS0"
kernel=[...fill in name of copied-out kernel...]Notes:
qemu-system-mips64el $common_args -kernel boot/$kernel -initrd boot/initrd.img -append "root=/dev/vda1 console=ttyS0" -serial null -monitor null
"root=..."
argument accordingly.
-cpu 5KEc
selects a different Linux idle loop,
utilising the wait
instruction. Without this, qemu will
consume 100% host CPU. This option also enables R2 instructions. (If the
latter is undesirable, use -cpu 5Kc
instead.)
qemu-system-mips64
, is an
alternative. The install process is somewhat different, and Debian dropped
big-endian MIPS some years ago. We don't provide detailed instructions
here. (We used to provide big-endian as the default instructions,
see the
internet archive.)
common_args="-m 256 -drive file=disk.img,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:09 -net tap,script=/etc/qemu-ifup -nographic"Install:
qemu-system-sparc $common_args -cdrom NetBSD-9.2-sparc.iso -boot dBoot:
qemu-system-sparc $common_argsNotes:
common_args="-m 512 -drive file=disk.img,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:10 -net tap,script=/etc/qemu-ifup -nographic"Install:
qemu-system-sparc64 $common_args -cdrom NetBSD-9.2-sparc64.iso -boot dBoot:
qemu-system-sparc64 $common_argsNotes:
hme
(which is also the default
since a few releases back) works OK. While qemu 6.x got most networking
problems fixed, no tried OS runs stably.
model=e1000
as a networking parameter.
Lastly tested with qemu 2.10.0.
-prom-env
'boot-device=/path/to/device'
.)
common_args="-drive file=disk.img,if=virtio,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:11,model=virtio -net tap -display vnc=:11"Install:
qemu-system-ppc $common_args -cdrom debian-8.11.0-powerpc-CD-1.iso -boot dPerform a default install using vncviewer. Note that the boot might hang on input with a screen with minimal contrasts; just hit ENTER to continue to a readable install dialogue.
qemu-system-ppc $common_args -kernel boot/vmlinux-3.16.0-4-powerpc -initrd boot/initrd.img-3.16.0-4-powerpc -append "root=/dev/vda3"Notes:
root=
argument depending on where
your / partition was put. It should be the one given here if you used the
default layout from Guided Partitioning.
xz -d FreeBSD-13.0-RELEASE-powerpc-powerpc64-disc1.iso.xz
common_args="-M pseries -cpu POWER9 -m 1024 -drive file=disk.img,if=virtio,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:12 -net tap,script=/etc/qemu-ifup -nographic -vga none"Install:
qemu-system-ppc64 $common_args -cdrom FreeBSD-13.0-RELEASE-powerpc-powerpc64-disc1.iso -boot dBoot:
qemu-system-ppc64 $common_args -serial null -monitor nullNotes:
if=scsi
instead of if=virtio
for the disk. Newer qemu releases will
not work. For these configurations, The mac address specified is
mangled in a weird way: If given aa:bb:cc:dd:ee:ff the mac address actually
used is aa:bb:cc:ff:00:00. Beware of collisions, and if you use DHCP, of
unexpected IP address assignments!
common_args="-M pseries -cpu POWER8 -m 512 -drive file=disk.img,if=virtio,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:13,model=virtio -net tap -display vnc=:13"Install:
qemu-system-ppc64 $common_args -kernel vmlinux -initrd initrd.gzPerform a normal install using vncviewer.
qemu-system-ppc64 $common_args -kernel boot/vmlinux -initrd boot/initrd.gz -append "root=/dev/vda1"Notes:
-kernel
and -initrd
parameters, but
then if=virtio
needs to be changed to if=scsi
.
qemu-system-ppc64
, is an alternative.
Download kernel
and initrd
instead of the files linked above, then proceed analogously. An advantage
here is that this allows us to use Debian's latest release.
common_args="-M virt -cpu cortex-a15 -m 256 -drive file=disk.img,if=none,format=raw,id=hd0 -device virtio-blk-device,drive=hd0 -netdev type=tap,id=net0 -device virtio-net-device,netdev=net0,mac=52:54:00:fa:ce:14 -nographic"Install:
qemu-system-arm $common_args -kernel vmlinuz -initrd initrd.gz -append "console=ttyAMA0 --"Follow the instructions in the section Copying out Linux kernel and initrd below.
qemu-system-arm $common_args -kernel boot/vmlinuz -initrd boot/initrd.img -append "root=/dev/vda1 rw console=ttyAMA0 --" -serial null -monitor null
"root=..."
argument accordingly.
common_args="-M versatilepb -m 256 -drive file=disk.img,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:15 -net tap -display vnc=:14"Install using vncviewer over the network:
qemu-system-arm $common_args -kernel vmlinuz-3.16.0-6-versatile -initrd initrd.gzFollow the instructions in the section Copying out Linux kernel and initrd below.
qemu-system-arm $common_args -kernel boot/vmlinuz -initrd boot/initrd.img -append "root=/dev/sda1"
common_args="-M virt -cpu cortex-a57 -m 256 -drive file=disk.img,if=none,format=raw,id=hd0 -device virtio-blk-device,drive=hd0 -netdev type=tap,id=net0 -device virtio-net-device,netdev=net0,mac=52:54:00:fa:ce:16 -nographic"Install:
qemu-system-aarch64 $common_args -kernel linux -initrd initrd.gz -append "console=ttyAMA0 --"Follow the instructions in the section Copying out Linux kernel and initrd below.
qemu-system-aarch64 $common_args -kernel boot/vmlinuz -initrd boot/initrd.img -append "root=/dev/vda1 rw console=ttyAMA0 --" -serial null -monitor null
xz -d FreeBSD-13.0-RELEASE-arm64-aarch64.raw.xz
qemu-system-aarch64 -M virt -cpu cortex-a57 -m 256 -drive file=FreeBSD-13.0-RELEASE-arm64-aarch64.raw,if=none,format=raw,id=hd0 -device virtio-blk-device,drive=hd0 -netdev type=tap,id=net0 -device virtio-net-device,netdev=net0,mac=52:54:00:fa:de:16 -bios QEMU_EFI.fd -serial telnet::4444,server -monitor null -nographic
gunzip arm64.img.gz
qemu-system-aarch64 -M virt -cpu cortex-a57 -m 256 -drive file=arm64.img,if=none,format=raw,id=hd0 -device virtio-blk-device,drive=hd0 -netdev type=tap,id=net0 -device virtio-net-device,netdev=net0,mac=52:54:00:fa:ee:16 -bios QEMU_EFI.fd -serial telnet::4444,server -monitor null -nographic
OpenBSD can also be made to work for ARM64. They don't provide a ready image, so we need to install things in a more standard fashion. Note that virtio devices don't work properly (with OpenBSD 6.9).
common_args="-M s390-ccw-virtio -m 512 -drive file=disk.img,if=none,format=raw,id=hd0 -device virtio-blk-ccw,drive=hd0,id=virtio-disk0 -netdev type=tap,id=net0 -device virtio-net-ccw,netdev=net0,mac=52:54:00:fa:ce:17,devno=fe.0.0001 -nographic"Install:
qemu-system-s390x $common_args -kernel kernel.debian -initrd initrd.debianPerform a default install using the terminal. This Debian install is somewhat unusual, but you'll be instructed by the installer.
qemu-system-s390x $common_args -kernel boot/vmlinuz -initrd boot/initrd.img -append "root=/dev/vda1"
vmlinuz
to vmlinux
.
common_args="-m 256 -drive file=disk.img,media=disk,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:18 -net tap -nographic"Install:
qemu-system-alpha $common_args -kernel vmlinux -initrd initrd.gz -drive file=debian-5010-alpha-netinst.iso,if=ide,media=cdrom -append "console=ttyS0"Follow the instructions in the section Copying out Linux kernel and initrd below.
qemu-system-alpha $common_args -kernel boot/vmlinux -initrd boot/initrd.img -append "root=/dev/hda3 console=ttyS0"
Qemu's Alpha firmware (SRM) is not ready and is probably not yet useful for anything. Therefore, we need to boot a kernel directly.
I've tried hard to make the kernel use the installation ISO as root image (with and without Gentoo's initrd image gentoo.igz), but the cdrom isn't found and a kernel panic ensues. I therefore use the trick of copying the ISO's contents into a temporary disk image ("inst-disk.img").
Note that these commands assume a GNU/Linux host (mkfs.ext3 command and loop mount syntax are non-portable). The end result should run under other OSes, though.
I recommend that you cut-and-paste the commands here and follow the progress of each command. The "set -e" is there should you be tempted to use it as a script; do not issue it to an interactive shell or you will find yourself logged out before long!
ISO=install-alpha-minimal-[DATE1].iso TARG=try1 # Subdir where to put results DISK_SIZE=16 # Disk size in GiB, should be >= 6 set -e # Don't paste this command to an interactive shell! mkdir $TARG cd $TARG mkdir iso iso_sub targ mount $ISO iso # Copy out a useful kernel from ISO zcat iso/boot/gentoo >kernel touch -r iso/boot/gentoo kernel # Prepare temporary install image "inst-disk.img". mount iso/image.squashfs iso_sub truncate --size 2G inst-disk.img mkfs.ext3 inst-disk.img mount inst-disk.img targ cp -a iso_sub/* targ # Edit inst-disk.img to not scramble the root password. sed --in-place="" 's/echo/#echo/' targ/etc/init.d/pwgen # Edit inst-disk.img password files to make initial root password empty. sed --in-place="" 's/^root:[^:]*:/root::/' targ/etc/passwd sed --in-place="" 's/^root:[^:]*:/root::/' targ/etc/shadow # Edit inst-disk.img config to allow root logins (but do not start ssh yet!). sed --in-place="" 's/.*PermitRootLogin.*/PermitRootLogin yes/' targ/etc/ssh/sshd_config umount targ umount iso_sub umount iso # Create main disk image. truncate --size ${DISK_SIZE}G disk.img # Clean up rmdir iso iso_sub targThat finishes the disk preparations. Now it is time to boot up the system and continue installation.
common_args="-smp 4 -m 512 -display vnc=:19 -serial null -monitor null"Install:
qemu-system-alpha $common_args -kernel kernel -drive file=disk.img,media=disk,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:19,model=e1000 -net tap -drive file=inst-disk.img,media=disk,format=raw,index=1 -append "root=/dev/sdb"Perform an almost plain Gentoo installation using vnc. Be warned that this is a difficult installation even for being Gentoo, since things are highly inconsistent; commands do not work as suggested or documented, partition numbers and names vary between wrong and dead wrong, etc, etc. Things to consider:
fdisk
, which apparently
cannot create anything but MBR labels. One may use no label at all, or if
a swap area and/or multiple file systems are desirable, a plain old MBR is
fine. (If you insist on using a BSD label, the parted
command
seems less broken.)
/etc/ssh/sshd_config
disables password authentication, so one
either needs to change that, or somehow copy a suitable public key to
/root/.ssh/authorized_keys
.
/proc/config.gz
to
/usr/src/linux/.config
, then make some changes:
File systems → [*] The Extended 4 (ext4) filesystem
Without this change, you will either need to generate an initrd or
stick to plain old ext2 for your root partition.
System setup → Timer interrupt frequency (HZ) (1024 Hz) --->
to e.g.,
System setup → Timer interrupt frequency (HZ) (32 Hz) --->
.
qemu-system-alpha $common_args -kernel vmlinux -drive file=disk.img,if=virtio,media=disk,format=raw,index=0 -net nic,macaddr=52:54:00:fa:ce:19,model=virtio -net tap -append "root=/dev/vda"Notes:
vda
might need to be adjusted to
vda1
, vda2
, or wherever you put the root file
system. Also, depending on kernel config, the prefix might need be "hd" or
"sd" instead of "vd"; adjust the model
argument appropriately.
-- dosshd
passwd=foo
" to work, setting the password to "foo". Alas, I have
had no success with that.
e69bf91c098b8cd54805a09b78e3fc93b18a13bb65cef845c52f0e7817837151 img.xz
63feacbfccc6747603f32d06a1cacd1d87b5fd777edaf90ded33b64e2004ba37 img
-net
line from the boot script, make an initial
boot and connect using vncviewer :5919
, change the root password
to something sensible, halt the system, and then re-instate
the -net
line before re-booting the system. (If there is access
from the Internet to the vnc port, the suggested precaution isn't going to do
much good. A vnc password would give some level of protection, though.)
When a boot loader cannot be installed, as in many cases above, one needs to supply a kernel and some initial files via an "initrd". In the case of GNU/Linux, the needed files should be in /boot at the end of installation.
There are several alternative ways one may copy these files to the host file system.
Just before the end of the installation.
In Debian (and presumably several of its derivatives), just as the Finishing the installation/Installation complete step is performed, choose Go back and then scroll down the menu to Execute a shell. In that shell do:
mount -t proc proc /target/proc
mount --rbind /sys /target/sys
mount --rbind /dev /target/dev
chroot /target bash
/etc/init.d/ssh start
Then, from the host system, do
ssh -p [arguments] [host] "tar -c -f - --exclude=lost+found /boot" | tar xf -
where [arguments] should be any required port number and [host] is the IP
address or name of the target host, or perhaps "localhost" if port
forwarding was used. Note that root logins might not work unless you edit
/etc/ssh/sshd_config
to allow root login.
Return to the installation screen, and exit the shell, and choose Finishing the installation.
Let the installation finish, then mount the disk image read-only from the host. This will usually work, but it might be hard if the host system cannot grok the disk label. Typically,
{ echo p; echo q; } | fdisk disk.img
will work. You should see a list such as:
disk.img1 * 2048 333823 331776 162M 83 Linux disk.img2 333824 3893247 3559424 1.7G 83 Linux disk.img3 3895294 4192255 296962 145M 5 Extended disk.img5 3895296 4192255 296960 145M 82 Linux swap / Solaris
You can now mount the partitions. To mount the first partition above on a GNU/Linux host, this should work:
mount -o loop,ro,offset=$((2048*512)) disk.img /mnt
If /boot lives in the 2nd partition above, use this instead:
mount -o loop,ro,offset=$((333824*512)) disk.img /mnt
Now just copy the files from /mnt. Don't forget to do:
umount /mnt.
qemu-nbd
. I haven't tried that myself.
Now, point the -kernel
and -initrd
arguments to the
files in just copied-out directory boot
. Exact arguments appear
in each example above.