显卡直通方案

一、前期准备

安装所需要软件

1
paru -S read-edid qemu libvirt edk2-ovmf virt-manager dnsmasq ebtables iptables bridge-utils gnu-netcat looking-glass looking-glass-module-dkms

添加用户组

1
sudo usermod -a -G libvirt-qemu,libvirt,kvm user

其中user为自己的用户名

启用并设置虚拟机服务和虚拟网卡开机自启

1
2
3
sudo systemctl enable --now libvirtd virtlogd.socket
sudo virsh net-start default
sudo virsh net-autostart default

编辑qemu配置文件

编辑/etc/libvirt/qemu.conf
把user = “ “ 这行和group = “ “ 这行取消注释
在user = “ “ 中填入自己的用户名
在group = “ “ 中填入libvirt

图1
重启libvirt服务

1
sudo systemctl restart libvirtd virtlogd.socket

二、创建一个非直通的虚拟机

安装系统

打开virt-manager新建虚拟机
图2
选择win的iso,并手动选择对应的操作系统
图3
内存看情况设置,但是建议不要超过宿主机物理内存的80%
cpu无须关注后续会再次设置
图4
按需要设置镜像大小
注:不会直接占用空间
图5
选择在安装前自定义配置
图6
选择固件如图所示(在virt-manager中的一切操作需要手动点击apply确认)
图7
手动设置cpu拓扑
一般情况下嵌套字都填1
核心数按需分配(不要超过物理核心数)
线程数应与物理cpu线程数相同
图8
硬盘总线和网卡型号选择virtIO
图9
图10
添加硬件
选择储存
设备类型选择CDROM设备
选中下载的virtio-win驱动
图11
接着添加tpm设备选择直通
图12

编辑XML

在</features>的上方添加如下代码

1
2
3
<kvm>
<hidden state='on'/>
</kvm>

并且在cpu中插入如下代码,这可以避免后续loogking-glass的问题

1
2
<feature policy='disable' name='hypervisor'/>
<maxphysaddr mode='passthrough' limit='40'/>

图14
开始安装
一定要选择专业版方便后续操作
选择加载驱动
图15
选择win11驱动
按正常流程安装windows
图16
按SHIFT + F10召唤cmd并且输入

1
OOBE\BYPASSNRO

来跳过联网验证
图17
接下来继续按正常流程安装windows
不要设置密码
安装如下两个驱动,无须进行更改
图19

三、正式开始直通

编译并安装IDD驱动

安装Visual Studio并安装使用C++的桌面开发 (MSVC)、通用Windwos平台开发 (Windows SDK)
接着点修改->单个组建->安装MSVC Spectre缓解库和WDK
文章中找到WDK最新版本并下载安装
下载此项目用vs打开.sln后缀文件
打开IddSampleDriver中的Driver.cpp并按需编辑
在s_SampleResolutionList[]{}中添加自己所需的分辨率,例如{2560,1600}
接着在s_SampleVSyncList[]{}中添加自己所需的刷新率,例如240
继续在s_SampleDefaultModes[]{}中添加自己所需的分辨率和刷新率的组合,例如{ 2560, 1600, 240 }
执行以下命令获取显示器的edid备用

1
sudo get-edid | tail -c 256 | hexdump -v -e '16/1 "0x%02X," "\n"'

接着找到s_SampleMonitors[]{}把里面的内容全部删去换成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static const struct IndirectSampleMonitor s_SampleMonitors[] =
{
{
{
/*
命令获取的edid的前8行,例如
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x06,0xAF,0xAE,0x8F,0xA1,0xFF,0x00,0x00,
0x0E,0x22,0x01,0x04,0xA5,0x22,0x16,0x78,0x03,0xEE,0x95,0xA3,0x54,0x4C,0x99,0x26,
0x0F,0x50,0x54,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0xBF,0xDE,0x00,0xA0,0xA0,0x40,0x66,0x67,0x30,0x20,
0xF6,0x0C,0x58,0xD7,0x10,0x00,0x00,0x18,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,
0x00,0x0D,0x24,0xFF,0x15,0x3C,0xFA,0x35,0x10,0x42,0xFA,0x00,0x00,0x20,0x01,0xBD,
*/
},
{
// 所需的分辨率和刷新率组合,例如
// { 2560, 1600, 240 },
},
0
},
};

接着注释或删除#define EDID_LESS
项目->属性->c/c++ ->代码生成->运行库->多线程(/MT)->确定
生成->生成解决方案
打开构建目录找到IddSampleDriver.cer安装证书->本地计算机->将所有的证书都放入下列存储->浏览->受信任的根证书颁发机构
图22
进入IddSampleDriver目录找到IddSampleDriver.inf文件并安装
接着返回上级目录
找到IddSampleApp.exe右键->属性->兼容性->以管理员身份允许此程序
来默认申请以管理员权限运行
键入win+R输入taskschd.msc
创建基本任务->给任务取个名->触发器:计算机启动时->操作:启动程序->启动程序:选择IddSampleApp->完成
然后找到这个任务->安全选项->选择不管用户是否登录都要运行->打开不存储密码,该任务只有本地计算机的访问权限->打开使用最高权限运行
图21

配置looking-glass

编辑/etc/tmpfiles.d/10-looking-glass.conf文件加入

1
f /dev/shm/looking-glass 0660 user(uid) kvm -

user(uid)中填入用户的uid
接着编辑/etc/udev/rules.d/99-kvmfr.rules文件添加

1
SUBSYSTEM=="kvmfr", OWNER="user", GROUP="kvm", MODE="0660"

按照以下公式计算数据

1
分辨率的长×分辨率的宽×BBP××22÷1024÷1024+10

其中BPP为4表示SDR,8表示HDR
然后把这个数据向上取整到2的整数次幂
编辑/etc/modprobe.d/looking-glass.conf文件添加

1
options kvmfr static_size_mb=你得出的数值

编辑/etc/modules-load.d/looking-glass.conf文件添加

1
kvmfr

编辑/etc/libvirt/qemu.conf文件找到cgroup_device_acl 这行取消它所包括的注释加入/dev/kvmfr0。如图
图23
如果存在AppArmor也同时创建/etc/apparmor.d/local/abstractions/libvirt-qemu并且加入

1
/dev/kvmfr0 rw,

编辑/etc/looking-glass-client.ini文件加入

1
2
[app]
shmFile=/dev/kvmfr0

接着重构内核并重启
编辑XML
修改首行为

1
<domain type="kvm" xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>

再在倒数二行加入

1
2
3
4
5
6
<qemu:commandline>
<qemu:arg value='-device'/>
<qemu:arg value='{"driver":"ivshmem-plain","id":"shmem0","memdev":"looking-glass"}'/>
<qemu:arg value='-object'/>
<qemu:arg value='{"qom-type":"memory-backend-file","id":"looking-glass","mem-path":"/dev/kvmfr0","size":刚才计算出的数值×1024×1024,"share":true}'/>
</qemu:commandline>

在</devices>内加入两行

1
2
<input type='mouse' bus='virtio'/>
<input type='keyboard' bus='virtio'/>

找到<memballoon model=”virtio”>将其改成<memballoon model=”none”/>
启动虚拟机
下载并安装looking-glass-host
键入win+R输入taskschd.msc
创建基本任务->给任务取个名->触发器:当前用户登录时->操作:启动程序->启动程序:选择C:\Program Files\Looking Glass (host)\looking-glass-host.exe->完成
然后找到这个任务->安全选项->打开使用最高权限运行
接着关闭windows自动休眠
然后把显卡设置成none

编辑grub

编辑grub配置文件/etc/default/grub添加启动参数

1
intel_iommu=on iommu=pt

如果在直通时出现整个iommu组退回宿主机的问题(之后简称问题1)
可以进一步添加以下内核参数(需要包含了ACS补丁的内核)

1
pcie_acs_override=downstream,multifunction

并更新配置文件并且重启

1
grub-mkconfig -o /boot/grub/grub.cfg

进行检验,执行

1
sudo dmesg | grep -e DMAR -e IOMMU

如果出现输出结果则说明成功

查询iommu分组

运行以下脚本

1
2
3
4
5
6
7
#!/bin/bash
shopt -s nullglob
for d in /sys/kernel/iommu_groups/*/devices/*; do
n=${d#*/iommu_groups/*}; n=${n%%/*}
printf 'IOMMU Group %s ' "$n"
lspci -nns "${d##*/}"
done;

找到需要直通显卡的iommu分组,记下分组中所有硬件对应的id备用
图20
如图我要直通的显卡是3060,它处于iommu16分组,那么我需要记下的id为10de:2520和10de:228e(如果出现问题1,则只需要记下显卡所对应的id,不需要直通整个iommu组)
接着编辑/etc/modprobe.d/vfio.conf
在其中以如下格式添加记下的所有id

1
options vfio-pci ids=10de:2520,10de:228e

配置hooks

随意找个目录执行

1
git clone https://gitlab.com/risingprismtv/single-gpu-passthrough.git

进入目录再进入hooks文件夹
编辑vfio-startup把里面内容换成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/bin/bash
set -x

MARKER="/tmp/my_script_has_run"

if [[ ! -f "$MARKER" ]]; then
touch "$MARKER"
systemctl stop sddm
sleep 2
fuser -v /dev/nvidia* |awk '{for(i=1;i<=NF;i++)print "kill -9 " $i;}' | sh
rmmod -f nvidia_drm
rmmod -f nvidia_modeset
rmmod -f nvidia_uvm
rmmod -f nvidia
sleep 1
virsh nodedev-detach pci_0000_02_00_0
modprobe vfio_pci
modprobe vfio
modprobe vfio_iommu_type1
sleep 1
systemctl start sddm

else
fuser -v /dev/nvidia* |awk '{for(i=1;i<=NF;i++)print "kill -9 " $i;}' | sh
sleep 2
rmmod -f nvidia_drm
rmmod -f nvidia_modeset
rmmod -f nvidia_uvm
rmmod -f nvidia
sleep 1
virsh nodedev-detach pci_0000_02_00_0
modprobe vfio_pci
modprobe vfio
modprobe vfio_iommu_type1
fi

注意:此脚本一旦执行就会杀死所有占用n卡的程序

接着编辑vfio-teardown把里面内容换成

1
2
3
4
5
6
7
8
9
10
11
set -x
modprobe -r vfio_pci
modprobe -r vfio
modprobe -r vfio_iommu_type1
sleep 1
virsh nodedev-reattach pci_0000_A1_B1_C1
sleep 1
modprobe nvidia
modprobe nvidia_modeset
modprobe nvidia_uvm
modprobe nvidia_drm

sleep的时间自行测试
pci_0000_A1_B1_C1填iommu分组与显卡同组的所有ID
重启libvirt服务

1
sudo systemctl restart libvirtd virtlogd.socket

修改qemu
把$OBJECT == “”里面替换成自己的虚拟机名
之后运行install_hooks.sh
打开virt-manager -> 添加硬件 -> PCI主机设备
添加需要直通的显卡和显卡同iommu组的其他设备
启动虚拟机
再次打开virt-manager可以看到虚拟机已经启动
接着为虚拟机安装驱动

大功告成!

你在重启后可以使用looking-glass-client来访问虚拟机

四、参考教程

https://wiki.archlinux.org/title/PCI_passthrough_via_OVMF
https://looking-glass.io/
https://gitlab.com/risingprismtv/single-gpu-passthrough
https://www.bilibili.com/video/BV1Er4y1i7pq/?spm_id_from=333.337.search-card.all.click&vd_source=8090ef867fc52ca54eca33de8f1ff58f
https://lostattractor.net/archives/nixos-gpu-vfio-passthrough#%E4%BD%BF%E7%94%A8
https://gitlab.com/liucreator/LEDs-single-gpu-passthrough
https://lantian.pub/article/modify-computer/laptop-muxed-nvidia-passthrough.lantian/
https://blog.rachelt.one/articles/fake-display-for-moonlight/


显卡直通方案
http://www.cactily.me/2025/07/30/显卡直通方案/
Author
ly
Posted on
July 30, 2025
Licensed under