2. ARM64 虚拟内存架构基础:VA、PA、IPA 地址空间概念,以及 4 级页表(L0-L3)的硬件规格

在深入 ARM64 启动流程中的页表创建与 MMU 初始化之前,必须首先理解其虚拟内存架构的底层基石。ARMv8-A 架构引入了三种独立的地址空间概念,并定义了从 L0 到 L3 的 4 级页表硬件规格。这些设计直接决定了操作系统如何管理内存、隔离进程以及实现虚拟化。

2.1 三大地址空间:VA、PA 与 IPA

ARM64 架构在硬件层面明确区分了三种地址空间,这是理解页表转换和系统软件设计的关键。

地址空间 全称 描述 典型宽度
VA Virtual Address 软件(如应用程序、内核)直接使用的地址。MMU 负责将 VA 转换为 PA。 48 位(或 52 位,取决于配置)
PA Physical Address 系统总线上实际访问内存的地址。由内存控制器直接解析。 48 位(或 52 位)
IPA Intermediate Physical Address 在虚拟化场景(如 KVM、Xen)中,Guest OS 看到的“物理地址”。由 Stage-2 转换映射为真实 PA。 48 位(或 52 位)
关键理解: 在非虚拟化环境中,系统只涉及 VA → PA 的转换(Stage-1)。在虚拟化环境中,系统涉及 VA → IPA(Stage-1)和 IPA → PA(Stage-2)的两阶段转换。IPA 是连接 Guest OS 与 Hypervisor 的桥梁。

下图展示了三种地址空间在典型系统中的关系与转换流程:

ARM64 地址空间转换关系图 ARM64 地址空间转换关系 VA 虚拟地址 IPA 中间物理地址 PA 物理地址 Stage-1 转换 Stage-2 转换 说明: 1. 非虚拟化场景:VA → PA(仅 Stage-1)。 2. 虚拟化场景:VA → IPA → PA(Stage-1 + Stage-2)。 3. IPA 仅存在于启用了虚拟化扩展(VHE)或运行在 EL2 的 Hypervisor 中。 4. 页表描述符中的地址字段宽度决定了最大可寻址范围。

2.2 4 级页表硬件规格:L0 到 L3

ARM64 的页表转换采用多级查找机制,默认支持 4 级页表(L0、L1、L2、L3)。每一级页表负责将虚拟地址的一部分索引转换为下一级页表的基地址或最终的物理页面地址。这种设计在灵活性和内存占用之间取得了平衡。

2.2.1 页表层级与粒度

每一级页表对应不同的翻译粒度(Translation Granule)和覆盖范围。最常用的粒度是 4KB 页面。

页表级别 索引位宽(4KB 粒度) 页表项数量 覆盖地址范围 描述
L0 VA[47:39] (9 bits) 512 512 GB 顶级页表,指向 L1 页表或 1TB 块。
L1 VA[38:30] (9 bits) 512 1 GB 二级页表,指向 L2 页表或 2MB 块。
L2 VA[29:21] (9 bits) 512 2 MB 三级页表,指向 L3 页表或 4KB 块。
L3 VA[20:12] (9 bits) 512 4 KB 最后一级页表,包含最终的物理页面描述符。
硬件规格要点:
  • 每个页表项(PTE)固定为 8 字节(64 位)。
  • 每个页表占用一个 4KB 页面(512 项 × 8 字节 = 4096 字节)。
  • 虚拟地址的低 12 位(VA[11:0])作为页面内偏移(Offset),不参与页表索引。
  • 支持块映射(Block Mapping):在 L1 或 L2 级别,描述符可以直接映射大块内存(如 2MB 或 1GB),跳过后续级别,减少页表层级深度。

2.2.2 虚拟地址分解(4KB 粒度,48 位 VA)

对于一个 48 位虚拟地址,MMU 硬件会将其分解为 5 个部分,用于遍历 4 级页表:

VA[47:0] = | L0索引(9位) | L1索引(9位) | L2索引(9位) | L3索引(9位) | 页内偏移(12位) |
            VA[47:39]    VA[38:30]    VA[29:21]    VA[20:12]    VA[11:0]

下图展示了 MMU 如何利用这 4 级索引进行地址转换的硬件流程:

ARM64 4级页表转换流程 ARM64 4级页表转换流程(4KB 粒度,48位 VA) L0 索引 (9位) L1 索引 (9位) L2 索引 (9位) L3 索引 (9位) 页内偏移 (12位) 虚拟地址 VA[47:0] L0 页表 TTBR0_EL1 基址 L1 页表 由 L0 描述符指向 L2 页表 由 L1 描述符指向 L3 页表 由 L2 描述符指向 物理地址 PA L3 描述符中的基址 + 页内偏移 硬件查找步骤: 1. 从 TTBRx_EL1 获取 L0 页表基址。 2. 使用 L0 索引找到 L1 页表基址。 3. 使用 L1 索引找到 L2 页表基址。 4. 使用 L2 索引找到 L3 页表基址。 5. 使用 L3 索引找到物理页面基址。 6. 物理页面基址 + 页内偏移 = 最终 PA。

2.3 页表描述符格式

每个页表项(PTE)是一个 64 位描述符,其格式由硬件严格定义。描述符的类型决定了它是无效项、指向下一级页表的表描述符,还是指向物理内存的块/页面描述符。

位域 宽度 描述
[1:0] 2 bits 描述符类型:00 = 无效,01 = 表描述符,10 = 块/页面描述符,11 = 保留。
[47:12] 36 bits 物理地址基址(对于 4KB 页面,低 12 位为 0)。
[11:2] 10 bits 属性位:访问权限(AP)、可共享性(SH)、内存类型(AttrIndx)、非安全(NS)、访问标志(AF)等。
[63:48] 16 bits 扩展属性或保留(取决于实现)。
常见陷阱:
  • 未初始化页表: 启动时如果 L0 页表未正确填充,MMU 将无法找到下一级页表,导致 Translation Fault。
  • 粒度不匹配: 如果配置的翻译粒度(如 16KB 或 64KB)与页表层级不匹配,会导致地址转换错误。
  • 块映射与页映射混淆: 在 L1 或 L2 级别使用块映射可以节省页表内存,但必须确保映射的物理内存是连续的。

2.4 页表基址寄存器:TTBR0_EL1 与 TTBR1_EL1

ARM64 使用两个独立的页表基址寄存器来管理不同的地址空间:

  • TTBR0_EL1: 用于用户空间(EL0)和内核空间低地址区域(通常为 0x0000_0000_0000_0000 到 0x0000_FFFF_FFFF_FFFF)。
  • TTBR1_EL1: 用于内核空间高地址区域(通常为 0xFFFF_0000_0000_0000 到 0xFFFF_FFFF_FFFF_FFFF)。

这种设计允许内核在切换进程时只需切换 TTBR0_EL1,而 TTBR1_EL1 保持不变,从而避免了内核页表的重复创建。

启动阶段注意: 在 MMU 初始化之前,通常使用 TTBR0_EL1 指向一个临时的恒等映射页表(Identity Map),确保开启 MMU 后指令地址不发生变化。这是启动流程中的关键步骤。