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。 KVM 的 EL2 組件雖然是 Linux 代碼庫的一部分,但它是一個負責多個 EL1 之間切換的小組件,完全由主機內核控制。管理程序組件是用 Linux 編譯的,但駐留在vmlinux映像的一個單獨的專用內存部分中。 pKVM 通過使用新功能擴展管理程序代碼來利用此設計,允許它對 Android 主機內核和用戶空間施加限制,並限制主機對來賓內存和管理程序的訪問。

開機程序

pKVM 啟動過程如圖 1 所示。第一步是引導加載程序在 EL2 進入支持 pKVM 的 Linux 內核。在早期引導期間,內核檢測到它在 EL2 上運行,將自己剝奪到 EL1 的特權,留下 pKVM。從這一點開始,Linux 內核繼續正常啟動,加載所有必要的設備驅動程序,直到到達用戶空間。這些步驟在 pKVM 的控制下發生。

引導過程信任引導加載程序僅在早期引導期間維護內核映像的完整性。當內核被剝奪特權時,它不再被管理程序視為信任,即使內核受到威脅,管理程序也會負責保護自己。

pKVM 引導程序

圖 1. pKVM 引導程序

將 Android 內核和虛擬機管理程序置於同一個二進制映像中,可以在它們之間建立非常緊密耦合的通信接口。這種緊密耦合保證了兩個組件的原子更新,這避免了保持它們之間接口穩定的需要,並提供了很大的靈活性,而不會影響長期可維護性。當兩個組件可以合作而不影響管理程序提供的安全保證時,緊密耦合還允許性能優化。

此外,在 Android 生態系統中採用 GKI 會自動允許 pKVM 管理程序以與內核相同的二進製文件部署到 Android 設備。

CPU內存訪問保護

Arm架構規定了一個內存管理單元(MMU),分為兩個獨立的階段,這兩個階段都可以用來實現地址轉換和對內存不同部分的訪問控制。階段 1 MMU 由 EL1 控制並允許進行第一級地址轉換。 Linux 使用第 1 階段 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 階段的地址轉換來為來賓實現複雜的 IPA/PA 映射,這為來賓創造了儘管物理碎片但內存連續的錯覺。但是,主機的第 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) 規範密切監視和控制。

管理程序負責跟踪系統中所有內存頁面的所有權,以及它們是否被共享或借給其他實體。大多數狀態跟踪是使用附加到主機和訪客的第 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) 作為源/目標的陷阱。

訪客界面

來賓可以使用超級調用和對受困區域的內存訪問的組合與受保護的來賓進行通信。 Hypercalls 根據SMCCC 標準公開,範圍保留給 KVM 的供應商分配。以下超級調用對 pKVM 來賓特別重要。

通用超級調用

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

pKVM 超級調用

  • 與主機共享內存。主機最初無法訪問所有來賓內存,但主機訪問對於共享內存通信和依賴共享緩衝區的半虛擬化設備是必需的。用於與主機共享和取消共享頁面的超調用允許來賓準確地決定哪些內存部分可供 Android 的其餘部分訪問,而無需握手。
  • 內存訪問陷阱到主機。傳統上,如果 KVM 來賓訪問不對應於有效內存區域的地址,則 vCPU 線程退出到主機並且訪問通常用於 MMIO 並由用戶空間中的 VMM 模擬。為了促進這種處理,pKVM 需要將有關故障指令的詳細信息(例如其地址、寄存器參數和可能的內容)播發回主機,如果沒有預料到陷阱,這可能會無意中暴露來自受保護客戶機的敏感數據。 pKVM 通過將這些故障視為致命故障來解決此問題,除非來賓先前已發出 hypercall 以將故障 IPA 範圍識別為允許訪問的範圍返回到主機。這個解決方案被稱為MMIO 守衛

虛擬 I/O 設備 (virtio)

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

Virtio 設備通常由 VMM 在主機的用戶空間中實現,VMM 攔截從來賓到 virtio 設備的 MMIO 接口的捕獲內存訪問並模擬預期行為。 MMIO 訪問相對昂貴,因為每次訪問設備都需要往返於 VMM 並返回,因此設備和來賓之間的大部分實際數據傳輸都是使用內存中的一組 virtqueue 進行的。 virtio 的一個關鍵假設是主機可以任意訪問客戶機內存。這種假設在 virtqueue 的設計中很明顯,它可能包含指向客戶機中緩衝區的指針,設備仿真旨在直接訪問這些緩衝區。

儘管前面描述的內存共享超級調用可用於從客戶機到主機共享 virtio 數據緩衝區,但這種共享必須以頁面粒度執行,並且如果緩衝區大小小於頁面大小,最終可能會暴露比所需更多的數據.相反,來賓被配置為從固定的共享內存窗口分配 virtqueue 及其相應的數據緩衝區,並根據需要將數據複製(反彈)到窗口或從窗口復制(反彈)。

虛擬設備

圖 4. Virtio 設備

與 TrustZone 互動

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

來自主機的 PSCI 調用被轉發到經過最少修改的 EL3 固件。具體來說,重寫 CPU 聯機或從掛起恢復的入口點,以便在返回到 EL1 的主機之前將階段 2 頁表安裝在 EL2。在引導期間,此保護由 pKVM 強制執行。

該架構依賴於支持 PSCI 的 SoC,最好通過使用最新版本的TF-A作為其 EL3 固件。

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

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

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

虛擬機監視器

crosvm 是一個虛擬機監視器 (VMM),它通過 Linux 的 KVM 接口運行虛擬機。 crosvm 的獨特之處在於它使用 Rust 編程語言和圍繞虛擬設備的沙箱來保護主機內核,從而專注於安全性。

文件描述符和 ioctl

KVM 使用構成 KVM API 的 ioctl 將/dev/kvm字符設備暴露給用戶空間。 ioctl 屬於以下類別:

  • 系統 ioctls 查詢和設置影響整個 KVM 子系統的全局屬性,並創建 pVM。
  • VM ioctls 查詢和設置創建虛擬 CPU (vCPU) 和設備的屬性,並影響整個 pVM,例如包括內存佈局和虛擬 CPU (vCPU) 和設備的數量。
  • vCPU ioctls 查詢和設置控制單個虛擬 CPU 操作的屬性。
  • 設備 ioctls 查詢和設置控制單個虛擬設備操作的屬性。

每個 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
虛擬機固件0x7FE00000
設備內存0x10000 - 0x40000000

使用mmap分配物理內存,並將內存捐贈給 VM 以使用KVM_SET_USER_MEMORY_REGION ioctl 填充其內存區域,稱為memslots 。因此,所有來賓 pVM 內存都歸於管理它的 crosvm 實例,如果主機開始用完可用內存,則可能導致進程被終止(終止 VM)。當 VM 停止時,內存會被管理程序自動擦除並返回給主機內核。

在常規 KVM 下,VMM 保留對所有來賓內存的訪問權。使用 pKVM,來賓內存在捐贈給來賓時會從主機物理地址空間中取消映射。唯一的例外是來賓顯式共享內存,例如 virtio 設備。

來賓地址空間中的 MMIO 區域未映射。訪客對這些區域的訪問被捕獲並導致 VM FD 上的 I/O 事件。該機制用於實現虛擬設備。在保護模式下,來賓必須確認其地址空間的一個區域已用於使用 hypercall 的 MMIO,以降低意外信息洩漏的風險。

調度

每個虛擬 CPU 都由一個 POSIX 線程表示,並由主機 Linux 調度程序進行調度。線程調用 vCPU FD 上的KVM_RUN ioctl,導致管理程序切換到來賓 vCPU 上下文。主機調度程序將在來賓上下文中花費的時間計為相應 vCPU 線程使用的時間。當有必須由 VMM 處理的事件時, KVM_RUN返回,例如 I/O、中斷結束或 vCPU 暫停。 VMM 處理該事件並再次調用KVM_RUN

KVM_RUN期間,線程保持可被主機調度程序搶占,但 EL2 管理程序代碼的執行除外,它是不可搶占的。來賓 pVM 本身沒有控制此行為的機制。

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

虛擬設備

crosvm 支持多種設備,包括:

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

虛擬機固件

pVM 固件 (pvmfw) 是 pVM 執行的第一個代碼,類似於物理設備的引導 ROM。 pvmfw 的主要目標是引導安全啟動並導出 pVM 的唯一秘密。 pvmfw 不限於與任何特定操作系統一起使用,例如Microdroid ,只要該操作系統受 crosvm 支持並且已正確簽名。

pvmfw 二進製文件存儲在同名的閃存分區中,並使用OTA進行更新。

設備開機

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

  1. Android 引導加載程序 (ABL) 將 pvmfw 從其分區加載到內存中並驗證映像。
  2. ABL 從信任根獲取其設備標識符組合引擎 (DICE) 機密(複合設備標識符 (CDI) 和引導證書鏈 (BCC))。
  3. ABL 執行 pvmfw 的秘密 (CDI) 的測量和 DICE 推導,並將它們附加到 pvmfw 二進製文件。
  4. ABL 在 DT 中添加了一個linux,pkvm-guest-firmware-memory reserved memory region 節點,描述了 pvmfw 二進製文件的位置和大小以及它在上一步中導出的秘密。
  5. ABL 將控制權交給 Linux,然後 Linux 初始化 pKVM。
  6. pKVM 從主機的第 2 階段頁表中取消映射 pvmfw 內存區域,並在整個設備正常運行期間保護它免受主機(和來賓)的影響。

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

虛擬機啟動

創建 pVM 時,crosvm(或另一個 VMM)必須創建一個足夠大的內存槽,以便管理程序用 pvmfw 映像填充。 VMM 也被限制在它可以設置初始值的寄存器列表中(x0-x14 用於主 vCPU,無用於輔助 vCPU)。其餘的寄存器被保留並且是 hypervisor-pvmfw ABI 的一部分。

當 pVM 運行時,管理程序首先將主 vCPU 的控制權交給 pvmfw。固件期望 crosvm 加載一個 AVB 簽名的內核,它可以是引導加載程序或任何其他圖像,以及一個未簽名的 FDT 到內存中已知的偏移量。 pvmfw 驗證 AVB 簽名,如果成功,則從接收到的 FDT 生成可信設備樹,從內存中擦除其秘密,並分支到有效負載的入口點。如果驗證步驟之一失敗,固件會發出 PSCI SYSTEM_RESET hypercall。

在啟動之間,有關 pVM 實例的信息存儲在一個分區(virtio-blk 設備)中並使用 pvmfw 的秘密加密,以確保在重新啟動後,秘密被提供給正確的實例。