3. ARM架构与虚拟化扩展

在嵌入式KVM平台中,ARMv8/v9 的虚拟化扩展是整套方案能否稳定运行的基础。与 x86 的 VMX/SVM 不同,ARM 把虚拟化能力直接嵌入异常等级模型、内存翻译路径和中断控制器中。理解 EL0~EL2 的职责划分、Stage-2 翻译、VGIC 与 Generic Timer 的虚拟化机制,是后续排查“虚拟机启动失败、中断丢失、时间漂移”等问题的前提。

一、异常等级 EL0~EL2 的职责划分

ARMv8/v9 定义了四个异常等级,嵌入式 KVM 场景中最常接触的是 EL0、EL1、EL2:

  • EL0:用户态,运行 Guest 的用户进程或 Host 的用户态程序,无特权,不能直接访问系统寄存器。
  • EL1:内核态,Guest OS 内核与 Host 内核都运行在这一级。Guest 内核认为自己“拥有”EL1,但实际上被 Hypervisor 在 EL2 拦截。
  • EL2:Hypervisor 层,KVM/ARM 的代码运行于此,负责 Stage-2 翻译、中断注入、定时器虚拟化和世界切换。
  • EL3:安全监控层,通常由 ATF/OP-TEE 占用,KVM 一般不直接运行在 EL3,但需要与其协作完成 PSCI 调用。

关键点在于:Guest 的 EL0/EL1 是“虚拟化视图”,当 Guest 执行敏感指令或访问敏感寄存器时,CPU 会自动陷入 EL2,由 KVM 决定是模拟、转发还是直接注入异常。这种“陷入—模拟—返回”的路径,是 KVM/ARM 性能与正确性的核心。

二、Stage-2 翻译:Guest 物理地址到 Host 物理地址

ARM 采用两阶段地址翻译。Stage-1 由 Guest 内核控制,把 Guest 虚拟地址(VA)翻译为 Guest 物理地址(IPA);Stage-2 由 Hypervisor 控制,把 IPA 翻译为真实物理地址(PA)。KVM 通过 VTTBR_EL2 指向 Stage-2 页表,通过 VTCR_EL2 配置翻译粒度、IPA 位宽和内存属性。

# 典型 Stage-2 相关寄存器(EL2)
VTTBR_EL2   : Stage-2 页表基址
VTCR_EL2    : Stage-2 翻译控制(T0SZ/SL0/IRGN/ORGN)
HCR_EL2     : VM 使能位、路由位(IMO/FMO/AMO)
SCTLR_EL2   : EL2 系统控制

Stage-2 翻译失败会产生 Stage-2 异常,KVM 据此判断是 MMIO 访问、缺页还是非法访问。对于嵌入式 KVM,常见场景是把 Guest 的 MMIO 区域映射为“不可访问”,触发异常后由 KVM 模拟设备寄存器读写,这就是虚拟设备的工作基础。

调试提示:若 Guest 启动时卡在早期串口输出,优先检查 Stage-2 是否把 UART 的 IPA 正确映射为 MMIO 陷阱区域,而不是直接映射为 Host 物理地址。

三、VGIC:虚拟中断控制器的注入路径

ARM 的中断控制器 GICv2/GICv3 在虚拟化下由 VGIC 组件管理。KVM 维护虚拟 Distributor 和虚拟 CPU Interface,Guest 读写这些寄存器时会被陷入 EL2,由 KVM 模拟。物理中断到达 Host 后,KVM 决定是否注入到某个 vCPU。

  • List Register:GICv2 使用 List Register 向 vCPU 注入虚拟中断,数量有限,需关注溢出。
  • GICv3/v4:使用 ICH_LR*_EL2 与 ICH_HCR_EL2,支持更多中断和直接注入。
  • 维护中断:当 LR 不足或需要 EOI 处理时,VGIC 产生维护中断,KVM 在 EL2 处理。

嵌入式平台常见问题是中断“丢失”或“重复”:前者多因 LR 溢出未及时处理,后者多因 EOI 模式配置错误。排查时应同时查看 /proc/interrupts、KVM 的 vGIC 状态以及 Host 侧 GIC 的 SPI 配置。

四、Generic Timer 虚拟化:CNTV 与时间漂移

ARM Generic Timer 提供物理计数器 CNTPCT_EL0 和虚拟计数器 CNTVCT_EL0。虚拟化时,KVM 通过 CNTVOFF_EL2 给每个 vCPU 一个偏移,使 Guest 看到自己的虚拟时间。同时 CNTHCTL_EL2 控制 Guest 对物理定时器的访问权限。

# 查看 Host 定时器频率
cat /proc/cpuinfo | grep -i timer
# 典型输出
arm_arch_timer: 频率 24000000 Hz

# KVM 中常见的定时器相关寄存器
CNTVOFF_EL2   : 虚拟计数器偏移
CNTHCTL_EL2   : EL1 物理定时器访问控制
CNTV_CVAL_EL0 : 虚拟定时器比较值

时间漂移通常来自三方面:CNTVOFF_EL2 未正确设置、Host 与 Guest 频率假设不一致、vCPU 被长时间调度出去导致定时器中断延迟。嵌入式实时场景中,建议把 KVM 的定时器注入与 Host 的 NO_HZ/CPU 隔离策略一起评估。

五、核心机制对比与排查清单

机制关键寄存器/组件典型故障排查入口
异常等级HCR_EL2、SCR_EL3Guest 陷入后无法返回KVM trace、ESR_EL2
Stage-2 翻译VTTBR_EL2、VTCR_EL2MMIO 未触发、缺页异常KVM 页表、dmesg
VGICICH_LR*_EL2、GICD/GICC中断丢失或重复/proc/interrupts、vGIC 状态
Generic TimerCNTVOFF_EL2、CNTHCTL_EL2时间漂移、定时器不触发CNTVCT、Host 频率
常见误区:把 Guest 的 EL1 当成真实 EL1 直接操作物理寄存器。实际上 Guest 对 ICC_*、CNTP_* 等寄存器的访问会被 HCR_EL2 路由到 EL2,若 KVM 未正确模拟,Guest 会收到 UNDEFINED 异常或静默失败。

六、案例:Guest 启动后串口无输出

某嵌入式板卡上 KVM 启动 Guest 后,串口无任何输出。排查顺序如下:

  1. 确认 Guest 内核已进入 EL1:查看 KVM 退出原因,若长期停留在 HVC 或 WFE,说明早期初始化未完成。
  2. 检查 Stage-2 映射:确认 UART 的 IPA 被标记为 MMIO 陷阱,而非直接映射。
  3. 检查 VGIC:确认 UART 中断已注入到正确 vCPU,LR 未溢出。
  4. 检查定时器:确认 CNTVOFF_EL2 已设置,Guest 能收到定时器中断推进时钟。

最终定位为 Stage-2 把 UART 区域错误地映射为 Host 物理地址,Guest 写寄存器直接落到真实硬件,但 Host 串口驱动同时占用该硬件,导致输出被覆盖。修正 Stage-2 映射后问题解决。

七、小结与调试建议

ARMv8/v9 虚拟化扩展把异常等级、Stage-2 翻译、VGIC 和 Generic Timer 紧密耦合在一起。嵌入式 KVM 调试时,建议按“异常等级 → Stage-2 → VGIC → Timer”的顺序逐层确认,每一层都有对应的寄存器和日志入口。掌握这些机制,才能在后续章节中快速定位虚拟机启动、中断和时间相关的复杂故障。

ARMv8/v9 虚拟化扩展核心机制 EL0 Guest 用户态 EL1 Guest OS / Host 内核 EL2 KVM Hypervisor EL3 ATF / OP-TEE Stage-2 翻译 VA → IPA (Stage-1, Guest) IPA → PA (Stage-2, KVM) VTTBR_EL2 / VTCR_EL2 VGIC 虚拟中断 虚拟 Distributor / CPU Interface List Register / ICH_LR*_EL2 维护中断处理 Generic Timer 虚拟化 CNTVCT_EL0 / CNTVOFF_EL2 CNTHCTL_EL2 访问控制 虚拟定时器中断注入 Guest 视角 EL0 / EL1 虚拟视图 VA → IPA 虚拟中断号 虚拟计数器 敏感操作触发陷入 HCR_EL2 路由 ESR_EL2 记录原因 KVM 模拟 / 注入 返回 Guest 调试入口: dmesg / trace / /proc/interrupts 陷入 / 注入