【SUSE.0x00】在 openSUSE 上食用 Win VM

本文最后更新于:2024年3月22日 早上

4202 年了,Linux Desktop 还会好吗?

0x00. 一切开始之前

笔者自从 Win 物理机 + Linux VM 转到 Almost all in Linux 的生活已经过了大半年了,用 openSUSE 也已经小半年了,除了 Nvidia on Wayland 日常撕裂以及盒盖行为设定为 Lock Screen 会醒不来以外似乎还没有太多问题,但除了代码以外的日常生活总感觉不是特别彳亍:

音乐

笔者比较喜欢使用酷狗音乐进行听歌( 👴一直都是高贵的酷狗年费会员啊 ),但是酷狗是没有官方的 Linux 安装包的,使用 wine 兼任层可以跑起来但是笔者捣鼓了大半天都没有声音:(

Arch 社区打的酷狗 AUR 包似乎可以正常播放,但是笔者也没有时间重新弄回 Arch 了,更没有那个闲时间去研究怎么在 SUSE 上弄了

网易云音乐 曾经有 Linux 版,但是用的 deb 打包,而且也非常久没有更新了,现在官网上也下架 Linux 版了 因为笔者不常用网易云所以也没有尝试用 wine 跑过,但以 wine 现在的水平估计也不会好到哪去(

腾讯音乐之类的就没有用过也不太打算尝试了:)

海外的大部分听歌软件像 Spotify 都是有 Linux 版 的,但是笔者也会听一些非常小众的歌,这些在 Spotify 上都是没有的,上边只有那些国际化大歌曲( 这就不得不提国内很多小众音乐人的含金量了,什么叫世界第一强国啊

但这还不算最要命的,Sportify 有个非常致命且非常 sb 的设计——电脑端的 Spotify 每听个一两首歌就会插入一段广告来逼你开会员,这样的产品策略让笔者很难去使用一些比较正面的词汇去进行评价

try not to c*m in 30 seconds

Office 套件

LibreOffice 作为大部分 Linux 发行版都会默认带着的开源 Office 套件,用着感觉虽然还行但是和 Microsoft Office 相比总感觉有哪里差点意思(就是用着总感觉有哪里不得劲),当然临时顶用其实还好但是 直接重启切回 Windows 写文档不好么

——Microsoft Office 虽然没有 Linux 版但是有个网页版,支持在网页进行编辑甚至在网页上完成 PPT 演示的功能,但是网页版的用着总感觉 也还是差点意思 ,而且和 native app 相比总感觉有些卡卡的,唯一的优点大概就是 all on cloud 不用担心数据丢失了:

临时做个 PPT 演示还行,但是这样为什么不直接用 Windows 呢

至于国产 Office 套件 WPS,技术水平暂且不谈,当笔者在自家老母亲的旧电脑上打开这软件后跳出一堆各种花花绿绿的广告铺满屏幕然后让电脑变得特别特别卡的时候笔者就决定哪怕再多花点钱多买几套正版的 Office 也不会再考虑这一款国产软件

当然,除了这个问题以外,WPS 还存在数据安全问题,那这是否就有点…

办公软件

在这个 QQ 都转向 NTQQ 的 4202 年微信还是迟迟不出 Linux 版就令人很难评了这就不得不提某人的含马量了(),使用 wine 来运行 WeChat 勉强算是一个可行的策略,但是强行用 wine 跑起来的微信只能说是勉强能用,各种功能缺失以及不完整再加上 wine 的功能不完善带来的令人难以直视的各种边框更是让人想要毁灭世界,而功能本就缺失的网页版微信也慢慢变得不再可用,想要在 openSUSE 上完美使用微信似乎已经变成了一种奢求

有没有深圳上单能去猎一下某人的🐎.jpg

海外的办公软件如 Slack 和 Discord (这个大概勉强也能算办公软件)倒是有原生 Linux 版,What’s App 有网页版但是笔者倒是没有用过,Teams 则只有网页版可用

飞书以及其海外版 Lark 却是都有非常好用的原生 Linux 版, 那这里👴就不得不夸一夸👴的前东家国际化大企业字节跳动的含金量了

至于像钉钉这样的软件似乎也是有 Linux 版,不过笔者基本用不到这个软件所以暂且就不进行深入了解了

游戏及各类其它问题

这里就不展开说了,因为前面已经讲了这么多废话了,同时笔者也懒得再打字了,总而言之 Linux 除了与计算机专业直接强相关的东西以外似乎都没有比较完美的原生解决方案,这令笔者十分苦恼 :(

How to 解决?

让我们重新回顾《虚拟化导论》中的一个基本概念:只要我们能够通过某种方式向上层提供表现相同的抽象接口,在上层看来我们就是正常的该层所提供的资源,从而就实现了对该层的虚拟化 ;因此对于 想要在 Linux 下运行 Windows 独占的软件 这个需求而言,我们只需要提供一个相应的兼容层便能运行——wine 便是这样一个软件,其通过提供相应的 Windows 执行环境(如重新实现各种 DLL)来实现,虽然说目前已经能够流畅地运行很多原生 Windows 软件,但是要把整个 Windows 都给重新实现一遍的工作量相当巨大,至少就目前而言还是缺少相当多的各种不同的运行库,难以真正地在 *NIX 系统上完美无缝运行 Windows 下的二进制程序

网易云其实原本是有 Linux 原生的,可惜前几年终止开发了

重新再看虚拟化层次的划分,Wine 是通过实现了 API 抽象层来完成 Windows 各种运行库的虚拟化,那么再往下一层呢——若是我们能够实现硬件抽象层,我们便能完成操作系统级的虚拟化,这样我们自然便有了一个对应操作系统的完整运行环境——即通过虚拟机(Virtual Machine)来运行一个完整的 Windows 系统,这样我们便能完美运行原生的 Windows 程序 ,若是之后我们再将一些必要的设备给 passthrough 进 Windows 中(或是纯模拟也行)、建立好完善的共享文件夹等机制,我们便能舒适地将 Windows 虚拟机融入到我们的 Linux 物理机当中:)

当然,虚拟机的性能损耗肯定是有的,但很多情况下这些性能损耗其实无所谓 :)

因此本篇文章将教大家如何在 Linux 上优雅地食用 Windows 虚拟机

——好吧, 运行一个 Windows 虚拟机来跑 Windows 程序这件事似乎是没有任何的新意但若是我们能够通过 RDP 连接直接映射不同的程序窗口而非整个桌面呢? ——远程桌面协议(Remote Desktop Protocol)让通过网络的远程计算机桌面图像传输与鼠标键盘事件传输成为了可能,同时 xfreerdp 支持仅传输单个应用窗口,由此我们就能达成在 Linux 桌面中近乎完美地原生运行 Windows 程序的效果

image.png

不过由于各种问题,目前这种办法依然存在较大的 bug,没办法足够舒适地运行,目前而言只能继续期待后人的努力:)

0x01. 通过 Virt-Manager + KVM 运行一个 Windows 虚拟机

安装虚拟化相关组件

参考 openSUSE - Virtualization GuideopenSUSE wiki - KVM 以及 SUSE Linux Enterprise Server 文档 - 虚拟化指南

首先检查自己的 CPU 是否支持硬件虚拟化(不过这个年代应该没有不支持硬件虚拟化的 CPU 了吧):

1
$ sudo LC_ALL=C lscpu | grep Virtualization

如果没有结果的话重启进 BIOS/UEFI 里开一下,有些机型默认不会开启 CPU 虚拟化,若是真的不支持硬件虚拟化那笔者还是建议你赶紧换一台电脑,或者至少换个 U 吧,都什么年代还在用传统电脑

然后安装 libvirt,一个非常舒适的虚拟机管理库,这里直接用 YaST 来安会非常方便:

这就是我们 SUSE 的 YaST 啊,你们有没有这样的 YaST 啊,真是 YY 又 aa、SS 又 TT 啊.jpg

这里笔者安完才发现忘了勾上 KVM server 了,但是再打开发现按钮灰了没办法补勾上…..不过也无所谓了反正看这篇文章的小伙伴记得勾上就好 :)

然后安装 virt-manager 和 libvirt-daemon:

1
2
3
$ sudo zypper in virt-manager libvirt-daemon
$ sudo systemctl enable libvirtd
$ sudo systemctl start libvirtd

QEMU 的话因为要做 kernel pwn 的缘故笔者很早就安装上了所以安装步骤这里就略过了,这里大家就自己想办法安吧(笑

.bash_profile 里导入这个变量:

1
export LIBVIRT_DEFAULT_URI=qemu:///system

把自己加相应的组里再重启:

1
2
$ sudo usermod -a -G libvirt $(whoami)
$ sudo usermod -a -G kvm $(whoami)

配置 default 网络:

1
2
3
$ sudo virsh net-define /usr/share/libvirt/networks/default.xml
$ sudo virsh net-autostart default
$ sudo virsh net-start default

注:libvirt 默认的 NAT 网络似乎有些小问题 (参见 openSUSE doc),如果你没法联网那笔者还是建议使用桥接模式

安装 Windows 虚拟机

首先在 virt-manager 里 enable 上 xml 编辑:

image.png

然后下载 Windows ISOKVM virtio Drivers

之后在 virt-manager 里创建一个虚拟机,创建过程中别忘了勾上 Customize configuration before install ,完成基本配置后挂上第二个 ISO:

image.png

然后在 CPU 的 XML 里编辑 clock修改为如下配置 ,这样可以减少待机开销:

1
2
3
4
<clock offset='localtime'>
<timer name='hpet' present='yes'/>
<timer name='hypervclock' present='yes'/>
</clock>

启用 shared memory

其他的比如说:

  • virt-manager 里该 VM 名字改为 RDPWindows
  • 配置自启动
  • 硬盘类型改为 virtio
  • 网卡模型改为 virtio(可选)

之类的过程笔者这里就略过了,网上资料一大把:)

接下来 Begin Installation 然后就是:Windows,启动————

忘了截开始界面了,无所谓了

我们的硬盘是 virtio 的,所以首先是 加载驱动程序

Windows 安装程序会自动扫到我们的 virtio 驱动的 CD,直接选对应版本:

Windows 11 默认没有网不给安装非常 sb,不过 shift + F10 唤出 cmd 输入 OOBE\BYPASSNRO 重新开始就能绕过了:

进系统以后找到我们的 virtio 驱动光碟,运行 virtio-win-gt-x64.msivirtio-win-guest-tools.exe 安驱动(全选就行),然后就有网了:

然后开浏览器把 https://github.com/Fmstrat/winapps/blob/main/install/RDPApps.reg 下载到本地并右键 合并

然后进设置里开远程桌面就行:

配置 Guest 与 Host 间的数据共享

为了方便直接通过 virt-manager 使用 VM,还可以进 VM 里安装 spice-guest-tools,这样便能支持 Host 与 Guest 之间各种不管是文本还是图片的各种互相复制粘贴了

如果剪贴板共享坏了,在 服务 里检查一下 SPICE 相关服务是不是挂了,如果经常挂的话建议配置为 自动(延迟启动)

对于 Host 与 Guest 之间的文件传输,使用 共享文件夹 似乎是一个不错的方法,将 VM 关机后进入设置中添加一个新的 Filesystem virtio 设备即可:

接下来在 Guest VM 中安装 WinFSP(Windows File System Proxy),aka FUSE for Windows,用以挂载我们的共享文件夹

只安装 Core 其实也行

前面我们已经安装过 virtio 驱动了,所以这里直接重启就行,打开设备管理器后我们便能看到一个 VirtIO FS 设备:

服务 中找到 VirtIO-FS Service 将其设为 自动应用 后点击 启动

然后就 ok 了:

如果你在配置了共享文件夹后 VM 无法启动,且提示 virtiofsd died unexpectedly ,首先检查 /var/log/libvirt/qemu/ 下的相关日志文件,若有类似 libvirt: error : cannot execute binary /usr/libexec/virtiofsd/virtiofsd: Permission denied 这样的提示,则有可能是 AppArmor 的原因

解决方案是在 /etc/apparmor.d/usr.sbin.libvirtd 中适当的位置添加一行:

1
/usr/{lib,lib64,lib/qemu,libexec}/virtiofsd/* PUx,

之后 sudo service apparmor restart 重启 AppArmor,理论上此时便能正常启动了

若还是不行,则检查 libvirtd 服务是否存活,并进行重启

如果重启后你的 VM 无法连上网,可能是防火墙禁止了转发(通过 iptables -A FORWARD -j ACCEPT 命令启用转发),也可能是 libvirtd 莫名其妙挂了,也可能是玄学问题多重启几遍就莫名其妙好了

_Extra. 同一物理磁盘分区直通_(not so recommend)

参考这篇文章

注:如果你是在已有数据的分区上进行操作,请首先确保已经完成了数据备份!!!

注2:仅分区直通很容易把 Windows 干碎,然后出现各种问题,推荐大家还是买个双盘位电脑然后单独通一个盘给 Windows

虚拟机硬盘直通放在 4202 年自然已经不是一件稀奇的事情,但是通常的硬盘直通架构都是要将一整块物理硬盘通给单个虚拟机,若是你和笔者一样买了一部只有单盘位的笔记本(但是笔者的上一部笔记本有三盘位, 这次属于是经典考虑不周了 ),同时希望保留原有物理硬盘的 Windows 直接启动的能力,那么我们只能先预先分好区,之后再通过将分区建立为新的虚拟磁盘设备的方式将这部分分区作为一块新磁盘通给虚拟机

创建 virtual RAID disk

首先载入点必要的驱动:

1
2
$ sudo modprobe loop
$ sudo modprobe linear

接下来创建三个文件作为 GPT 表、 EFI 分区、 GPT 元数据,并挂载为 loopback 设备:

1
2
3
4
5
6
$ dd if=/dev/zero of=gpt bs=1M count=1
$ dd if=/dev/zero of=efi1 bs=1M count=100
$ dd if=/dev/zero of=efi2 bs=1M count=1
$ sudo losetup -f gpt
$ sudo losetup -f efi1
$ sudo losetup -f efi2

此时用 losetup -a 应当能看到三个 loopback 设备:

打开 YaST2 Partitioner (或是其他你所熟悉的能够查看分区信息的工具比如说 fdisk -l),找到原本属于 Windows 的那几个分区当中的数据分区

注:如果你原本就没有已经装好了的 Windows 分区,直接在待用分区上创建一个新的逻辑卷给 Windows 用就行

需要注意的是,笔者在安装 Linux 时使用了新的 EFI 分区,而并非和 Windows 共用一个

然后使用 madam (Multiple Disk and Device Administration)创建 RAID,以及注意不要把物理盘的 EFI 分区选进来了):

1
$ sudo mdadm --build --verbose /dev/md0 --chunk=512 --level=linear --raid-devices=6 /dev/loop0 /dev/loop1 /dev/nvme0n1p2 /dev/nvme0n1p3 /dev/nvme0n1p4 /dev/loop2

然后进入 parted 进行分区,注意分区大小,注意 mkpart 子命令的格式为 mkpart [part‐type name fs‐type] start end注意自行计算分区起始位置

数据无价,谨慎操作!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ sudo parted /dev/md0
GNU Parted 3.6
Using /dev/md0
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) unit s
(parted) mktable gpt
(parted) mkpart primary fat32 2048 206847
(parted) mkpart msr 206848 239615
(parted) mkpart primary ntfs 239616 1677961215
(parted) mkpart primary ntfs 1677961216 -2049
(parted) set 1 boot on
(parted) set 1 esp on
(parted) set 2 msftres on
(parted) set 3 msftdata on
(parted) set 4 hidden on
(parted) set 4 diag on
(parted) name 1 EFI
(parted) name 3 Windows
(parted) quit
Information: You may need to update /etc/fstab.

然后用 mkfs.msdos 格式化 EFI 分区:

1
$ sudo mkfs.msdos -F 32 -n EFI /dev/md0p1

接下来编辑虚拟机磁盘:

1
2
3
4
5
6
7
8
<disk type="block" device="disk">
<driver name="qemu" type="raw" cache="none" io="native"/>
<source dev="/dev/md0"/>
<target dev="vda" bus="virtio"/>
<boot order="1"/>
<address type="pci" domain="0x0000" bus="0x04" slot="0x00" function="0x0"/>
</disk>

然后重新把我们的 Windows 安装光碟与 virtio 驱动挂上去,放到第一个启动:

注:你需要在启动时 press any key 才会从 CDROM 启动

重启后首先按惯例进入安装界面装载 virtio 驱动:

完成后 shift + f10 唤出 cmd,在 diskpart 中找到 EFI 分区,分配驱动号 B

最后通过如下命令将 virtio 驱动注入 C 盘,并在 EFI 分区中配置好启动项:

1
2
dism /image:c:\ /add-driver /driver:e:\viostor\w11\amd64\viostor.inf
bcdboot C:\Windows /s B: /f ALL

重启,重新进行账户验证,然后就是见证奇迹的时刻——

唉👴的桌面这么私密的空间都给大家看了.jpg

通过 libvirt hook 配置 RAID 自创建与卸载

RAID 的配置不能持久化,因此需要我们写一个脚本来在每次启动 VM 前先检查是否创建了 RAID,若否,则先创建 RAID 再启动

这里我们可以通过创建 /etc/libvirt/hooks/qemu 脚本来实现 VM 启动前的 hook,在其中写入如下内容即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash

VM_NAME="Windows11PhysVM"

if [[ $1 == $VM_NAME ]] && [[ $2 == "prepare" || $2 == "stopped" ]] ; then
if [[ $2 == "prepare" ]] ; then
# startup logic here
modprobe loop
modprobe linear
LOOP1=$(sudo losetup -f)
losetup ${LOOP2} /home/arttnba3/Desktop/VM/win11vm/disk/gpt
LOOP2=$(sudo losetup -f)
losetup ${LOOP2} /home/arttnba3/Desktop/VM/win11vm/disk/efi1
LOOP3=$(sudo losetup -f)
losetup ${LOOP3} /home/arttnba3/Desktop/VM/win11vm/disk/efi2
mdadm --build --verbose /dev/md0 --chunk=512 --level=linear --raid-devices=6 /dev/loop0 /dev/loop1 /dev/nvme0n1p2 /dev/nvme0n1p3 /dev/nvme0n1p4 /dev/loop2
else
# shutdown logic here
echo "Stopping array"
mdadm --stop /dev/md0
echo "removing loopback devices"
losetup | grep -i "win11vm" | awk '{print $1}' | xargs sudo losetup -d
fi
fi

配置 TPM 直通

可信平台模块Trusted Platform Module)芯片是主板上的一块用来确保物理安全性的芯片,Windows 用其确保系统的物理安全性,包括 Pin 码等,自 Win 11 起 TPM 成为了操作系统启动的硬性要求

QEMU 虽然可以模拟 vTPM,但是与物理机 TPM 的非一致性会让系统在 Win 物理机与虚拟机间切换时造成一定的问题(在 Windows 看来这破坏了系统的完整性),由于在 Linux PC 上我们通常并不使用 TPM 特性,因此这里我们可以选择直接将 TPM 模块直通给虚拟机

TPM 模块的设备路径通常为 /dev/tpm0 (v1.2)与 /dev/tpmrm0 (v2.0),通常我们应当将前者进行直通才能达到完整直通的效果:

真是简简又单单,你们有没有这样的 virt-manager 啊)

然后就能用 pin 登录了:

可能遇到的问题

  • Host 重启至 Windows 后 WLAN 适配器丢失:在网络适配器里卸载 Kernel Mode Debugger,然后在管理里选择无线网卡设备,卸载设备,扫描设备更改,然后右键桌面右下角网络图标打开 诊断网络问题 ,启用 WLAN 适配器即可

0x02. 在 Host 侧配置 RDP 连接

配置 RDP 硬体加速

运行 gpedit.msc 找到 *计算机配置->管理模板->Windows组件->远程桌面服务->远程桌面会话主机->远程会话环境,在其中配置 RDP 硬体加速,以此获得更好的 RDP 性能:

然后打开注册表编辑器路径 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations ,新建一个名为 DWMFRAMEINTERVAL 的 DWORD 值并设为 15

重启即可

配置 RDP 连接

如果你的 RDP 连接没有声音,可以参见 这个链接 进行修复

如果无法连接上 Guest ,也可以检查一下是不是 Windows 防火墙的问题

使用 xfreerdp 仅运行单个应用

首先安装上 freedrp:

1
$ sudo zypper in freerdp

接下来输入如下命令进行测试:

1
$ xfreerdp /rfx /d:"你的 VM 主机名" /u:"你的 VM 用户名" /p:"你的 VM 密码" /v:"你的 VM 的 IP" +auto-reconnect +home-drive +clipboard /scale:100 /dynamic-resolution /audio-mode:0 /microphone /app:"explorer.exe"

接下来就是见证奇迹的时刻——

碉堡了好吗

这里的 RDP 命令帮我们配置好了共享 /home 文件夹和剪贴板,所以你可以直接往 VM 里粘贴包括文件在内的东西,也可以在 VM 中访问 Host 侧的文件夹:)

使用 sdl-freerdp

和 wlfreerdp 一样,不支持仅转发单个应用(似乎是 wayland 缺失了某些特性)

zypper 官方源里没有,可以手动添加,也可以像笔者一样通过 flatpak 进行安装

1
2
$ flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
$ flatpak install com.freerdp.FreeRDP

然后运行:

1
$ flatpak run com.freerdp.FreeRDP /rfx /d:"你的 VM 主机名" /u:"你的 VM 用户名" /p:"你的 VM 密码" /v:"你的 VM 的 IP" +auto-reconnect +home-drive +clipboard /scale:100 /dynamic-resolution /audio-mode:0 /microphone

在应用列表中添加 Windows 程序图标

把自己想要运行的 Windows 程序路径写成 Desktop Entry 就行,后缀名改为 .desktop 并放在 /usr/share/applicaitions/ 目录下:

1
2
3
4
5
6
7
8
9
10
11
[Desktop Entry]
# 该入口的类型
Type=Application
# 是否显示终端(可选)
Terminal=false
# 点击该入口执行的命令
Exec=xfreerdp /rfx /d:"你的 VM 主机名" /u:"你的 VM 用户名" /p:"你的 VM 密码" /v:"你的 VM 的 IP" +auto-reconnect +home-drive +clipboard /scale:100 /dynamic-resolution /audio-mode:0 /microphone /app:"VM 里的可执行程序路径名"
# 该程序名称
Name=Name of Application
# 入口显示的图标路径(可选)
Icon=/path/to/icon

0x03. 实现显卡直通 ,这样就能玩👴最爱的 Genshin Impact

参考 SUSE - Configuring GPU Pass-Through for NVIDIA cards

众所周知,《原神》是由米哈游自主研发的一款全新开放世界冒险游戏。游戏发生在一个被称作「提瓦特」的幻想世界,在这里,被神选中的人将被授予「神之眼」,导引元素之力。你将扮演一位名为「旅行者」的神秘角色,在自由的旅行中邂逅性格各异、能力独特的同伴们,和他们一起击败强敌,找回失散的亲人——同时,逐步发掘「原神」的真相。我现在每天玩原神都能赚150原石,每个月差不多5000原石的收入, 也就是现实生活中每个月5000美元的收入水平,换算过来最少也30000人民币,虽然我 只有14岁,但是已经超越了中国绝大多数人(包括你)的水平,这便是原神给我的骄傲的资本…

差不多得了😅屁大点事都要拐上原神,原神一没招你惹你,二没干伤天害理的事情,到底怎么你了让你一直无脑抹黑,米哈游每天费尽心思的文化输出弘扬中国文化,你这种喷子只会在网上敲键盘诋毁良心公司,中国游戏的未来就是被你这种人毁掉的😅

扯得有些远了, 总而言之有的时候我们想要在 Windows 上进行一些需要用显卡才能完成的任务,亦或是使用显卡进行图形加速等,virt-manager 默认提供的 RedHat QXL 终究只是一个由 QEMU 进行模拟的设备,性能上自然很难与真正的显卡相比拟

而笔者的笔记本刚好有两个显卡:一个是 Intel 的核显,另一个是一张 NVIDIA 的卡,因为笔者日常也不用这台电脑炼丹( 再说了就一张小破 4060 能练个啥玩意出来 ),因此笔者决定将 N 卡直通到 VM 当中以提高 VM 的图形化性能, 同时闲暇之余还能打开 VM 玩玩游戏这样子

配置 IOMMU

《系统虚拟化导论》 中我们知道集成在北桥上的 IOMMU 可以通过 DMA 重映射与中断重映射的方式让我们实现 I/O 虚拟化:

IOMMU 默认是关闭的,因此首先我们要先启用 IOMMU 这一功能,首先检查一下你是否有两张卡以及是否在 UEFI 里开启了虚拟化相关特性:

1
2
$ dmesg | grep -e "Directed I/O"
$ lspci | grep -i "vga"

image.png

如果你只有一张显示卡的话,那笔者不推荐弄显卡直通,因为同一时间只有一个系统能够占用显卡,Guest 拿掉之后 Host 肯定是没有图形输出的,Guest 关机后才能回到 Host 图形界面,这种直通还不如直接装双系统后重启回 Windows

/etc/default/grubGRUB_CMDLINE_LINUX_DEFAULT 参数中添加 intel_iommu=on iommu=pt rd.driver.pre=vfio-pci (AMD CPU 则添加 iommu=pt amd_iommu=on rd.driver.pre=vfio-pci),之后通过如下命令更新 grub:

1
$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg 

重启后通过 dmesg | grep -e DMAR -e IOMMU 命令我们便能看到 IOMMU 已经开启:

image.png

接下来我们运行 lspci -nn | grep -i nvidia 命令找到 N 卡后面的设备 id,记录下这串数字:

image.png

实现显卡动态直通

大部分双显卡直通的教程上来就是让把老 N 卡的 nouveau 驱动给卸了,但是笔者一开始就装了 N 卡闭源驱动,现在再给卸了有点不太现实,而且万一以后有空了也想用这台电脑小练点丹也说不定,所以这里笔者选择配置动态显卡重装载策略——

  • 默认卸载 N 卡驱动,在需要时再装载 N 卡驱动
  • 在 VM 启动时卸载 N 卡驱动,在 VM 结束运行后再重新装载,桌面环境则仅在核显上运行(其实 Linux 桌面环境通常情况下用核显的性能基本上都是足够的)

配置 VFIO

首先配置 VFIO,这里我们要防止 nvidia 驱动在开机时便被装载(后面用到再手动装载,否则会比较麻烦),首先在 /etc/modprobe.d/ 中创建文件 vfio.conf 并写入我们刚刚获取到的标识符:

1
options vfio-pci ids=10de:28a0

然后创建文件 /etc/dracut.conf.d/gpu-passthrough.conf 以装载 VFIO 驱动:

1
add_drivers+="vfio vfio_iommu_type1 vfio_pci vfio_virqfd"

接下来使用 dracut 重新生成 initrd:

1
sudo dracut --force /boot/initrd $(uname -r)

然后为 Guest VM 禁用 MSR(防止崩溃),创建 /etc/modprobe.d/kvm.conf 文件并写入:

1
options kvm ignore_msrs=1

重启电脑后你就会发现 N 卡驱动载入失败,因为此时 VFIO 先一步取得了N 卡的所有权:)

如果你想再用上 N 卡闭源驱动搞事情,可以通过如下脚本装载回 N 卡驱动,这里我们用 virsh 来重新装载设备,注意查看 N 卡对应的 pci 号设备:

1
2
3
4
5
6
7
#!/bin/bash
sudo modprobe -r vfio-pci
virsh nodedev-reattach pci_0000_01_00_0
sudo modprobe nvidia
sudo modprobe nvidia_modeset
sudo modprobe nvidia_uvm
sudo modprobe nvidia_drm

重新用回 VFIO 的话就是反着来的脚本:

1
2
3
4
5
6
7
#!/bin/bash
sudo modprobe -r nvidia_drm
sudo modprobe -r nvidia_uvm
sudo modprobe -r nvidia_modeset
sudo modprobe -r nvidia
virsh nodedev-detach pci_0000_01_00_0
sudo modprobe vfio-pci

配置显卡直通

接下来我们将这张卡通进 VM 中,首先用如下脚本查看显卡所在 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

比较幸运的是笔者的 N 卡所在组只有一个设备

接下来在 virt-manager 中添加 PCI Host Device显卡所在的整个 IOMMU group 设备即可:

小学生都会做

启动前注意把 Video 换成 virtio 而非 QXL ,否则在 SPICE 服务启动后鼠标没法正常工作:(

之后就完成直通了:

如果你和笔者一样电脑自带的视频输出口都走独显,则可以通过外接显示器直接输出虚拟机内容,这样就不需要忍受 virtio/qxl 那极其底下的性能了:

配置 libvirt hook 动态装载显卡

我们只需要将前面的两个脚本写到 /etc/libvirt/hooks/qemu 脚本中即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash

VM_NAME="Windows11PhysVM"

if [[ $1 == $VM_NAME ]] && [[ $2 == "prepare" || $2 == "stopped" ]] ; then
if [[ $2 == "prepare" ]] ; then
sudo modprobe -r nvidia_drm
sudo modprobe -r nvidia_uvm
sudo modprobe -r nvidia_modeset
sudo modprobe -r nvidia
virsh nodedev-detach pci_0000_01_00_0
sudo modprobe vfio-pci
else
sudo modprobe -r vfio-pci
virsh nodedev-reattach pci_0000_01_00_0
sudo modprobe nvidia
sudo modprobe nvidia_modeset
sudo modprobe nvidia_uvm
sudo modprobe nvidia_drm
fi
fi

在虚拟机里玩原神 (To do)

最后自然就是大家最喜欢的,原神,启动—— 家人们大事不妙了,👴发现原神有 VM 检测, 今天玩不了了,咱们下回再见吧

0x04. 一些亟待解决的小问题

虚拟化组件问题汇总

1. libvirtd 不会自启动

似乎是因为 libvirtd 被拆成了数个小组件,所以不管怎么 enable 都没法让他自启动,但是 libvirtd 不启动的话 virtiofsd 也没法正常工作,因此暂时还需要每次启动后都得手动 systemctl start libvirtd.service

xfreerdp 问题综合汇总

1. 存在画面撕裂

尤其是在仅转发单个应用而非整个桌面时

2. 新开单应用转发导致所有窗口重新开启

其实桌面都在同一个 session 里,但是 xfreerdp 需要重新建一遍窗口,感觉是设计上的缺陷 :(

3. /app 参数纯后台应用无法保持窗口留存

开个酷狗/网易云之类的窗口一关 RDP 连接就也没了,感觉还是设计上的缺陷 :(

不能玩 👴 最爱的 Genshin Impact

原神似乎用了比较严苛的虚拟化检查方案,所以这个后日再议了(笑)


【SUSE.0x00】在 openSUSE 上食用 Win VM
https://arttnba3.github.io/2024/02/27/SUSE-0X00_WIN_VM_SETUP_GUIDE/
作者
arttnba3
发布于
2024年2月27日
许可协议