今年二月我發了一篇關於 osx-proxmox-next 的文章,這個工具只需一個指令就能在 Proxmox 上建立 macOS 虛擬機,而不必花一整個下午去手動編輯 OpenCore plist。

約有 1,500 人閱讀了那篇文章。其中有些人實際安裝在他們的硬體上。

也就是在那個時候,真正有趣的 Bug 開始浮現。經過 150 次 commit 之後,以下是五個在任何 macOS-on-Proxmox 指南中都找不到的問題,以及每個問題的真正根本原因。


1. 安裝程式停滯在 100% CPU 且沒有任何進度

症狀: macOS 安裝程式進入複製階段。CPU 使用率飆到 100%。磁碟 IO 和網路吞吐量都維持零。畫面永遠卡在那裡。

只發生在 Xeon E5/E7 v2-v4 主機上。

我最初的修復是錯的。看起來像是網路問題,所以我以為是 vmxnet3 kext 在安裝時無法載入,因此把那些主機換成 e1000-82545em。然後就發佈了。

之後issue #103 從使用實際硬體的人那裡回報: vmxnet3 網路正常,而 e1000-82545em 根本無法附加。我把情況搞得更糟了。

真正的原因在兩層之下。這些晶片是真正的 HEDT 處理器,具有雙插槽/多晶片架構,而 -cpu host 會直接將該架構洩漏給 guest。搭配 MacPro7,1 SMBIOS(macOS 會視為支援多插槽),XNU 的排程器在大量多執行緒 IO 時會發生 livelock。安裝程式的複製階段正好就是這種工作負載。

解決方法是停止將主機拓樸傳遞給 guest:

_XEON_HEDT_PATTERN = re.compile(r"Xeon.*E[57][ -]*\d+ *v([234])", re.IGNORECASE)

def _xeon_hedt_cpu_model(model_name: str) -> str:
    match = _XEON_HEDT_PATTERN.search(model_name)
    if not match:
        return ""
    if match.group(1) == "2":
        return "Haswell-noTSX,model=158,stepping=3"
    return "Broadwell-noTSX,model=158"

Enter fullscreen mode Exit fullscreen mode

我一直重複學到的教訓:症狀出現在網路層,但原因卻藏在 CPU 拓樸裡。從症狀猜測原因,讓我損失了一次發行。

2. 虛擬機永遠啟動到 Recovery

症狀: 新安裝完成後,每次後續啟動都會回到 Recovery。在 OpenCore 選單中選擇 macOS 只能暫時成功,無法固定為預設。

原因:預設 OpenCore 設定中 AllowSetDefault 是關閉的。這是開機選單中 Ctrl+Enter 背後的設定。關閉後,你無法將 macOS 項目固定為預設,而磁碟上的 Recovery 磁區會持續贏得開機競爭。

在設定檔中只需修改一個鍵值:

Misc -> Security -> AllowSetDefault = True

Enter fullscreen mode Exit fullscreen mode

然後在選單中:反白你的 macOS 磁區,按 Ctrl+Enter。設定就會固定。

3. 安裝後的開機順序和你想像的相反

macOS 安裝完成後,開機順序必須是:

ide0;virtio0

Enter fullscreen mode Exit fullscreen mode

OpenCore (ide0) 優先,macOS 磁碟 (virtio0) 次之。我曾發佈提示建議 virtio0;ide0,看起來比較自然,但其實是錯的:它會直接啟動 macOS 磁碟,跳過 OpenCore,導致虛擬機在第一次開機時停滯且沒有錯誤訊息。

在 hypervisor 上運行的 macOS 永遠需要 OpenCore 在前面引導。每次開機都是如此,不只是安裝時。

osx-next-cli post-install --vmid 100 --execute

Enter fullscreen mode Exit fullscreen mode

4. 每次開機都顯示「Memory Modules Misconfigured」

雖然只是外觀問題,但每次開機都會出現,非常惱人。

每個虛擬機都使用 MacPro7,1 SMBIOS,而預設的 OpenCore 映像並未包含 RestrictEvents.kext。macOS 檢查模擬的記憶體配置,認為真正的 Mac Pro 不會這樣配置,因此一直跳出通知。

搜尋相關解答時,會有人建議加入 revpatch=memtab 作為開機參數。但在這裡完全沒用。 revpatch=memtab 僅影響 MacBookAir 的 SMBIOS 型號。在 MacPro7,1 上是 no-op,這也是很多人回報「沒作用」的原因。

真正的解決方法是直接加入該 kext。RestrictEvents 1.1.6 放入 EFI/OC/Kexts,並在 config.plist 中註冊,其預設的 revblock=auto 會同時阻擋 MemorySlotNotificationExpansionSlotNotification。不需要任何開機參數。

5. 在 Sequoia 和 Tahoe 上無法登入 Apple ID

在較舊的 macOS 版本中,只要有 vmgenid 和靜態 MAC 就能登入 iMessage 和 FaceTime。但在 Sequoia 15 和 Tahoe 26 上,這樣做已經不夠了。

Apple 的 DeviceCheck 現在會讀取 hv_vmm_present sysctl。在虛擬機中它會回傳 1,導致登入在還沒輸入憑證前就被拒絕。

社群的解決方法是在核心的字串表中進行字串替換,由 OpenCore 在開機時套用。找到 hibernatecount sysctl 名稱,將其替換為 hv_vmm_present

Find:    ...hibernatehidready\0 hibernatecount\0
Replace: ...hibernatehidready\0 hv_vmm_present\0

Enter fullscreen mode Exit fullscreen mode

查詢現在會解析到休眠計數器,而在新啟動時該值為 0。macOS 讀取到 0,DeviceCheck 就會認為是實體機器,登入就能成功。

這是針對特定字串配置的 kernel patch,因此在任何 macOS 小版本更新時都有可能失效。基於這個原因,它被放在明確的 --apple-services 旗標之後。

額外:複製 macOS 虛擬機會悄悄破壞兩台的 iCloud

Proxmox 的完整複製是位元複製,這表示複製機會繼承來源虛擬機的 SMBIOS 序號、UUID、MLB 和 ROM。兩台機器共用同一組 Apple 硬體識別碼。Apple 會偵測到,因此其中一台會出現登入失敗。

osx-next-cli clone 會先進行完整複製,然後立即重新產生 SMBIOS 四元組、vmgenid 和靜態 MAC,讓複製機在 Apple 眼中成為獨立的機器。


所有問題都變成了一個 doctor 指令

以上每個問題都是設定層級的錯誤,直到 macOS 以看似不相關的方式出錯時才會發現。因此現在有 12 項檢查可以對任何虛擬機執行:

osx-next-cli doctor --vmid 100

Enter fullscreen mode Exit fullscreen mode

包含 Balloon、q35、核心數為 2 的冪次、記憶體、CPU 型號、網路卡型號、agent、smbios1、開機順序,以及 virtio0 / ide0 / ide2 的存在與否。它會逐行輸出 OK / WARN / FAIL 並附上修復提示,且直接讀取虛擬機設定檔,因此即使不是用我的工具建立的虛擬機也能使用。如果你是用指南手動建立 macOS 虛擬機,可以用這個指令檢查。

尚未解決:GPU 直通

我目前無法從這裡修復的問題。將實體 GPU 直通給 macOS guest 需要我沒有測試環境的硬體,而隨意猜測就像前面 #1 那樣會產生 Bug。這是目前最常被要求的,但老實說受限於測試機台。

以上所有問題都已發佈並合併到 main

curl -fsSL https://raw.githubusercontent.com/lucid-fabrics/osx-proxmox-next/main/install.sh | bash

Enter fullscreen mode Exit fullscreen mode

Repo: github.com/lucid-fabrics/osx-proxmox-next

這仍然是一個獨立開發的專案。如果你在我沒有擁有的硬體上遇到 Bug,請開 issue。這也是上面五個問題中有四個被發現的原因。