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 模拟设备寄存器读写,这就是虚拟设备的工作基础。
三、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_EL3 | Guest 陷入后无法返回 | KVM trace、ESR_EL2 |
| Stage-2 翻译 | VTTBR_EL2、VTCR_EL2 | MMIO 未触发、缺页异常 | KVM 页表、dmesg |
| VGIC | ICH_LR*_EL2、GICD/GICC | 中断丢失或重复 | /proc/interrupts、vGIC 状态 |
| Generic Timer | CNTVOFF_EL2、CNTHCTL_EL2 | 时间漂移、定时器不触发 | CNTVCT、Host 频率 |
ICC_*、CNTP_* 等寄存器的访问会被 HCR_EL2 路由到 EL2,若 KVM 未正确模拟,Guest 会收到 UNDEFINED 异常或静默失败。六、案例:Guest 启动后串口无输出
某嵌入式板卡上 KVM 启动 Guest 后,串口无任何输出。排查顺序如下:
- 确认 Guest 内核已进入 EL1:查看 KVM 退出原因,若长期停留在
HVC或WFE,说明早期初始化未完成。 - 检查 Stage-2 映射:确认 UART 的 IPA 被标记为 MMIO 陷阱,而非直接映射。
- 检查 VGIC:确认 UART 中断已注入到正确 vCPU,LR 未溢出。
- 检查定时器:确认
CNTVOFF_EL2已设置,Guest 能收到定时器中断推进时钟。
最终定位为 Stage-2 把 UART 区域错误地映射为 Host 物理地址,Guest 写寄存器直接落到真实硬件,但 Host 串口驱动同时占用该硬件,导致输出被覆盖。修正 Stage-2 映射后问题解决。
七、小结与调试建议
ARMv8/v9 虚拟化扩展把异常等级、Stage-2 翻译、VGIC 和 Generic Timer 紧密耦合在一起。嵌入式 KVM 调试时,建议按“异常等级 → Stage-2 → VGIC → Timer”的顺序逐层确认,每一层都有对应的寄存器和日志入口。掌握这些机制,才能在后续章节中快速定位虚拟机启动、中断和时间相关的复杂故障。