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 的桥梁。
下图展示了三种地址空间在典型系统中的关系与转换流程:
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 级索引进行地址转换的硬件流程:
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 后指令地址不发生变化。这是启动流程中的关键步骤。