3. ARM64 多级页表硬件原理:TTBR0/TTBR1 寄存器、TCR 寄存器配置、翻译粒度与地址映射
ARM64 体系结构采用 多级页表 实现虚拟地址到物理地址的转换。与 x86 不同,ARM64 的页表硬件设计高度灵活,允许操作系统根据应用场景选择不同的翻译粒度、页表级数和地址空间划分。本章深入剖析 TTBR0/TTBR1、TCR 等关键寄存器的配置逻辑,以及它们如何共同决定地址映射的最终行为。
3.1 地址空间划分:TTBR0 与 TTBR1
ARM64 将虚拟地址空间划分为两个独立的区域,分别由 TTBR0_EL1(Translation Table Base Register 0)和 TTBR1_EL1(Translation Table Base Register 1)管理。这种设计使得操作系统内核和用户进程可以拥有完全独立的页表结构,互不干扰。
| 寄存器 | 管理区域 | 典型用途 | 地址范围(48位虚拟地址) |
|---|---|---|---|
| TTBR0_EL1 | 低地址空间(用户空间) | 用户态进程页表基址 | 0x0000_0000_0000_0000 ~ 0x0000_FFFF_FFFF_FFFF |
| TTBR1_EL1 | 高地址空间(内核空间) | 内核态页表基址 | 0xFFFF_0000_0000_0000 ~ 0xFFFF_FFFF_FFFF_FFFF |
3.2 TCR 寄存器:翻译控制的核心
TCR_EL1(Translation Control Register)是 ARM64 页表配置的“总控开关”。它定义了页表的粒度、地址空间大小、缓存策略以及 ASID(Address Space ID)等关键参数。TCR 的配置直接影响 TLB 的命中率和上下文切换的开销。
3.2.1 TCR 关键字段解析
| 字段 | 位域 | 功能描述 | 典型值(Linux 4KB 页) |
|---|---|---|---|
| T0SZ | [5:0] | TTBR0 区域地址大小偏移量。虚拟地址宽度 = 64 - T0SZ | 16(48位虚拟地址) |
| T1SZ | [21:16] | TTBR1 区域地址大小偏移量 | 16(48位虚拟地址) |
| TG0 | [15:14] | TTBR0 区域的翻译粒度(Granule Size) | 00(4KB) |
| TG1 | [31:30] | TTBR1 区域的翻译粒度 | 10(4KB) |
| IPS | [34:32] | 中间物理地址大小(IPA Size) | 010(40位物理地址) |
| SH0/SH1 | [13:12]/[29:28] | 可共享性属性(Inner/Outer Shareable) | 11(Inner Shareable) |
| ORGN0/ORGN1 | [11:10]/[27:26] | Outer 缓存策略(Write-Back/Write-Through) | 01(Write-Back, Read-Allocate) |
| IRGN0/IRGN1 | [9:8]/[25:24] | Inner 缓存策略 | 01(Write-Back, Read-Allocate) |
__cpu_setup 汇编函数会设置 TCR_EL1。对于 4KB 页、48 位虚拟地址的典型配置,TCR 的值通常为 0x80823510。其中 T0SZ=16,TG0=00(4KB),IPS=010(40位物理地址),IRGN0/ORGN0=01(Write-Back)。
3.3 翻译粒度与多级页表结构
ARM64 支持三种翻译粒度:4KB、16KB 和 64KB。不同的粒度决定了页表的级数和单次翻译覆盖的内存范围。粒度越大,页表级数越少,TLB 覆盖范围越大,但内存浪费(内部碎片)也越严重。
3.3.1 粒度与页表级数关系
| 粒度 | 页表级数(48位 VA) | 每级页表条目数 | 单页大小 | TLB 覆盖范围(单条目) |
|---|---|---|---|---|
| 4KB | 4 级(L0~L3) | 512(9位索引) | 4KB | 4KB |
| 16KB | 3 级(L0~L2) | 2048(11位索引) | 16KB | 16KB |
| 64KB | 2 级(L0~L1) | 8192(13位索引) | 64KB | 64KB |
3.3.2 4KB 粒度四级页表地址翻译流程
以最常用的 4KB 粒度、48 位虚拟地址为例,地址翻译过程如下:
- L0 索引:VA[47:39] 作为 L0 页表的索引,从 TTBR0/TTBR1 指向的基址读取 L0 描述符。
- L1 索引:VA[38:30] 作为 L1 页表的索引,从 L0 描述符指向的下一级表基址读取 L1 描述符。
- L2 索引:VA[29:21] 作为 L2 页表的索引,读取 L2 描述符。
- L3 索引:VA[20:12] 作为 L3 页表的索引,读取 L3 描述符(页表条目,PTE)。
- 物理地址:L3 PTE 中的物理页帧号(PFN)与 VA[11:0](页内偏移)拼接,形成最终的物理地址。
early_pgtable_alloc)使用相同的粒度。
3.4 地址映射与页表描述符
ARM64 页表的每个条目(描述符)占用 8 字节。描述符的类型由最低两位决定:
- 00:无效描述符(Fault)。
- 01:表描述符(指向下一级页表)。
- 10:块描述符(直接映射大块内存,如 2MB 或 1GB)。
- 11:页描述符(仅 L3 级,映射 4KB/16KB/64KB 页)。
3.4.1 块映射与页映射对比
| 映射类型 | 所在页表级别 | 映射大小(4KB 粒度) | TLB 占用 | 适用场景 |
|---|---|---|---|---|
| 页映射 | L3 | 4KB | 1 个 TLB 条目 / 4KB | 用户进程堆栈、匿名页 |
| 块映射 | L2 | 2MB | 1 个 TLB 条目 / 2MB | 内核线性映射、大块 DMA 缓冲区 |
| 块映射 | L1 | 1GB | 1 个 TLB 条目 / 1GB | IO 空间映射、大容量内存区域 |
/proc/meminfo 中的 PageTables 字段可以估算页表内存开销。如果发现页表占用过高,可以考虑在 TCR 中启用 块映射(通过页表描述符的 Block 位),或者使用更大的翻译粒度(如 64KB)来减少页表级数。
3.5 内联 SVG:ARM64 四级页表地址翻译流程
3.6 页表配置调试要点
在实际工程中,页表配置错误是导致系统崩溃或性能异常的常见原因。以下是基于 ARM64 硬件原理的调试检查清单:
- 检查 TCR 一致性:使用
mrs x0, tcr_el1读取当前 TCR 值,确认 T0SZ/T1SZ 与虚拟地址宽度匹配,TG0/TG1 与页表生成粒度一致。 - 验证 TTBR 对齐:TTBR0/TTBR1 必须按照页表粒度对齐(4KB 粒度时至少 4KB 对齐,64KB 粒度时至少 64KB 对齐)。未对齐会导致 Alignment Fault。
- 检查描述符类型:在页表遍历代码中,检查每个描述符的最低两位。如果期望是表描述符(01)却读到无效描述符(00),说明页表构建不完整。
- ASID 配置:如果 TCR 的
A1位(位 22)设置为 1,则 TTBR1 使用 ASID;否则使用 TTBR0 的 ASID。上下文切换时务必正确切换 ASID,否则会导致 TLB 污染。
3.7 本章小结
ARM64 多级页表的硬件原理围绕 TTBR0/TTBR1 的地址空间划分、TCR 的灵活配置以及翻译粒度的选择展开。理解这些寄存器的位域含义和它们对页表遍历路径的影响,是进行页表调试和性能分析的基础。在下一工程案例中,我们将基于本章的硬件原理,实际演示如何通过 Linux 内核的 pgd_offset、pud_offset、pmd_offset 和 pte_offset 宏进行页表遍历,并结合性能计数器分析 TLB 缺失率。