Optimus笔记本虚拟机显卡直通
这个不是我想到的方法,因为这种笔记本想做到直通太麻烦了。不过机缘巧合,我在这个博客中看到了方法,尝试之后也成功了,遂来记录一下。
上面的博客有着非常详细的操作步骤及一些基本原理介绍,有兴趣可以阅读一下。不过在我尝试的过程中发现文章漏掉了一些步骤,再加上我的电脑与文章的博客不同,获取vBIOS的过程有些许出入,所以这里只记录一下操作过程,每一步的解释及更多扩展请看原博客。
我的电脑:HP暗影精灵一代,i7-6700hq+950M独显
事前准备:KVM虚拟机拥有一个UEFI启动的Win10,含有QXL显卡、实体机启动的win10、Linux宿主在使用英特尔核显,独显驱动禁用或驱动被卸载
一、隔离独显
lspci -nn | grep NVIDIA
,获得类似输出:01:00.0 3D controller [0302]: NVIDIA Corporation GP107M [GeForce GTX 1050 Mobile] [10de:1c8d] (rev a1)
记录中括号[10de:1c8d]中的ID
- 编辑或创建
/etc/modprobe.d/vfio.conf
,内容书写options vfio-pci ids=刚刚记录的ID
- 修改
/etc/mkinitcpio.conf
,在MODULES里增加vfio_pci vfio vfio_iommu_type1 vfio_virqfd
mkinitcpio -P
更新initramfs
二、增加英特尔虚拟核显
- 编辑
/etc/default/grub
,在GRUB_CMDLINE_LINUX
中增加i915.enable_gvt=1 kvm.ignore_msrs=1 intel_iommu=on
编辑或创建
/etc/modules-load.d/lantian.conf
,增加三行kvmgt vfio-iommu-type1 vfio-mdev
grub-mkconfig -o /boot/grub/grub.cfg
重新生成grub- 重启
lspci | grep "HD Graphics"
记录最开始的PCI地址,如00:02.0
创建显卡
sudo su echo "af5972fb-5530-41a7-0000-fd836204445b" > "/sys/devices/pci0000:00/0000:刚刚记录的PCI地址/mdev_supported_types/i915-GVTg_V5_4/create"
前面两部的成功性验证:
二.4重启之后,使用lspci -nnk
查找英伟达,如果kernel in use
为vfio
,则表明隔离NVIDIA成功;二.6第二行命令可以成功执行,则表明创建虚拟核显成功。
三、增加虚拟显卡进虚拟机
virsh edit 虚拟机名
,在</device>
前增加
<hostdev mode='subsystem' type='mdev' managed='no' model='vfio-pci' display='off'>
<source>
<address uuid='af5972fb-5530-41a7-0000-fd836204445b'/>
</source>
</hostdev>
保存后启动虚拟机,如果在设备管理器中多了一个Microsoft 基本显示适配器
,则说明增加成功,耐心等待驱动安装成功,便会显示出Intel核显名称了。
一旦驱动安装完成,关闭虚拟机,再次编辑配置。
- 刚刚增加的hostdev标签,将
display='off'
更改为display='on'
删除
<graphics>
和<video>
两块,用如下内容替换<graphics type='spice'> <listen type='none'/> <image compression='off'/> <gl enable='yes'/> </graphics> <video> <model type='none'/> </video>
在
</domain>
前增加<qemu:commandline> <qemu:arg value='-set'/> <qemu:arg value='device.hostdev0.ramfb=on'/> <qemu:arg value='-set'/> <qemu:arg value='device.hostdev0.driver=vfio-pci-nohotplug'/> <qemu:arg value='-set'/> <qemu:arg value='device.hostdev0.x-igd-opregion=on'/> <qemu:arg value='-set'/> <qemu:arg value='device.hostdev0.xres=1920'/> <qemu:arg value='-set'/> <qemu:arg value='device.hostdev0.yres=1080'/> <qemu:arg value='-set'/> <qemu:arg value='device.hostdev0.romfile=/vbios_gvt_uefi.rom'/> <qemu:env name='MESA_LOADER_DRIVER_OVERRIDE' value='i965'/> </qemu:commandline>
其中,vbios_gvt_uefi.rom从这里下载,并对应修改上面的文件路径。
- 把
<domain type='kvm'>
更改为<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
启动虚拟机,如果图像正常显示且设备管理器中只有英特尔核显,则说明截至目前,操作都是成功的。
四、获取显卡vBIOS
这部分在实体Windows下进行。
- 前往惠普支持官网,下载适合自己的惠普BIOS更新程序
- 百度下载MaxwellBiosTweaker备用
- 启动更新程序,选择第三项Copy
- 解压缩导出的所有bin文件(已知7z可成功解压)
- 进入解压出来的文件夹,打开到有.rom文件的文件夹
- 打开MaxwellBiosTweaker,依次将.rom拖入文件,如果程序成功显示各种内容,则这个文件是一个显卡BIOS文件
- 对于是显卡BIOS的文件, 对照显示的内容,找到是自己显卡的rom文件,复制出来备用
五、编译虚拟机UEFI固件
这部分回到Linux下进行。
# 根据 GitHub 上用户反馈,UEFI 固件编译完成后不能移动位置
# 所以要先找好存放的地方
cd /opt
# 使用root用户进行,避免sudo前缀
sudo su
git clone https://github.com/tianocore/edk2.git
# 安装编译过程中需要的依赖
pacman -S git python2 iasl nasm subversion perl-libwww vim dos2unix
yaourt -S gcc5
# 假设你导出的显卡 BIOS 存放在 /vbios.rom
cd edk2/OvmfPkg/AcpiPlatformDxe
xxd -i /vbios.rom vrom.h
# 编辑 vrom.h,把 unsigned char 数组(在第一行)的名字修改成 VROM_BIN
# 把文件末尾的长度变量(在最后一行)改名为 VROM_BIN_LEN,并记录下长度值,我的是 167936
wget https://github.com/jscinoz/optimus-vfio-docs/files/1842788/ssdt.txt -O ssdt.asl
# 编辑 ssdt.asl,修改第 37 行为 VROM_BIN_LEN 的值
# 然后执行下面这行命令,会报错但是没关系,只要 Ssdt.aml 有了就行
iasl -f ssdt.asl
xxd -c1 Ssdt.aml | tail -n +37 | cut -f2 -d' ' | paste -sd' ' | sed 's/ //g' | xxd -r -p > vrom_table.aml
xxd -i vrom_table.aml | sed 's/vrom_table_aml/vrom_table/g' > vrom_table.h
# 返回 edk2 的目录下打补丁
cd ../..
wget https://gist.github.com/jscinoz/c43a81882929ceaf7ec90afd820cd470/raw/139799c87fc806a966250e5686e15a28676fc84e/nvidia-hack.diff
dos2unix nvidia-hack.diff
dos2unix OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
patch -p1 < nvidia-hack.diff
# 开始编译 OVMF
make -C BaseTools
source edksetup.sh
# 修改 Conf/target.txt 中如下变量的值:
# - ACTIVE_PLATFORM = OvmfPkg/OvmfPkgX64.dsc
# - TARGET_ARCH = X64
# - TOOL_CHAIN_TAG = GCC5
build
# 等待编译完成,确认 Build/OvmfX64/DEBUG_GCC5/FV 文件夹下有这两个文件:
# - OVMF_CODE.fd
# - OVMF_VARS.fd
# 然后替换你的虚拟机的 UEFI 参数,注意修改虚拟机名
cp Build/OvmfX64/DEBUG_GCC5/FV/OVMF_VARS.fd /var/lib/libvirt/qemu/nvram/虚拟机名_VARS.fd
六、增加独显进虚拟机
visrh edit 虚拟机名称
,做如下配置
<!-- 把 os 一段改成这样,注意对应你的 OVMF_CODE.fd 路径 -->
<os>
<type arch='x86_64' machine='pc-q35-4.2'>hvm</type>
<loader readonly='yes' type='pflash'>/opt/edk2/Build/OvmfX64/DEBUG_GCC5/FV/OVMF_CODE.fd</loader>
<nvram>最后复制的那个路径</nvram>
</os>
<!-- 把 features 一段改成这样,就是让 QEMU 隐藏虚拟机的特征 -->
<features>
<acpi/>
<apic/>
<hyperv>
<relaxed state='on'/>
<vapic state='on'/>
<spinlocks state='on' retries='8191'/>
<vendor_id state='on' value='GenuineIntel'/>
</hyperv>
<kvm>
<hidden state='on'/>
</kvm>
<vmport state='off'/>
</features>
<!-- 添加显卡直通的 PCIe 设备,必须放在核显 hostdev 的后面 -->
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</source>
<rom bar='off'/>
<!-- 注意这里的 PCIe 总线地址必须是 01:00.0,一点都不能差 -->
<!-- 如果保存时提示 PCIe 总线地址冲突,就把其它设备的 <address> 全部删掉 -->
<!-- 这样 Libvirt 会重新分配一遍 PCIe 地址 -->
<address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0' multifunction='on'/>
</hostdev>
<!-- 在 </qemu:commandline> 之前添加这些参数 -->
<qemu:arg value='-set'/>
<!-- 下面的两个id为 lspci -nn中的那两个id -->
<qemu:arg value='device.hostdev1.x-pci-vendor-id=0x10de'/>
<qemu:arg value='-set'/>
<qemu:arg value='device.hostdev1.x-pci-device-id=0x1c8d'/>
<qemu:arg value='-set'/>
<!-- 下面的两个id为 lspci -nnk中,subsystem显示的id那两个 -->
<qemu:arg value='device.hostdev1.x-pci-sub-vendor-id=0x17aa'/>
<qemu:arg value='-set'/>
<qemu:arg value='device.hostdev1.x-pci-sub-device-id=0x39d1'/>
<qemu:arg value='-acpitable'/>
<qemu:arg value='file=/ssdt1.dat'/>
最后一行的ssdt1.dat从这里下载,并将路径对应修改。
启动虚拟机,等Win10自动安装驱动。
如果不出意外的话,直通就完成了。