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
关键概念: 虚拟地址的最高有效位(MSB)决定了使用哪个 TTBR。如果 MSB 为 0,硬件自动选择 TTBR0;如果 MSB 为 1,则选择 TTBR1。这种硬件决策机制无需软件干预,极大提升了地址翻译的效率。

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)
配置示例: 在 Linux 内核启动阶段,__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 位虚拟地址为例,地址翻译过程如下:

  1. L0 索引:VA[47:39] 作为 L0 页表的索引,从 TTBR0/TTBR1 指向的基址读取 L0 描述符。
  2. L1 索引:VA[38:30] 作为 L1 页表的索引,从 L0 描述符指向的下一级表基址读取 L1 描述符。
  3. L2 索引:VA[29:21] 作为 L2 页表的索引,读取 L2 描述符。
  4. L3 索引:VA[20:12] 作为 L3 页表的索引,读取 L3 描述符(页表条目,PTE)。
  5. 物理地址:L3 PTE 中的物理页帧号(PFN)与 VA[11:0](页内偏移)拼接,形成最终的物理地址。
性能陷阱: 如果 TCR 配置的粒度与页表实际使用的粒度不匹配,硬件会触发 Translation Fault。例如,TCR 配置为 4KB 粒度,但页表描述符中使用了 2MB 块映射(块描述符),硬件仍然可以正常工作(块映射是 ARM64 支持的),但如果 TCR 配置为 64KB 粒度,则无法识别 4KB 页表结构。务必确保 TCR 与页表生成代码(如 Linux 的 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 四级页表地址翻译流程

ARM64 4KB 粒度四级页表地址翻译流程 虚拟地址 (48位): VA[47:0] L0 索引 [47:39] L1 索引 [38:30] L2 索引 [29:21] L3 索引 [20:12] 偏移 [11:0] L0 页表 (512 条目) 基址: TTBR0/TTBR1 L1 页表 (512 条目) 基址: L0 描述符 L2 页表 (512 条目) 基址: L1 描述符 L3 页表 (512 条目) 基址: L2 描述符 物理地址 = PFN[47:12] + VA[11:0] 说明: 每个页表条目(描述符)8 字节。L0~L2 描述符可以是表描述符(指向下一级)或块描述符(直接映射 2MB/1GB)。L3 描述符只能是页描述符(4KB)。

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 污染。
实战案例: 在某 ARM64 服务器上,内核启动时触发 Translation Fault 并进入 Exception Level 3(EL3)的同步异常。通过 JTAG 读取 TCR_EL1 发现 TG0 字段被错误配置为 10(64KB 粒度),但页表实际使用 4KB 粒度。修正 TG0 为 00 后系统正常启动。此案例说明 TCR 与页表生成代码必须严格同步。

3.7 本章小结

ARM64 多级页表的硬件原理围绕 TTBR0/TTBR1 的地址空间划分、TCR 的灵活配置以及翻译粒度的选择展开。理解这些寄存器的位域含义和它们对页表遍历路径的影响,是进行页表调试和性能分析的基础。在下一工程案例中,我们将基于本章的硬件原理,实际演示如何通过 Linux 内核的 pgd_offset、pud_offset、pmd_offset 和 pte_offset 宏进行页表遍历,并结合性能计数器分析 TLB 缺失率。