虚拟化概览

Fuchsia 的虚拟化堆栈支持在客户机上运行 系统。Zircon 会实现一个 Type-2 Hypervisor, 公开系统调用,使用户空间组件能够创建和配置 CPU 以及 内存虚拟化。虚拟机管理器 (VMM) 组件基于 通过定义内存映射来组建虚拟机 设置 trap,以及模拟各种设备和外围设备。访客管理员 这些组件位于 VMM 之上,提供客户机专用的二进制文件,以及 配置。Fuchsia 目前支持 3 种访客套餐;未经修改的 客户机、Zircon 客户机和基于 Termina 的 Linux 客户机。

具有 VMX 且基于 Intel 的 x64 设备支持 Fuchsia 虚拟化 支持启动到 EL2 的大多数 arm64(ARMv8.0 及更高版本)设备。 值得注意的是,目前不支持 AMD SVM。

显示虚拟化组件的示意图

Hypervisor

Hypervisor 公开系统调用,允许创建内核对象, 虚拟化。创建新 Hypervisor 对象的系统调用要求 调用方可以访问 Hypervisor 资源,这样组件就能够 可以由产品控制的虚拟机。在 也就是说,必须授权 Fuchsia 组件创建 客户机操作系统,因此产品可以限制 如何利用这些功能

CPU 虚拟化

zx_vcpu_create 系统调用会创建新的虚拟 CPU (VCPU) 对象并绑定 分配给调用线程然后,VMM 可以使用 用于读取和写入架构的 zx_vcpu_{read|write}_state 系统调用 该 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 系统调用会创建新的客户机内核对象。重要的一点是 该系统调用会返回一个虚拟内存地址区域 (vmar) 表示来宾实际地址空间的句柄。然后,VMM 为客户机提供“物理内存”,也就是将虚拟内存 对象 (vmo) 复制到此 vmar。由于此 vmar 表示 访客实体地址空间,到此 vmar 的偏移量将对应于 访客实际地址。例如,如果 VMM 希望公开 1GiB 的 位于客户机物理地址范围 [0x00000000 - 0x40000000),即 VMM 的内存 将创建一个 1GiB 的 vmo,并将其映射到偏移量为 0 的 Guest-Physical vmar

此 Guest-Physical vmar 是使用第二级地址实现的 转换 (SLAT),可让 Hypervisor 定义 将主机实际地址 (HPA) 翻译为访客实际地址 (GPA)。然后,客机操作系统便可以安装自己的页面表格 用于处理从访客虚拟地址 (GVA) 到访客物理地址的转换 地址。

显示两级地址转换的示意图

zx_guest_set_trap 系统调用允许 VMM 安装 用于设备模拟。访客可以使用 内存映射 I/O (MMIO),涉及客户机读取和 使用处理内存时相同的指令来写入设备 访问权限。对于 MMIO,在 设备的 GPA,导致客户机陷入 Hypervisor。

x86 提供了一种称为 Port-Mapped(端口映射)的 IO 设备的寻址方式 I/O (PIO)。对于 PIO,嘉宾使用备用说明 访问设备,但这些说明仍会让访客陷入困境 由 Hypervisor 进行处理。

陷阱的处理方式详情取决于之前使用的陷阱类型。 创建时间:

ZX_GUEST_TRAP_MEM - 为 MMIO 设置陷阱。对 与此陷阱关联的访客物理地址空间中的地址范围将 使 zx_vcpu_enter 系统调用返回到 VMM,然后 负责模拟访问,更新 vCPU 寄存器状态,然后 再次调用 zx_vcpu_resume 以返回给访客。

ZX_GUEST_TRAP_IO - 类似于 ZX_GUEST_TRAP_MEM,不同之处在于没有设置 该 trap 将被安装在 IO 中, 处理器空间如果架构不支持 PIO,该操作将失败。

ZX_GUEST_TRAP_BELL - 为 MMIO 设置异步陷阱。当访客对 与此 trap 关联的客机物理地址范围,而不是导致 zx_vcpu_enter 返回到 VMM,则 Hypervisor 会改为将 并立即恢复 vCPU 无需返回用户空间。这可用于模拟设备 可采用这种模式例如,Virtio 设备允许 客户机驱动程序有待完成的工作,以通知虚拟设备 写入到客户机物理内存中的特殊页面。

不支持在 IO 空间中设置异步陷阱。从具有 ZX_GUEST_TRAP_BELL 集不受支持。

陷阱处理

通常,vCPU 线程的大部分时间都会在 zx_vcpu_enter 处于阻塞状态, 这意味着它在客机上下文中执行。从此系统调用返回到 VMM 表示发生了错误,或者更常见的是 VMM 需要干预才能模拟某些行为。

为便于演示,我们通过几个具体示例来 说明如何捕获 trap 由 VMM 处理。

MMIO 同步陷阱示例

以 ARM PL011 串行端口模拟为例。请注意,虽然 在实践中是特定于 ARM 的设备,因此 trap 处理也会以类似方式发生 在 ARM 和 x86 上。

首先,VMM 注册一个同步 MMIO 陷阱 针对 [0x808300000 - 0x808301000) 这一“访客-实际地址”范围, 告知 Hypervisor 对此区域的任何访问都必须导致 zx_vcpu_enter 将控制流返回到 VMM

接下来,VMM 将在一个或多个 vCPU 上调用 zx_vcpu_enter 以进行上下文切换 放入访客中在某些情况下,PL011 驱动程序会尝试从 PL011 设备中的串行端口控制寄存器 UARTCR 寄存器。这个 寄存器位于偏移量 0x30,因此对应于 Guest-Physical 在此示例中为 0x808300030

由于针对访客实际地址 0x808300030 注册了陷阱,因此读取 导致客户机陷入 Hypervisor 进行处理。Hypervisor 您会发现此访问权限具有关联的 ZX_GUEST_TRAP_MEM,并且会传递 (通过从 zx_vcpu_enter 返回并提供以下详细信息)到 VMM 的控制流: zx_port_packet_t中包含的陷阱。然后,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 注册铃铛 trap 本身,以便将 trap 传送到 Virtio Device。 组件,而不是 VMM。在Virtio Device完成所有设置后, 陷阱,VMM 开始执行具有 zx_vcpu_enter 的 vCPU,且控制流 。

在某个时间点,访客驱动程序将对访客物理地址发出 MMIO 写入操作 已经被Virtio Device困住了。此时,访客将陷入 迁移到 Hypervisor,这会导致系统发出通知 使用 zx_port_packet_t 传递给 Virtio Device。值得注意的是, 在处理此 trap 期间,zx_vcpu_enter 绝不会返回。 Hypervisor 能够快速切换回客户机环境, 处于阻塞状态的时间

Virtio Device 收到 zx_port_packet_t 后, 处理该 trap 的设备专属步骤。这通常涉及 来写入到客机物理内存,但它能在不阻塞 vCPU 执行。设备完成请求后,就可以通知 通过使用 zx_vcpu_interrupt 发送中断,让驾驶员在访客中进行通话。

因为这类交流大多是通过共享记忆完成的, 使用同步陷阱,Virtio 设备的效率远高于设备 非常依赖同步陷阱

显示异步 MMIO 陷阱的图表

陷阱处理中的架构差异

虽然陷阱处理方法大致相同,但有一些重要的 为应对陷阱而需要采取哪些措施,具体取决于 底层硬件支持最值得注意的是,在 ARM 上,底层数据中止 提供一些解码后的信息, 我们可以转发到用户空间的访问权限(例如:访问大小、读取或写入、目标 注册等)。在 Intel 上,不会发生这种情况,因此 VMM 需要 进行一些指令解码以推断出相同的内容 信息。

中断虚拟化

Fuchsia 实现了一些平台所说的“拆分 irqchip”,模拟 LAPIC/GICC 在内核中完成,I/OAPIC/GICD 模拟在 用户空间。用户空间 I/OAPIC 和 GICD 将中断转发到目标 CPU 使用 zx_vcpu_中断 系统调用。

虚拟机管理器 (VMM)

虚拟机蒙蒂尔 (VMM) 是负责 使用 Hypervisor 系统调用来构建和管理虚拟机, 设备模拟。VMM 使用 GuestConfig FIDL 结构,其中包含 关于应向虚拟机提供哪些设备的 以及用于客户机内核、ramdisk 和块设备的资源。

概括来讲,VMM 会使用 Hypervisor 组建虚拟机。 用于创建客户机和 vCPU 内核对象的系统调用。它会按照 创建一个 VMO 并将其映射到客户机物理内存 vmar。它使用 zx_guest_set_trap,用于为虚拟硬件注册 MMIO 和 port-io 处理程序 。VMM 可模拟 PCI 总线,并可将设备连接到该总线。它 将客户机内核加载到内存中,并使用各种资源设置启动数据 例如设备树 blob 或 ACPI 表。

内存

VMM 将分配一个 vmo 用作客户机物理内存,并映射此 vmo 到客户机物理内存 vmar(由 zx_guest_create 创建)中。时间 在客户机物理内存 vmar 中寻址内存,我们称之为 “访客实际地址”(GPA)。VMM 还会将同一个 vmo 映射到其 处理地址空间,以便其直接访问此内存。时间 在 VMM 的 vmar 中寻址内存,我们将这些地址称为“主机虚拟 地址’ (HVA)。VMM能够将 GPA 转换为 HVA,因为它 同时知道客户机内存映射以及 客户机内存的映射

Virtio 设备和组件

许多设备使用虚拟 I/O 向客户机公开 (Virtio) 通过 PCI 传输。Virtio 规范定义了一组 能够在虚拟化环境中高效运行, DMA 访问客户机物理内存,并最大限度地减少 同步 IO 陷阱。为了增强设备之间的安全性和隔离性, 每个 Virtio 设备在自己的 Zircon 进程中,并且仅路由功能 该组件所需的资源例如,仅提供 Virtio 块设备 支持虚拟磁盘的特定文件或设备的句柄;以及 Virtio 控制台只能访问串行流的 zx::socket。

VMM和设备之间的通信是使用 fuchsia.virtualization.hardware FIDL 库。对于每个 会有一小段代码关联到 VMM,称为 控制器,充当这些 FIDL 的客户端 服务,并连接到实现设备的组件 启动。每个实例有一个进程,因此如果一个虚拟 机器有 3 个 Virtio 块设备,那么会有 3 个控制器实例和 3 个 Virtio 阻止 3 个 zircon 进程中的组件。

Virtio 设备采用共享数据结构的概念, 客户机物理内存。客户机驱动程序将分配和初始化这些 并在启动时为 VMM 提供指向这些结构的指针 客户机物理内存。当驾驶员想要通知设备 向这些结构发布了新作品时, 客户机物理内存中特定于设备的“通知”页面,并且设备可以推断出 基于写入到此“通知”页面的偏移量的特定事件。每个 设备组件将为该区域注册 ZX_GUEST_TRAP_BELL,以便 Hypervisor 可以直接将这些事件转发到目标组件, 而无需在 VMM 中弹跳。然后,设备组件可以 并通过 HVA 读取这些结构来直接编写这些结构。

启动

VMM 不提供任何客户机 BIOS 或固件,而是加载 客户机资源直接复制到内存中,并将启动 vCPU 配置为 内核入口点具体情况因内核 加载中。

Linux 访客

对于 x64 Linux 客户机,VMM 会将可启动的内核映像(例如:bzImage)加载到 客户机物理内存(取决于 Linux 启动) 协议,并更新 Real-Mode 内核标头 以及包含其他内核资源(ramdisk、 内核命令行)。VMM 还会生成并加载一组 ACPI 以下表格:描述提供给 来宾。

Arm64 Linux 客户机的行为与之相似,只不过我们遵循 arm64 启动 协议并提供设备树 blob (DTB) 而不是 ACPI 表

Zircon 访客

VMM 还支持根据 Zircon 启动方式启动 Zircon 客户机 要求。此处提供了一些关于锆石靴子的详细信息。

邀请对象管理员

访客管理器组件的作用是打包访客二进制文件 (内核、ramdisk、磁盘映像)和配置(要启用的设备、 客机内核配置选项),并在启动时将这些选项提供给 VMM

树内有 3 位嘉宾管理员,其中两位相当简单, 我又来了一门高级课程简单的访客管理员没有任何特定于访客的代码, 仅包含传递给 VMM 的配置和二进制文件。这些邀请对象 然后通过虚拟控制台或虚拟帧缓冲区使用。

简单的访客管理器:ZirconGuestManager DebianGuestManager

更高级的访客管理器是 TerminaGuestManager,它提供 使用在 Virtio Vsock 上运行的 gRPC 服务功能。通过 TerminaGuestManager 具有连接到这些服务的额外功能 并提供更多功能(在客户机中运行命令、装载文件系统、 启动应用)。

如需详细了解如何在 Fuchsia 上启动和使用虚拟化,请参阅 开始使用 Fuchsia 虚拟化