背景
目前業界云平臺一般會提供 Windows 虛擬機和 Linux 虛擬機,用戶可以在這些虛擬機上部署符合 Windows生態和 Linux 生態的服務和應用。MacOS 作為 Apple 生態的基本操作系統,MacOS 虛擬化的實現將會為使用 Apple 生態的用戶帶來很大的便利,如直接在 MacOS 的虛擬機上進行測試、構建iOS 應用,甚至使用 MacOS 虛擬機作為辦公環境等。
目前社區對于 Intel x86 Mac mini 平臺的 MacOS虛擬化方案已經比較成熟,隨著 2020年 Apple 推出自研的 ARM64 M1芯片,Mac mini 增加了對 ARM64 平臺的支持,后續 Apple 將逐漸從 Intel x86 芯片轉向自研的 ARM64 芯片。相對于 Intel x86 平臺,社區對 ARM64 平臺的 MacOS 虛擬化方案還在探索和起步階段,于是字節跳動 STE 團隊和 App Infra 團隊率先進行了深入的研究,本文將與大家分享針對采用 ARM64 M1 芯片的 Mac mini M1 機型虛擬化方案的探索和實現。
方案介紹
什么是 Mac mimi 虛擬化
Mac mini 虛擬化是指在一臺 Mac mini 物理機上模擬出多個互相隔離的 Mac mini 虛擬機,使得每個虛擬機用戶都認為自己在一臺真正的物理機上以獨占的方式運行。
整個方案中,我們采用了云平臺常見的 Linux + KVM(Kernel-based Virtual Machine)+ QEMU(Quick EMUlator) 這樣全開源的虛擬化軟件棧,以方便把控和實現對 Mac mini M1 機型(包括 CPU / MEM /片上設備/外設等)的全方位模擬。在 Mac mini M1 的虛擬化實現中,我們基于 QEMU 既有的機型框架實現了對 Mac mini M1 的軟件模擬(TCG, Tiny Code Generator)支持,又通過 KVM 為Mac mini M1 提供了ARM64平臺硬件虛擬化能力的支持。
Linux
Asahilinux 對 M1 機型上的硬件設備做了足夠多的逆向工程分析,并根據推出來的硬件邏輯來嘗試實現linux下的各設備驅動,目前已實現了對 M1 機型的基本支持,其中對 M1 GPU的支持也在不斷完善中。Asahilinux這部分工作也在不斷的合入Linux upstream。
我們采用Asahilinux作為運行在M1裸機上的操作系統。依賴Asahilinux,我們可以獲得M1 的 linux 運行時物理機環境,并以此為基礎逐步構建和實現M1虛擬化所需的虛擬化軟件棧。
QEMU
QEMU 作為機型/系統模擬的常用軟件,構造了機型模擬的基本框架,并在此基礎上提供了不同平臺上很多具體機型的模擬實現,比如 x86 平臺的 piix 和 Q35 機型,ARM 的 virt 和 raspi2 等。在 M1 機型的虛擬化中我們需要基于 ARM64 通用機型,構造一個新的 M1 機型,并集成到 QEMU 提供的機型模擬框架中。
QEMU 提供的基本模擬方式是純軟件的,即對于每一條虛擬機內部執行的指令都需要退出到 QEMU 的上下文來翻譯執行(TCG),該方式性能很低。KVM 可以幫助我們解決 QEMU 純軟件模擬帶來的低性能問題。
KVM
KVM 作為 Linux 下支持虛擬化的內核模塊,將虛擬化場景下的硬件加速功能通過 ioctl 的方式導出給QEMU使用。在 KVM 的支持下,虛擬機內部的常用指令將不再需要退出到QEMU來模擬執行,而是在虛擬機的 CPU 上下文中直接執行,只有個別特權指令才需要退出到物理機上,由物理機來具體執行。對于虛擬機的內存訪問,也不再需要 QEMU 進行頁表翻譯,而是直接依賴硬件實現的兩級頁表翻譯機制(虛擬機內部頁表實現 Guest Virtual Address向Guest Physical Address的轉化,使用stage2 page table 實現 Guest Physical Address向Host Physical Address的轉化),從而實現內存的快速訪問。
M1機型的模擬
了解了基本的虛擬化軟件棧后,我們開始考慮如何模擬一臺真正的 Mac mini M1。那么,模擬 M1 機型包括模擬哪些內容呢?
M1 VCPU
QEMU 中使用線程來模擬 VCPU,即每一個 VCPU 對應一個單獨的 QEMU 線程;使用必要的數據結構記錄 VCPU 相關的信息,如寄存器/運行狀態等。區別于其他機型的 VCPU,M1 需要增加的特有支持如下: 1)多個 M1 特有寄存器和對應的讀寫邏輯; 2)對 Apple PMGR(電源管理模塊)的模擬。M1 通過 PMGR 來實現多核(SMP,symmetric multiprocessing)的喚醒和管理;
3)Cluster 內和 Cluster 間的 Fastipi 通信機制;
M1 內存布局
M1 的內存布局,是指將VM的整個地址空間按照設備和用途進行合理的劃分,如 Flash device、CPU 相關的固件、中斷控制器、Spec 設備地址、MMIO 和 PCI 地址范圍以及虛擬內存等。在 QEMU 層設置 M1 機型的內存布局需要注意以下幾個問題: 1)注意 CPU 地址域和設備地址域的關系; 2)System Memory 范圍不能跟 DTB(Device Tree Blob) 中各設備的物理地址空間沖突; 3)需要連續的足夠大的地址范圍來映射 System Memory;
System Memory 作為虛擬機的主存,在啟動初期會被用來存放內核鏡像、設備樹以及Ramdisk等,這些內容的加載地址也需要額外配置,注意不能存在交叉,以避免內存中內容互相覆蓋。
M1 設備模擬
區別于 M1 物理機,M1 虛擬機的存儲和網絡主要采用虛擬化場景下常見的 virtio-pci 設備,如 virtio-blk / virtio-net 設備等。我們需要在模擬的 M1 機型上插上 PCIE 的線,其下接各種 PCI / PCIE 設備。另外,為了讓設備的中斷能夠順利分配、觸發并遞送到目的 VCPU 中,還需要模擬 M1 的中斷控制器 AIC。
AIC 是 M1 Apple Interrupt Controller 的簡稱, 主要負責中斷掩碼/中斷狀態設置、接受/遞送中斷等。對 AIC 的模擬主要是對上述的功能的模擬,可以通過 Asahilinux 的 AIC 驅動部分的代碼邏輯來推斷出 AIC 具體地址空間的含義,從而實現 AIC 的模擬。對 AIC 的模擬中我們使用了 MMIO(Memory-Mapped IO) 空間來實現對 AIC 內部相關硬件地址的訪問邏輯。
對 M1 新插上的 PCIE 總線的模擬基于 QEMU 提供的通用 PCIE 線——gpex-pcihost 來實現,在通用的 gpex-pcihost 模型的基礎上,我們需要根據 M1 的 DTB 文件來制定 M1 PCIE 總線配置空間和bar空間,以及其下 PCI 設備的 PCI 配置空間和 Bar 地址空間范圍。
整體的中斷設計如下:
將模擬的 M1 PCIE (gpex-pcihost )總線的 4 個 gpio_out pin 依次連接到 AIC 的 gpio-in pin 上去。gpex-pcihost 通過該連線傳遞中斷給AIC;
AIC 有 cpu_num 個 gpio_out pin ,這些引腳分別連接到各個 CPU 上,AIC 通過該連線遞送中斷給 CPU。
至此,我們已經在 QEMU 層面實現了對 M1 的 CPU、PMGR、AIC、PCIE 總線等設備的基本模擬,可以通過 QEMU 命令在 PCIE 線上插上所需的 PCI 設備,如 virtio-blk、virtio-net 等,來嘗試啟動 MacOS 虛擬機了。
虛擬化下的MacOS和啟動
既然已經完成了對 M1 虛擬機所需要的基本組件的模擬,接下來我們考慮如何在模擬的 M1 機型上啟動真正的 MacOS 。
預啟動階段
MacOS 的啟動流程是怎樣的呢?MacOS 在 M1 物理機上的啟動流程如下(更多詳見M1 啟動過程:https://eclecticlight.co/2022/01/05/booting-an-m1-mac-from-hardware-to-kexts-2-llb-and-iboot/)
機器上電,由 Boot ROM 驗證并加載位于 Flash 中的 LLB(Low Level Bootloader)
LLB 設置安全策略、解密/認證并加載位于 preboot volume 中的 iBoot 文件
iBoot 根據 DTB 對 M1 固件和設備做早期的初始化,驗證并加載 KernelCache(KernelCache 是 MacOS 啟動鏡像,類似于 linux 的 Image.gz),更新并加載 DTB
進入 MacOS 內核啟動流程。
其中無論是 LLB 還是 iBoot 都是閉源的,這為我們完整的模擬 M1 啟動帶來了難處。為了能成功的在 QEMU / KVM 虛擬化場景下啟動 M1,我們對 MacOS 的啟動過程做了簡化,割舍掉 LLB 和 iBoot 階段,直接由 QEMU 來完成早期設備的初始化(如 CPU 狀態等)、KernelCache 文件的解析與加載、DTB 的更新與加載等。
在 QEMU 完成早期初始化和預加載后,虛擬機啟動時刻的 System Memory 地址空間布局狀態如下:
其中,各文件的含義如下:
KernelCache:包含了 XNU 內核和 kext 內核擴展,是 MacOS 的內核鏡像
Ramdisk:HFS 格式的 rootfs,可作為 MacOS 的啟動盤使用,非必需(當使用 virtio-blk 盤作為啟動盤時,可取消 Ramdisk 的使用)
DTB:M1 的設備樹,MacOS 內核需要通過 DTB 來獲取到 M1 的硬件信息,QEMU 模擬的 M1 設備信息(如中斷路由和設備地址信息等)要和 DTB 中維護的信息一致
xnu_arm64_boot_args:Macos 內核的啟動參數
隨后 QEMU 將 Primary VCPU 的 pc 寄存器設置為 KernelCache 中的 start 函數地址,開始VCPU 的運行。
啟動流程
MacOS 在 QEMU / KVM 虛擬化場景下整體的啟動流程如圖,大致分如下四個階段:
1. QEMU 階段
該階段在 QEMU 中實現,主要是為 MacOS 的運行做準備,具體包括 VCPU 線程的創建,各設備狀態的初始化,System Memory 的加載映射(如KernelCache、 Ramdisk、DTB),bootargs 的創建和 DTB 數據的更新等,隨后將 VCPU0 的 pc 寄存器指向 MacOS XNU 內核的_start 函數,VCPU0 開始執行。
2. 早期初始化階段
在 MacOS 啟動早期的匯編代碼階段,_start 函數設置了內核啟動早期的中斷/異常向量表和頁表等,并對內核參數進行解析,配置 MMU 和 vbar 等。
3. Kernel 階段
在 arm_init 和 kernel_bootstrap 中, 主要是第一個內核線程啟動前的準備工作,如進行 KernelCache 解析、DTB 轉化,console 設置,per cpu data 設置和 process 初始化、系統內存的初始化以及內核各個子系統的啟動。
kernel_bootstrap_thread/bsd 中,主要是啟動其他的系統維護線程構筑 MacOS 的運行時環境,如初始化 I/O Kit 框架,初始化 BSD 子系統。其間還會根據 DTB 的配置來設置 SMP,喚醒其他 VCPU;其中, I/O Kit 是MacOS XNU 內核為設備驅動程序提供的完整的運行時環境,用戶可以基于 I/O Kit 提供的面向對象能力來快速高效地編寫自己的驅動程序。
在 bsdinit 中,初始化 BSD 核心子系統, 掛載 rootfs 并啟動 /sbin/launched。
4. 用戶態服務和進程
/sbin/launched 進程的 pid 為1,為第一個用戶態進程,它根據預定的安排或者實際的需要加載位于 /System/Library/LaunchAgents 和 /System/Library/LaunchDeamons 下的其他應用程序或作業,包括守護程序如 atd、crond、inetd 等,以及代理程序如 GUI shell、 Terminal shell 等。
至此 MacOS M1 基本啟動已經完成。
虛擬化下完整的MacOS 軟件棧
QEMU/KVM 虛擬化場景下的 MacOS 整體架構圖如下,從下到上依次是M1物理機、支持 M1 各類型設備的 Asahilinux 內核、提供硬件加速功能的內核模塊 KVM、實現 M1 機型模擬的用戶態 QEMU,運行在 QEMU 上的 M1 Guest OS。
其中,M1 Guest OS 在 XNU 內核的基礎上會加載多個 Kext 以實現完整的 MacOS 功能;既支持 APFS 格式的根文件系統,也支持 HFS 格式的 Ramdisk 根文件系統;dyld 類似于 linux 上的 ld,負責加載和鏈接應用程序,dyld_shared_cache 提供了系統中各進程共享的基礎庫緩存,有效地降低了各進程通用庫的內存占用率。
后續規劃和展望
目前我們已經完成了 QEMU / KVM 虛擬化場景下 MacOS M1 的基本啟動,后續需要有更多的優化從功能完備性、性能以及兼容性的角度來展開。
5.1 更完善的MacOS 運行時環境
目前我們的方案在 QEMU 層實現了 M1 基本設備的模擬、在 KVM 層實現了硬件加速功能、在 MacOS 中實現了部分系統服務的啟動,然而整個方案與一臺真正的 MacOS 還有一定的距離,如默認啟動的數百個系統服務、友好的 GUI 界面、更完備的設備支持以及更好的用戶支持度等等,后續將持續優化。
5.2 兼容性
在整個虛擬化方案中為了實現 MacOS 的啟動,我們對 KernelCache 里的 XNU 內核和 Kext 部分進行了熱 Patch。后續需要實現合理的熱 patch 的工具來完成不同的 MacOS 版本熱 Patch 的高效定制。
5.3 性能問題
模擬 M1 機型時,像 PMGR 和 AIC 這樣的設備,我們目前是直接在 QEMU 里進行模擬的,這會使得虛擬機對 PMGR 和 AIC 的操作,都需要退回到 QEMU 中來進行具體行為的模擬,后續可考慮將這些組件集成到 KVM 中,減少用戶態和內核的切換,減少虛擬化開銷,提高 VM 的性能。
審核編輯:湯梓紅
-
Linux
+關注
關注
87文章
11304瀏覽量
209476 -
WINDOWS
+關注
關注
3文章
3545瀏覽量
88687 -
操作系統
+關注
關注
37文章
6822瀏覽量
123331 -
Mac
+關注
關注
0文章
1106瀏覽量
51474 -
虛擬機
+關注
關注
1文章
917瀏覽量
28187
原文標題:探索Mac mini M1的QEMU/KVM虛擬化實現
文章出處:【微信號:OSC開源社區,微信公眾號:OSC開源社區】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論