All the examples & notes use qemu-system-x86_64 but in most cases this can be swapped with the system emulator for other architectures.


Graphic mode:

Ctrl+Alt+g         release mouse capture from VM

Ctrl+Alt+1         switch to display of VM
Ctrl+Alt+2         switch to qemu monitor

No graphic mode:

Ctrl+a h           print help
Ctrl+a x           exit emulator
Ctrl+a c           switch between monitor and console

VM config snippet

Following command-line gives a good starting point to assemble a VM:

qemu-system-x86_64                 \
    -cpu host -enable-kvm -smp 4   \
    -m 8G                          \
    -vga virtio -display sdl,gl=on \
    -boot menu=on                  \
    -cdrom <iso>                   \
    -hda <disk>                    \
    -device qemu-xhci,id=xhci      \
    -device usb-host,bus=xhci.0,vendorid=0x05e1,productid=0x0408,id=capture-card


# Emulate host CPU in guest VM, enabling all supported host featured (requires KVM).
# List available CPUs `qemu-system-x86_64 -cpu help`.
-cpu host

# Enable KVM instead software emulation.

# Configure number of guest CPUs.
-smp <N>

# Configure size of guest RAM.
-m 8G

Graphic & Display

# Use sdl window as display and enable openGL context.
-display sdl,gl=on

# Use vnc server as display (eg on display `:42` here).
-display vnc=localhost:42

# Confifure virtio as 3D video graphic accelerator (requires virgl in guest).
-vga virtio

Boot Menu

# Enables boot menu to select boot device (enter with `ESC`).
-boot menu=on

Block devices

# Attach cdrom drive with iso to a VM.
-cdrom <iso>

# Attach disk drive to a VM.
-hda <disk>

# Generic way to configure & attach a drive to a VM.
-drive file=<file>,format=qcow2

Create a disk with qemu-img

To create a qcow2 disk (qemu copy-on-write) of size 10G:

qemu-img create -f qcow2 disk.qcow2 10G

The disk does not contain any partitions or a partition table. We can format the disk from within the guest as following example:

# Create `gpt` partition table.
sudo parted /dev/sda mktable gpt

# Create two equally sized primary partitions.
sudo parted /dev/sda mkpart primary 0% 50%
sudo parted /dev/sda mkpart primary 50% 100%

# Create filesystem on each partition.
sudo mkfs.ext3 /dev/sda1
sudo mkfs.ext4 /dev/sda2

lsblk -f /dev/sda
  ├─sda1 ext3         ....
  └─sda2 ext4         ....


Host Controller

# Add XHCI USB controller to the VM (supports USB 3.0, 2.0, 1.1).
# `id=xhci` creates a usb bus named `xhci`.
-device qemu-xhci,id=xhci

USB Device

# Pass-through USB device from host identified by vendorid & productid and
# attach to usb bus `xhci.0` (defined with controller `id`).
-device usb-host,bus=xhci.0,vendorid=0x05e1,productid=0x0408


# Open gdbstub on tcp `<port>` (`-s` shorthand for `-gdb tcp::1234`).
-gdb tcp::<port>

# Freeze guest CPU at startup and wait for debugger connection.

IO redirection

# Create raw tcp server for `serial IO` and wait until a client connects
# before executing the guest.
-serial tcp:localhost:12345,server,wait

# Create telnet server for `serial IO` and wait until a client connects
# before executing the guest.
-serial telnet:localhost:12345,server,wait

# Configure redirection for the QEMU `mointor`, arguments similar to `-serial`
# above.
-monitor ...

In server mode use nowait to execute guest without waiting for a client connection.


# Redirect host tcp port `1234` to guest port `4321`.
-nic user,hostfwd=tcp:localhost:1234-:4321

Shared drives

# Attach a `virtio-9p-pci` device to the VM.
# The guest requires 9p support and can mount the shared drive as:
#   mount -t 9p -o trans=virtio someName /mnt
-virtfs local,id=someName,path=<someHostPath>,mount_tag=someName,security_model=none

Debug logging

# List debug items.
-d help

# Write debug log to file instead stderr.
-D <file>

# Examples
-d in_asm       Log executed guest instructions.


# List name of all trace points.
-trace help

# Enable trace points matching pattern and optionally write trace to file.
-trace <pattern>[,file=<file>]

# Enable trace points for all events listed in the <events> file.
# File must contain one event/pattern per line.
-trace events=<events>

VM snapshots

VM snapshots require that there is at least on qcow2 disk attached to the VM (VM Snapshots).

Commands for qemu Monitor or QMP:

# List available snapshots.
info snapshots

# Create/Load/Delete snapshot with name <tag>.
savevm <tag>
loadvm <tag>
delvm <tag>

The snapshot can also be directly specified when invoking qemu as:

qemu-system-x86_64 \
    -loadvm <tag>  \

VM Migration

Online migration example:

# Start machine 1 on host ABC.
qemu-system-x86_64 -monitor stdio -cdrom <iso>

# Prepare machine 2 on host DEF as migration target.
# Listen for any connection on port 12345.
qemu-system-x86_64 -monitor stdio -incoming tcp:

# Start migration from the machine 1 monitor console.
(qemu) migrate tcp:DEF:12345

Save to external file example:

# Start machine 1.
qemu-system-x86_64 -monitor stdio -cdrom <iso>

# Save VM state to file.
(qemu) migrate "exec:gzip -c > vm.gz"

# Load VM from file.
qemu-system-x86_64 -monitor stdio -incoming "exec: gzip -d -c vm.gz"

The migration source machine and the migration target machine should be launched with the same parameters.

Appendix: Direct Kernel boot

Example command line to directly boot a Kernel with an initrd ramdisk.

qemu-system-x86_64                                                     \
    -cpu host                                                          \
    -enable-kvm                                                        \
    -kernel <dir>/arch/x86/boot/bzImage                                \
    -append "earlyprintk=ttyS0 console=ttyS0 nokaslr init=/init debug" \
    -initrd <dir>/initramfs.cpio.gz                                    \

Instructions to build a minimal Kernel and initrd.

Appendix: Cheap instruction tracer

test: test.s
	as -o test.o test.s
	ld -o test test.o testc.o

trace: test
	qemu-x86_64 -singlestep -d nochain,cpu ./test 2>&1 | awk '/RIP/ { print $$1; }'

	$(RM) test test-bin test.o
.section .text, "ax"

.global _start
    xor %rax, %rax
    mov $0x8, %rax
    cmp $0, %rax
    je 2f
    dec %rax
    jmp 1b
    # x86-64 exit(2) syscall
    mov $0, %rdi
    mov $60, %rax