- About
- [Optional] Making a NVIDIA GPU not interact with Linux at all
- [Optional] GPU passthrough
- 1. Virtual machine requisites
- 2. Installing Hypervisor Phantom
- 3. Virtual machine setup
- 4. Optimizing VM performance and hiding the VM from some software
- [Optional] Looking Glass setup
- I’m having stuttering / frame pacing issues
- [Optional] Sharing files to the Windows VM without enabling shared memory (for better performance)
-
Your CPU has less than 8 cores and 16 threads, with 16 cores and 32 threads being recommended.
-
Keep in mind that Intel CPUs aren’t tested for.
-
-
Your total RAM is less than 32GB.
-
You don’t have two GPUs.
This guide’s goal is to provide you with a very high-quality VM setup, and to bypass some anti-VM measures without slowing down performance.
The guide is built on CachyOS / Arch Linux using systemd-boot or Limine (recommended) and KDE Plasma desktop. The host GPU is from AMD, while the guest (VM / Virtual Machine / VFIO) GPU is from NVIDIA.
|
ℹ️
|
These instructions also work for GPUs from Intel or AMD, but there’s no advice on resolving the reset bug or other problems they may have inside a virtual machine. |
Programs such as OBS Studio will keep trying to use NVIDIA drivers instead of the AMD drivers if the LIBVA_DRIVER_NAME is still set to nvidia, in which the libva-nvidia-driver package enforces; for OBS, this means you can’t record using GPU hardware acceleration:
sudo pacman -R libva-nvidia-driver
If you’re not running the NVIDIA GPU outside of virtual machines, remove its drivers in the host system:
sudo pacman -Rnsc linux-cachyos-nvidia-open nvidia-utils
Also remove the mkinitcpio entries for NVIDIA:
sudo sed -i '/nvidia/d' /etc/mkinitcpio.conf.d/*
Create the directory if it doesn’t exist:
mkdir -p ~/.config/environment.d
Edit ~/.config/environment.d/99-amdgpu.conf
LIBVA_DRIVER_NAME=radeonsi
sudo rm /etc/profile.d/nvidia-vaapi.sh
This will fix the AMD GPU issue with OBS Studio after a reboot (don’t reboot now).
Find the IDs to pass to vfio-pci for your guest GPU:
lspci -vnn
For this example, you would passthrough the IDs 10de:2705 and 10de:22bb:
05:00.0 VGA compatible controller [0300]: NVIDIA Corporation AD103 [GeForce RTX 4070 Ti SUPER] [10de:2705] (rev a1) (prog-if 00 [VGA controller])
Subsystem: Gigabyte Technology Co., Ltd Device [1458:413d]
Flags: bus master, fast devsel, latency 0, IRQ 101, IOMMU group 21
Memory at dd000000 (32-bit, non-prefetchable) [size=16M]
Memory at f800000000 (64-bit, prefetchable) [size=16G]
Memory at fc00000000 (64-bit, prefetchable) [size=32M]
I/O ports at e000 [size=128]
Expansion ROM at de000000 [virtual] [disabled] [size=512K]
Capabilities: <access denied>
Kernel driver in use: nvidia
Kernel modules: nouveau, nvidia_drm, nvidia
05:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:22bb] (rev a1)
Subsystem: Gigabyte Technology Co., Ltd Device [1458:413d]
Flags: bus master, fast devsel, latency 0, IRQ 169, IOMMU group 21
Memory at de080000 (32-bit, non-prefetchable) [size=16K]
Capabilities: <access denied>
Kernel driver in use: snd_hda_intel
Kernel modules: snd_hda_intel
Sudo edit: /etc/modprobe.d/vfio.conf
softdep drm pre: vfio-pci softdep nvidia pre: vfio-pci softdep amdgpu pre: vfio-pci softdep snd_hda_intel pre: vfio-pci # Change these PCIe IDs to yours! options vfio-pci ids=10de:2705,10de:22bb
Sudo edit: /etc/mkinitcpio.conf
# Replace MODULES=() with the following: # MODULES=(vfio_pci vfio vfio_iommu_type1) # If you already have modules in there, put the new vfio entries before those modules. # Check to see if 'modconf' is already included in HOOKS=(), it's necessary.
Sudo edit: /etc/sysctl.d/99-qemu.conf
# Prevents GPU timeouts or other VM hanging while running video games. kernel.split_lock_mitigate=0 # Replicates the value set by default in SteamOS. Not strictly necessary but here just incase. vm.max_map_count = 2147483642
Sudo edit: /etc/modprobe.d/qemu.conf
# Prevents Windows BSODs and performance decreases in instances of MSRs faults. options kvm ignore_msrs=Y report_ignored_msrs=N kvmclock_periodic_sync=N # Disable KVM's polling loop to prevent trashing the CPU's L3 cache, can also decrease power usage. options kvm halt_poll_ns=0 # Improves VM performance and lowers DPC latency drastically. # Remove nested=0 if you plan on nesting VMs (a VM in a VM, for anti-VM detection purposes). options kvm_amd sev=N nested=0 npt=Y avic=Y # Pause Loop Exit is useful when the CPU is overcommitted (with how a gaming VM is setup, it won't be), such as multiple VMs accessing the same CPU affinities; this lowers DPC latency, which is important for gaming. options kvm_intel ple_gap=0 ple_window=0 # Prevents issues with passing through a GPU, which can occur if a text-mode framebuffer gets allocated to the passthrough GPU. options vfio-pci disable_vga=1
Sudo edit /etc/sdboot-manage.conf.d/99-qemu.conf or /etc/default/limine (do not append LINUX_OPTIONS="" if using Limine):
# Remove 'intel_iommu=on' if using an AMD CPU # 'pcie_aspm=off' turns off PCIe power management but helps with GPU related stutters LINUX_OPTIONS="intel_iommu=on iommu=pt tsc=reliable no_timer_check pcie_aspm=off split_lock_detect=off"
-
sudo sdboot-manage genorsudo limine-mkinitcpio -
(Unnecessary on Limine) Apply the new modprobe.d and mkinitcpio.conf changes:
sudo mkinitcpio -P
Reboot your PC:
reboot
-
sudo pacman -Syu virt-manager iptables-nft dnsmasq virglrenderer hwloc dmidecode usbutils swtpm samba
Assign your user to the correct groups to prevent issues in the future:
sudo usermod -a -G qemu,video,kvm,libvirt,libvirt-qemu "$USER"
Sudo edit: /etc/libvirt/network.conf, and change firewall_backend to iptables. This is necessary since we’re using iptables-nft and not nftables standalone.
Enable and start the required services to use Virtual Machine Manager:
sudo systemctl enable --now libvirtd.service
|
ℹ️
|
Always install required missing packages, and keep the source to make repatching quicker. |
-
git clone --single-branch --depth=1 https://github.com/Scrut1ny/Hypervisor-Phantom && cd Hypervisor-Phantom/ && chmod -R +x * -
sudo ./main.sh -
Select QEMU (Patched) Setup.
-
Build & install QEMU to /usr/local/bin [y/n]: y
-
Select EDK2 (Patched) Setup.
-
When asked to apply host’s or custom, choose host’s.
-
Select Auto Libvirt XML Setup.
-
This will generate a VM for you called 'win10', we’re going to edit this later on.
-
The kernel setup is skipped unless you absolutely need it. You’ll know for yourself later on if you need it.
The operating system of choice either CachyOS or Windows 10 LTSC 2019 (get that from the MyDigitalLife forums). Please don’t use Windows 11, it will not work without purposefully sacrificing your performance after the June 2025 updates.
Enable XML editing in Virtual Machine Manager before doing anything else: Edit → Preferences → Check "Enable XML editing", then close.
If you plan to use CachyOS / a Linux distro in a VM, ensure in "Display Spice" that OpenGL is checked and the "Listen type" is set to 'None'; unless you’re doing GPU passthrough, then don’t do this.
|
|
Make sure the disk type is virtio before installing Windows, otherwise you have to follow these steps to correct this mistake later for higher performance: https://superuser.com/a/1253728 - Be sure that the Video 'VGA' is used inorder to see any graphical output in Safe Mode, you can revert to Video 'None' afterwards. |
Ensure the disk file location you’ll use for the VM is mounted to /mnt instead of /run/media, or you could pass-through an entire NVMe disk. Up to you.
When creating a disk file, ensure its Format is 'raw' instead of 'qcow', and use this calculator to get exact disk size measurements. VirtualBox is the only good option for snapshots in my opinion, so if you do malware analysis, that’s the way to go.
For Windows: Add an additional SATA CDROM to the VM, so you can load the latest virtio-win-*.iso into it; get the ISO from here. At first this is used so you have disk drivers, but later on you can install all of its drivers inside of the VM by running virtio-win-guest-tools (after Windows is installed).
Add the PCI Host Device for your NVIDIA GPU and its accompanying audio device.
Looking Glass has its own recommendations, follow those in addition to what my guide recommends below.
Now lets edit the VM config created by Phantom Hypervisor called 'win10':
<!-- Prioritizes our VM over other tasks. 0-7 is 4 cores 2 threads. -->
<!-- It's recommended to allocate half of your available CPU cores and threads. -->
<cputune>
<vcpusched vcpus="0-7" scheduler="fifo" priority="99"/>
</cputune>
<!-- Put after </currentMemory> -->
<memoryBacking>
<hugepages/>
<nosharepages/>
<locked/>
<allocation mode="immediate"/>
</memoryBacking>
<!-- Replace <clock> with this: -->
<clock offset="localtime">
<timer name="tsc" tickpolicy="discard" mode="native"/>
<timer name="rtc" tickpolicy="catchup"/>
<timer name="pit" present="no"/>
<timer name="hpet" present="no"/>
<timer name="kvmclock" present="yes"/>
<timer name="hypervclock" present="yes"/>
</clock>
<!-- Remove stimer to support nested VMs (for further anti-VM detections), which you do by enabling Virtual Machine Platform in "Turn Windows features on or off" -->
<!-- Change AuthenticAMD to GenuineIntel if using an Intel CPU! -->
<!-- NOTE: If you don't plan to do nested VMs, just do <hyperv mode="passthrough"/> to clean up the config.
<features>
<acpi/>
<apic/>
<hyperv mode="custom">
<relaxed state="on"/>
<vapic state="on"/>
<spinlocks state="on" retries="4095"/>
<vpindex state="on"/>
<runtime state="on"/>
<synic state="on"/>
<stimer state="on">
<direct state="on"/>
</stimer>
<reset state="on"/>
<vendor_id state="on" value="AuthenticAMD"/>
<frequencies state="on"/>
<reenlightenment state="on"/>
<tlbflush state="on">
<direct state="on"/>
<extended state="on"/>
</tlbflush>
<ipi state="on"/>
<avic state="on"/>
<emsr_bitmap state="on"/>
<xmm_input state="on"/>
</hyperv>
<kvm>
<hidden state="on"/>
</kvm>
<pmu state="off"/>
<vmport state="off"/>
<smm state="on"/>
<ioapic driver="kvm"/>
<msrs unknown="fault"/>
</features>
<!-- Put under <cpu>: -->
<feature policy="require" name="topoext"/>
<feature policy="require" name="invtsc"/>
<feature policy="require" name="hypervisor"/>
<feature policy="disable" name="vmx-vnmi"/>
<!-- AMD specific -->
<feature policy="disable" name="svm"/>
<feature policy="disable" name="ibpb"/>
<feature policy="disable" name="ibrs"/>
<feature policy="disable" name="stibp"/>
<feature policy="disable" name="ssbd"/>
<feature policy="disable" name="amd-ssbd"/>
<feature policy="disable" name="virt-ssbd"/>
<feature policy="disable" name="rsba"/>
<feature policy="disable" name="skip-l1dfl-vmentry"/>
<!-- Intel-specific -->
<feature policy="disable" name="vmx"/>
<feature policy="disable" name="mds-no"/>
<feature policy="disable" name="pschange-mc-no"/>
<feature policy="disable" name="fb-clear"/>
<feature policy="disable" name="taa-no"/>
<feature policy="disable" name="l1tf"/>
</cpu>
<!-- If you are using Looking Glass:
Replace <audio id="1" type="none"/> with:
<audio id="1" type="spice"/>
Be sure to add Graphics -> Spice Server (with default settings) inorder to use this! -->
<!-- Put inside <devices> -->
<panic model="hyperv"/>
<!-- Remove the entire <shmem name="looking-glass"> block; it's an old and slower method of running LG -->
<!-- Remove this if present, it will reduce performance -->
<qemu:arg value="-overcommit"/>
<qemu:arg value="cpu-pm=on"/>
<!-- More optimal settings for virtio on NVMe drives
Be sure your disk is 'raw' instead of 'qcow2'; if it is 'qcow2', convert it to 'raw' -->
<disk type="file" device="disk">
<driver name="qemu" type="raw" cache="none" io="native" discard="unmap" iothread="1" queues="8"/>
<source file="/mnt/nvme/win11.img"/>
<target dev="vda" bus="virtio"/>
</disk>
<!-- Under <interface type="network">, ensure the model type is "virtio" -->
5. Setup the QEMU hook for automatic: 2MB hugepages, CPU performance scheduler, max process priority, and realtime priority
|
ℹ️
|
CPU pinning is not done anymore as I found out that pinning causes lag issues with the host (non-VM), and has the same performance inside the VM as non-pinning. You will not stutter less by using CPU pinning unless your CachyOS setup is suboptimal, which this guide helps with later on. |
-
sudo mkdir -p /etc/libvirt/hooks -
git clone https://github.com/felikcat/qemu-hidden-
Use its instructions to install the QEMU hook with your preferences:
python qemu-hidden/generator.py --help
-
-
Ensure the file permissions are correct, then restart libvirtd so it recognizes the new hook:
sudo chmod +x /etc/libvirt/hooks/qemu; sudo systemctl restart virtqemud.service libvirtd.service
paru -S looking-glass looking-glass-module-dkms obs-plugin-looking-glass
Follow through the official documentation but skip past the "Installing" step as you’ve already done that the Arch way.
|
ℹ️
|
Run sudo chmod 0660 /dev/kvmfr0 if you’re getting permission errors for /dev/kvmfr0 from QEMU.
|
Install the Looking Glass Host to the Windows or Linux VM, and run it.
Be sure to plug in a display port into the passed through GPU, otherwise Looking Glass will not work. It could be a real HDMI or DisplayPort plug (recommended), or a dummy plug.
|
ℹ️
|
All the client options are listed here: https://looking-glass.io/docs/B7/usage/ |
To stop the annoyance of getting asked to allow the microphone and hide the microphone icon, edit: ~/.config/looking-glass/client.ini:
[audio] micDefault=allow micShowIndicator=no
Ensure the display scaling in KDE Plasma is set to intervals of 25%, otherwise Looking Glass will look blurry (such as on 115% scaling instead of 125% scaling).
-
If your disk is type="qcow2", you can convert to "raw" for additional performance; the disks are likely in
/var/lib/libvirt/images, if so then you also need to usesudobefore the command:
qemu-img convert -f qcow2 -O raw DISK_NAME_HERE.qcow2 DISK_NAME_HERE.raw-
This will allocate the full space of the disk, you could add
-S 0beforeDISK_NAME_HERE.qcow2but it’s not recommended to use "sparse" disks as there’s a performance loss.
-
-
For Btrfs, ZFS, or bcachefs filesystems, we’ll disable CoW (Copy on Write) for the directory the disks are located in:
sudo chattr +C /var/lib/libvirt/images
-
Run "Edit group policy". Go to Computer Configuration → Administrative Templates → System → Device Guard → Turn On Virtualization Based Security, and set it to "Enabled". Ensure "Select Platform Security Level" is set to "Secure Boot", and the rest of the options are left as "Not Configured".
-
Run "Turn Windows features on or off". Ensure that "Guarded Host", "Hyper-V", "Virtual Machine Platform", "Windows Hypervisor Platform", "Windows Sandbox", and "Windows Subsystem for Linux" is left unchecked as these features will lower performance.
|
💡
|
This is sometimes unavoidable with Looking Glass, which would require VRR to hide stutters, but the following steps will help greatly. |
See my ways of improving CachyOS' performance stability before trying anything below.
Enable VRR (also called: FreeSync, G-SYNC, or Adaptive Sync) on your monitor. In KDE’s Display Configuration you’d want to set Adaptive Sync to 'Always'.
If you can go without VRR, do so.
-
For Windows: Enable HAGS (Hardware Accelerated GPU Scheduling), disable Game Mode, set High Performance in the Power Options, disable Dynamic Ticks and set TSC Sync Policy to enhanced then reboot:
bcdedit /deletevalue useplatformclock
bcdedit /deletevalue useplatformtick
bcdedit /set disabledynamictick yes
bcdedit /set tscsyncpolicy enhanced -
For Windows: Go into the old audio settings ("More sound settings" in Windows 11), right-click "Speakers" and click "Properties", then in "Enhancements" disable all enhancements and in "Advanced" ensure the Default Format is: 16 bit, 48000 Hz.
-
Install NVIDIA driver 566.36.
-
Use the minimum components to install.
-
Enable Perform a Clean Installation.
-
Disable Multiplane Overlay (MPO).
-
Disable Ansel.
-
In Show Expert Tweaks → Disable NVIDIA HD Audio device sleep timer.
-
Disable Driver Telemetry, and ensure to "Use method compatible with Easy-Anti-Cheat".
-
Enable Message Signaled Interrupts, with Interrupt Policy "All Close Processors" and Interrupt Priority "High".
-
Disable HDCP.
-
-
After the NVIDIA driver is installed, in the NVIDIA Control Panel, set the 'Power management mode' to 'Prefer Maximum Performance'.
Edit: /etc/samba/smb.conf
[global] # Security client min protocol = SMB3 ## SMB3_11 is also faster than previous versions. server min protocol = SMB3 ## Allow local IPs. hosts allow = 192.168.0.0/16 ## Deny all other IPs. hosts deny = 0.0.0.0/0 restrict anonymous = 2 disable netbios = Yes dns proxy = No # Performance use sendfile = Yes ## Don't use outside local IPs! smb encrypt = No # Other server role = standalone server # Disable printer support disable spoolss = Yes load printers = No printcap name = /dev/null show add printer wizard = No printing = bsd # 'share1' is what Windows 10 will see in its file manager. [share1] path = /directory/to/folder read only = No ## If the user is not 'admin', rename the group and user. force group = admin force user = admin
Validate the SMB server config, it should return no errors:
testparm
Add an SMB login for your username. It’s recommended to use a different password than your real Linux password:
sudo smbpasswd -a $USER
Allow the SMB ports through the firewall:
sudo ufw allow 445; sudo ufw allow 139
Enable and start the SaMBa service:
sudo systemctl enable --now smb.service
Find the correct local IP address to connect to inside the Windows VM for the file sharing; for me the interface was "enp14s0":
ip a
Open the 'Run' program in the Windows VM, and run: \\192.168.50.179 (replace with your local IP that was shown earlier).