The Pragmatic Addict

Virtualize Windows 10 using QEMU


As with all projects this was to scratch an itch. Starting with Debian 10, VirtualBox was no longer included in the distribution. Don’t get me wrong, VirtualBox is an awesome product but I wanted to “stretch my legs” and try something a bit different. QEMU is the grandaddy of virtualization starting way back in 2007. In this case I’ll be using QEMU with KVM which adds hardware virtualization support for x86(32 & 64 bit) architectures.

The goal is to mimic all the functionality I had with Virtualbox but with QEMU. In my case Windows 10 is a necessary evil if you own any Garmin GPS devices and need to update them (ZERO Linux support).

This document will walk you through the configuration I have developed to make this work smoothly. Note, it is assumed you are extremely familiar with Linux and a bash (or some other) shell.

The following sites were used as reference to help piece this together:

Create a disk image

This command will create a qcow2 (can’t make this up) disk image of 100Gb. Note this image file is NOT fully allocated at creation, be sure you have room for this file to expand to it’s capacity!

qemu-img create -f qcow2 win10.qcow2 100G

Create a script to launch the virtual environment.

Trust me, you are going to want to do this very important step. There are WAY too many command line options to just type in on the fly. This is almost a direct rip off from the gentoo instructions linked above:

exec qemu-system-x86_64 \
 -enable-kvm -cpu host -m 4G \
 -drive file=win10.qcow2,if=virtio \
 -device virtio-tablet \
 -rtc base=localtime \
 -net nic,model=virtio-net-pci -net user,hostname=win10 \
 -monitor stdio \
 -name "win10" \
 -usb -device usb-ehci -device usb-host,vendorid=0x091e \
 -display gtk,grab-on-hover=on \

Lets talk about what this does line by line:

-enable-kvm -cpu host -m 4G
This enables the KVM virtualization support and passthrough the CPU specs. Note this only creates one cpu in the Virtual environment. In addition the system will have 4gb of memory.
-drive file=win10.qcow2,if=virtio
Very important to mount your drive image!
-device virtio-tablet
The virtio-tablet is needed if you wish the virtual window to release the keyboard/mouse when you move the mouse out of the window. Otherwise you have to use Ctrl-Alt-g (huge pain)
-rtc base=localtime
This is will format the virtual real time clock so Windows will get the correct time.
-net nic,model=virtio-net-pci -net user,hostname=win10
Networking is pretty basic with a simple NAT setup.
-monitor stdio
The QEMU control console will be launched from the same terminal this script runs from.
-name “win10”
win10 will be displayed at the top of the virtual window.
-usb -device usb-ehci -device usb-host,vendorid=0x091e
This is the USB pass-thru. For some reason USB 3 wouldn’t work and I had to define a USB 2 virtual device to host the pass through. It is critical to define the USB hub device BEFORE the pass-thru definitions. The vendorid allows Windows 10 to capture any Garmin device plugged into the host machine (more below).
-display gtk,grab-on-hover=on
A very important undocumented feature! When you move the mouse into the virtual window it auto-magically captures the keyboard!
The last line is some bash-fu to pass extra options.

Boot Windows 10 Installation

./ -boot d -drive file=win10cd.iso,media=cdrom -drive file=virtio-win.iso,media=cdrom

This is where the fancy bash-fu comes into practice. The above command will do the following:

Windows Initial Install

Your windows installation media iso should boot in the VM window.

You can “free” your mouse/keyboard by using Ctrl-Alt-g.

Windows Driver Install

QEMU has a few paravirtualized drivers to make your virtual machine perform better.

Ethernet Controller Driver

HID Driver (Human Interface Device)

Display Driver

Install the Guest Agent

Post install notes

Time & Date

USB Pass-Thru

Finding the VendorID & ProductID

Run the lsusb command once your device is plugged in. You should see a line similar to the following:

Bus 001 Device 020: ID 091e:2a1a Garmin International

In our case we want to pass through ALL Garmin devices so we’ll focus on VendorID, in this case it’s “091e”

Set permissions in UDEV

Create the following file: /etc/udev/rules.d/10-qemu-hw-users.rules

SUBSYSTEM=="usb", ATTRS{idVendor}=="091e", TAG+="uaccess"

Following this run the following to update the udev system.

udevadm control --reload-rules

This should give you the necessary permissions to manage these devices without root access.

In your VM you should see a new “USB Mass Storage Device” called “Garmin Drive”

Created: 2020-07-02 Modified: 2024-05-09