I try to avoid working with Windows as much as possible, but let’s face it —sometimes there are no other options, mainly because some tools are only available on Windows. For instance, in the automotive realm, there are several tools that don't give us any other choice. However, I refuse to go fully Windows, and working with a dual boot setup is just too uncomfortable. Yes, I know we can use WSL, but it has its limitations, and I don't want to compromise my workflow in Linux. My solution is virtualization, and for good virtualization, there's no better option than KVM and QEMU on Linux.
Install QEMU and Virt Manager
We need to verify if our CPU supports virtualization or if it's enabled. You can use the following command. Here, you can see that virtualization is enabled on my machine, and I’m using an AMD processor. For Intel processors, you should see 'VT-x' displayed. If nothing appears, then you may need to enable virtualization in the BIOS, or perhaps your CPU doesn't support virtualization
$ lscpu | grep "Virtualization"
Virtualization: AMD-V
Virtualization magic in Linux is supported by KVM, which stands for Kernel-based Virtual Machine. It is already included in the Linux kernel (version 2.6.0 and later), so you don’t need to install anything. However, we need to install a program to emulate the desired hardware, and that program is QEMU, along with some additional utilities, which I will describe below:
- dnsmasq: A lightweight DNS and DHCP server with support for DHCPv6, PXE, and a TFTP server.
- vde2: A set of programs to provide virtual, software-defined Ethernet network interface controllers (NICs) across multiple devices.
- bridge-utils: Utilities needed to create and manage bridge devices for setting up networks for hosted virtual machines.
- ebtables and iptables: Utilities used to set up tables of rules (inside the Linux kernel) that inspect Ethernet frames.
- libguestfs: A set of tools for accessing and modifying virtual machine (VM) disk images.
- ovmf: Enables UEFI support for virtual machines.
- swtpm: Software emulation of cryptographic keys. This is essential for running Windows 11.
$ sudo pacman -S qemu-base virt-viewer vde2 ebtables iptables-nft nftables dnsmasq bridge-utils ovmf swtpm libguestfs-tools
In case you need or prefer a graphical user interface you should also install Virt Manager. As usual I’m personally prefer command line options for later automation
$ sudo pacman -S virt-manager
Now, we verify that the corresponding kernel modules are loaded with the lsmod command
$ lsmod | grep kvm
kvm_amd 208896 0
kvm 1368064 1 kvm_amd
ccp 180224 1 kvm_amd
After the Installation time to do some configuration, open the file libvirtd.conf with your favorite editor with root privileges
$ sudo nano /etc/libvirt/libvirtd.conf
Inside the file you need to uncomment the following two lines, to make libvirt group available for your user, and avoid typing sudo
...
# This is restricted to 'root' by default.
unix_sock_group = "libvirt"
...
# If not using PolicyKit and setting group ownership for access
# control, then you may want to relax this too.
unix_sock_rw_perms = "0770"
Then add your local user to group libvirt and kvm
$ sudo usermod -aG kvm,libvirt $USER
Make libvirt to start when you boot your computer and initiate the service daemon, the last command is just to verify if the service is active
$ sudo systemctl enable libvirtd
$ sudo systemctl start libvirtd
$ sudo systemctl status libvirtd
○ libvirtd.service - libvirt legacy monolithic daemon
Loaded: loaded (/usr/lib/systemd/system/libvirtd.service; enabled; preset:>
Active: inactive (dead) since Fri 2024-1
...
Open the file /etc/libvirt/qemu.conf
with your preferred editor using root privileges. Locate the user
and group
lines, uncomment both, and replace <username>
with your Linux username. This will eliminate the need to launch your virtual machines using sudo
.
# Some examples of valid values are:
#
# user = "qemu" # A user named "qemu"
# user = "+0" # Super user (uid=0)
# user = "100" # A user named "100" or a user with uid=100
#
user = "<useranme>"
# The group for QEMU processes run by the system instance. It can be
# specified in a similar way to user.
group = "<username>"
Restart the libvirt
daemon to apply the changes we made to qmeu.conf file
$ sudo systemctl restart libvirtd
Verify the default network is enable, persistent and auto start is also active we need the network to be active when the computer start to avoid start manually every time
$ virsh -c qemu:///system net-list --all
Name State Autostart Persistent
--------------------------------------------
default active yes yes
I add the -c qemu:///system
option to manage all my VM resources at the system level rather than the user level. This means that all my network configurations and disk images are stored in the /var/lib/libvirt
directory, while the XML configuration files are located in /etc/libvirt/qemu
. Alternatively, if you don't use this option, the resources would be stored under ~/.config/libvirt
.
In case none of the last option are active use virsh net-autostart
command
$ virsh -c qemu:///system net-autostart default
Network default marked as autostarted
Execute the following command to verify if the nested virtualization is active by default. If you get the output like "Y" or "1", it means the feature nested virtualization is enabled. Otherwise, you will see the error message as "No such file or directory".
$ cat /sys/module/kvm_amd/parameters/nested
1
In case not, enable virtualization inside our virtual machines in some kind of inception mode, this is useful in case you want to run WSL or docker inside you virtual windows machine. Note use kvm_intel instead of kvm_amd on the line below in case you are not using an AMD processor
$ sudo modprobe -r kvm_amd
$ sudo modprobe kvm_amd nested=1
Installing Windows 11
Got to the official windows portal and download the latest Windows 11 ISO and then go to https://github.com/virtio-win/virtio-win-pkg-scripts and download the Latest virtio-win ISO. Copy both images we download to a more suitable directory
$ sudo mkdir /var/lib/libvirt/images/iso
$ sudo mv Downloads/Win11_24H2_English_x64.iso /var/lib/libvirt/images/iso
$ sudo mv Downloads/virtio-win-0.1.266.iso /var/lib/libvirt/images/iso
Then proceed to the installation process with the command below. keeping in mid you have to set the memory and cores according to your own host machine resources. The most important options for you to congit are name, ram and vcpus, there is another you should pay attention and is the diks size for you virtual machine, we are setting 80Gigs with disk size option. The last line --enable-kvm --cpu
enable the enlightenment options to improve virtualization inside the Windows virtual machine, if you are using and Intel processor append also hv_evmcs
at the end
virt-install \
--connect=qemu:///system \
--name=Windows11 \
--ram=12288 \
--vcpus=8 \
--os-variant=win11 \
--disk size=80,cache=none,bus=virtio \
--disk path=/var/lib/libvirt/images/iso/virtio-win-0.1.266.iso,device=cdrom \
--cdrom /var/lib/libvirt/images/iso/Win11_24H2_English_x64.iso \
--video qxl \
--sound default \
--graphics spice,listen=0.0.0.0 \
--tpm backend.type=emulator,backend.version=2.0,model=tpm-crb \
--network network=default,model=virtio \
--cpu host \
--features kvm_hidden=on,smm=on hv_relaxed,hv_vapic,hv_spinlocks="8191",hv_vpindex,hv_runtime,hv_synic \
--features hv_time,hv_stimer,hv_reset,hv_vendor_id="KVM Hv",hv_frequencies,hv-reenlightenment,hv-tlbflush,hv-ipi
From here, simply follow the usual Windows installation steps. Watch the following video (since I'm not in the mood to make my own) from minute 6:21 to 7:10 to learn how to mount the VirtIO drivers: https://www.youtube.com/watch?v=WmFpwpW6Xko&t=659s . At some point during the installation process, you'll be prompted to install the network drivers. You can find them in the CD drive (E:) under the path virtio-win-0.1.266 → NetKVM → w11 → amd64
.
After the installation, it is strongly recommended to debloat your Windows installation using the following script: https://github.com/Raphire/Win11Debloat , and disable automatic updates.
Additionally, you need to install some guest tools that are included with the virtio-win-0.1.266.iso
image, which should still be mounted in your virtual machine. To install the guest additions:
- Navigate to the CD drive (E:)
virtio-win-0.1.266
. - Double-click
virtio-win-guest-tools.exe
to install the guest additions.
This will enable copy-and-paste functionality between the host and guest, as well as automatic window resizing for your virtual machine.
If you want to run your Windows virtual machine again, you need to use the virsh start
command first and then use virt-manager or virt-viewer to view your running virtual machine.
$ virsh -c qemu:///system start Windows11
Domain 'Windows11-2' started
$ virt-viewer Windows11 &
Sharing drives
To make this a real hybrid workflow, we need to make a few improvements, such as sharing a folder where all projects are located. In my case, I always use a folder called Workspace in my user root directory.
Using virt-xml
, we are going to add some new parameters to our recent virtual machine. First, we’ll enable the shared memory option.
$ virt-xml Windows11 --edit --memorybacking source.type=memfd,access.mode=shared
Domain 'Windows11' defined successfully.
Next, we set the directory to be shared with the option source.dir=/home/<username>/Workspace
. Just replace <username>
with your actual Linux username.
The target.dir
option is where you specify the name that Windows will recognize for the shared folder. In my case, I decided to name it linux. You can choose any name you like for the shared folder in Windows, but ensure it's easy to remember and access.
$ virt-xml Windows11 --add-device --filesystem driver.type=virtiofs,source.dir=/home/diego/Workspace,target.dir=linux
Domain 'Windows11' defined successfully.
The previous instructions can also be applied when creating the virtual machine with virt-install
by simply adding the --filesystem
and --memorybacking
options to the virt-install
command.
--filesystem driver.type=virtiofs,source.dir=/home/diego/Workspace,target.dir=linux \
--memorybacking source.type=memfd,access.mode=shared
On your Windows machine, we need to set up a few things. First, download the latest version of WinFSP from here, and then configure some options in Windows Services. While I could write detailed instructions and share images, it’s easier to follow the next video from the "My Linux For Work" YouTube channel (I recommend his channel if you’re into Arch and Hyperland). You can find the relevant information from minute 5:28 to 6:00 in the video: https://www.youtube.com/watch?v=oVHkvx9ZLJc&t=284s
The VirtIO disk is no longer required for our installation. To remove the entire unit containing the VirtIO image from your virtual machine, simply type the following command
$ virt-xml Windows11 --remove-device --disk 2
Domain 'Windows11' defined successfully.
To remove the Windows ISO image from our cd-rom unit you can type
$ virt-xml Windows11 --edit target=sdb --disk path=
If you wanna insert a cd-rom content
$ virt-xml Windows11 --edit target=sdb --disk path=/path/to/your/cdrom/image
USB passthrough
As embedded engineers, we need USB passthrough, as some Windows tools require communication with external hardware. To passthrough a USB device, type lsusb
to list the connected USB devices. You can see that I have attached a Nucleo board with an onboard ST-Link V2.
$ lsusb
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 002: ID 3434:0213 Keychron Keychron K1 Pro
Bus 003 Device 003: ID 8087:0029 Intel Corp. AX200 Bluetooth
Bus 003 Device 004: ID 0483:374b STMicroelectronics ST-LINK/V2.1
Using the information from lsub
we use virt-xml
to add our device with --host 003.004
$ virt-xml Windows11 --add-device --update --hostdev 003.004
Device hotplug successful.
Domain 'Windows11' defined successfully.
Go to Device manager on your windows and verify out device has been passthrough
If we want to disconnect the same device form our virtual machine , just type
$ virt-xml Windows11 --remove-device --update --hostdev 003.004
Device hotunplug successful.
Domain 'Windows11' defined successfully.
Windows as a linux app
This is the ultimate integration, we are going to make our windows to be super integrated as if it was one linux app using a remote desktop protocol called XRDP. In linux we can use freeRDP to do XRDP connections with our guest machine with our guest machine
$ sudo pacman -S freerdp
Get the Windows 11 IP address using the command , virsh net-dhcp-leases <network>
, remember we are using a network called default
$ virsh -c qemu:///system net-dhcp-leases default
Expiry Time MAC address Protocol IP address Hostname Client ID or DUID
-----------------------------------------------------------------------------------------------------------
2024-11-29 13:29:18 52:54:00:b7:85:05 ipv4 192.168.122.19/24 Windows 01:52:54:00:b7:85:05
or you can also use domifaddr <vm machine>
$ virsh -c qemu:///system domifaddr Windows11
Name MAC address Protocol Address
-------------------------------------------------------------------------------
vnet1 52:54:00:b7:85:05 ipv4 192.168.122.19/24
Find what kind of display server are you using
$ echo $XDG_SESSION_TYPE
wayland
It's time to connect to your virtual machine. If your display server is Xorg, use the command xfreerdp
, but if you're using Wayland, use wlfreerdp
. The line below shows the options you should use, replacing <ip_address>
with the IP address of your Windows virtual machine, <username>
with your Windows username, and <password>
with your Windows password:
$ wlfreerdp -grab-keyboard /v:<ip address> /u:<username> /p:<password> /size:100% /d: /dynamic-resolution /gfx-h264:avc444 +gfx-progressive
That was a long line, but you can write in your .bashrc file a function to launch you windows machine with a single command, we decide type pass the password as a parameter to avoid hard code our super secret password
windows()
{
virsh -c qemu:///system start Windows11
sleep 30
xfreerdp -grab-keyboard /v:192.168.122.19 /u:diego /p:$1 /size:100% /d: /dynamic-resolution /gfx-h264:avc444 +gfx-progressive
}
The you can use you newly function as, where ***** will be your password
$ windows *****
Extend the windows disk size
In case you want to modify your virtual machine disk space because you are running out of space. Identify Windows machine disk image and its path
$ virsh -c qemu:///system domblklist Windows11
Target Source
------------------------------------------------------------------
sda /var/lib/libvirt/images/iso/Win11_24H2_English_x64.iso
sdb /var/lib/libvirt/images/iso/virtio-win-0.1.266.iso
vda /var/lib/libvirt/images/Windows11.qcow2
Indicate how many Gigabytes you would like to increase using qemu-img
, in this example I’m adding just ten more gigs
$ sudo qemu-img resize /var/lib/libvirt/images/Windows11.qcow2 +10G
Image resized
On your Windows machine, go to the Disk Management application. You will see the newly added disk space listed as Unallocated. Now, proceed to assign this empty space to your C: drive. If you're unsure how to do this, you can watch this video for a step-by-step guide: https://www.youtube.com/watch?v=5jvpSv-WSc0
Delete the virtual machine
If for some reason you don’t want the virtual machine anymore, the remove a virtual machine and its storage drive
$ virsh undefine Windows11 --nvram --remove-all-storage
Domain 'Windows11' has been undefined
Volume 'vda'(/var/lib/libvirt/images/Windows11.qcow2) removed.
If you are really interested in deep on Virtual Machines here is a couple of Links