[tutorial] The Ultimate Linux Laptop for PC Gamers — feat. KVM and VFIO

bland_man_studios
10 min readJan 9, 2023

--

Demo and Video Tutorial

Overview

This laptop has the ability to play modern AAA games with reasonably demanding graphics and stable performance but it also solves the biggest problem facing a Linux enthusiast and PC gamer.

Switching back and forth between Linux and Windows

Phase 1 — Laptop Purchase and Initial Testing

The Purchase

ASUS TUF Gaming F15 FX507ZE-RS73 15.6" Laptop Computer

Reasons

  • Integrated GPU: CPU has “Intel® Iris® Xe Graphics” shown here
  • Dedicated GPU: NVIDIA GeForce RTX 3050 Ti
  • GPU MUX Switch: Shown on ASUS Page
  • CPU Supports IOMMU: VT-D=YES shown here
  • Motherboard supports IOMMU and has good group isolation: IOMMU Group 15 only includes the two devices for the GPU, shown here

Initial Testing

Press Power Button

Answer a few questions

Plug in ethernet cable with network access

Unplug ethernet cable when it asks for a Microsoft account

Process without network connection and create a local login

Let the installer finish

Uninstall McAfee programs

Install OBS for Windows using instructions here (download link)

Install Steam for Windows from here (download link)

Download a game and play it

Phase 2 Installing Linux and GPU Drivers

Shrink windows partition by 300GB (307200MB)

Download Fedora Media Writer from here (download link) to create a Linux live USB install disk with Fedora 37

Insert USB Drive

Create Fedora installation media

Reboot

Press F2 while the computer starts to launch UEFI/BIOS

Disable Fastboot

Save and edit

Press F2 while the computer starts to launch UEFI/BIOS

Select Boot Menu

Boot from the newly created Linux live USB

Test the media

Start the installer

Create Linux partitions in the empty space

Verify that the installer is not deleting any partitions

Run through the rest of the installer

Once it boots into fedora, click through any menus

With internet connected, update all software

sudo dnf update

Reboot

Install nvidia drivers from rpmfusion instructions here

sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm
sudo dnf install akmod-nvidia
sudo dnf install xorg-x11-drv-nvidia-cuda

Use the following command to see if the drivers are done building

modinfo -F version nvidia

Reboot -> F2 to get to UEFI/BIOS -> disable SecureBoot

Install OBS from rpmfusion from instructions here

sudo dnf install obs-studio

Install steam which is also available via the non-free rpmfusion repos we enabled above (or follow the instructions here)

sudo dnf install steam 

Download and play a game using GPU

Phase 3: Getting Ready for Gaming VMs

Install virtualization software

sudo dnf groupinstall --with-optional virtualization

Install KDE Plasma Workspaces

sudo dnf groupinstall "KDE Plasma Workspaces"

Install gedit

sudo dnf install gedit

Run this command to view GPU PCI IDs

lspci -nnk

Run this command to open the grub configuration

sudo gedit /etc/sysconfig/grub

Add this stuff to the GRUB_CMDLINE_LINUX arguments

intel_iommu=on iommu=pt rd.driver.pre=vfio-pci rd.driver.blacklist=nouveau modprobe.blacklist=nouveau nvidia-drm.modeset=1 vfio-pci.ids=10de:25a0,10de:2291 module_blacklist=nouveau

So the file looks like this

GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="rd.driver.blacklist=nouveau modprobe.blacklist=nouveau nvidia-drm.modeset=1 rhgb quiet intel_iommu=on iommu=pt rd.driver.pre=vfio-pci rd.driver.blacklist=nouveau modprobe.blacklist=nouveau nvidia-drm.modeset=1 vfio-pci.ids=10de:25a0,10de:2291 module_blacklist=nouveau"
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true

Regenerate grub configuration

sudo grub2-mkconfig -o /boot/grub2/grub.cfg

Create a new file with gedit

sudo gedit /etc/dracut.conf.d/local.conf

Add the following content to this new file

add_drivers+=" vfio vfio_iommu_type1 vfio_pci vfio_virqfd "

Regenerate initramfs with dracut

sudo dracut -f --kver `uname -r`

Reboot -> F2 to get to UEFI/BIOS -> Enable anything that looks like it has to do with virtualization or VT-d if it isn’t already enabled

Next time you login select Plasma (Wayland) as the display environment using the settings icon in the bottom right of the screen before you enter your password

Right now the GPU is detached and ready to be used in a VM. We can see the “Kernel driver in use” is vfio-pci by running the following command

lspci -nnk

Lets practice reattaching it so we can play some games on Linux

sudo virsh nodedev-reattach pci_0000_01_00_0
sudo rmmod vfio_pci vfio_pci_core vfio_iommu_type1
sudo modprobe -i nvidia_modeset nvidia_uvm nvidia

Now lets disable it again so the GPU is ready to be used in a VM

sudo rmmod nvidia_modeset nvidia_uvm nvidia
sudo modprobe -i vfio_pci vfio_pci_core vfio_iommu_type1
sudo virsh nodedev-detach pci_0000_01_00_0

These are things we want to do repeatedly so lets add them to our .bashrc file

Run this command

gedit ~/.bashrc

Add these things to the end of the file

alias hows-my-gpu='echo "NVIDIA Dedicated Graphics" | grep "NVIDIA" && lspci -nnk | grep "NVIDIA Corporation GA107M" -A 2 | grep "Kernel driver in use" && echo "Intel Integrated Graphics" | grep "Intel" && lspci -nnk | grep "Intel.*Integrated Graphics Controller" -A 3 | grep "Kernel driver in use" && echo "Enable and disable the dedicated NVIDIA GPU with nvidia-enable and nvidia-disable"'
alias nvidia-enable='sudo virsh nodedev-reattach pci_0000_01_00_0 && echo "GPU reattached (now host ready)" && sudo rmmod vfio_pci vfio_pci_core vfio_iommu_type1 && echo "VFIO drivers removed" && sudo modprobe -i nvidia_modeset nvidia_uvm nvidia && echo "NVIDIA drivers added" && echo "COMPLETED!"'
alias nvidia-disable='sudo rmmod nvidia_modeset nvidia_uvm nvidia && echo "NVIDIA drivers removed" && sudo modprobe -i vfio_pci vfio_pci_core vfio_iommu_type1 && echo "VFIO drivers added" && sudo virsh nodedev-detach pci_0000_01_00_0 && echo "GPU detached (now vfio ready)" && echo "COMPLETED!"'

So it should now look like this

# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi

# User specific environment
if ! [[ "$PATH" =~ "$HOME/.local/bin:$HOME/bin:" ]]
then
PATH="$HOME/.local/bin:$HOME/bin:$PATH"
fi
export PATH

# Uncomment the following line if you don't like systemctl's auto-paging feature:
# export SYSTEMD_PAGER=

# User specific aliases and functions
if [ -d ~/.bashrc.d ]; then
for rc in ~/.bashrc.d/*; do
if [ -f "$rc" ]; then
. "$rc"
fi
done
fi

unset rc
alias hows-my-gpu='echo "NVIDIA Dedicated Graphics" | grep "NVIDIA" && lspci -nnk | grep "NVIDIA Corporation GA107M" -A 2 | grep "Kernel driver in use" && echo "Intel Integrated Graphics" | grep "Intel" && lspci -nnk | grep "Intel.*Integrated Graphics Controller" -A 3 | grep "Kernel driver in use" && echo "Enable and disable the dedicated NVIDIA GPU with nvidia-enable and nvidia-disable"'
alias nvidia-enable='sudo virsh nodedev-reattach pci_0000_01_00_0 && echo "GPU reattached (now host ready)" && sudo rmmod vfio_pci vfio_pci_core vfio_iommu_type1 && echo "VFIO drivers removed" && sudo modprobe -i nvidia_modeset nvidia_uvm nvidia && echo "NVIDIA drivers added" && echo "COMPLETED!"'
alias nvidia-disable='sudo rmmod nvidia_modeset nvidia_uvm nvidia && echo "NVIDIA drivers removed" && sudo modprobe -i vfio_pci vfio_pci_core vfio_iommu_type1 && echo "VFIO drivers added" && sudo virsh nodedev-detach pci_0000_01_00_0 && echo "GPU detached (now vfio ready)" && echo "COMPLETED!"'

Open a new terminal and you can now use these commands

nvidia-enable
nvidia-disable
hows-my-gpu

Phase 4: Creating our first VM

Download Windows 10 iso from here

Download Windows 10 virtio driver is from here (download link)

Open virt-manager

Create a new VM

Select an amount of CPU and RAM

Create a new 100GB Virtual Disk

Give the VM a unique name

Check the box for advanced configuration

Ensure the VM is set for KVM, Q35, and UEFI

Set CPU Topology with one socket and multiple threads

Set the windows 10 install disk to bootable

Remove the network interface card (so it doesnt make you connect to a Microsoft account during the install)

Start the VM and run through the windows 10 installer

Press any key

Install Now

I dont have a product key

Windows 10 Home

Accept terms and conditions

Install to the newly created 100GB virtual drive

Its going to reboot

Answer some questions

Disable all this unnecessary stuff like usage metrics, location tracking, and the virtual assistant

Should take a few minutes and its done! We’ve got a windows 10 VM

Shutdown the VM

Make the main disk bootable

Remove the windows 10 install disk

Add the downloaded virtio driver disk

Create a new small virtual disk with virtio as the bus type

Boot the VM, find the driver disk and install the drivers

Open device manager and verify that the small virtio device is visbile under storage devices

Power down the VM

In Virtual Machine Manager: Click Edit > Preferences and check Enable XML Editing

For the main disk, we want to change from SATA type bus to virtio

In the config window for your VM, open the XML tab for the storage device and change the bus type from sata to “virtio” and the address to “pci”. When you click “Apply” it should look like this

<disk type="file" device="disk">
<driver name="qemu" type="qcow2"/>
<source file="/var/lib/libvirt/images/win10-vm1.qcow2"/>
<target dev="sda" bus="virtio"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x0e" function="0x0"/>
</disk>

Remove the virtio driver disk

Remove the small newly created virtio drive and delete associated file

Boot the VM again and everything should still work

Shutdown the VM

Add the network interface card

Add the PCI devices for the GPU

Plug in an external monitor

Start the VM

Navigate to the NVIDIA webpage and install nvidia drivers from here (download link)

Phase 5: Looking Glass and usability upgrades

Find out which devices are your mouse and keyboard

ls /dev/input/by-id/
ls /dev/input/by-path/

When I run this command and move my mouse around I get lots of info coming out so I know that’s the right device

sudo cat /dev/input/by-id/usb-PixArt_Lenovo_USB_Optical_Mouse-event-mouse

Press Ctrl-C to get out of that

When I run this command and touch things on the keyboard I get lots of info coming out so I know that’s the right device

sudo cat /dev/input/by-path/platform-i8042-serio-0-event-kbd

Press Ctrl-C to get out of that

Add the following XML to your VM config

<input type="evdev">
<source dev="/dev/input/by-id/usb-PixArt_Lenovo_USB_Optical_Mouse-event-mouse"/>
</input>
<input type="evdev">
<source dev="/dev/input/by-path/platform-i8042-serio-0-event-kbd" grab="all" grabToggle="ctrl-ctrl" repeat="on"/>
</input>

Remove the tablet input device because now you have a better one and you don’t want these confusing each other

Build Looking Glass from Source according to instructions here

Download the stable release source code for the Looking Glass Client from here (download link)

Unzip the looking glass source code

cd Downloads
tar -xzvf looking-glass-B6.tar.gz
cd looking-glass-B6

Install dependencies required to build/install Looking Glass (from the list here )

sudo dnf install cmake gcc gcc-c++ libglvnd-devel fontconfig-devel spice-protocol make nettle-devel \
pkgconf-pkg-config binutils-devel libXi-devel libXinerama-devel libXcursor-devel \
libXpresent-devel libxkbcommon-x11-devel wayland-devel wayland-protocols-devel \
libXScrnSaver-devel libXrandr-devel dejavu-sans-mono-fonts
sudo dnf install pipewire-devel libsamplerate-devel
sudo dnf install pulseaudio-libs-devel libsamplerate-devel

Build the Looking Glass Client

mkdir client/build
cd client/build
cmake ../
make

Install the Looking Glass Client

sudo make install

Now you can run the Looking Glass Client

looking-glass-client

Configure looking glass using the instructions here

Create a new file

sudo gedit /etc/tmpfiles.d/10-looking-glass.conf

Give it the following contents

# Type Path               Mode UID  GID Age Argument
f /dev/shm/looking-glass 0660 steeve qemu -

Run the following command

sudo semanage fcontext -a -t svirt_tmpfs_t /dev/shm/looking-glass

Reboot

Check that the file was created with the correct selinux context

ls -alZ /dev/shm/looking-glass

Add the following to your VM XML

<shmem name='looking-glass'>
<model type='ivshmem-plain'/>
<size unit='M'>32</size>
</shmem>

Start the VM

Download the Looking Glass Host application inside the VM from here (download link)

Install the looking glass host applicaiton

Run the looking glass host application (in the future it should run automatically on boot)

On the host connect ot the VM with the Looking Glass client

looking-glass-client -s

Set the default audio to spekers in the VM

Make sure the spice display is open but minimized

On the host connect ot the VM with the Looking Glass client but this time with hotkeys bound to right control

looking-glass-client -s -m 97

Save this as another alias command to ~/.bashrc

gedit ~/.bashrc

So now it looks like this

# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi

# User specific environment
if ! [[ "$PATH" =~ "$HOME/.local/bin:$HOME/bin:" ]]
then
PATH="$HOME/.local/bin:$HOME/bin:$PATH"
fi
export PATH

# Uncomment the following line if you don't like systemctl's auto-paging feature:
# export SYSTEMD_PAGER=

# User specific aliases and functions
if [ -d ~/.bashrc.d ]; then
for rc in ~/.bashrc.d/*; do
if [ -f "$rc" ]; then
. "$rc"
fi
done
fi

unset rc
alias hows-my-gpu='echo "NVIDIA Dedicated Graphics" | grep "NVIDIA" && lspci -nnk | grep "NVIDIA Corporation GA107M" -A 2 | grep "Kernel driver in use" && echo "Intel Integrated Graphics" | grep "Intel" && lspci -nnk | grep "Intel.*Integrated Graphics Controller" -A 3 | grep "Kernel driver in use" && echo "Enable and disable the dedicated NVIDIA GPU with nvidia-enable and nvidia-disable"'
alias nvidia-enable='sudo virsh nodedev-reattach pci_0000_01_00_0 && echo "GPU reattached (now host ready)" && sudo rmmod vfio_pci vfio_pci_core vfio_iommu_type1 && echo "VFIO drivers removed" && sudo modprobe -i nvidia_modeset nvidia_uvm nvidia && echo "NVIDIA drivers added" && echo "COMPLETED!"'
alias nvidia-disable='sudo rmmod nvidia_modeset nvidia_uvm nvidia && echo "NVIDIA drivers removed" && sudo modprobe -i vfio_pci vfio_pci_core vfio_iommu_type1 && echo "VFIO drivers added" && sudo virsh nodedev-detach pci_0000_01_00_0 && echo "GPU detached (now vfio ready)" && echo "COMPLETED!"'

alias looking-glass='looking-glass-client -s -m 97'

Open a new terminal and launch looking glass using the new alias

looking-glass

For audio to work in the VM, set the output to Speakers and make sure you have the spice display open or minimized (there will be a small audio delay)

For the primary display to be looking-glass/dummy plug, set that to main display and move the spice display off to the right

Setup Windows auto login using instructions here

Open Registry Editor

Locate this path

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon

Create new string entries for the following variables with the following values

AutoAdminLogon=1
DefaultUserName=steeve
DefaultPassword=password

Exit

Reboot (windows should auto login this time)

Notes on using your laptop

Press both Ctrl keys at the same time to switch your mouse and keyboard between guest and host

Make sure you have the spice window open and minimized (so you have audio come through). Note: the VM will need to be set to default speakers

Also, i like to turn off “dim the screen”, and “turn the screen off” in the power saving settings, so that way linkux doesnt put my display to sleep while i’m gaming in linux (because my inputs are going straight to the VM, linux might not know to stay awake) — i also turn off the “auto lock” after 5 minutes which is under the screenlock settings

Fin

--

--

bland_man_studios

We make creative stuff and talk about the technology behind it - Check us out on itch.io and YouTube