虛擬化總覽

Fuchsia 的虛擬化堆疊提供執行訪客作業系統的能力。Zircon 會實作 Type-2 管理程序,會顯示 syscall,讓使用者空間元件建立及設定 CPU 和記憶體虛擬化。虛擬機器管理工具 (VMM) 元件建構在管理程序之上,可定義記憶體對應、設定陷阱及模擬各種裝置和周邊裝置,藉此組合虛擬機器。接著,訪客管理員元件位於 VMM 上方,提供訪客專屬的二進位檔和設定。Fuchsia 目前支援 3 種訪客套件;一種未經修改的 Debian 訪客、Zircon 訪客以及採用 Termina 的 Linux 訪客。

搭載 Intel 的 x64 裝置,以及可啟動進入 EL2 的大多數 arm64 (ARMv8.0 及以上版本) 裝置都支援 Fuchsia 虛擬化功能。 值得注意的是,目前不支援 AMD SVM。

顯示虛擬化元件的圖表

管理程序

管理程序會公開 syscall,以便建立核心物件來支援虛擬化。建立新管理程序物件的 Syscall 會要求呼叫者存取管理程序資源,這樣元件建立虛擬機器的功能可由「產品」控管。也就是說,必須授予 Fuchsia 元件建立訪客作業系統的能力,讓產品能夠限制哪些元件能使用這些功能。

CPU 虛擬化

zx_vcpu_create Syscall 會建立新的虛擬 CPU (VCPU) 物件,並將該 vCPU 繫結至呼叫執行緒。這樣一來,VMM 就能使用 zx_vcpu_{read|write}_state Syscall 讀取並寫入該 vCPU 的架構註冊。zx_vcpu_enter 系統呼叫是封鎖的系統呼叫,用於將情境切換切換到訪客,而 zx_vcpu_enter 的回傳則代表內容切換回主機的情境。換句話說,如果 zx_vcpu_enter 中目前沒有執行緒,則在訪客內容內並不會執行任何執行緒。所有的 zx_vcpu_read_statezx_vcpu_write_statezx_vcpu_enter必須從名為 zx_vcpu_create 的執行緒呼叫。

zx_vcpu_kick 系統呼叫已存在,可讓主機明確要求 vCPU 退出,並導致任何對 zx_vcpu_enter 的呼叫傳回。

記憶體與 IO 虛擬化

zx_guest_create Syscall 會建立新的訪客核心物件。重要的是,這個系統呼叫會傳回虛擬記憶體位址區域 (vmar) 控點,代表訪客的實體位址空間。接著,VMM 可將虛擬記憶體物件 (vmo) 對應至此 vmar,藉此提供訪客的「實體記憶體」。由於這個 vmar 代表訪客實體位址空間,因此在這個 vmar 中的偏移量會對應至訪客實體地址。舉例來說,如果 VMM 希望在訪客實體位址範圍 [0x00000000 - 0x40000000) 內公開 1 GiB 的記憶體,VMM 會建立 1GiB vmo,並在偏移量為 0 時將其對應至訪客實體 vmar

這個訪客實體 vmar 是透過第二級位址轉譯 (SLAT) 實作,可讓管理程序定義主機實體地址 (HPA) 轉譯至訪客實體地址 (GPA) 的作業。接著,訪客作業系統即可安裝自己的頁面資料表,處理從訪客虛擬地址 (GVA) 到訪客實體位址的轉譯。

顯示 2 層位址轉譯的圖表

zx_guest_set_trap 系統呼叫可讓 VMM 安裝用於裝置模擬的陷阱。訪客可以使用記憶體對應 I/O (MMIO) 與硬體介面,過程中的訪客讀取及寫入裝置時,會採用與記憶體存取相同的指示。以 MMIO 來說,裝置 GPA 的 SLAT 中沒有對應項目,導致訪客納入管理程序。

x86 提供另一種定 IO 裝置的方式,稱為通訊埠對應 I/O (PIO)。透過 PIO,訪客會使用替代操作說明存取裝置,但這些操作說明仍會導致訪客陷入管理程序進行處理。

計算陷阱的處理方式會因建立的陷阱類型而有所不同:

ZX_GUEST_TRAP_MEM:設定 MMIO 的陷阱。在與這個陷阱相關聯的訪客實體位址空間中,對位址範圍的讀取或寫入作業,會導致 zx_vcpu_enter 系統呼叫傳回 VMM,然後負責模擬存取權、更新 VCPU 註冊狀態,並再次呼叫 zx_vcpu_resume 以返回訪客。

ZX_GUEST_TRAP_IO - 與 ZX_GUEST_TRAP_MEM 類似,但差別在於系統會在處理器的 IO 空間安裝陷阱,而非在訪客實體位址空間中設定陷阱。如果架構不支援 PIO,則會失敗。

ZX_GUEST_TRAP_BELL:設定 MMIO 的非同步陷阱。當訪客寫入與這個陷阱相關聯的訪客實體位址範圍時,而非導致 zx_vcpu_enter 返回 VMM,管理程序改為將訊息排入與這個陷阱相關聯的通訊埠,並立即繼續執行 VCPU 執行作業,而不會傳回使用者空間。這可用來模擬專為此模式設計的裝置。例如,Virtio 裝置可讓訪客驅動程式庫藉由寫入訪客實體記憶體中的特殊頁面,通知虛擬裝置有工作完成。

不支援在 IO 空間中設定非同步陷阱。系統不支援從設有 ZX_GUEST_TRAP_BELL 組合的區域讀取內容。

陷阱處理

VCPU 執行緒通常會花費大部分的時間在 zx_vcpu_enter 上遭到封鎖,也就是說,執行緒會在訪客環境內執行。這個 Sys 呼叫到 VMM 的回傳表示發生錯誤,或者通常是 VMM 必須介入措施才能模擬某些行為。

為了示範,我們來看看幾個例子,說明 VMM 如何處理陷阱。

MMIO 同步陷阱範例

例如,考慮使用 ARM PL011 序列埠模擬。請注意,雖然這是實際運作的 ARM 專用裝置,但就像在 ARM 和 x86 上一樣,陷阱都會以類似方式處理。

首先,VMM[0x808300000 - 0x808301000) 的訪客實體位址範圍中註冊同步 MMIO 陷阱,告知管理程序發生任何存取該區域時,都必須導致 zx_vcpu_enter 將控制流程回傳至 VMM

接下來,VMM 會在一或多個 vCPU 上呼叫 zx_vcpu_enter,以便環境切換到訪客。有時,PL011 驅動程式庫會嘗試在 PL011 裝置上註冊的序列埠控制註冊器 UARTCR 讀取資料。此登記位置位於 0x30 偏移位置,因此在本範例中對應訪客實體地址 0x808300030

由於系統針對訪客實體地址 0x808300030 註冊陷阱,因此這個讀取會導致訪客陷入管理程序進行處理。管理程序可以觀察這個存取權具有相關聯的 ZX_GUEST_TRAP_MEM,並透過從 zx_vcpu_enter 傳回,以及 zx_port_packet_t 內的陷阱詳細資料,將控制流程傳遞至 VMM。然後,VMM 可以使用存取權的訪客實體地址,與對應的虛擬裝置邏輯建立關聯。在此情況下,裝置會將註冊值保留在成員變數中。

// `relative_addr` is relative to the base address of the trapped region.
zx_status_t Pl011::Read(uint64_t relative_addr, IoValue* value) {
  switch (static_cast<Pl011Register>(relative_addr)) {
    case Pl011Register::CR: {
      std::lock_guard<std::mutex> lock(mutex_);
      value->u16 = control_;
      return ZX_OK;
    }
    // Handle other registers...
  }
}

這樣做會傳回 16 位元值,但我們仍需向訪客公開這個結果。由於訪客執行了 MMIO,訪客會預期結果會位於載入指示中指定的任何暫存器。方法是使用 zx_vcpu_read_statezx_vcpu_write_state 系統呼叫來更新目標暫存器的值,以及模擬 MMIO 的結果。

顯示同步 MMIO 陷阱的圖表

Bell Trap 範例

接下來我們要示範鈴鐺陷阱的運作過程。在這種情況下,我們會在主要 VMM 以外的元件中實作 Virtio Device。在初始化期間,VMM 會要求 Virtio Device 自行註冊鈴鐺陷阱,讓陷阱傳送到 Virtio Device 元件,而非 VMM。在 Virtio Device 完成任何陷阱後,VMM 就會以 zx_vcpu_enter 開始執行 vCPU,並將控制流程轉移到訪客中。

有時訪客驅動程式庫會將 MMIO 寫入作業,並寫入已受 Virtio Device 幹擾的訪客實體地址。此時,訪客會將訪客背景資料納入管理程序,導致系統使用 zx_port_packet_t 將通知傳送給 Virtio Device。值得注意的是,在這種情況下,zx_vcpu_enter 在處理這個陷阱期間一律不會回傳,管理程序也可以快速切回訪客環境,因此能將 VCPU 花費的封鎖時間降到最低。

Virtio Device 收到 zx_port_packet_t 後,就會採取裝置專用的步驟來處理該陷阱。這通常涉及直接讀取及寫入訪客實體記憶體,但可以在不封鎖 vCPU 執行作業的情況下執行此操作。裝置完成要求後,裝置可以使用 zx_vcpu_interrupt 傳送中斷訊息,通知訪客中的驅動程式庫。

由於大部分通訊都是使用共用記憶體完成,且不使用同步陷阱,因此 Virtio 裝置比高度仰賴同步陷阱的裝置更有效率。

顯示非同步 MMIO 陷阱的圖表

陷阱處理的架構差異

雖然 Trap 的處理方法大致相同,但根據基礎硬體的支援,回應陷阱需要有一些重要差異。最值得注意的是,在 ARM 中,由硬體產生的基礎資料取消作業會提供有關存取的解碼資訊,以便轉送至使用者空間 (例如:存取權大小、讀取/寫入、目標註冊等)。不過在 Intel 上不會出現這種情況,因此 VMM 需要進行一些指示解碼來推論相同資訊。

幹擾虛擬化

Fuchsia 實作某些平台呼叫了「分割 Irqchip」,並模擬在核心中執行的 LAPIC/GICC,以及使用者空間中發生的 I/OAPIC/GICD 模擬。使用者空間 I/OAPIC 和 GICD 使用 zx_vcpu_中斷 syscall 時,將會中斷至目標 CPU 的轉送作業。

虛擬機器管理員 (VMM)

虛擬機器 Montior (VMM) 是使用者空間元件,其可使用管理程序系統呼叫來建構及管理虛擬機器,並執行裝置模擬。VMM 會使用給其提供的 GuestConfig FIDL 結構來建構虛擬機器,其中包含了應將哪些裝置提供給虛擬機器的設定,以及訪客核心、ramdisk 和封鎖裝置的資源。

整體來說,VMM 會使用管理程序系統呼叫來建立訪客和 vCPU 核心物件,藉此組合虛擬機器。應用程式會建立 VMO 並對應至訪客實體記憶體 vmar,藉此分配訪客 RAM。這個 API 會使用 zx_guest_set_trap 註冊 MMIO 和通訊埠 ID 處理常式,以進行虛擬硬體模擬。VMM 會模擬 PCI 匯流排,並可將裝置連接到該公車。它會將訪客核心載入記憶體,並透過訪客核心所需的各種資源設定啟動資料,例如裝置樹狀結構 blob 或 ACPI 資料表。

記憶體

VMM 會分配 vmo 來當做訪客實體記憶體,並將這個 vmo 對應至訪客實體記憶體 vmar (由 zx_guest_create 建立)。在處理訪客實體記憶體 vmar 中的記憶體時,我們稱這些位址為「訪客實體地址」(GPA)。VMM 也會將相同的 vmo 對應至其程序位址空間,以便直接存取這個記憶體。處理 VMM vmar 中的記憶體時,我們稱這些位址為「主機虛擬位址」(HVA)。VMM 能將 GPA 轉譯為 HVA,因為它知道訪客記憶體對應,以及訪客記憶體所對應 vmar 中的位址。

Virtio 裝置與元件

許多裝置都會透過 PCI 使用虛擬 I/O (Virtio) 向訪客公開。Virtio 規格定義了一組經過特別設計,可在虛擬化環境中有效率地執行訪客實體記憶體的裝置,並盡量減少同步 IO 陷阱。為提高裝置安全性及隔離裝置,我們會使用自己的 Zircon 程序執行每部 Virtio 裝置,並只轉送該元件需要的功能。舉例來說,Virtio Block 裝置只會為支援虛擬磁碟的特定檔案或裝置提供控制代碼,而 Virtio 控制台則只能存取序列串流的 zx::socket。

VMM 和裝置之間的通訊會使用 fuchsia.virtualization.hardware FIDL 程式庫完成。每部裝置都有一個連結到 VMM 的一小段程式碼 (稱為「控制器」),該程式碼可做為用戶端與這些 FIDL 服務,並連結至在啟動期間實作實作裝置的元件。每個裝置執行個體都有一個程序,因此如果虛擬機器有 3 個 Virtio Block 裝置,就會有 3 個控制器執行個體和 3 個 Virtio 區塊元件,屬於 3 個 Zircon 程序。

Virtio 裝置是按照訪客實體記憶體中的共用資料結構概念運作。訪客驅動程式庫會在啟動時分配及初始化這些結構,並在訪客實體記憶體中為 VMM 提供這些結構的指標。當驅動程式庫想要通知裝置已對這些結構發布新工作時,會在訪客實體記憶體中寫入特定裝置專用的「通知」頁面,且裝置可以根據這個「通知」頁面中的寫入偏移來推斷特定事件。每個裝置元件都會為此區域註冊 ZX_GUEST_TRAP_BELL,以便管理程序將這些事件直接轉送至目標元件,無需透過 VMM 跳出。這樣一來,裝置元件就可以藉由讀取及寫入這些結構,直接讀取及寫入這些結構。

啟動中

VMM 不提供任何訪客 BIOS 或韌體,而是直接將訪客資源載入記憶體,並設定啟動 vCPU,直接跳到核心進入點。這項作業的細節會因要載入的核心核心而異。

Linux 訪客

對於 x64 Linux 訪客,VMM 會根據 Linux 啟動通訊協定,將可開機的核心映像檔 (例如:bzImage) 載入訪客實體記憶體,並利用其他核心資源 (ramdisk、核心指令列) 更新 Real-Mode 核心標頭和 Zero PageVMM 也會產生並載入一組 ACPI 資料表,描述提供給訪客的模擬硬體。

Arm64 Linux 訪客的行為類似,但我們遵循 arm64 啟動通訊協定,並提供裝置樹狀結構 blob (DTB),而非 ACPI 資料表。

Zircon 訪客

VMM 也支援根據 Zircon 啟動需求啟動 Zircon 訪客。這裡有幾個 Zircon 靴子的詳細資訊。

邀請對像管理員

訪客管理員元件的角色是封裝訪客二進位檔 (核心、ramdisk、磁碟映像檔) 和設定 (要啟用的裝置、訪客核心設定選項),並在啟動時提供給 VMM

樹狀結構內有 3 位訪客管理員,其中兩個較為簡單,也更進階。簡單的訪客管理員沒有任何訪客專屬程式碼,只是會傳遞至 VMM 的設定和二進位檔。然後,這些訪客會用於虛擬主控台或虛擬影格緩衝區。

簡易訪客管理員:ZirconGuestManager DebianGuestManager

更進階的訪客管理員是 TerminaGuestManager,提供透過 Virtio Vsock 執行的 gRPC 服務提供更多功能。TerminaGuestManager 擁有可連線至這些服務的額外功能,可提供更多功能,例如在訪客中執行指令、掛接檔案系統、啟動應用程式。

如要進一步瞭解如何在 Fuchsia 上啟動及使用虛擬化功能,請參閱「開始使用 Fuchsia 虛擬化功能」。