快轉到主要內容

Ubuntu單GPU直通方法,Nvidia顯示卡直通給Windows 11 QEMU/KVM虛擬機

Linux系統 虛擬機與容器技術 GPU Passthrough Ubuntu QEMU Libvirt
🗓️ 民國113年 甲辰年
✍ 切換正體/簡體字
目錄

之前Ivon討論過Linux 虛擬機雙GPU直通的做法,並透過Looking Glass存取Windows桌面。但是,如果你真的生不出第二個螢幕,連HDMI欺騙器都不想買,那麼我們還有「單GPU直通」這招。

本文Ivon講述,如何在只有一個GPU、CPU無內顯、只有一台螢幕的情況下,進行單GPU直通(Single GPU Passthrough)

1. 單GPU直通原理
#

參考網路上的許多做法後,我採用Libvirt hook的做法,即讓虛擬機開關機後自動觸發指令。

構想是:啟動Windows虛擬機之後,讓Linux斷開螢幕連結,把螢幕交給Windows虛擬機使用,這樣只有一個螢幕也能直通。

以我的配備來說,過程會變成這樣:

  1. 螢幕接在電腦主機的Nvidia顯示卡上,開機進入Linux
  2. 進入SDDM登入畫面,登入KDE桌面
  3. 開啟Virt Manager,啟動Windows 11虛擬機
  4. 中止顯示管理器和桌面環境行程,卸除Linux的圖形驅動程式(Nvidia核心模組),將GPU與宿主機取消連結,載入VFIO核心模組
  5. 螢幕稍微黑一下,隨後變成Windows的畫面
  6. Windows關機後,回到Linux,SDDM重新啟動,回到SDDM登入畫面。

以上過程可以透過Libvirt的hook功能自動化執行,設定好一次後就行。

缺點:沒辦法同時使用二個系統,因為螢幕被Windows搶走了。在Windows虛擬機執行的時候,Linux宿主機只能透過SSH存取

…例如從Windows虛擬機裡面,SSH回Linux宿主機,跳脫Matrix,反察自身。

2. 系統環境
#

  • 主機板:ASUS K31CD-K
  • CPU: Intel® Core™ i5-7400
  • GPU:Intel® UHD Graphics 630 (內顯)
  • GPU:NVIDIA GTX 1050 Ti(獨顯),已安裝閉源驅動
  • 宿主機Host OS:Ubuntu 22.04 LTS
  • Linux核心版本:6.5.0
  • 桌面環境:KDE Plasma 5.24 (X11)
  • 虛擬機Guest OS:Windows 11 23H2
  • QEMU版本:6.2
  • Libvirt版本:8.0.0

3. 安裝Libvirt與Windows虛擬機
#

參考 安裝Windows 11虛擬機

4. 啟用IOMMU
#

  1. 給Intel啟用IOMMU,nvidia-drm.modeset=0防止DRM載入,後面的kvm.ignore_msrs防止Windows BSOD
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on iommu=pt nvidia-drm.modeset=0 kvm.ignore_msrs=1 kvm.report_ignored_msrs=0"
  1. 更新initramfs和GRUB
sudo update-initramfs -u
sudo update-grub

到這裡就可以了,不需要解除Nvidia驅動,不用黑名單Nvidia核心模組,也不用綁VFIO裝置,剩下的交給Libvirt hook處理即可。

5. 撰寫Libvirt hook指令稿
#

  1. lscpi -nnk指令查看Nvidia顯示卡的匯流排位址,如下所示,二個裝置位址轉譯為:pci_0000_01_00_0pci_0000_01_00_1
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP107 [GeForce GTX 1050 Ti] [10de:1c82] (rev a1)
        Subsystem: ASUSTeK Computer Inc. GP107 [GeForce GTX 1050 Ti] [1043:85d6]
        Kernel driver in use: nvidia
        Kernel modules: nvidiafb, nouveau, nvidia_drm, nvidia
01:00.1 Audio device [0403]: NVIDIA Corporation GP107GL High Definition Audio Controller [10de:0fb9] (rev a1)
        Subsystem: ASUSTeK Computer Inc. GP107GL High Definition Audio Controller [1043:85d6]
        Kernel driver in use: snd_hda_intel
        Kernel modules: snd_hda_intel
  1. sudo lsmod | grep nvidia確認目前有用到哪些核心模組
nvidia_uvm           1794048  0
nvidia_drm             94208  8
nvidia_modeset       1314816  20 nvidia_drm
nvidia              56786944  1198 nvidia_uvm,nvidia_modeset
  1. 建立Hooks Helper script,注意我的虛擬機名稱叫做Windows11所以建立的目錄也要叫Windows11
sudo mkdir /etc/libvirt/hooks
sudo touch /etc/libvirt/hooks/qemu
sudo chmod +x /etc/libvirt/hooks/qemu
  1. /etc/libvirt/hooks/qemu填入以下內容:
#!/bin/bash

GUEST_NAME="$1"
HOOK_NAME="$2"
STATE_NAME="$3"
MISC="${@:4}"

BASEDIR="$(dirname $0)"

HOOKPATH="$BASEDIR/qemu.d/$GUEST_NAME/$HOOK_NAME/$STATE_NAME"
set -e # If a script exits with an error, we should as well.

if [ -f "$HOOKPATH" ]; then
eval \""$HOOKPATH"\" "$@"
elif [ -d "$HOOKPATH" ]; then
while read file; do
  eval \""$file"\" "$@"
done <<< "$(find -L "$HOOKPATH" -maxdepth 1 -type f -executable -print;)"
fi
  1. 建立虛擬機開機啟動的hook
sudo mkdir -p /etc/libvirt/hooks/qemu.d/Windows11/prepare/begin

sudo touch /etc/libvirt/hooks/qemu.d/Windows11/prepare/begin/start.sh

sudo chmod +x /etc/libvirt/hooks/qemu.d/Windows11/prepare/begin/start.sh
  1. /etc/libvirt/hooks/qemu.d/Windows11/prepare/begin/start.sh填入以下內容:
#!/bin/bash
set -x

# 停止顯示管理器
systemctl stop sddm

# Wayland下需要停止KDE Plasama服務
#systemctl --user -M "你的使用者名稱" stop plasma-plasmashell.service

# Unbind VTconsoles
echo 0 > /sys/class/vtconsole/vtcon0/bind
echo 0 > /sys/class/vtconsole/vtcon1/bind

# Unbind EFI Framebuffer
echo efi-framebuffer.0 > /sys/bus/platform/drivers/efi-framebuffer/unbind

# 停止Nvidia服務
systemctl stop nvidia-persistenced.service

sleep 2

# 取消載入NVIDIA核心模組
modprobe -r nvidia_drm nvidia_modeset nvidia_uvm nvidia

sleep 2

# 從宿主機移除GPU裝置和GPU音訊裝置
virsh nodedev-detach pci_0000_01_00_0
virsh nodedev-detach pci_0000_01_00_1

# 載入VFIO核心模組
modprobe vfio-pci
  1. 建立虛擬機關機後的hook:
sudo mkdir -p /etc/libvirt/hooks/qemu.d/Windows11/release/end
sudo touch /etc/libvirt/hooks/qemu.d/Windows11/release/end/stop.sh
sudo chmod +x /etc/libvirt/hooks/qemu.d/Windows11/release/end/stop.sh
  1. /etc/libvirt/hooks/qemu.d/Windows11/release/end/stop.sh填入以下內容
#!/bin/bash
set -x

# 將GPU裝置加回宿主機
virsh nodedev-reattach pci_0000_01_00_0
virsh nodedev-reattach pci_0000_01_00_1

# 取消載入VFIO核心模組
modprobe -r vfio-pci

# Rebind framebuffer to host
echo "efi-framebuffer.0" > /sys/bus/platform/drivers/efi-framebuffer/bind

# 載入NVIDIA核心模組
modprobe nvidia_drm
modprobe nvidia_modeset
modprobe nvidia_uvm
modprobe nvidia

# 啟動Nvidia服務
systemctl start nvidia-persistenced.service

# Bind VTconsoles
echo 1 > /sys/class/vtconsole/vtcon0/bind
echo 1 > /sys/class/vtconsole/vtcon1/bind

# 啟動顯示管理器
systemctl start sddm
  1. 重新啟動Libvritd服務
sudo systemctl restart libvirtd
  1. 你可以先在SSH模式下,從另一台電腦登入,一條一條的測試指令,確認指令稿的指令都能執行後再繼續。Libvirt的虛擬機在SSH下用virsh start "虛擬機名稱"指令開機。

  2. 如果虛擬機啟動後指令稿沒啟動,你可以用sudo dmesg看錯誤訊息。

6. 將鍵鼠全部直通給虛擬機
#

  1. 設定好Linux宿主機的SSH服務,確保你可以在緊急時用SSH登入。手機SSH客戶端可以用 Termux

  2. 關閉電腦的休眠機制,KDE可以到系統設定 → 電源管理,關閉休眠

  3. 編輯虛擬機硬體,將Nvidia顯示卡的PCI裝置加入至虛擬機

  4. 接著將QXL顯示卡改為None,這樣開機後就只有一個螢幕。

  5. 因為開機後無法使用SPICE,點選新增USB裝置,把滑鼠和鍵盤全部加入到虛擬機。

  6. 音效卡部分,最簡單的方法是準備一個USB音效卡,或是含有3.5mm耳機孔的Type-C擴充埠,並將其直通進去。不然的話開機後Linux宿主機的桌面行程被幹掉,SPICE無法透過PipeWire輸出音訊。

參考資料
#

相關文章

Ubuntu:Nvidia GPU直通給Windows虛擬機 + Looking Glass安裝教學
Linux系統 虛擬機與容器技術 GPU Passthrough Ubuntu QEMU Windows
啟用巢狀Hyper-V,讓Windows 11 QEMU/KVM虛擬機支援WSL與WSA
Linux系統 虛擬機與容器技術 Libvirt Windows Subsystem for Linux QEMU Windows
Ubuntu安裝QEMU/KVM和Virt Manager虛擬機管理員
Linux系統 虛擬機與容器技術 Libvirt Ubuntu QEMU

留言板

此處提供二種留言板。點選按鈕,選擇您覺得方便的留言板。要討論程式碼請用Giscus,匿名討論請用Disqus。

這是Giscus留言板,需要Github帳號才能留言。支援markdown語法,若要上傳圖片請貼Imgur連結。您的留言會在Github Discussions向所有人公開。

這是Disqus留言板,您可能會看到Disqus強制投放的廣告。有時留言可能會被系統判定需審核,導致延遲顯示,請見諒。