AVF架構

Android 提供了實現 Android 虛擬化框架所需的所有元件的參考實作。目前此實作僅限於 ARM64。本頁解釋了框架架構。

背景

Arm 架構最多允許四個異常級別,異常級別 0 (EL0) 的特權最低,異常級別 3 (EL3) 的特權最高。 Android 程式碼庫的最大部分(所有使用者空間元件)在 EL0 上運行。其餘通常稱為“Android”的部分是 Linux 內核,運行在 EL1 上。

EL2 層允許引入虛擬機管理程序,該程序能夠將記憶體和設備隔離到 EL1/EL0 上的各個 pVM 中,並具有強大的機密性和完整性保證。

管理程式

受保護的基於核心的虛擬機器 (pKVM)構建於Linux KVM 管理程式之上,該管理程式已進行了擴展,能夠限制對在創建時標記為「受保護」的來賓虛擬機中運行的有效負載的訪問。

KVM/arm64 支援不同的執行模式,取決於某些 CPU 功能的可用性,即虛擬化主機擴充 (VHE)(ARMv8.1 及更高版本)。在其中一種模式(通常稱為非 VHE 模式)中,虛擬機器管理程式程式碼在引導期間從核心映像中分離並安裝在 EL2 上,而核心本身在 EL1 上運行。儘管是 Linux 程式碼庫的一部分,KVM 的 EL2 元件只是一個負責多個 EL1 之間切換的小元件,並且完全由主機核心控制。虛擬機器管理程式元件是用 Linux 編譯的,但駐留在vmlinux映像的單獨專用記憶體部分。 pKVM 透過使用新功能擴展虛擬機器管理程式程式碼來利用此設計,使其能夠對 Android 主機核心和使用者空間施加限制,並限制主機對來賓記憶體和虛擬機器管理程式的存取。

pKVM 供應商模組

pKVM 供應商模組是特定於硬體的模組,包含特定於裝置的功能,例如輸入輸出記憶體管理單元 (IOMMU) 驅動程式。這些模組可讓您將需要異常等級 2 (EL2) 存取的安全功能移植到 pKVM。

若要了解如何實作和載入 pKVM 供應商模組,請參閱實作 pKVM 供應商模組

開機程式

下圖描述了 pKVM 啟動過程:

pKVM 啟動過程

圖 1. pKVM 啟動過程

  1. 引導程式在 EL2 處進入通用核心。
  2. 通用核心偵測到它正在 EL2 上運行,並剝奪自己的 EL1 權限,而 pKVM 及其模組繼續在 EL2 上運行。此外,此時也會載入 pKVM 供應商模組。
  3. 通用核心繼續正常啟動,載入所有必需的裝置驅動程序,直到到達用戶空間。此時,pKVM 已到位並處理第 2 階段頁表。

引導過程信任引導程式僅在早期引導期間維護核心映像的完整性。當核心被剝奪特權時,虛擬機器管理程式不再認為它是可信的,即使核心受到損害,虛擬機器管理程式也會負責保護自身。

將 Android 核心和管理程式放在同一個二進位映像中可以在它們之間實現非常緊密耦合的通訊介面。這種緊密耦合保證了兩個組件的原子更新,從而無需保持它們之間的介面穩定,並提供了很大的靈活性,而不會影響長期可維護性。當兩個元件可以協作而不影響虛擬機器管理程式提供的安全保證時,緊密耦合還可以實現效能最佳化。

此外,Android 生態系統中 GKI 的採用會自動允許 pKVM 管理程式以與核心相同的二進位檔案部署到 Android 裝置。

CPU記憶體存取保護

Arm架構指定將記憶體管理單元(MMU)分為兩個獨立的階段,這兩個階段都可用於實現對記憶體不同部分的位址轉換和存取控制。第一級 MMU 由 EL1 控制,並允許第一級位址轉換。 Linux 使用第一階段 MMU 來管理提供給每個使用者空間進程及其自己的虛擬位址空間的虛擬位址空間。

第 2 級 MMU 由 EL2 控制,並可對第 1 級 MMU 的輸出位址套用第二次位址轉換,產生實體位址 (PA)。管理程式可以使用第 2 階段轉換來控制和轉換來自所有來賓 VM 的記憶體存取。如圖2所示,當兩個階段的轉換都啟用時,第1階段的輸出位址稱為中間實體位址(IPA)。註:虛擬位址(VA)先轉換為IPA,然後再轉換為PA。

CPU記憶體存取保護

圖 2. CPU 記憶體存取保護

從歷史上看,KVM 在運行來賓時啟用第 2 階段轉換,在運行主機 Linux 核心時禁用第 2 階段。該架構允許來自主機第 1 級 MMU 的記憶體存取通過第 2 級 MMU,從而允許從主機到客戶記憶體頁面進行不受限制的存取。另一方面,pKVM 即使在主機上下文中也能實現第 2 階段保護,並讓虛擬機器管理程式(而非主機)負責保護來賓記憶體頁面。

KVM 在第 2 階段充分利用位址轉換來為 guest 虛擬機實現複雜的 IPA/PA 映射,這為 guest 虛擬機創建了連續記憶體的錯覺,儘管存在物理碎片。然而,主機的第 2 階段 MMU 的使用僅限於存取控制。主機階段 2 是身分映射的,確保主機 IPA 空間中的連續內存在 PA 空間中是連續的。這種架構允許在頁表中使用大型映射,從而減少轉換後備緩衝區 (TLB) 的壓力。由於身分映射可以透過 PA 進行索引,因此主機階段 2 也用於直接在頁表中追蹤頁所有權。

直接記憶體存取 (DMA) 保護

如前所述,在 CPU 頁表中從 Linux 主機取消映射來賓頁面對於保護來賓記憶體是必要但不充分的步驟。 pKVM 還需要防止主機核心控制下支援 DMA 的裝置進行記憶體訪問,以及惡意主機發動 DMA 攻擊的可能性。為了防止此類裝置存取來賓內存,pKVM 需要為系統中的每個支援 DMA 的裝置配備輸入輸出記憶體管理單元 (IOMMU) 硬件,如圖 3 所示。

DMA記憶體存取保護

圖 3. DMA 記憶體存取保護

至少,IOMMU 硬體提供了以頁粒度授予和撤銷設備對實體記憶體的讀取/寫入存取權限的方法。但是,此 IOMMU 硬體限制了 pVM 中設備的使用,因為它們採用身分映射第 2 階段。

為了確保虛擬機器之間的隔離,代表不同實體產生的記憶體事務必須能夠被 IOMMU 區分,以便可以使用適當的頁表集進行轉換。

此外,減少 EL2 處 SoC 特定程式碼的數量是減少 pKVM 整體可信任運算基礎 (TCB) 的關鍵策略,這與在虛擬機器管理程式中包含 IOMMU 驅動程式背道而馳。為了緩解此問題,EL1 上的主機負責輔助 IOMMU 管理任務,例如電源管理、初始化以及(如果適用)中斷處理。

然而,讓主機控制設備狀態對 IOMMU 硬體的編程介面提出了額外的要求,以確保權限檢查不能透過其他方式繞過,例如在設備重置後。

Arm 系統記憶體管理單元 (SMMU) 架構是 Arm 裝置的標準且受良好支援的 IOMMU,它使隔離和直接分配成為可能。該架構是推薦的參考解決方案。

記憶體所有權

在啟動時,所有非虛擬機器管理程式記憶體都被假定為主機所有,並由虛擬機器管理程式進行追蹤。當 pVM 產生時,主機會捐贈記憶體頁面以允許其啟動,並且虛擬機器管理程式會將這些頁面的所有權從主機轉移到 pVM。因此,虛擬機器管理程式在主機的第 2 階段頁表中設定存取控制限制,以防止其再次存取這些頁面,從而為來賓提供機密性。

主機和訪客之間的通訊是透過受控的記憶體共享來實現的。允許來賓使用超級呼叫與主機共享他們的一些頁面,這指示虛擬機器管理程式重新對應主機第 2 階段頁表中的這些頁面。同樣,主機與 TrustZone 的通訊是透過記憶體共享和/或借出操作實現的,所有這些操作都由 pKVM 使用Arm 韌體框架 (FF-A) 規範進行密切監視和控制。

由於 pVM 的記憶體需求可能會隨著時間的推移而變化,因此提供了超級調用,允許將屬於調用者的指定頁面的所有權交還給主機。實際上,此超級呼叫與 virtio 氣球協定一起使用,以允許 VMM 以受控方式從 pVM 請求返回內存,並讓 pVM 通知 VMM 已放棄的頁面。

虛擬機器管理程式負責追蹤系統中所有記憶體頁面的所有權以及它們是否被共享或借給其他實體。大多數狀態追蹤是使用附加到主機和客戶機的第 2 階段頁表的元資料、使用頁表條目 (PTE) 中的保留位元來完成的,顧名思義,這些保留位元是為軟體使用而保留的。

主機必須確保它不會嘗試存取已被虛擬機器管理程式設定為不可存取的頁面。非法主機存取會導致虛擬機器管理程式將同步異常注入主機,這可能導致負責的使用者空間任務接收 SEGV 訊號,或導致主機核心崩潰。為了防止意外訪問,捐贈給來賓的頁面不符合主機內核交換或合併的條件。

中斷處理和定時器

中斷是來賓與裝置互動以及 CPU 之間通訊方式的重要組成部分,其中處理器間中斷 (IPI) 是主要通訊機制。 KVM 模型將所有虛擬中斷管理委託給 EL1 中的主機,為此目的,主機充當虛擬機器管理程式的不受信任部分。

pKVM 提供基於現有 KVM 程式碼的完整通用中斷控制器版本 3 (GICv3) 模擬。計時器和 IPI 作為不受信任的類比程式碼的一部分進行處理。

GICv3 支持

EL1 和 EL2 之間的介面必須確保完整的中斷狀態對 EL1 主機可見,包括與中斷相關的管理程式暫存器的副本。這種可見性通常是使用共享記憶體區域來實現的,每個虛擬 CPU (vCPU) 一個。

系統暫存器運行時支援程式碼可以簡化為僅支援軟體產生中斷暫存器(SGIR)和停用中斷暫存器(DIR)暫存器擷取。該架構要求這些暫存器始終陷阱到 EL2,而其他陷阱迄今僅用於減少勘誤。其他一切都由硬體處理。

在 MMIO 方面,一切都在 EL1 上進行模擬,重複使用 KVM 中的所有目前基礎架構。最後,等待中斷 (WFI)始終中繼到 EL1,因為這是 KVM 使用的基本調度原語之一。

定時器支援

虛擬計時器的比較器值必須暴露給每個捕獲 WFI 上的 EL1,以便 EL1 可以在 vCPU 被阻止時注入計時器中斷。實體定時器是完全模擬的,所有陷阱都轉發到 EL1。

MMIO處理

若要與虛擬機器監視器 (VMM) 通訊並執行 GIC 仿真,必須將 MMIO 陷阱中繼回 EL1 中的主機以進行進一步分類。 pKVM 需要以下內容:

  • IPA 和存取大小
  • 寫入時的數據
  • 捕獲時 CPU 的位元組順序

此外,以通用暫存器 (GPR) 作為來源/目標的陷阱使用抽象傳輸偽暫存器進行中繼。

訪客介面

來賓可以使用超級呼叫和對受困區域的記憶體存取的組合來與受保護的來賓進行通訊。超級呼叫根據SMCCC 標準公開,並為 KVM 供應商分配保留範圍。以下超級呼叫對於 pKVM 來賓特別重要。

通用超級調用

  • PSCI 為來賓提供了一種標準機制來控制其 vCPU 的生命週期,包括上線、下線和系統關閉。
  • TRNG 為來賓提供了一種標準機制,可以從 pKVM 請求熵,pKVM 將呼叫中繼到 EL3。在無法信任主機虛擬化硬體隨機數產生器 (RNG) 的情況下,此機制特別有用。

pKVM 超級調用

  • 與主機共享記憶體。主機最初無法存取所有來賓內存,但主機存取對於共享內存通訊和依賴共享緩衝區的半虛擬化設備是必需的。與主機共享和取消共享頁面的超級呼叫允許來賓準確決定哪些記憶體部分可供 Android 的其餘部分訪問,而無需握手。
  • 將記憶體讓渡給主機。所有來賓記憶體通常都屬於來賓,直到被銷毀為止。對於記憶體需求隨時間變化的長期虛擬機器來說,此狀態可能不夠。 relinquish超級呼叫允許來賓明確地將頁面所有權轉移回主機,而不需要來賓終止。
  • 主機的記憶體存取捕獲。傳統上,如果 KVM 來賓存取不對應於有效記憶體區域的位址,則 vCPU 執行緒將退出到主機,並且該存取通常用於 MMIO 並由使用者空間中的 VMM 進行模擬。為了促進這種處理,pKVM 需要將有關故障指令的詳細資訊(例如其位址、暫存器參數及其可能的內容)通告回主機,如果未預料到陷阱,這可能會無意中暴露來自受保護客戶的敏感數據。 pKVM 透過將這些錯誤視為致命錯誤來解決此問題,除非來賓先前已發出超級呼叫來將錯誤的 IPA 範圍識別為允許存取被捕獲回主機的範圍。此解決方案稱為MMIO 防護

虛擬 I/O 設備 (virtio)

Virtio 是一種流行的、可移植的、成熟的標準,用於實現半虛擬化設備並與之互動。大多數暴露給受保護訪客的設備都是使用 virtio 實現的。 Virtio 還支援用於受保護的來賓與 Android 其餘部分之間通訊的 vsock 實作。

Virtio 設備通常由 VMM 在主機的用戶空間中實現,VMM 攔截從來賓到 virtio 設備的 MMIO 介面的捕獲記憶體存取並模擬預期行為。 MMIO 存取相對昂貴,因為對裝置的每次存取都需要往返於 VMM,因此裝置和 guest 虛擬機之間的大部分實際資料傳輸都是使用記憶體中的一組 virtqueues 進行的。 virtio 的一個關鍵假設是主機可以任意存取客戶記憶體。這種假設在 virtqueue 的設計中很明顯,virtqueue 可能包含指向裝置模擬要直接存取的來賓緩衝區的指標。

儘管前面描述的記憶體共享超級呼叫可用於從客戶機到主機共享virtio 資料緩衝區,但這種共享必須以頁面粒度執行,並且如果緩衝區大小小於頁面大小,最終可能會暴露比所需更多的數據。相反,客戶配置為從共享記憶體的固定視窗分配 virtqueues 及其相應的資料緩衝區,並根據需要將資料複製(彈回)到視窗或從視窗複製(反彈)。

虛擬設備

圖 4. Virtio 設備

與 TrustZone 互動

儘管來賓無法直接與 TrustZone 交互,但主機仍然必須能夠向安全世界發出 SMC 呼叫。這些呼叫可以指定主機無法存取的實體尋址記憶體緩衝區。由於安全軟體通常不知道緩衝區的可存取性,因此惡意主機可以使用該緩衝區執行混淆代理攻擊(類似於 DMA 攻擊)。為了防止此類攻擊,pKVM 會擷取對 EL2 的所有主機 SMC 調用,並充當主機和 EL3 安全監視器之間的代理。

來自主機的 PSCI 呼叫只需最少的修改即可轉送到 EL3 韌體。具體來說,CPU上線或從掛起恢復的入口點被重寫,使得第二階段頁表在返回EL1處的主機之前安裝在EL2處。在引導期間,這種保護由 pKVM 強制執行。

該架構依賴支援 PSCI 的 SoC,最好使用最新版本的TF-A作為其 EL3 韌體。

Arm 韌體框架 (FF-A) 標準化了正常世界和安全世界之間的交互,特別是在存在安全虛擬機器管理程序的情況下。該規範的主要部分定義了一種與安全世界共享記憶體的機制,使用通用訊息格式和底層頁面的明確定義的權限模型。 pKVM 代理 FF-A 訊息,以確保主機不會嘗試與沒有足夠權限的安全端共用記憶體。

此架構依賴於強制實施記憶體存取模型的安全世界軟體,以確保僅當記憶體由安全世界獨佔或已使用FF 與其明確共享時,受信任的應用程式和在安全世界中運行的任何其他軟體才能訪問內存-A。在具有 S-EL2 的系統上,強制執行記憶體存取模型應由安全分區管理器核心 (SPMC) 來完成,例如Hafnium ,它為安全世界維護第 2 階段頁表。在沒有 S-EL2 的系統上,TEE 可以透過其第一階段頁表強制執行記憶體存取模型。

如果對 EL2 的 SMC 呼叫不是 PSCI 呼叫或 FF-A 定義的訊息,則未處理的 SMC 將轉發到 EL3。假設(必須可信任的)安全韌體可以安全地處理未處理的 SMC,因為韌體了解維護 pVM 隔離所需的預防措施。

虛擬機器監控

crosvm 是一個虛擬機器監視器 (VMM),它透過 Linux 的 KVM 介面運行虛擬機器。 crosvm 的獨特之處在於它注重安全性,使用 Rust 程式語言和虛擬裝置周圍的沙箱來保護主機核心。有關 crosvm 的更多信息,請參閱此處的官方文件。

文件描述符和 ioctl

KVM 使用組成 KVM API 的 ioctl 將/dev/kvm字元設備公開給使用者空間。 ioctl 屬於以下類別:

  • 系統ioctls查詢和設定會影響整個KVM子系統的全域屬性,並建立pVM。
  • VM ioctl 查詢和設定創建虛擬 CPU (vCPU) 和設備的屬性,並影響整個 pVM,例如包括記憶體佈局以及虛擬 CPU (vCPU) 和設備的數量。
  • vCPU ioctl 查詢和設定控制單一虛擬 CPU 操作的屬性。
  • 設備 ioctl 查詢和設定控制單一虛擬設備操作的屬性。

每個 crosvm 程序只運行一個虛擬機器實例。此程序使用KVM_CREATE_VM系統 ioctl 建立可用於發出 pVM ioctl 的 VM 檔案描述符。 VM FD 上的KVM_CREATE_VCPUKVM_CREATE_DEVICE ioctl 建立 vCPU/設備並傳回指向新資源的檔案描述符。 vCPU 或設備 FD 上的 ioctl 可用來控制使用 VM FD 上的 ioctl 建立的設備。對於 vCPU,這包括運行來賓程式碼的重要任務。

在內部,crosvm 使用邊緣觸發的epoll介面向核心註冊 VM 的檔案描述符。然後,每當任何檔案描述符中有新事件待處理時,核心都會通知 crosvm。

pKVM 新增了一項新功能KVM_CAP_ARM_PROTECTED_VM ,可用於取得 pVM 環境的資訊並為 VM 設定保護模式。如果傳遞了--protected-vm標誌,crosvm 在 pVM 建立過程中會使用它來查詢並為 pVM 韌體保留適當的記憶體量,然後啟用保護模式。

記憶體分配

VMM 的主要職責之一是分配VM 的記憶體並管理其記憶體佈局。 crosvm產生下表中粗略描述的固定記憶體佈局。

正常模式下的 FDT PHYS_MEMORY_END - 0x200000
可用空間...
記憶體磁碟ALIGN_UP(KERNEL_END, 0x1000000)
核心0x80080000
引導程式0x80200000
BIOS 模式下的 FDT 0x80000000
實體記憶體基礎0x80000000
pVM韌體0x7FE00000
裝置記憶體0x10000 - 0x40000000

實體記憶體透過mmap進行分配,並將記憶體捐贈給 VM 以使用KVM_SET_USER_MEMORY_REGION ioctl 填入其記憶體區域(稱為memslots ) 。因此,所有來賓 pVM 記憶體都歸屬於管理它的 crosvm 實例,如果主機開始耗盡可用內存,可能會導致進程被終止(終止 VM)。當虛擬機器停止時,虛擬機器管理程式會自動擦除記憶體並返回主機核心。

在常規 KVM 下,VMM 保留對所有來賓記憶體的存取權。使用 pKVM,當將客機記憶體捐贈給客機時,客機記憶體不會從主機實體位址空間對應。唯一的例外是由來賓明確共享的內存,例如 virtio 設備。

來賓位址空間中的 MMIO 區域未對應。來賓對這些區域的存取會被捕獲並導致 VM FD 上發生 I/O 事件。此機制用於實現虛擬設備。在保護模式下,客戶機必須確認其位址空間的某個區域用於使用超級呼叫的 MMIO,以降低意外資訊洩漏的風險。

調度

每個虛擬 CPU 由一個 POSIX 執行緒表示,並由主機 Linux 調度程序進行調度。該執行緒在 vCPU FD 上呼叫KVM_RUN ioctl,導致虛擬機器管理程式切換到來賓 vCPU 上下文。主機排程器將在來賓上下文中花費的時間計算為對應 vCPU 執行緒所使用的時間。當存在必須由 VMM 處理的事件(例如 I/O、中斷結束或 vCPU 停止)時, KVM_RUN會傳回。 VMM 處理該事件並再次呼叫KVM_RUN

KVM_RUN期間,執行緒仍可被主機調度程式搶佔,但執行 EL2 管理程式程式碼除外,程式碼不可搶佔。 guest pVM 本身沒有控制此行為的機制。

由於所有 vCPU 執行緒都像任何其他使用者空間任務一樣進行調度,因此它們受所有標準 QoS 機制的約束。具體來說,每個 vCPU 執行緒都可以關聯到實體 CPU、放置在 cpuset 中、使用利用率箝位進行提升或限制、更改其優先權/調度策略等等。

虛擬設備

crosvm 支援多種設備,包括:

  • virtio-blk 用於複合磁碟映像,唯讀或讀寫
  • vhost-vsock 用於與主機通訊
  • virtio-pci 作為 virtio 傳輸
  • pl030 實時時鐘(RTC)
  • 16550a UART 用於串行通信

pVM韌體

pVM 韌體 (pvmfw) 是 pVM 執行的第一個程式碼,類似於實體設備的引導 ROM。 pvmfw 的主要目標是引導安全啟動並匯出 pVM 的獨特秘密。 pvmfw 不限於與任何特定作業系統一起使用,例如Microdroid ,只要該作業系統受 crosvm 支援並且已正確簽署即可。

pvmfw 二進位檔案儲存在同名的快閃記憶體分割區中,並使用OTA進行更新。

設備啟動

以下步驟序列被加入到啟用 pKVM 的設備的引導過程中:

  1. Android Bootloader (ABL) 將 pvmfw 從其分割區載入到記憶體中並驗證映像。
  2. ABL 從信任根取得其裝置識別碼組合引擎 (DICE) 機密(複合裝置識別碼 (CDI) 和啟動憑證鏈 (BCC))。
  3. ABL 對 pvmfw 的秘密 (CDI) 進行測量和 DICE 推導,並將其附加到 pvmfw 二進位檔案中。
  4. ABL 將linux,pkvm-guest-firmware-memory保留記憶體區域節點新增至 DT 中,描述 pvmfw 二進位檔案的位置和大小以及它在上一步中匯出的機密。
  5. ABL 將控制權交給 Linux,Linux 初始化 pKVM。
  6. pKVM 從主機的第 2 階段頁表中取消 pvmfw 記憶體區域的映射,並在整個裝置正常運作時間內保護它免受主機(和來賓)的影響。

裝置啟動後,Microdroid 將按照Microdroid文檔的啟動順序部分中的步驟啟動。

pVM啟動

建立 pVM 時,crosvm(或另一個 VMM)必須建立足夠大的 memslot,以便虛擬機器管理程式可以用 pvmfw 映像填滿。 VMM 也受到其可以設定初始值的暫存器清單的限制(對於主 vCPU 為 x0-x14,對於輔助 vCPU 則沒有)。其餘暫存器被保留,並且是 hypervisor-pvmfw ABI 的一部分。

當 pVM 運作時,虛擬機器管理程式首先將主 vCPU 的控制權移交給 pvmfw。韌體期望 crosvm 載入一個 AVB 簽署的核心(可以是引導程式或任何其他映像),以及一個未簽署的 FDT 到已知偏移量的記憶體中。 pvmfw 驗證 AVB 簽名,如果成功,則從接收的 FDT 產生受信任的裝置樹,從記憶體中清除其秘密,然後分支到有效負載的入口點。如果驗證步驟之一失敗,韌體將發出 PSCI SYSTEM_RESET超級呼叫。

在啟動之間,有關 pVM 執行個體的資訊儲存在分割區(virtio-blk 裝置)中,並使用 pvmfw 的金鑰進行加密,以確保在重新啟動後,將金鑰配置給正確的執行個體。