1. 前言

在家里一连复习了几天,想着要休息一下,于是就试试用KVM装了个黑苹果。

折腾了两天总算可以运行起来,完成度50%,不是很满意,最后还是装回了Windows。

这里记录下操作过程,以后Github上的开源项目完善,有时间再折腾时,可以做参考。

2. 机器配置

  • CPU:E5-2680v2 X 2
  • 内存:8GB ECC X 4
  • 显卡:RX580
  • 主板:华南金牌X79双路
  • 硬盘:阿斯加特500GB NVME
  • 散热:PWM风扇 X N
  • 电源:鑫谷全模750电源(650W)

黑苹果需要打上许多补丁才能运行,冷门的配置和X79山寨板几乎是无法驱动的,于是很自然的就想到了虚拟化的方式。

虚拟化的方式并不稀奇,CPU在虚拟化的情况下性能损失不多,主要问题是图形界面,常用的VirtualBox、VMware都有安装黑苹果的教程,即使开启了3D加速,图形界面依旧是PPT般的卡顿,这个时候突然想到了KVM的PCIe透传。

RHEL5开始就有相关的文档介绍如何将Host的PCI设备附加到客户机上:CHAPTER 15. PCI PASSTHROUGH

PCIe总线是直连CPU的,BIOS中开启VT-d后,配合启用IOMMU的内核,可以将任何走PCIe总线的宿主机设备直接分配给客户机,包括:USB控制器、网卡、蓝牙、GPU、NVME固态硬盘等等。相比虚拟化设备的方式,性能接近原生。

目前家里的电脑太多了,算下来一共有过7台台式机和5个笔记本,这是笔者的第二台E5主机,第一台E5已经打包准备送人了。因此最初的计划是在这台双路E5上安装Linux系统,再通过虚拟化的方式运行macOS、Windows和其他图形化桌面的Linux。

Windows和Linux在兼容性方面没有太多问题,本身就适配了众多的机器,但macOS还需要一些补丁来适配黑苹果机器,自己能想到自然早就有更多人想到,下面是目前Github上的两个热门项目。

3. 项目地址

kholia/OSX-KVM

地址:https://github.com/kholia/OSX-KVM

foxlet/macOS-Simple-KVM

https://github.com/foxlet/macOS-Simple-KVM

注意,两个教程的前提都是:

  • 图形化桌面的Linux宿主机环境
  • 双显卡,双独显、集显+独显都可以
  • 2011年后的英特尔CPU或AMD的RYZEN系列CPU

教程的步骤基本类似:

  • 系统镜像下载与和转换:不需要一个额外的MacBook了,使用Python2或Python3的环境就可以
  • 安装qemu,使用kvm作为底层驱动
  • 安装libvirtd,启动virsh0网卡,额外配置一个tap0网卡供虚拟机使用
  • 初始化一个虚拟机系统盘,安装系统:这一步耗时最长,可以在图形化的Linux环境下安装或者使用VNC远程访问
  • 启动已安装好的macOS系统,在系统内安装Clover引导,打补丁,配置驱动
  • 其他配置,如PCIe透传

4. 软件安装与配置

在过去两天里,笔者使用 OSX-KVM 的配置文件,测试了三个系统:CentOS 8、Ubuntu 18.04、Ubuntu 20.04,最后在Ubuntu 20.04上完成了安装。

Ubuntu 20.04的VFIO驱动还存在问题,虽然Linux 5.4内核整合了VFIO,但使用时异常,只能在Grub启动命令中隔离PCIe设备。整个安装最终挂在 USB控制器 透传上,键鼠设备始终无法直接在虚拟机环境下使用。

这里推荐使用Ubuntu 18.04,Linux内核版本为4.18。在不进行PCIe透传前,三个系统的配置大同小异,而关于VFIO的配置教程,目前是Ubuntu 18.04最多, OSX-KVM 教程默认也使用这个系统。

笔者没有安装桌面系统,直接使用命令行的方式操作,安装系统后通过ssh远程配置,下面所有的命令都是在 OSX-KVM 目录下以root用户权限操作,安装macOS 10.15,如下:

1. 安装基础软件

1
apt install --no-install-recommends qemu-system-x86 qemu-kvm uml-utilities dmg2img git wget libguestfs-tools ovmf libvirt-daemon-system

上述命令主要安装了命令行版本的qemu、VFIO驱动、libvirtd等

2. kvm模块配置

按照OSX-KVM的教程,将 kvm.conf 文件复制到 /etc/modprobe.d/kvm.conf

3. 下载与转换系统镜像

执行 fetch-macOS.py 命令下载macOS 10.15,它会使用Python通过苹果的CDN下载 BaseSystem.dmg,然后使用上面安装的 dmg2img 工具转换格式,输出文件 BaseSystem.img

4. 网络配置

安装好 libvirt-daemon-system 后,就会默认启动一个 virbr0 网卡,我们再添加一个 tap0 供虚拟机使用:

1
2
3
ip tuntap add dev tap0 mode tap
ip link set tap0 up promisc on
ip link set dev tap0 master virbr0

关机重启后 tap0 会失效,可以配置一个systemd服务开机运行上述脚本。

5. 系统安装

首先创建一个系统盘,这里大小直接取512GB,文件大小随使用增长:

1
qemu-img create -f qcow2 mac_hdd_ng.img 512G

启动系统,这里在OSX-KVM提供的boot-macOS-Catalina.sh命令中添加了VNC配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
qemu-system-x86_64 -enable-kvm -m 3072 -cpu Penryn,kvm=on,vendor=GenuineIntel,+invtsc,vmware-cpuid-freq=on,$MY_OPTIONS\
	  -machine q35 \
	  -smp 4,cores=2 \
	  -usb -device usb-kbd -device usb-mouse \
	  -device isa-applesmc,osk="ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc" \
	  -drive if=pflash,format=raw,readonly,file=$OVMF/OVMF_CODE.fd \
	  -drive if=pflash,format=raw,file=$OVMF/OVMF_VARS-1024x768.fd \
	  -smbios type=2 \
	  -device ich9-intel-hda -device hda-duplex \
	  -device ich9-ahci,id=sata \
	  -drive id=Clover,if=none,snapshot=on,format=qcow2,file=./'Catalina/CloverNG.qcow2' \
	  -device ide-hd,bus=sata.2,drive=Clover \
	  -device ide-hd,bus=sata.3,drive=InstallMedia \
	  -drive id=InstallMedia,if=none,file=BaseSystem.img,format=raw \
	  -drive id=MacHDD,if=none,file=./mac_hdd_ng.img,format=qcow2 \
	  -device ide-hd,bus=sata.4,drive=MacHDD \
	  -netdev tap,id=net0,ifname=tap0,script=no,downscript=no -device vmxnet3,netdev=net0,id=net0,mac=52:54:00:c9:18:27 \
	  -monitor stdio \
	  -vnc 0.0.0.0:0 -k en-us 

上述的命令作用如下:

  • 启动qumu,使用kvm驱动
  • 分配3GB内存,双核心四线程CPU
  • qumu模拟键鼠
  • 注入OVMF驱动
  • 模拟声卡
  • 模拟sata设备,按启动顺序挂载了Clover引导盘、系统安装盘、系统盘
  • 分配网卡,使用tap0
  • 输出命令行响应到标准输出
  • 启动后默认监听0.0.0.0:5900端口,接收外部VNC连接

5. VNC模式安装系统

对于没有双显卡的用户,宿主机安装系统时可选择Linux服务器版本,不启用图形化桌面,安装macOS时VNC模式是唯一的连接方式,在Windows、Linux和macOS都有各自的VNC客户端,这里以macOS为例。

注意: macOS自带的VNC客户端需要输入密码,在这一步中无法使用,我们需要下载或安装一个TigerVNC客户端

1
brew cask install tigervnc-viewer

启动VNC客户端连接宿主机的5900端口后,可以使用当前的键鼠操作安装系统,按顺序格式化硬盘为HFS+、安装系统,这一步耗时约30分钟,安装成功重启后,需要约20分钟完成系统自动配置。

系统完成启动后创建用户,登录系统,下载Clover、安装配置引导,这一步就结束了。

6. VFIO配置

VFIO的配置目的是:

  • 获取指定PCIe设备并进行隔离
  • 使用vfio-pci驱动代替内核提供的原生驱动运行PCIe设备

步骤如下:

1. 配置BIOS

进入BIOS后,找到VT-d选项并启用,如有提供配置PCIe的Above 4G Decoding,也一并启用。

2. 配置grub启动参数

/etc/default/grub 文件的 GRUB_CMDLINE_LINUX_DEFAULT 选项中,加入以下参数

1
iommu=pt intel_iommu=on video=efifb:off

3. 禁用驱动

编辑 /etc/modprobe.d/blacklist.conf ,屏蔽常见的显卡驱动

1
2
3
4
5
...
blacklist radeon
blacklist nouveau
blacklist nvidia
blacklist amdgpu

这里多添加了一个amdgpu,Linux内核默认使用该驱动驱动amd的RX400和RX500系列显卡

4. 启用vfio内核模块

编辑 /etc/modules 添加以下内容:

1
2
3
4
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd

5. 获取PCIe设备

指令命令 lspci -nn获取PCIe设备列表,对应的BF ID和pcie ID,如下

1
lspci -nn|grep 'AMD\|USB'

输出

1
2
3
4
...
03:00.0 ... AMD VGA [10de:1c82]
03:00.1 ... AMD AUDIO [10de:0fb9]
00:1a.0 USB controller: VIA USB 3.0 Host Controller [1b21:1242]

最左侧的ID:03:00.0等为后续分配给客户机的BF ID,最右侧的10de:1c82等为需要隔离的PCIe设备ID

6. 隔离PCIe设备

在第二步的参数命令中追加需要隔离的显卡和USB控制器的PCIe设备ID,完成的参数如下:

1
GRUB_CMDLINE_LINUX_DEFAULT="iommu=pt intel_iommu=on video=efifb:off vfio-pci.ids=10de:1c82,10de:0fb9,1b21:1242"

7. 更新grub与内核模块

1
2
update-grub2
update-initramfs -k all -u

8. 验证

重启系统后我们可以在dmesg中看到IOMMU和VFIO的信息,执行以下命令可以查看

1
dmesg|grep 'IOMMU\|DMAR\|vfio'

7. PCIe透传

完成上述配置后,需要更新qemu启动命令,将键鼠接到隔离的USB端口,显卡连接显示器,然后执行以下命令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
qemu-system-x86_64 -enable-kvm -m 3072 -cpu Penryn,kvm=on,vendor=GenuineIntel,+invtsc,vmware-cpuid-freq=on,$MY_OPTIONS\
	  -machine q35 \
	  -smp 4,cores=2 \
	  -usb -device usb-kbd -device usb-mouse \
	  -device isa-applesmc,osk="ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc" \
	  -drive if=pflash,format=raw,readonly,file=$OVMF/OVMF_CODE.fd \
	  -drive if=pflash,format=raw,file=$OVMF/OVMF_VARS-1024x768.fd \
	  -smbios type=2 \
	  -device ich9-intel-hda -device hda-duplex \
	  -device ich9-ahci,id=sata \
	  -drive id=Clover,if=none,snapshot=on,format=qcow2,file=./'Catalina/CloverNG.qcow2' \
	  -device ide-hd,bus=sata.2,drive=Clover \
	  -device ide-hd,bus=sata.3,drive=InstallMedia \
	  -drive id=InstallMedia,if=none,file=BaseSystem.img,format=raw \
	  -drive id=MacHDD,if=none,file=./mac_hdd_ng.img,format=qcow2 \
	  -device ide-hd,bus=sata.4,drive=MacHDD \
	  -netdev tap,id=net0,ifname=tap0,script=no,downscript=no -device vmxnet3,netdev=net0,id=net0,mac=52:54:00:c9:18:27 \
	  -monitor stdio \
	  -vga none
	  -device vfio-pci,host=03:00.0,bus=pcie.0,multifunction=on \
	  -device vfio-pci,host=03:00.1,bus=pcie.0 \
	  -device vfio-pci,host=00:1a.0,bus=pcie.0

上述命令移除了VNC,将显卡和USB控制器接入到客户机的PCIe总线中,启动后就可以在显示器上看到macOS系统启动界面了。

8. 总结

KVM相比其他的虚拟化方式,最大的优点还是可以透传PCIe设备,分配显卡给客户机系统实现3D加速,网上最多的资料可能就是KVM运行Windows,macOS的KVM虚拟化还需要比较多的macOS系统内的配置工作。

虽然RX580在macOS下免驱,外接的4K显示器也正常启动了,但还是卡在了 USB控制器 的透传上,猜测是 OSX-KVM 项目的默认Kext中缺少一些识别通过PCIe总线连接的USB设备的驱动。

再说一些题外话,除了软件外,台式机或者笔记本的整机使用体验也很重要。macOS在温度管理上一直比较保守,用高温代替了噪音,对需要集中精神的工作是有利的。我们在组装台式机时,可以多使用分体式水冷、一体式水冷、减速线和PWM风扇来降低风扇噪音,显卡首选支持低温时风扇自动停转的类型。