使用PCI直通使得虚拟机里能使用显卡。

参考:

核显+独显直通: https://lantian.pub/article/modify-computer/laptop-intel-nvidia-optimus-passthrough.lantian/

Archwiki: https://wiki.archlinux.org/index.php/PCI_passthrough_via_OVMF_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)

github:https://github.com/ledisthebest/LEDs-single-gpu-passthrough/blob/main/VFIO/Setting%20up%20a%20basic%20KVM%20cn.md

KVM:https://www.bilibili.com/video/BV1dQ4y1o78R

飞蚊话:https://www.bilibili.com/video/BV1ff4y1q7fA

〇、吾电脑的情况

和台式机一样,核显出厂被屏蔽,只剩独显了。这就需要编写脚本使得显卡能在宿主机、客户机之间切换。

截屏2021-08-19 21.57.52.png

  • 优点:

    • 高性能,游戏渲染延迟小(独显直通显示器,核显不会和 CPU 和独显抢电)
    • 省钱(没有复杂的切换电路)
  • 缺点:

    • 费电(独显需要一直开着)

      • 但你买几万块钱的游戏本估计也不关心续航了。
    • 对显卡直通的毁灭性打击

      • 因为你只有一块显卡,一旦直通进虚拟机,你的主系统就没有显卡可用了。
      • 如果你要坚持进行直通,你需要自己编写显卡在虚拟机和主机之间切换的脚本,并且需要准备一种方法在失去显示的时候进行调试。
      • 艺高人胆大的可以上。

双显卡的步骤:

安装 vfio-pci 防止宿主机调用独显,
克隆核显
配置 Intel GVT-g 虚拟核显
配置NVIDIA显卡直通

欺骗NVIDIA驱动

一、创建非直通KVM虚拟机

1. 启用IOMMU

若是systemd 启动器:

nano /boot/loader/entries/arch.conf

我用grub引导,所以

sudo vim /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT 中,添加intel_iommu=oniommu=pt

之后,生成配置文件:

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

在重启之后,检查 dmesg 以确认 IOMMU 已经被正确启用:

sudo dmesg | grep -e DMAR -e IOMMU

脚本检测结果:(USB-C 和显卡在一个组)

IOMMU group 1
        00:01.0 PCI bridge [0604]: Intel Corporation 6th-10th Gen Core Processor PCIe Controller (x16) [8086:1901] (rev 07)
                Driver: pcieport
        01:00.0 VGA compatible controller [0300]: NVIDIA Corporation TU116M [GeForce GTX 1660 Ti Mobile] [10de:2191] (rev a1)
                Driver: nvidia
        01:00.1 Audio device [0403]: NVIDIA Corporation TU116 High Definition Audio Controller [10de:1aeb] (rev a1)
                Driver: snd_hda_intel
        01:00.2 USB controller [0c03]: NVIDIA Corporation TU116 USB 3.1 Host Controller [10de:1aec] (rev a1)
                Driver: xhci_hcd
                Usb bus:
                        Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
                Usb bus:
                        Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
        01:00.3 Serial bus controller [0c80]: NVIDIA Corporation TU116 USB Type-C UCSI Controller [10de:1aed] (rev a1)
                Driver: nvidia-gpu

2. 启用VFIO模块

若是双显卡直通,则需要启动时禁用NVIDIA独显,则需要编辑钩子 /etc/mkinitcpio.conf,在MODULE()中添加:

MODULES=(vfio_pci vfio vfio_iommu_type1 vfio_virqfd)

并且删除 nvidia 等与 NVIDIA 驱动相关的内核模块。

这样 PCIe 直通模块就会在系统启动的早期抢占独显,阻止 NVIDIA 驱动后续占用。

之后,运行 mkinitcpio -P 更新 initramfs,重启电脑。

单显卡直通,则需要在进入虚拟机时移交显卡的控制权。因此只需要添加内核的配置文件

/etc/modprobe.d/vfio.conf
options vfio-pci ids=10de:2191,10de:1aeboptions vfio-pci disable_idle_d3=1options vfio-pci disable_vga=1

vfio-pci 这个负责 PCIe 直通的内核驱动一个配置,让它去管理独显。ids 参数就是要直通的独显的制造商 ID 和设备 ID。

先实验只直通图像和音频不知道是否可行

3. 创建虚拟机

安装qemu和libvirt所需要的包:

sudo pacman -S qemu libvirt edk2-ovmf virt-manager dnsmasq ebtables iptables bridge-utils

开机启用服务

sudo systemctl enable libvirtdsudo systemctl enable virtlogd.socket

开始Libvirt

sudo systemctl start libvirtdsudo systemctl start virtlogd.socket

启用虚拟网络

sudo virsh net-start defaultsudo virsh net-autostart default

启用 sshd 以备后续操作 systemctl enable sshd --now

创建一个虚拟机

下载vitro-win 驱动 https://github.com/virtio-win/virtio-win-pkg-scripts

安装虚拟机前需要先设置CPU数、virtIO驱动盘。安装界面需要加载驱动。

二、Libvirt QEMU 配置和脚本

配置libvirt脚本

在Libirt里面创建一个hooks文件夹
sudo mkdir /etc/libvirt/hooks

这里的三个文件放到hooks文件夹里面

都变成可执行的文件
sudo chmod +x /etc/libvirt/hooks/*

把两个脚本软链接到根目录的bin文件夹

sudo ln -s /etc/libvirt/hooks/vfio-startup.sh /bin/vfio-startup.shsudo ln -s /etc/libvirt/hooks/vfio-teardown.sh /bin/vfio-teardown.sh

防止Libvirt在运行时休眠

sudo nano /etc/systemd/system/[email protected]

[email protected]里面添加:

[Unit]Description=Preventing sleep while libvirt domain "%i" is running[Service]Type=simpleExecStart=/usr/bin/systemd-inhibit --what=sleep --why="Libvirt domain \"%i\" is running" --who=%U --mode=block sleep infinity

更改权限:

sudo chmod 644 -R /etc/systemd/system/[email protected]

变成系统文件

sudo chown root:root /etc/systemd/system/[email protected]

配置qemu

编辑libvirt.conf

sudo nano /etc/libvirt/libvirtd.conf

找到这两行,把前面的#删掉:

unix_sock_group = "libvirt"unix_sock_rw_perms = "0770"

可以选择把这两行添加到文件的最后面(日志文件):

log_filters="1:qemu"log_outputs="1:file:/var/log/libvirt/libvirtd.log"

编辑qemu.conf

sudo nano /etc/libvirt/qemu.conf

#user = "root" 改成 user = "yourusername",
#group = "root" 改成 group = "libvirt"

把自己添加到libvirt组里面:
sudo usermod -a -G libvirt 用户名

之后,重启电脑或者重启libvirt

sudo systemctl restart libvirtd.servicesudo systemctl restart virtlogd.socket

三、显卡直通

提取显卡vbios,使用 GPU-Z

或者使用hp下载bios,但我未成功。

打vbios补丁(NVIDIA)

  1. 安装十六进制编辑器bless,打开显卡vbios .rom

    1. 查找字符"VIDEO"
      VIDEO前面第一个大写U.(十六进制是55)之前的所有的headers都删掉.
    2. 保存到某个地方
      我在Arch上保存到个人主目录文件夹是没问题的
      Arch、Fedora可以保存到/var/lib/libvirt/vbios里面,
      Ubuntu/OpenSUSE/Mint(用AppArmour的发行版)可以保存到/usr/share/vgabios里面。
    3. 修改权限:
      sudo chmod -R 660 vbios文件.rom
    4. 更改拥有者 sudo chown 你的用户名:users vbios文件.rom

[可选项] 绕过错误43

**2021年4月以后的英伟达显卡驱动已经不需要绕过虚拟化检测了。因此这一步不需要做了。
但要记住,直通进去后,装上新版的显卡驱动。**

把显卡 BIOS 添加到虚拟机的 UEFI 固件(即 OVMF)中。

# 根据 GitHub 上用户反馈,UEFI 固件编译完成后不能移动位置
# 所以要先找好存放的地方
cd /opt
git clone https://github.com/tianocore/edk2.git
# 安装编译过程中需要的依赖
pikaur -S git python2 iasl nasm subversion perl-libwww vim dos2unix 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
patch -p1 < nvidia-hack.diff
# 开始编译 OVMF
make -C BaseTools
. ./edksetup.sh BaseTools
# 修改 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/Win10_VARS.fd

配置qemu

添加libvirt的pcie直通

在Libvirt中把显卡相关的PCIE部件全部添加进去。并编辑它们的XML设置,添加:
rom 在这里 source

  <rom bar="on" file="/home/gakki/qemu_VM/vBIOS/TU106.rom"/>

改善性能的设置

设置CPU模式为host-passthrough

配置CPU拓扑:套接字、核心、超线程