Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

介绍

KVM(Kernel-based Virtual Machine)是一个开源的系统虚拟化模块,选择KVM的原因是因为自己的主力系统的Linux。但是出于不可抗力的原因(臭打游戏的)也不能完全脱离Winwdows,起初笔记本装了双系统,但是来回切换还是太麻烦了,虽然Linux有Wine、Proton等一些兼容层,但是效果还是不尽人意。。

于是就想在Linux上运行一台Windows虚拟机,但是KVM虚拟机配合QEMU的QXL图形渲染还是达不到(畅玩游戏)要求的=m=。所以要利用KVM PCIe直通功能,让虚拟机独占高性能显卡,直接加载显卡对应的驱动,和显卡直接通信,给它加一双翅膀,拥有和物理机几乎同样的渲染性能。

环境:

neofetch

虚拟机镜像:Windows 10 LTSC 2019

Host:神舟战神G7-CT7NK

套件:libvirt + qemu + virt-manager(图形前端)

查看笔记本架构

目前主流的笔记本架构是MUXedMUXLess

相比MUXLess来说,MUXed架构的笔记本更容易直通,因为它采用的是核显/独显切换工作方式;MUXLess架构是核显输出,独显渲染

lspci | grep VGA查看架构

如果独显以 3D Controller 开头,那么属于MUXLess

如果独显以 VGA Controller 开头,并且由于一个VGA核显输出(Intel/AMD),那么属于MUXed

lspci

开启VT-d虚拟化以及IOMMU模块

BIOS开启VT-d,并在宿主机内核参数添加iommu=on,iommu=pt,其中iommu=pt为了防止不能直通的设备造成错误

dmesg | grep -e DMAR -e IOMMU 验证IOMMU启用有效

iommu

添加vfio模块屏蔽GPU

vfio_pci vfio vfio_iommu_type1 vfio_virqfd添加到initramfs中,重新生成内核并reboot

确保组有效

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_group

运行archwiki参考的脚本可以看到group1对应的是本机独显设备

记录设备id添加到/etc/modprobe.d/vfio.conf中启用vfio-pci驱动隔离GPU

1
options vfio-pci=device-id1,devicd-id2...

创建虚拟机

使用virt-manager创建一台虚拟机:
- 架构:Q35
- 固件:UEFI
- CPUs:host-passthrogh
- 内存:8192(8G)
- 启动盘&数据盘:驱动使用virtio
- 网络:NIC/NAT,驱动依然virtio

默认先用QEMU的QXL渲染,安装好virtio驱动

显卡直通

先确定一下显卡device-id和vendor-id和PCI总线ID是否一致,大概率是不一致的,终端运行lspci -nnk | egrep -A3 "VGA|3D"检查ID

subSystem

如图我的独显的vendor-id是10de,device-id是2191; PCI系统总线的vendor-id是1558,device-id是8550

拿到ID,现在virt-manager直通独显设备,注意要将IOMMU组里的全部设备直通进去

addPCIe

由于NVIDIA今年放开了虚拟化限制,提取显卡vbios是一个可选项; 不需要绕过检测

此时如果开机,查看设备管理器会多出一个Microsoft基本显示适配器,说明已经识别到设备了,但是点开一看报了代码31,无法加载驱动,这时候
前面记录的PCIe总线id来作用了,在虚拟机xml配置添加如下标签匹配总线ID(注意进制转换)

1
2
3
4
5
6
7
8
<qemu:override>
<qemu:device alias="hostdev0">
<qemu:frontend>
<qemu:property name="x-pci-sub-vendor-id" type="unsigned" value="5464"/>
<qemu:property name="x-pci-sub-device-id" type="unsigned" value="34128"/>
</qemu:frontend>
</qemu:device>
</qemu:override>

第一行<domain>标签配置qemu命名空间<domain xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0" type="kvm">

如果你的qemu版本低于7.0

那么用如下配置即可

1
2
3
4
5
6
<qemu:commandline>
<qemu:arg value='-set'/>
<qemu:arg value='device.hostdev0.x-pci-sub-vendor-id=5464'/>
<qemu:arg value='-set'/>
<qemu:arg value='device.hostdev0.x-pci-sub-device-id=34128'/>
</qemu:commandline>

做完这些重启虚拟机,驱动应该是正常运行,此时算是大功告成,关闭虚拟机,删除QXL和Spice服务器等等等等,直通上鼠标键盘,快乐玩耍。

直通显卡后,开启虚拟机后要切换独显输出,因为宿主机用的是核显,此时可以选择外接一个显示器,或者利用libvirt hooks钩子函数让独显抢占当前笔记本显示器

至此显卡基本直通到此结束,还是趟了很多坑的。小小记录一下

参考链接

  1. ArchWiki - PCI passthrough via OVMF (简体中文)
  2. ArchWiki - Intel_GVT-g
  3. ArchWiki - Libvirt
  4. 笔记本 Optimus MUXless 下的 Intel 和 NVIDIA 虚拟机显卡直通
  5. 双显卡笔记本独显直通
  6. ledis 的单显卡直通教程
  7. 系统总线ID不一致问题
  8. KVM虚拟机GPU直通,step by step

XML完整配置

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
<domain xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0" type="kvm">
<name>win10</name>
<uuid>7c121caf-30db-4a3a-82f6-d59ca87b802f</uuid>
<metadata>
<libosinfo:libosinfo xmlns:libosinfo="http://libosinfo.org/xmlns/libvirt/domain/1.0">
<libosinfo:os id="http://microsoft.com/win/10"/>
</libosinfo:libosinfo>
</metadata>
<memory unit="KiB">12582912</memory>
<currentMemory unit="KiB">12582912</currentMemory>
<vcpu placement="static">8</vcpu>
<os>
<type arch="x86_64" machine="pc-q35-7.0">hvm</type>
<loader readonly="yes" type="pflash">/usr/share/edk2-ovmf/x64/OVMF_CODE.fd</loader>
<nvram>/var/lib/libvirt/qemu/nvram/win10_VARS.fd</nvram>
</os>
<features>
<acpi/>
<apic/>
<hyperv mode="custom">
</hyperv>
<vmport state="off"/>
</features>
<cpu mode="host-passthrough" check="none" migratable="on">
<topology sockets="1" dies="1" cores="4" threads="2"/>
</cpu>
<clock offset="localtime">
<timer name="rtc" tickpolicy="catchup"/>
<timer name="pit" tickpolicy="delay"/>
<timer name="hpet" present="no"/>
<timer name="hypervclock" present="yes"/>
</clock>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<pm>
<suspend-to-mem enabled="no"/>
<suspend-to-disk enabled="no"/>
</pm>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<disk type="file" device="disk">
<driver name="qemu" type="qcow2" cache="writeback" discard="ignore"/>
<source file="/home/twac/VM/storage/win10.qcow2"/>
<target dev="vda" bus="virtio"/>
<boot order="1"/>
<address type="pci" domain="0x0000" bus="0x04" slot="0x00" function="0x0"/>
</disk>
<disk type="file" device="disk">
<driver name="qemu" type="qcow2" cache="writeback" discard="ignore"/>
<source file="/home/twac/VM/storage/DATA.qcow2"/>
<target dev="vdb" bus="virtio"/>
<address type="pci" domain="0x0000" bus="0x0a" slot="0x00" function="0x0"/>
</disk>
<disk type="file" device="cdrom">
<driver name="qemu" type="raw"/>
<source file="/home/twac/VM/ISO/Win10_21H2_Chinese(Simplified)_x64.iso"/>
<target dev="sdb" bus="sata"/>
<readonly/>
<boot order="2"/>
<address type="drive" controller="0" bus="0" target="0" unit="1"/>
</disk>
<disk type="file" device="cdrom">
<driver name="qemu" type="raw"/>
<source file="/home/twac/VM/ISO/virtio-win-0.1.208.iso"/>
<target dev="sdc" bus="sata"/>
<readonly/>
<address type="drive" controller="0" bus="0" target="0" unit="2"/>
</disk>
<controller type="usb" index="0" model="qemu-xhci" ports="15">
<address type="pci" domain="0x0000" bus="0x02" slot="0x00" function="0x0"/>
</controller>
<controller type="pci" index="0" model="pcie-root"/>
<controller type="pci" index="1" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="1" port="0x10"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x0" multifunction="on"/>
</controller>
<controller type="pci" index="2" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="2" port="0x11"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x1"/>
</controller>
<controller type="pci" index="3" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="3" port="0x12"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x2"/>
</controller>
<controller type="pci" index="4" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="4" port="0x13"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x3"/>
</controller>
<controller type="pci" index="5" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="5" port="0x14"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x4"/>
</controller>
<controller type="pci" index="6" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="6" port="0x15"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x5"/>
</controller>
<controller type="pci" index="7" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="7" port="0x16"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x6"/>
</controller>
<controller type="pci" index="8" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="8" port="0x17"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x7"/>
</controller>
<controller type="pci" index="9" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="9" port="0x18"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x0" multifunction="on"/>
</controller>
<controller type="pci" index="10" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="10" port="0x19"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x1"/>
</controller>
<controller type="pci" index="11" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="11" port="0x1a"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x2"/>
</controller>
<controller type="pci" index="12" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="12" port="0x1b"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x3"/>
</controller>
<controller type="pci" index="13" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="13" port="0x1c"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x4"/>
</controller>
<controller type="pci" index="14" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="14" port="0x1d"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x5"/>
</controller>
<controller type="pci" index="15" model="pcie-root-port">
<model name="pcie-root-port"/>
<target chassis="15" port="0x8"/>
<address type="pci" domain="0x0000" bus="0x00" slot="0x01" function="0x0"/>
</controller>
<controller type="pci" index="16" model="pcie-to-pci-bridge">
<model name="pcie-pci-bridge"/>
<address type="pci" domain="0x0000" bus="0x0b" slot="0x00" function="0x0"/>
</controller>
<controller type="sata" index="0">
<address type="pci" domain="0x0000" bus="0x00" slot="0x1f" function="0x2"/>
</controller>
<controller type="virtio-serial" index="0">
<address type="pci" domain="0x0000" bus="0x03" slot="0x00" function="0x0"/>
</controller>
<interface type="network">
<mac address="52:54:00:98:77:e9"/>
<source network="default"/>
<model type="virtio"/>
<address type="pci" domain="0x0000" bus="0x0c" slot="0x00" function="0x0"/>
</interface>
<input type="mouse" bus="ps2"/>
<input type="keyboard" bus="ps2"/>
<sound model="ich6">
<address type="pci" domain="0x0000" bus="0x10" slot="0x01" function="0x0"/>
</sound>
<audio id="1" type="none"/>
<hostdev mode="subsystem" type="pci" managed="yes">
<driver name="vfio"/>
<source>
<address domain="0x0000" bus="0x01" slot="0x00" function="0x0"/>
</source>
<rom bar="on" file="/var/tmp/1660ti.rom"/>
<address type="pci" domain="0x0000" bus="0x06" slot="0x00" function="0x0"/>
</hostdev>
<hostdev mode="subsystem" type="pci" managed="yes">
<source>
<address domain="0x0000" bus="0x01" slot="0x00" function="0x1"/>
</source>
<rom bar="on" file="/var/tmp/1660ti.rom"/>
<address type="pci" domain="0x0000" bus="0x07" slot="0x00" function="0x0"/>
</hostdev>
<hostdev mode="subsystem" type="pci" managed="yes">
<source>
<address domain="0x0000" bus="0x01" slot="0x00" function="0x2"/>
</source>
<rom bar="on" file="/var/tmp/1660ti.rom"/>
<address type="pci" domain="0x0000" bus="0x08" slot="0x00" function="0x0"/>
</hostdev>
<hostdev mode="subsystem" type="pci" managed="yes">
<source>
<address domain="0x0000" bus="0x01" slot="0x00" function="0x3"/>
</source>
<rom bar="on" file="/var/tmp/1660ti.rom"/>
<address type="pci" domain="0x0000" bus="0x09" slot="0x00" function="0x0"/>
</hostdev>
<hostdev mode="subsystem" type="usb" managed="yes">
<source>
<vendor id="0x046d"/>
<product id="0xc332"/>
</source>
<address type="usb" bus="0" port="1"/>
</hostdev>
<hostdev mode="subsystem" type="usb" managed="yes">
<source>
<vendor id="0x05ac"/>
<product id="0x024f"/>
</source>
<address type="usb" bus="0" port="2"/>
</hostdev>
<hostdev mode="subsystem" type="pci" managed="yes">
<source>
<address domain="0x0000" bus="0x00" slot="0x1f" function="0x0"/>
</source>
<address type="pci" domain="0x0000" bus="0x10" slot="0x02" function="0x0"/>
</hostdev>
<hostdev mode="subsystem" type="pci" managed="yes">
<source>
<address domain="0x0000" bus="0x00" slot="0x1f" function="0x3"/>
</source>
<address type="pci" domain="0x0000" bus="0x10" slot="0x03" function="0x0"/>
</hostdev>
<hostdev mode="subsystem" type="pci" managed="yes">
<source>
<address domain="0x0000" bus="0x00" slot="0x1f" function="0x4"/>
</source>
<address type="pci" domain="0x0000" bus="0x10" slot="0x04" function="0x0"/>
</hostdev>
<hostdev mode="subsystem" type="pci" managed="yes">
<source>
<address domain="0x0000" bus="0x00" slot="0x1f" function="0x5"/>
</source>
<address type="pci" domain="0x0000" bus="0x10" slot="0x05" function="0x0"/>
</hostdev>
<memballoon model="virtio">
<address type="pci" domain="0x0000" bus="0x05" slot="0x00" function="0x0"/>
</memballoon>
</devices>
<qemu:override>
<qemu:device alias="hostdev0">
<qemu:frontend>
<qemu:property name="x-pci-sub-vendor-id" type="unsigned" value="5464"/>
<qemu:property name="x-pci-sub-device-id" type="unsigned" value="34128"/>
</qemu:frontend>
</qemu:device>
</qemu:override>
</domain>