TECHNICAL FIELD GUIDE

虚拟化技术指南

用于配合 16 周计划阅读。每章都回答三件事:硬件或软件在做什么、应当追踪哪些代码与寄存器、如何用实验证明理解正确。

阅读约定本文以 ARMv8-A/AArch64 为主。AxVisor 与 ArceOS 的符号、目录和配置格式可能随版本变化,因此源码定位应以“职责和调用关系”为准,不要死记函数名。
01 · MENTAL MODEL

先建立系统心智模型

AxVisor 是运行在最高控制层的 Hypervisor。它不直接“运行 Linux 程序”,而是创建并运行 Linux 客体;Linux 完成自己的内核启动后,再由用户态 init 启动 App A、App B。RTOS 则作为另一个客体,拥有独立入口、内存、定时器和任务调度器。

Linux VMApp A · App B · Linux Kernel
RTOS VMTask 1 · Task 2 · RTOS Kernel
AxVisor @ EL2vCPU · Stage-2 · vIRQ · Device
Hardware / QEMUCPU · RAM · GIC · Timer · UART

必须区分的四个对象

对象含义典型问题
Hypervisor控制真实 CPU、内存、中断与客体生命周期VM 如何创建?何时进入客体?
VM一组虚拟硬件与资源的容器内存和设备如何划分?
vCPU客体所看到的虚拟处理器及其上下文寄存器如何保存、恢复和调度?
Guest workloadLinux、RTOS,以及它们内部的任务或程序镜像入口和启动参数是否正确?
02 · ARMV8-A

ARM 虚拟化基础:为什么需要 EL2

AArch64 使用异常级控制权限。普通应用通常运行在 EL0,客体操作系统运行在 EL1,Hypervisor 运行在 EL2。EL2 配置哪些 EL1 操作可以直接执行、哪些操作必须陷入 Hypervisor。

EL2AxVisor控制虚拟化扩展、Stage-2 和陷入策略
EL1Linux / RTOS Kernel管理客体自身的页表、中断和进程
EL0Linux App A / App B通过系统调用请求客体内核服务

关键 EL2 寄存器

寄存器作用调试价值
HCR_EL2虚拟化总开关与陷入策略,如是否启用 Stage-2客体异常行为不符合预期时首先检查
VTTBR_EL2当前 VM 的 Stage-2 页表根和 VMID确认当前运行的是哪个地址空间
VTCR_EL2Stage-2 地址宽度、粒度和页表级数判断页表解释方式是否正确
ESR_EL2异常类别 EC 和具体原因 ISS定位系统寄存器、MMIO、指令等 Exit
ELR_EL2异常返回后继续执行的客体 PC确认发生异常的指令地址
FAR_EL2 / HPFAR_EL2故障虚拟地址和中间物理地址信息定位 Stage-2 地址故障
VM Exit 的本质

ARM 文档通常称其为“从低异常级同步异常进入 EL2”。工程中常用 VM Exit 泛指客体因敏感操作、地址故障、中断或主动调用而把控制权交回 Hypervisor。

03 · BOOT SEQUENCE

从上电到两个客体运行

  1. 01
    固件交接

    CPU 从复位入口执行。固件准备内存和启动参数,将主核置于合适异常级,再跳转到 AxVisor 镜像入口。

  2. 02
    早期运行环境

    入口汇编建立栈、清零 BSS、保存启动参数;随后初始化页表、MMU、堆、串口和 per-CPU 数据。

  3. 03
    解析 VM 配置

    读取客体类型、CPU、内存、镜像地址、入口、DTB、设备和中断分配。

  4. 04
    装载客体镜像

    把 Linux Image、initramfs、DTB 或 RTOS 镜像复制/映射到约定的 Guest Physical Address。

  5. 05
    建立 Stage-2

    为每个 VM 建立 IPA→PA 映射,并设置读写执行与设备内存属性。

  6. 06
    构造 vCPU

    设置客体入口 PC、PSTATE、通用寄存器、虚拟定时器和虚拟中断状态。Linux arm64 通常通过 x0 接收 DTB 地址。

  7. 07
    首次进入客体

    写入当前 VM 的 VTTBR_EL2,恢复 vCPU 上下文并通过异常返回进入 EL1。

  8. 08
    Exit 与恢复

    客体陷入 EL2 后,读取 ESR_EL2 分类处理,必要时模拟设备并推进 PC,然后恢复客体。

  9. 09
    客体内部启动

    Linux 挂载根文件系统并执行 init;RTOS 初始化时钟、中断和调度器。最终用户程序和实时任务开始运行。

// 概念性伪代码:真实接口以当前 AxVisor 源码为准
hypervisor_main() {
    init_memory_and_percpu();
    init_interrupt_controller();

    for cfg in vm_configs {
        vm = create_vm(cfg);
        load_guest_images(vm, cfg);
        build_stage2_page_table(vm);
        setup_vcpus(vm);
        register_vm(vm);
    }

    schedule_first_vcpu();
}
04 · MEMORY

Stage-2:虚拟机隔离的核心

客体内部仍然可以使用自己的 Stage-1 页表,把虚拟地址 VA 转换为中间物理地址 IPA。硬件随后使用 Hypervisor 建立的 Stage-2 页表,把 IPA 转换为真实物理地址 PA。

Guest VA0xffff…
Stage-1
Guest OS
Guest IPA0x4000_0000
Stage-2
AxVisor
Host PA0x9000_0000

建立映射时检查四件事

  • 范围:IPA 起点、PA 起点和长度必须按页对齐,且不同 VM 的 PA 区域不可重叠。
  • 权限:代码段通常可读可执行,数据段可读写;不应把所有区域长期映射成 RWX。
  • 内存类型:普通 RAM 与 UART/GIC 等 Device Memory 必须使用正确属性,否则可能出现乱序或缓存问题。
  • TLB:修改活动映射后,需要按架构要求执行屏障与 TLB invalidation。
// 教学用映射示意,不代表 AxVisor 的真实 API
map_guest(vm_linux,
    ipa = 0x4000_0000,
    pa  = 0x9000_0000,
    len = 512 * MiB,
    attr = NORMAL | READ | WRITE | EXEC);

map_guest(vm_rtos,
    ipa = 0x4000_0000,
    pa  = 0xB000_0000,
    len = 64 * MiB,
    attr = NORMAL | READ | WRITE | EXEC);
注意

两个客体可以拥有相同 IPA 布局,因为它们使用不同的 VTTBR_EL2/VMID;但最终 PA 区域必须隔离。示例地址仅用于理解,必须根据实际平台内存图修改。

05 · VCPU

vCPU 上下文与 VM Exit 循环

vCPU 不只是一个线程对象,而是一份“能够恢复客体处理器状态”的完整快照。最少包含通用寄存器、PC/PSTATE、必要的 EL1 系统寄存器、虚拟定时器,以及与虚拟中断控制器相关的状态。

通用状态x0–x30、SP、PC、PSTATE
地址空间TTBR、TCR、MAIR、SCTLR
异常状态VBAR、ESR、FAR、SPSR
虚拟设备Timer、GIC list registers
loop {
    install_vm_stage2(vcpu.vm);
    restore_guest_context(vcpu);
    enter_guest();              // ERET

    save_guest_context(vcpu);   // 从 EL1 陷入 EL2 后继续
    syndrome = read(ESR_EL2);

    match decode(syndrome) {
        MMIO       => emulate_or_forward_device(vcpu),
        SYSREG     => emulate_system_register(vcpu),
        HVC        => handle_hypercall(vcpu),
        STAGE2     => handle_or_report_page_fault(vcpu),
        other      => inject_or_stop_guest(vcpu, other),
    }
}

调试时应同时记录:VM ID、vCPU ID、ESR_EL2、ELR_EL2、FAR_EL2、HPFAR_EL2 和本次处理结果。只打印“guest fault”通常不足以定位问题。

06 · INTERRUPT & DEVICE

中断和设备为什么最容易卡住

客体需要看到“虚拟硬件”。设备可以完全模拟、半虚拟化、直通或静态分区。学习阶段建议先从串口和虚拟定时器入手,因为日志与调度都依赖它们。

方式工作方式特点
Trap-and-emulate客体 MMIO 访问触发 Stage-2 fault,由 Hypervisor 模拟易控制但 Exit 开销较高
半虚拟化客体使用约定接口,如 virtio 或 hypercall性能与通用性平衡
设备直通把物理设备及中断直接分配给一个 VM性能好,但隔离与 IOMMU 更复杂
静态分区设备固定归属某个 VM,不动态共享适合混合关键系统

虚拟定时器中断路径

物理/虚拟计时器到期 → EL2/GIC 获得事件 → 标记目标 vCPU 的 pending vIRQ → vCPU 运行时注入 → 客体 EL1 中断处理 → 调度器 tick

如果 RTOS 已输出启动日志但任务不切换,优先检查计时器频率、比较值、虚拟 IRQ 号、GIC 使能位以及中断是否注入到正确 vCPU。

07 · ARCEOS

ArceOS:组件化内核如何支撑 Hypervisor

ArceOS 采用组件化设计:应用或上层系统按 feature 组合所需能力,而不是固定包含所有内核服务。AxVisor 可以复用其中的平台抽象、运行时、内存、任务与驱动能力,把精力集中在虚拟机管理上。

常见组件职责阅读重点
axhal架构与平台硬件抽象入口、页表、中断、定时器、CPU 操作
axruntime组织系统早期初始化初始化顺序和主入口交接
axalloc页和堆内存分配可用物理内存从何而来
axtask任务、调度与同步vCPU 是否映射为任务、如何调度
axdriver设备探测与驱动框架UART、块设备、网络等如何初始化
axstd面向应用的类 std 接口应用入口与底层组件如何连接

推荐源码阅读方法

  1. 从运行命令确定目标架构、平台与启用 feature。
  2. 找到链接脚本和入口符号,确认镜像装载地址。
  3. 追踪“第一条串口日志”之前的调用路径。
  4. 为内存、驱动、调度器初始化分别增加一对 begin/end 日志。
  5. 最后才深入单个组件内部算法,避免一开始陷入细节。
版本差异

组件名和入口函数可能调整。遇到文档与源码不一致时,优先使用 rg "no_mangle|entry|rust_main|main"、链接脚本和构建日志重新建立调用链。

08 · AXVISOR

AxVisor:从配置到 vCPU 运行

理解 AxVisor 时,不要只按目录浏览。沿一台 VM 的生命周期追踪:配置解析 → VM 对象 → 内存区域 → 镜像装载 → vCPU 创建 → 架构上下文 → 首次运行 → Exit 分发。

VM ConfigVM ObjectStage-2vCPUGuest Run

建议建立的源码索引

  • 系统入口:AxVisor 主入口、主核/次级核初始化、启动第一个 VM 的位置。
  • VM 管理:VM ID 分配、配置结构、内存 region、设备与 vCPU 集合。
  • 架构层:EL2 初始化、Stage-2 页表、vCPU 寄存器上下文和异常向量。
  • 运行循环:进入客体、保存现场、异常解码与处理分发。
  • 设备与 IRQ:MMIO 区域注册、虚拟 IRQ 注入、直通设备归属。

第一次改代码应做什么

不要先实现新设备。先为 VM 创建和 Exit 路径增加结构化日志,例如:

[vm-create] vm=0 ipa=0x40000000 pa=0x90000000 size=512M
[vcpu-enter] vm=0 cpu=0 pc=0x40200000 vttbr=...
[vm-exit] vm=0 cpu=0 ec=0x24 elr=... far=... hpfar=...
[vcpu-resume] vm=0 cpu=0 next_pc=...

当你能解释这些日志,就已经建立了后续调试 Linux、RTOS 和设备的骨架。

09 · GUEST BOOT

Linux 与 RTOS 客体的启动差异

LINUX

通用操作系统启动

  • 准备内核 Image、DTB、可选 initramfs
  • arm64 通常在 x0 传入 DTB IPA
  • 内核建立自己的 Stage-1 页表
  • 初始化驱动、挂载 rootfs、执行 init
  • init 再启动 App A 与 App B
RTOS

板级实时系统启动

  • 准备 ELF/raw image 与入口地址
  • 匹配客体内存图和链接地址
  • 初始化 BSS、堆、向量表、时钟和中断
  • 创建任务并启动调度器
  • 验证 tick 与任务切换持续发生

Linux 必须对齐的启动契约

  • 内核镜像装载地址、入口 PC 与 DTB 地址必须落在有效 IPA 映射中。
  • DTB 的 memory、CPU、chosen、timer、interrupt-controller、UART 节点必须与虚拟硬件一致。
  • 命令行优先开启合适的 earlyconconsole=,否则“无输出”不等于内核没运行。
  • initramfs 中必须存在可执行的 /init,并包含程序依赖的动态库;学习阶段优先使用静态链接程序。
#!/bin/sh
# initramfs 中的 /init 概念示例
mount -t proc proc /proc
mount -t sysfs sysfs /sys

/bin/app-a &
/bin/app-b &

echo "[PASS] linux apps started"
exec /bin/sh

RTOS 移植检查表

  • 链接地址与 AxVisor 配置的 guest load/entry 地址一致。
  • 串口基址、中断号、计时器频率与 DTB/板级配置一致。
  • 异常向量和栈位于已映射、可写且不与镜像重叠的区域。
  • 如果使用 SMP,先完成单核启动,再逐步加入次级核。
10 · ISOLATION

多客体资源规划与隔离

混合关键系统首先追求“资源边界清晰”,再考虑动态共享。学习阶段建议使用静态 CPU 和内存分区。

资源Linux VM 示例RTOS VM 示例检查方法
物理 CPUCPU 0–1CPU 2打印 vCPU→pCPU 绑定
物理内存0x9000_0000 / 512 MiB0xB000_0000 / 64 MiB排序所有 PA region,检查交集
UART虚拟/共享控制台独立或日志复用日志增加 VM 前缀
中断Linux 设备 IRQ 集合Timer + RTOS 设备 IRQ打印 IRQ owner 路由表

最终故障实验:让 Linux 客体主动 panic 或访问未映射 IPA,AxVisor 应捕获并停止/重启该 VM;RTOS 的周期任务应继续输出。反向也要测试一次。

11 · DEBUGGING

分层调试:只追踪最后一个成功边界

现象优先检查关键证据
AxVisor 完全无输出镜像入口、当前 EL、栈、UART 基址PC/SP/CurrentEL 与第一条串口写
创建 VM 后卡住镜像地址、入口 PC、Stage-2 regionVTTBR_EL2、vCPU 初始寄存器
立即 Stage-2 faultIPA 是否映射、权限、内存类型ESR_EL2、FAR_EL2、HPFAR_EL2
Linux 无 earlyconDTB UART、console 参数、MMIO 映射ELR_EL2 与 UART access exit
Linux 找不到 rootfsinitramfs 地址、bootargs、/init 权限内核日志最后一个 mount/init 错误
RTOS 有日志但不调度timer、vIRQ 注入、GIC 使能tick 计数与 IRQ pending/ack
双客体互相影响PA/IRQ/设备是否重叠资源表与故障注入结果

建议的 GDB 观察点

# 符号名需按当前版本替换
b <hypervisor_entry>
b <vm_create>
b <vcpu_first_run>
b <lower_el_sync_handler>

info registers pc sp
p/x $CurrentEL
x/8i $pc
bt

每次只改变一个变量,并保存:启动命令、commit、配置文件、完整串口日志和期望/实际结果。这样才能区分代码问题、配置问题和镜像问题。

12 · HANDS-ON

六个必须完成的编码实验

LAB 01

异常解码器

读取 ESR_EL2 的 EC/ISS,打印可读 Exit 原因及 ELR/FAR/HPFAR。

验收:能区分 HVC、系统寄存器和数据中止。
LAB 02

Stage-2 映射检查器

遍历 VM 内存 region,检查对齐、重叠、权限和设备内存属性。

验收:故意制造重叠时拒绝启动并指出区域。
LAB 03

vCPU 生命周期日志

为 create、enter、exit、resume、stop 增加 VM/vCPU 标识。

验收:可从日志还原一次完整运行循环。
LAB 04

Linux 双应用 initramfs

编写两个静态程序并由 /init 后台启动,加入健康心跳。

验收:启动后可持续看到两个不同 PID 的日志。
LAB 05

RTOS 双任务

创建不同周期的两个任务,记录 tick 和任务切换。

验收:两个任务运行频率符合设计。
LAB 06

隔离故障注入

让一个客体访问未映射地址或主动 panic,记录 Hypervisor 处理。

验收:另一个客体连续运行且资源状态正确。

完成本指南后,返回路线图逐周勾选结果。

进入完整教材与实验手册 返回 16 周路线图 阅读 Markdown 版本