RFC-0210:虛擬化功能轉送

RFC-0210:虛擬功能轉送
狀態已接受
區域
  • 虛擬化技術
說明

定義虛擬化功能在平台和產品之間的轉送方式。

問題
變更
作者
審查人員
提交日期 (年/月)2022-11-07
審查日期 (年/月)2023-02-23

摘要

本文件說明如何將建立虛擬機器的功能轉送至產品工作階段。透過建立關聯,本文件也會說明 Fuchsia 平台中包含的虛擬化功能和 API 組合,以及產品提供的元件。

提振精神

目前所有虛擬化元件都是由 Fuchsia 核心平台提供,包括:

  • 用於模擬虛擬機器和虛擬裝置的元件
  • 提供特定訪客作業系統二進位檔的套件
  • 將訪客專屬功能與產品整合的元件

這有一些明顯的缺點。我們把所有訪客專用的元件放在平台中,導致產品無法以產品特定方式整合虛擬化。此外,許多訪客仍會保留狀態並保存使用者資料,而我們必須確保此使用者資料連同所有其他使用者資料,一併經過加密與保護。本文件說明產品皆適用的設計,不過大部分設計都會將工作站產品的要求做為激勵範例。

本提案的明確目標是讓虛擬化成為 Fuchsia 平台功能。這表示我們將為產品工作階段公開一組功能,允許產品利用虛擬化機制,而不必將虛擬化所需的所有低層級特殊權限功能轉送至工作階段本身。這部分根據 RFC-0092 - 工作階段RFC-0194 - 附加條款:工作階段中的指南進行。如要進一步瞭解相關資訊,請參閱安全性考量

相關人員

誰擔心是否接受這個 RFC?(本節為選填,但建議填寫)。

講師:

受 FEC 指派的使用者透過 RFC 程序阻斷這個 RFC。

審查者:

  • 阿布杜拉
  • Alerad
  • Dahastin
  • 傑森基
  • Aaronwood
  • Ypomortsev

顧問:

列出應審查 RFC,但不需要經過核准的人員。

社群媒體化:

本計畫的草稿已在虛擬化、元件架構和安全性領域的相關人員交流。

詞彙解釋

  • 虛擬機器管理工具 (vmm):負責模擬及驅動虛擬機器的元件。
  • 虛擬裝置:負責模擬單一裝置的元件 (例如區塊、網路等)。
  • 對比:半虛擬化的通訊端裝置,允許訪客與主機之間進行 0 個設定通訊端連線。通常做為訪客的控制層。

背景

Fuchsia 上的虛擬機器是由 vmm (虛擬機器管理員) 元件驅動。vmm 元件本身會管理多個實作虛擬裝置的子元件 (例如:virtio_block.cmvirtio_net.cm 等),不過本文件將將所有元件群組當做 vmm 來參照。vmm 會為虛擬機器提供基本資源 (記憶體、vcpus) 和虛擬裝置。vmm 元件屬於訪客通用,沒有任何訪客 OS 專屬邏輯。每個 vmm 元件執行個體都是由訪客 OS 專屬的 GuestManager 元件管理,例如 TerminaGuestManagerGuestManager。包含適用於訪客生命週期及任何訪客特定服務或功能的較高層級的邏輯。GuestManager 負責提供所有啟動資源 (核心、ramdisk、區塊裝置等) 和訪客設定,例如要提供的裝置、數量的虛擬 CPU 或要提供多少記憶體。

目前 Fuchsia 支援的每個訪客作業系統,目前都是使用靜態元件路徑建立模型。舉例來說,TerminaGuestManager 元件 (用於工作站上 Linux 終端機的訪客) 具有由核心資料分割提供的靜態子項 vmm 元件。有些路徑可讓工作階段中的 Linux 終端機元件連線至核心領域中的 TerminaGuestManager,以及可讓訪客在圖形殼層 (使用 virtio-wayland) 中建立視窗的路徑。

顯示目前能力轉送圖表

儲存空間

GuestManager 元件負責開啟 vmm 所需的所有檔案和裝置;vmm 本身只會接收處理檔案、封鎖裝置或其他有狀態功能的控制代碼。TerminaGuestManager 會在 CFv2 data 儲存空間能力提供的目錄中建立檔案,來初始化有狀態分區。系統會針對 TerminaGuestManager 套件本身所包含的圖片開啟 blobfs 檔案,藉此提供其他唯讀有狀態分區。

TerminaGuestManager 是核心領域的元件,因此將提供的 data 儲存空間能力也來自核心運作範圍。這表示在工作站等產品上,工作站的磁碟區會依使用者驗證因子而加密,即使其中可能包含機密的使用者資料,訪客資料儲存空間也不會位於這個磁碟區。此外,假如我們想像的是多使用者系統,如果在核心領域中只有單一元件,則所有帳戶之間都會共用單一 vmm 執行個體和資料,但並非完全啟用解決方案。

設計

由於 vmm 元件需要多項特殊權限,因此建議將元件保留在核心中。此外,由於 vmm 提供啟動虛擬機器的機制,因此 vmm 中並沒有產品專屬政策或行為。建議您將 GuestManager 元件從核心中擷取,因為這些套件是產品專屬的套件,可提供只與特定產品相關的邏輯和訪客作業系統。只要將這些元件移出 core,產品就能自由自訂虛擬化在產品中整合的方式,無須處理虛擬機器模擬的低階細節。

我們會導入新的核心資料分割,允許工作階段在 CFv2 集合中建立 vmm 元件執行個體,但所有狀態都必須由用戶端提供及管理。接著,我們會提供能力轉送功能來啟動虛擬機器,而不必使用每個 GuestManager 元件的靜態子項 vmm.cm。如此一來,我們就能將 GuestManager 元件移到工作階段中,而不必要求 vmm 本身移至工作階段。換句話說,我們保留 vmm,這是 core 中的產品與訪客特性,我們會將產品專屬的 GuestManagers 移至工作階段。

為此,我們將推出新的 VmmLauncher 元件來處理 vmm 元件的建立作業。這個元件會公開由 vmm 公開的 GuestLifecycle 通訊協定。差別在於 VmmLauncher 會為每個 GuestLifecycle 連線建立新的 vmm 執行個體,並將 FIDL 管道的伺服器端轉送至新元件,而非直接實作 GuestLifecycleGuestLifecycle 通訊協定會用於初始化 VM,然後啟動執行作業。

顯示建議能力轉送圖表

/// The guest control plane allows for creating, starting, and stopping the guest.
protocol GuestLifecycle {
    /// Create a VMM configured with the provided config. This instantiates all
    /// devices and loads the kernel without starting the VCPU or device dispatch
    /// loops.
    ///
    /// `Create` must not be called after a call to `Run` until `Stop` is called.
    /// Once a guest has been stopped with a call to `Stop`, then `Create` may be
    /// called again to re-initialize the guest.
    Create(resource struct {
        guest_config GuestConfig;
    }) -> (struct {}) error GuestError;

    /// Binds to the Guest protocol for an initialized guest.
    ///
    /// This operation must be called between `Create` and `Stop`, otherwise
    /// the provided channel will be immediately closed.
    Bind(resource struct {
        guest server_end:Guest;
    }) -> (struct {}) error GuestError;

    /// Start the VCPU and device dispatch loops. This will not return until the
    /// dispatch loops exit. On a clean shutdown (either guest or client initiated)
    /// this will return success.
    ///
    /// If forced to stop by the guest manager calling stop, a SHUTDOWN_FORCED
    /// error will be returned. This will also return any runtime error that forces
    /// the guest to stop.
    ///
    /// `Run` must only be called after a call to `Create`. Once a guest is stopped with
    /// a call to `Stop`, then `Run` may not be called again until `Create` is called
    /// to re-initialize the guest. Notably, we do not support calling `Stop`
    /// and then `Run` directly; the call to `Create` after `Stop` is a requirement.
    Run() -> (struct {}) error GuestError;

    /// Stop a running VMM. Returns once the dispatch loops have stopped. After
    /// Stop returns, `Create` and then `Run` can be called again.
    Stop() -> ();
};

type GuestError = strict enum {
    /// Catch all VMM error.
    INTERNAL_ERROR = 1;

    /// A device endpoint was requested via the guest client API, but the device isn't enabled.
    DEVICE_NOT_PRESENT = 2;

    /// The config failed VMM validation for reasons such as a missing required field.
    BAD_CONFIG = 3;

    /// The VMM failed to initialize the guest object, usually due to capability routing issues
    /// or memory layout problems.
    GUEST_INITIALIZATION_FAILURE = 4;

    /// The VMM failed to initialize a device.
    DEVICE_INITIALIZATION_FAILURE = 5;

    /// The VMM failed to start a device, usually because the device component returned a failure.
    DEVICE_START_FAILURE = 6;

    /// Two or more devices have attempted to register overlapping memory ranges.
    DEVICE_MEMORY_OVERLAP = 7;

    /// Failed to connect to a required service. Check the routing in the manifest.
    FAILED_SERVICE_CONNECT = 8;

    /// Failed to add a public service.
    DUPLICATE_PUBLIC_SERVICES = 9;

    /// General error when loading the guest kernel.
    KERNEL_LOAD_FAILURE = 10;

    /// Error when starting a VCPU.
    VCPU_START_FAILURE = 11;

    /// A VCPU encountered a fatal error while running.
    VCPU_RUNTIME_FAILURE = 12;

    /// The VMM was asked to run before it was created.
    NOT_CREATED = 13;

    /// A VMM is already running. The VMM must be stopped and a new VMM must be created before it
    /// can be run again.
    ALREADY_RUNNING = 14;

    /// A running VMM was forced to stop by the VMM controller.
    CONTROLLER_FORCED_HALT = 15;
};

對設定公開的用戶端參數組合會包含在 GuestConfig FIDL 資料表中。以便設定機器形狀 (vcpu 和記憶體) 和可用的虛擬裝置。

type GuestConfig = resource table {
    /// Type of kernel to load.
    1: kernel_type KernelType;
    /// File to load the kernel from.
    2: kernel client_end:fuchsia.io.File;
    /// File to load the initial RAM disk from.
    3: ramdisk client_end:fuchsia.io.File;
    /// File to load the dtb overlay for a Linux kernel from.
    4: dtb_overlay client_end:fuchsia.io.File;
    /// Kernel command-line to use.
    5: cmdline string:MAX;
    /// Additional kernel command-lines to append to the main command-line.
    6: cmdline_add vector<string:MAX>:MAX;
    /// The number of CPUs to provide to a guest.
    7: cpus uint8;
    /// Amount of guest memory required, in bytes. This value may be rounded up
    /// depending on the system configuration.
    8: guest_memory uint64;
    /// A list of block devices to give a guest. Cannot be changed from the
    /// command-line.
    9: block_devices vector<BlockSpec>:MAX_BLOCK_DEVICES;
    /// A list of specifications for network devices.
   10: net_devices vector<NetSpec>:MAX_NET_DEVICES;
    /// Optional virtio-wl device.
   11: wayland_device WaylandDevice;
    /// Optional virtio-magma device.
   12: magma_device MagmaDevice;
    /// Whether to add a default network device.
   13: default_net bool;
    /// Enable virtio-balloon.
   14: virtio_balloon bool;
    /// Enable virtio-console.
   15: virtio_console bool;
    /// Enable virtio-gpu.
   16: virtio_gpu bool;
    /// Enable virtio-rng.
   17: virtio_rng bool;
    /// Enable virtio-vsock.
   18: virtio_vsock bool;
    /// Enable virtio-sound.
   19: virtio_sound bool;
    /// Enable input streams (capture) for virtio-sound.
   20: virtio_sound_input bool;
    /// Host ports to listen for guest initiated vsock connections on. This can be
    /// used for simplicity if a Listener is known at config creation time, or if a
    /// Listener must be available at the moment of guest creation for timing
    /// reasons.
    /// To add a Listener after a guest starts, see HostVsockEndpoint::Listen.
   21: vsock_listeners vector<Listener>:MAX;
};

Guest 通訊協定可讓您從 VM 存取執行階段服務。

/// A `Guest` provides access to services of a guest instance.
protocol Guest {
    /// Get a guest console.
    ///
    /// The details regarding what output is produced and what input is accepted
    /// are determined by each guest, but will typically be a read/write socket
    /// with a shell.
    ///
    /// Returns ZX_ERR_UNAVAILABLE if the guest has no configured console.
    GetConsole() -> (resource struct {
        socket zx.handle:SOCKET;
    }) error zx.status;

    /// Get the socket for low-level guest debug logs.
    ///
    /// The details regarding what output is produced and what input is accepted
    /// are determined by each guest, but will typically be a read-only socket
    /// with the guest kernel's serial logs.
    GetSerial() -> (resource struct {
        socket zx.handle:SOCKET;
    }) error zx.status;

    /// Get the vsock endpoint for the guest.
    ///
    /// This endpoint can be used to register listeners for guest initiated
    /// connections, and to initiate connections from a client. If listeners need
    /// to be registered before the guest starts so that they are immediately
    /// available, set them via the `GuestConfig` instead of using this protocol.
    ///
    /// Returns error VSOCK_NOT_PRESENT if the guest was started without a vsock
    /// device.
    GetHostVsockEndpoint(resource struct {
        endpoint server_end:HostVsockEndpoint;
    }) -> (struct {}) error GuestError;

    /// Get the balloon controller endpoint for the guest.
    ///
    /// Returns error BALLOON_NOT_PRESENT if the guest was started without a
    /// balloon device.
    GetBalloonController(resource struct {
        controller server_end:BalloonController;
    }) -> (struct {}) error GuestError;
};

實作

我們會使用名為 vmm_launcher.cm 的核心元件實作 VmmLauncher。這個元件會管理用於啟動 vmm 元件的 CFv2 集合。接著,GuestManager 元件會更新,以便從其 "parent" (而非透過靜態子項) 存取 GuestLifecycle 能力。系統會在 CFv2 元件階層的 /core/virtualization 使用核心資料分割提供 vmm_launcher.cm。我們選擇將這個資料分割顯示為 virtualization 資料分割,因為任何支援虛擬化的產品都會使用這個資料分割。

然後,我們會向產品工作階段公開 GuestLifecycle 通訊協定,並更新工作站產品工作階段以加入 TerminaGuestManager

工具、診斷和開發人員人體工學

我們需要在核心產品上執行訪客作業系統的能力。目前的作法是使用 GN 引數手動新增至建構作業的其他核心資料分割。每個核心資料分割都為單一訪客提供支援。本提案並無任何變更運作方式要求,但會大幅簡化元件轉送,因為大部分的能力路徑都由 vmm 和相關聯的裝置負責流入訪客管理員資料分割。

於日後的工作,我們可以變更啟動 GuestManager 元件的方式,以達到開發目的。debianzircon 等簡易訪客不需要 GuestLifecycle 以外的任何功能,因此我們可以支援將這些元件啟動到自己專屬的集合,以減少支援虛擬化工作流程所需的自訂核心領域數量。

安全性考量

vmm 需要一些特殊權限才能正常運作 (例如 HypervisorResource、某些 /dev 目錄)。這種設計可避免將這些特殊權限功能洩漏給工作階段。

與這種設計的有一項能力差異,就是在提供代表單一 VM 執行個體的通訊協定 (例如 fuchsia.virtualization.TerminaGuestManager) 之前,而現在將提供使用 fuchsia.virtualization.GuestManager 通訊協定建立任意數量 VM 執行個體的能力。這項能力只能透過 vmm 元件公開,我們不允許工作階段在包含 vmm 執行個體的集合中啟動任意元件。

每當 GuestLifecycle 管道關閉時,工作階段建立的所有訪客都會停止,以確保在使用者工作階段停止後,沒有任何邀請對象繼續執行。

為避免在 vmm 執行個體之間外洩,可變動目錄功能系統會從與每個執行個體相關聯的訪客 Manager 元件動態轉送到 vmm 執行個體,而不是以靜態方式轉送共用目錄能力。這也可讓工作階段以一致的方式管理及保護有狀態的 VM 資料。

說明文件

我們會在 fuchsia.dev 的虛擬化說明文件中新增章節,詳細說明產品的虛擬化使用方式。其中包括產品工作階段可使用的 FIDL 通訊協定組合的詳細資料。

測試

在這項異動中,所有現有的整合測試都會轉換為使用 vmm_launcher 來展示正確的功能。我們也會探索各種可能的 vmm_launcher 測試方法,藉此確認 vmm 執行個體不會轉送和有狀態的共用功能。

考慮改用的替代方案

移除 VmmLauncher 元件的需求

本文件提議的 vmm_launcher.cm 沒有任何作用,只會建立集合來回應傳入連線要求。這項功能可由元件架構直接處理,讓我們能直接移除這個元件。舉例來說,為了支援這個元件架構,您可以藉此建立同質元件集合。與其在集合中建立動態子項,而是會在元件連線時自動啟動。

新增訂製元件並不會執行大量工作,但如果這是常見的模式,不妨考慮直接將此元件新增至元件架構。

新增明確的 VmmLauncher 通訊協定

我們可以為 VmmLauncher 建立新的具名通訊協定,而不是以隱含方式建立新的 vmm 元件來回應現有 GuestLifecycle 通訊協定的連線。如果我們發現需要擴大 API 途徑以納入比目前提議更多的功能,就很適合採用這項設計。

library fuchsia.virtualization;

@discoverable
protocol VmmLauncher {
    /// Launches a new, uninitialized virtual machine. The bound
    /// `GuestLifecycle` can be used to initialize the VM and start or stop it.
    ///
    /// The VM will be stopped and the component destroyed when the
    ///`GuestLifecycle` channel is closed.
    Launch(resource struct {
        lifecycle server_end:GuestLifecycle;
    });

    /// ... other functionality ...
};

由於我們不需任何其他功能,因此傾向只使用現有的通訊協定組合。