关于 macOS 虚拟机,我最初的尝试是:无头模式的 Debian 10 虚拟机下开启嵌套虚拟化,然后使用 macOS-Simple-KVM,但是效果并不理想:在 High Sierra 和 Mojave 下,安装程序无法连接到恢复服务器(“The recovery server could not be contacted” 中有人切换到 dhcpd 配置网络后成功连接服务器),而 Catalina 完全无法启动安装程序
之后尝试使用 kholia / OSX-KVM ,但依然无法在无头模式的虚拟机下安装 macOS,之后跟随官方推荐的 nicksherlock.com 中的 PVE 的使用教程,成功创建 macOS 虚拟机
macOS 虚拟机本质上是 OpenCore 引导的黑苹果,新建虚拟机后的安装过程也较为相似,OpenCore 相较于 Clover 省去了很多繁琐的配置步骤,适用于 High Sierra、Mojave、Catalina 和 Big Sur
因为 PVE 也是基于 Debian 和 KVM/QEMU,所以理论上应该适用于所有安装了 KVM/QEMU 的 Linux 系统,区别在于安装过程有没有 GUI
强烈建议机器连接显示器,不要使用无头模式,QEMU 的 -nographic 和 -curses 模式并不好用,并且会产生各种问题导致 macOS 无法安装
下文将展示如何在 Proxmox VE 6.2 下新建 macOS Catalina 虚拟机
准备
- Proxmox VE 6.2
- 支持 SSE 4.2 的 CPU
- Nehalem 及之后架构(带 Core 名称)的 Intel CPU 均支持 SSE 4.2 指令集
- 正常运行的 Linux/Windows 电脑
参考
- PVE 虚拟化
- OSK 获取
步骤
生成安装镜像
- 安装 python3、git
- 获取 OSX-KVM
- 执行
git clone https://github.com/kholia/OSX-KVM.git
- 执行
- 获取 BaseSystem
- 执行
cd OSX-KVM ./fetch-macOS.py
- 选择 13,下载最新版本 Catalina 10.15.7
- 执行
- 转换 BaseSystem.dmg 镜像格式
- macOS 下执行
hdiutil convert BaseSystem.dmg -format UDTO -o Catalina-Installer.iso mv Catalina-Installer.iso.cdr Catalina-Installer.iso
- Linux 下可以安装 dmg2img
dmg2img BaseSystem.dmg Catalina-installer.iso
或者可以使用 qemu-img(需要安装 qemu-utils)
qemu-img convert BaseSystem.dmg -O raw Catalina-installer.iso
- macOS 下执行
- 将 Catalina-installer.iso 镜像上传到 PVE
获取 OpenCore 镜像
访问 thenickdude / KVM-Opencore,下载最新的 OpenCore.iso.gz 文件(目前为 v10),解压得到 OpenCore.iso,上传到 PVE
最终 PVE 中的镜像如下图,应有 Catalina-Installer.iso 和 OpenCore.iso(忽略其他镜像)
新建 Catalina 虚拟机
如图新建虚拟机
新建虚拟机
镜像选择 OpenCore.iso,操作系统选择其他
系统选择 VMWare兼容,BIOS选择 OVMF(UEFI),勾选添加 EFI 磁盘,机器选择 q35
硬盘选择 SATA 0,缓存选择 Write back(不安全),勾选 SSD 仿真(建议新建在 SSD 上,体验较好)
CPU核心按需选择,类别选择 Penryn
内存容量按需设置,取消勾选 Ballooning 设备
网络模型选择 VMWare vmxnet3
配置虚拟机
- 在 虚拟机-选项 中开启 使用平板指针
- 在 虚拟机-硬件 中添加 CD/DVD Drive,总线选择 IDE 0,镜像选择 Catalina-Installer.iso
- 获取 OSK 密钥(需要 macOS,可跳过)
- 可以直接使用:ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc
- 或者手动获取(需要先安装 xcode):新建 smc_read.c 文件,写入以下内容
#include <stdio.h> #include <IOKit/IOKitLib.h> typedef struct { uint32_t key; uint8_t __d0[22]; uint32_t datasize; uint8_t __d1[10]; uint8_t cmd; uint32_t __d2; uint8_t data[32]; } AppleSMCBuffer_t; int main(void) { io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleSMC")); if (!service) return -1; io_connect_t port = (io_connect_t)0; kern_return_t kr = IOServiceOpen(service, mach_task_self(), 0, &port); IOObjectRelease(service); if (kr != kIOReturnSuccess) return kr; AppleSMCBuffer_t inputStruct = { 'OSK0', {0}, 32, {0}, 5, }, outputStruct; size_t outputStructCnt = sizeof(outputStruct); kr = IOConnectCallStructMethod((mach_port_t)port, (uint32_t)2, (const void*)&inputStruct, sizeof(inputStruct), (void*)&outputStruct, &outputStructCnt); if (kr != kIOReturnSuccess) return kr; int i = 0; for (i = 0; i < 32; i++) printf("%c", outputStruct.data[i]); inputStruct.key = 'OSK1'; kr = IOConnectCallStructMethod((mach_port_t)port, (uint32_t)2, (const void*)&inputStruct, sizeof(inputStruct), (void*)&outputStruct, &outputStructCnt); if (kr == kIOReturnSuccess) for (i = 0; i < 32; i++) printf("%c", outputStruct.data[i]); printf("\n"); return IOServiceClose(port); }
- 编译并执行 smc_read
gcc -o smc_read smc_read.c -framework IOKit ./smc_read
- 编辑虚拟机配置文件
- SSH 连接 PVE,或者使用 PVE 自带 Web SSH
- 打开 /etc/pve/qemu-server/[虚拟机ID].conf,添加新行
args: -device isa-applesmc,osk="ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc" -smbios type=2 -device usb-kbd,bus=ehci.0,port=2
- 如果使用 Intel CPU,在 args 新增行的行末添加
-cpu host,kvm=on,vendor=GenuineIntel,+kvm_pv_unhalt,+kvm_pv_eoi,+hypervisor,+invtsc
如果使用 AMD CPU,在 args 新增行的行末添加
-cpu Penryn,kvm=on,vendor=GenuineIntel,+kvm_pv_unhalt,+kvm_pv_eoi,+hypervisor,+invtsc,+pcid,+ssse3,+sse4.2,+popcnt,+avx,+avx2,+aes,+fma,+fma4,+bmi1,+bmi2,+xsave,+xsaveopt,check
- 找到两个 CD 驱动器的配置行,将 “media=cdrom” 更改为 “cache=unsafe”
- 最终的虚拟机配置文件如下
配置 Proxmox VE
启动虚拟机前的最后操作
- 防止 macOS 无限重启
echo 1 > /sys/module/kvm/parameters/ignore_msrs
- 使设置持续生效
echo "options kvm ignore_msrs=Y" >> /etc/modprobe.d/kvm.conf && update-initramfs -k all -u
安装 Catalina
- 启动虚拟机,进入 OpenCore 启动界面
- 选择进入 macOS Base System
- 进入 磁盘工具 格式化硬盘
- 安装 Catalina
- 安装完毕后会重启,再次进入 OpenCore 启动界面,选择进入 macOS Install 磁盘(通常为第二个选项),不要再次进入 macOS Base System
- 等待安装完毕,系统再次重启,这次选择格式化时的磁盘名称,进入 macOS 的欢迎界面,选择跳过登录 Apple ID
- 成功安装 macOS
配置 OpenCore
以下两步将解决 macOS 的 EFI 启动问题并开启自启动
复制 OpenCore EFI
此时虚拟机的启动依赖于挂载的 OpenCore.iso 镜像,此步骤会将 OpenCore 的 EFI 复制到 macOS 所在磁盘
- 进入 macOS 后,打开 终端,查看所有磁盘和分区
diskutil list
如图所示,OpenCore.iso 磁盘内只有一个 EFI 分区,大小约为 150 MB
- 将 OpenCore 磁盘内的 EFI 分区复制到 macOS 的 EFI 分区
sudo dd if=/dev/disk1s1 of=/dev/disk2s1
/dev/disk1s1 为 OpenCore 的 EFI 分区,/dev/disk2s1 为 macOS 磁盘的 EFI 分区
- 关机,在 虚拟机-硬件 中删除 OpenCore.iso 和 Catalina-Installer.iso 两个 CD 驱动器
OpenCore 自动启动
经过以上步骤,macOS 虚拟机启动时可以不需要挂载 OpenCore.iso 镜像,但是仍然需要手动选择启动磁盘,以下步骤将开启 OpenCore 自启动
- 进入 macOS,挂载 EFI 分区
- 查看 EFI 分区路径
diskutil list # 找到 EFI 分区路径
- 挂载分区
sudo mkdir /Volumes/EFI sudo mount -t msdos /dev/[EFI 分区路径] /Volumes/EFI
- 编辑 OC 文件夹下的 config.plist 配置文件
- 将 Misc -> Boot -> Timeout 值设置为非 0,以开启自动启动
- 查看 EFI 分区路径
效果