RFC-0210:虚拟化功能路由

RFC-0210:虚拟化功能路由
状态已接受
领域
  • 虚拟化
说明

定义虚拟化容量如何在平台和产品之间路由。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2022-11-07
审核日期(年-月-日)2023-02-23

摘要

本文档介绍我们将如何路由创建虚拟机的功能, 进入产品会话通过关联,此文档还会描述该集合 Fuchsia 平台中包含的虚拟化功能和 API 以及产品提供的组件

设计初衷

目前,所有虚拟化组件都由 Fuchsia 核心提供 包括:

  • 用于模拟虚拟机和虚拟设备的组件
  • 提供特定客户机操作系统二进制文件的软件包
  • 将访客专用功能与产品集成的组件

但这样做也存在一些显著的缺点。通过放置所有客户机专用组件, 我们使产品无法集成到 虚拟化。此外,许多房客还会留住 我们需要确保这些用户数据 和所有其他用户数据一起得到保护。本文档介绍了一种 与产品无关,但大部分设计都会考虑 工作站产品作为激励示例。

此方案的一个明确目标是, Fuchsia 平台功能。这意味着,我们将在本课程中 产品会话,允许产品利用 而无需路由所有低级别特权 虚拟化所需的功能。这里是 部分,具体取决于 RFC-0092 - SessionsRFC-0194 - 附录:会话。 有关详情,请参阅安全性 注意事项

利益相关方

谁对是否接受此 RFC 有影响?(此部分为可选内容, encouraged.)

教员

由 FEC 指定通过 RFC 流程监管此 RFC 的人员。

审核者

  • Abdulla
  • 阿拉德
  • Dahastin
  • 伊桑基
  • 艾伦伍德
  • Ypomortsev

已咨询

列出应审查 RFC 但无需批准的人员。

社交化

该计划的草稿在虚拟化、 “组件框架”和“安全性”

术语库

  • 虚拟机管理器 (vmm):负责模拟 和驱动虚拟机
  • 虚拟设备:负责模拟单个设备的组件(例如: 屏蔽、广告联盟等)。
  • vsock:一种允许使用 0 配置套接字的半虚拟化套接字设备 建立物理连接。通常用作 来宾。

背景

Fuchsia 上的虚拟机由 vmm(虚拟机管理器)驱动 组件。vmm 组件本身管理着若干子组件, 实现虚拟设备(例如 virtio_block.cmvirtio_net.cm 等),不过 在本文档中,所有组件均简称为 vmmvmm 将为虚拟机提供基本资源(内存、 vCPU)和虚拟设备。vmm 组件与客机无关,因此 具有任何客机操作系统专用逻辑。每个 vmm 组件实例都由 例如,特定于客机操作系统的 GuestManager 组件 TerminaGuestManagerGuestManager。包含 客机生命周期以及任何特定于客机的服务或功能。通过 GuestManager 负责提供所有启动资源(内核、 ramdisk、块设备等)以及客户机配置(如 提供多少虚拟 CPU 或提供多少内存。

Fuchsia 上支持的所有来宾操作系统目前完全根据模型 使用静态组件路由例如,TerminaGuestManager 组件 (为工作站上的 Linux 终端提供支持的访客)有一个静态子级 由核心分片提供的 vmm 组件。有一些路由允许 Linux 会话中的终端组件,用于连接到TerminaGuestManager 核心领域以及允许客户机在图形界面创建窗口的路由。 shell(使用 virtio-wayland)。

显示当前功能路由的示意图

存储

GuestManager 组件负责打开所有文件,以及 vmm所需的设备;vmm 本身仅接收 文件、块设备或其他有状态功能TerminaGuestManager 将通过在目录中创建一个文件来初始化有状态分区 由 CFv2 data 存储功能提供。另一个只读有状态 通过打开 blobfs 文件提供分区, TerminaGuestManager 软件包本身。

由于 TerminaGuestManager 是核心领域的一个组件,因此 data 为其提供的存储空间容量也来自核心 Realm.这意味着在工作站等产品上 基于用户身份验证因素加密单独的账号卷, 访客数据存储将不位于此卷上,尽管它可能 包含敏感用户数据。此外,假设有一个多用户系统, 在核心领域具有单个组件意味着只有一个 vmm 实例 和数据将在所有账号之间共享,这不是一个可靠的解决方案。

设计

最好将 vmm 组件保留在核心中,因为它需要多个、 特权功能此外,由于 vmm 提供了一种机制来 启动虚拟机时,还没有专门针对特定产品的政策或行为 在 vmm 中。需要从核心中提取 GuestManager 组件 因为它们是特定于产品的软件包,包含相关的逻辑和 操作系统。将这些 我们可以让产品自由自定义core 虚拟化以产品特定的方式集成在一起, 虚拟机模拟的底层细节。

我们将引入一个新的核心分片,以允许会话创建 vmm 组件实例,但必须提供任何状态,并且 由客户端管理之后,我们将提供启动一个 机器使用功能路由,而不是使用 vmm.cm(作为 每个 GuestManager 组件。这样,我们就可以将 GuestManager 组件添加到会话中,而无需移动 VMware 本身 会话换言之,我们保留 vmm,即商品和客机 并将特定于产品的 GuestManagers 移至 core 中, 会话。

为此,我们将引入一个新的 VmmLauncher 组件来处理创建 vmm 组件。此组件将公开相同的 GuestLifecycle 协议 由 vmm 公开的区别在于 GuestLifecycle直接,VmmLauncher将为vmm新实例 每个 GuestLifecycle 连接,并转发 FIDL 通道的服务器端 添加到新组件中。GuestLifecycle 协议用于初始化虚拟机 然后开始执行。

显示建议的功能路由的示意图

/// 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 协议提供从虚拟机访问运行时服务的权限。

/// 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;
};

实现

我们将实现 VmmLauncher 并在核心中添加一个名为 vmm_launcher.cm。该组件将管理一个 CFv2 集合 启动 vmm 组件。然后,系统会更新 GuestManager 组件 从其 "parent"(而非GuestLifecycle 通过静态子项传递。系统将使用位于以下位置的核心分片提供 vmm_launcher.cm: CFv2 组件层次结构中的 /core/virtualization。我们选择公开 指定为 virtualization 分片,因为任何产品都会使用此分片 支持虚拟化

然后,我们将在产品会话中公开 GuestLifecycle 协议, 更新工作站产品会话以包含 TerminaGuestManager

工具、诊断和开发者工效学设计

我们需要保持在核心服务器上运行客机操作系统的能力 产品。现在,使用手动添加的其他核心分片来实现这一点 使用 GN 参数添加到 build 中。每个核心分片为单个核心分片提供支持 来宾。此提案中的任何内容都不需要更改运作方式, 大大简化了组件路由 负责传入客机管理器的大部分功能路由 处理。

在未来的工作中,我们可以更改针对以下产品启动 GuestManager 组件的方式: 开发应用。简单的邀请对象(如 debianzircon)不需要任何设置 GuestLifecycle以外的功能,这样我们就能够支持发布这些 一个单独的组件集合中, 需要核心域分片来支持虚拟化工作流。

安全注意事项

vmm 需要一些特权功能才能正常运行(例如: HypervisorResource、一些 /dev 目录)。这种设计可避免 将这些特权功能提供给会话

此设计的一个功能不同之处在于,我们在提供 代表单个虚拟机实例(例如: fuchsia.virtualization.TerminaGuestManager),现在我们将提供一项功能, 使用 fuchsia.virtualization.GuestManager 协议。此功能公开 只能通过 vmm 组件启动会话,我们不允许启动会话 包含 vmm 实例的集合中的任意组件。

通过会话创建的所有邀请对象都会在他们的GuestLifecycle时停止 此频道已关闭,以确保用户会话结束后没有任何访客继续互动 已停止。

为了避免 vmm 实例之间的状态泄露,可变目录功能 都将从相应的 vmm实例 与每个实例相关联的 GuestManager 组件,而不是使用 静态路由的共享目录功能。这还允许会话 以一致的方式管理和保护有状态虚拟机数据

文档

有关产品如何使用虚拟化的详细信息将记录在新的 虚拟化文档中关于 fuchsia.dev 的部分。这将包括 产品会话可以使用的 FIDL 协议集以及 任何最佳做法

测试

在进行这项更改的过程中,所有现有的集成测试都将转换为 请使用 vmm_launcher 展示正确的功能。我们还 探索对 vmm_launcher 进行模糊测试的可能方法,以验证 vmm 不会路由共享的有状态功能。

考虑的替代方案

不再需要 VmmLauncher 组件

本文档中建议的 vmm_launcher.cm 不会执行任何操作 它只是将组件创建到集合中,以响应对 绑定请求这个问题可以通过 该组件框架直接允许我们移除该组件。对于 例如,为了支持这一点,组件框架可以提供一种 均相同的组件集合。您不必创建动态子级 这些集合,组件会在连接后自动启动 目标。

添加定制组件来完成这项工作并不算太多 是一种常见的模式,那么合理的做法是考虑将此元素添加到 框架

添加显式 VmmLauncher 协议

而不是隐式创建新的 vmm 组件来响应连接 与现有的 GuestLifecycle 协议相比,我们可以创建一个新的已命名协议。 用于 VmmLauncher。如果我们发现 我们需要扩展 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 ...
};

由于我们不需要任何其他功能,因此更希望 改用现有的协议集