2. ARM64架构基础
在嵌入式虚拟化工程中,ARM64 并不是“把 x86 的虚拟化概念平移过来”那么简单。AArch64 执行状态、EL0~EL3 异常等级、系统寄存器、弱内存模型以及多级页表,共同构成了 KVM/ARM 在底层运行、陷入、模拟和隔离的硬件基础。理解这些机制,才能看懂 KVM 为什么在异常入口处保存哪些寄存器、为什么需要 stage-2 页表、为什么必须插入内存屏障。
一、AArch64 执行状态与异常等级
AArch64 是 ARMv8-A 引入的 64 位执行状态,提供 31 个 64 位通用寄存器(X0~X30)、独立的 SP_ELx 栈指针、PC 以及 PSTATE。与 AArch32 不同,AArch64 的异常等级 EL0~EL3 是权限与异常处理的核心分层:EL0 是用户态,EL1 通常运行 Guest OS 内核或 Host 内核,EL2 是虚拟化扩展层,EL3 是安全监控层。KVM 在 ARM64 上通常运行于 EL2,Guest 内核运行于 EL1,Guest 用户进程运行于 EL0。
异常等级决定了“谁能访问哪些系统寄存器、谁能执行哪些特权指令”。例如,Guest 内核在 EL1 执行 WFI、访问 TTBR0_EL1 时,若未开启对应 trap,则直接在 EL1 生效;若 KVM 设置了 HCR_EL2.TWI 等陷入位,则会触发异常进入 EL2,由 KVM 决定是模拟还是注入。EL2 与 EL1 之间的切换由 ERET、SVC、HVC、IRQ、FIQ 等异常向量驱动。
二、系统寄存器与陷入控制
ARM64 的系统寄存器是虚拟化控制的关键抓手。KVM 通过 HCR_EL2(Hypervisor Configuration Register)决定哪些操作会从 EL1 陷入到 EL2。常见控制位包括:TWI 陷入 WFI、TWE 陷入 WFE、TSC 陷入 SMC、IMO/FMO 路由 IRQ/FIQ、VM 开启 stage-2 页表。此外,VBAR_EL2 指定 EL2 异常向量基址,ESR_EL2 记录异常原因,FAR_EL2 记录出错地址,HPFAR_EL2 记录 stage-2 缺页的 IPA。
在 vCPU 切换时,KVM 需要保存和恢复 TTBR0_EL1、TTBR1_EL1、TCR_EL1、MAIR_EL1、SCTLR_EL1 等寄存器,否则 Guest 的地址翻译上下文会互相污染。下面是一段典型的 vCPU 进入 Guest 前的寄存器准备逻辑:
/* 简化示意:KVM/ARM64 vcpu_enter_guest 前的关键寄存器设置 */
write_sysreg(read_sysreg(HCR_EL2) | HCR_VM | HCR_TWI | HCR_TWE, HCR_EL2);
write_sysreg(vcpu->arch.vttbr, VTTBR_EL2); /* stage-2 页表基址 */
write_sysreg(vcpu->arch.vtcr, VTCR_EL2); /* stage-2 翻译控制 */
write_sysreg(vcpu->arch.ttbr0, TTBR0_EL1); /* Guest 用户页表 */
write_sysreg(vcpu->arch.ttbr1, TTBR1_EL1); /* Guest 内核页表 */
write_sysreg(vcpu->arch.tcr, TCR_EL1);
write_sysreg(vcpu->arch.mair, MAIR_EL1);
write_sysreg(vcpu->arch.sctlr, SCTLR_EL1);
isb(); /* 确保系统寄存器生效 */
三、内存模型与页表机制
ARM64 采用弱内存模型,CPU 和编译器都可能对访存指令重排。虚拟化场景下,Guest 与 Host 共享内存、vCPU 与物理 CPU 并发访问,必须使用 dmb、dsb、isb 等屏障指令保证顺序。例如,在修改 stage-2 页表后,需要执行 dsb 再 tlbi,最后 isb,否则可能读到旧翻译。
页表方面,ARM64 支持 4KB、16KB、64KB 三种粒度,常用 4KB 粒度下采用 4 级或 3 级页表。虚拟化引入两阶段翻译:stage-1 由 Guest 的 TTBR0_EL1/TTBR1_EL1 完成 VA→IPA,stage-2 由 VTTBR_EL2 完成 IPA→PA。KVM 通过 stage-2 页表实现 Guest 物理地址的隔离与映射,缺页时产生 stage-2 fault,由 KVM 填充页表或注入异常。
| 机制 | 关键寄存器/指令 | 在 KVM 中的作用 |
|---|---|---|
| 异常等级 | EL0~EL3、PSTATE | 划分 Guest 用户、Guest 内核、KVM、安全监控权限 |
| 陷入控制 | HCR_EL2、ESR_EL2 | 截获 WFI/WFE/SMC/IRQ 等,交给 KVM 模拟 |
| 两阶段翻译 | TTBR0/1_EL1、VTTBR_EL2 | VA→IPA→PA,实现 Guest 内存隔离 |
| 内存屏障 | dmb、dsb、isb | 保证页表修改、vCPU 切换的顺序可见性 |
| 缺页处理 | FAR_EL2、HPFAR_EL2 | 定位 stage-2 缺页地址并填充映射 |
四、典型场景与常见误区
假设 Guest 内核在 EL1 执行 WFI 进入空闲。若 HCR_EL2.TWI=1,该指令会陷入 EL2,KVM 捕获后可将 vCPU 标记为阻塞,调度到其他 vCPU,从而避免物理 CPU 空转。若未设置 TWI,Guest 的 WFI 会直接在 EL1 生效,Host 可能失去调度机会。这就是“陷入控制位决定虚拟化行为”的典型体现。
常见误区:把 EL2 当成“另一个内核态”而忽略 HCR_EL2 的细粒度控制;或在修改 stage-2 页表后只执行 tlbi 而遗漏 dsb/isb,导致 TLB 与页表不一致;又或者认为 Guest 的 TTBR0_EL1 可以直接被 Host 复用,造成地址空间串扰。
调试建议:遇到 Guest 异常时,优先查看 ESR_EL2 的 EC 字段判断异常类型,再结合 FAR_EL2、HPFAR_EL2 定位地址;若怀疑内存序问题,可在关键路径插入 dsb sy 验证是否消失。理解 EL0~EL3、系统寄存器、弱内存模型与两阶段页表,是后续阅读 KVM/ARM64 源码和排查虚拟化故障的基本功。