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_state
、zx_vcpu_write_state
和
zx_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_state
和 zx_vcpu_write_state
来实现的。
用于更新目标寄存器的值的系统调用
以返回模拟 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
设备的效率远高于设备
非常依赖同步陷阱
陷阱处理中的架构差异
虽然陷阱处理方法大致相同,但有一些重要的
为应对陷阱而需要采取哪些措施,具体取决于
底层硬件支持最值得注意的是,在 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 虚拟化。