3. 页表描述符详解:Stage 1 页表描述符格式、属性域

在 ARMv8-A 的 MMU 架构中,Stage 1 页表负责将虚拟地址(VA)转换为物理地址(PA),并携带内存访问所需的全部权限与属性。页表中的每一个条目称为页表描述符(Page Table Descriptor),其格式和属性域直接决定了驱动能否安全、高效地访问外设内存。

3.1 Stage 1 页表描述符格式

Stage 1 页表描述符根据页表级别(Level 0 ~ Level 3)和条目类型(块/页/表)有所不同。以最常用的 Level 2 和 Level 3 为例,描述符的低位用于类型判断,高位用于物理地址和属性控制。

关键格式要点:
  • Bit[1:0]:描述符类型。00 = 无效(Fault),01 = 表(Table),10 = 块(Block)或页(Page),11 = 保留。
  • Bit[47:12](4KB 粒度):输出物理地址的高 36 位,低 12 位由块/页内偏移决定。
  • Bit[11:2]:属性域和标志位,包括访问权限、内存类型、安全位等。
Stage 1 页表描述符位域结构(4KB 粒度) Stage 1 页表描述符位域结构(4KB 粒度) Type Attr RSVD AP MemAttr XN PXN AF SH Output Physical Address [47:12] 0 2 4 6 8 10 11 12 13 14 47 说明: Type[1:0]:00=Fault, 01=Table, 10=Block/Page, 11=Reserved Attr[3:2]:内存属性索引;RSVD[5:4]:保留位;AP[7:6]:访问权限;MemAttr[9:8]:内存类型

3.2 核心属性域详解

Stage 1 描述符中的属性域控制着内存的访问权限、执行权限、脏页管理以及缓存策略。对于驱动开发者,以下五个属性域最为关键:

属性域 位域位置 功能描述 驱动开发影响
XN (Execute Never) Bit[54] 禁止在该内存区域执行指令 保护数据段不被恶意代码注入执行
PXN (Privileged Execute Never) Bit[53] 禁止特权级(EL1/EL2)在该区域执行指令 防止内核漏洞利用用户空间代码
AF (Access Flag) Bit[10] 访问标志位,指示该页是否被访问过 用于页面回收和脏页跟踪
AP (Access Permission) Bit[7:6] 定义读写权限组合(EL0/EL1 分离) 控制用户态与内核态对设备内存的访问
MemAttr (Memory Attributes) Bit[5:4] + MAIR 指定内存类型(Normal/Device)和缓存策略 决定外设寄存器是否被缓存、是否合并写

3.3 XN 与 PXN:执行权限控制

XN 和 PXN 是 ARMv8 引入的两级执行权限控制机制,用于防御代码执行类攻击。

  • XN (Execute Never):当 XN=1 时,无论当前异常级别如何,均不允许从该内存区域取指执行。驱动通常将数据缓冲区、设备 MMIO 区域标记为 XN,防止 CPU 误将数据当作指令执行。
  • PXN (Privileged Execute Never):当 PXN=1 时,仅禁止特权级(EL1/EL2)执行,非特权级(EL0)不受影响。这用于防止内核态跳转到用户态代码执行(如 ret2user 攻击)。
驱动实践建议: 在映射设备内存(如 DMA 缓冲区、FIFO 寄存器)时,务必同时设置 XN=1 和 PXN=1。即使设备内存本身不包含指令,设置执行保护可以防止因指针错误导致的意外执行。
XN/PXN 执行权限决策流程 XN/PXN 执行权限决策流程 取指请求到达 MMU XN=1 ? 拒绝执行 PXN=1 & EL1? 拒绝执行 允许执行

3.4 AF:访问标志位

AF(Access Flag)位用于记录该页表描述符对应的物理页是否被访问过。当 AF=0 时,MMU 在第一次访问该页时会触发 Access Flag 故障,由操作系统软件将 AF 置 1 后重试。

  • 硬件行为:MMU 在每次访问页表时检查 AF 位。若 AF=0,则产生同步异常(Data Abort),异常类型为 Access Flag Fault。
  • 软件职责:内核的缺页异常处理程序需要识别该故障类型,将对应描述符的 AF 置 1,并清除 TLB 中对应的缓存条目。
  • 驱动影响:驱动在初始化时应当确保所有映射的描述符 AF=1,避免首次访问时产生不必要的性能开销。对于热插拔设备或动态映射区域,建议在映射完成后立即设置 AF=1。
常见陷阱: 某些 SoC 的 MMU 实现要求 AF 位必须由软件显式置 1,否则即使映射正确也会持续触发 Access Flag 异常。驱动在 ioremap 或 dma_alloc_coherent 后,应检查底层页表是否已正确设置 AF 位。

3.5 AP:访问权限控制

AP(Access Permission)域由两位组成,定义该页在 EL0(用户态)和 EL1(内核态)下的读写权限组合。

AP[7:6] EL1 权限 EL0 权限 典型用途
00 读写 无访问 内核专用数据、设备寄存器
01 读写 读写 用户共享内存、帧缓冲区
10 只读 无访问 内核只读数据(rodata)
11 只读 只读 用户只读数据(代码段)

驱动在映射设备内存时,通常使用 AP=00(仅内核读写),确保用户态程序无法直接操作设备寄存器。若需要将 DMA 缓冲区共享给用户态,则使用 AP=01,并配合 XN=1 防止执行。

3.6 MemAttr:内存类型与缓存策略

MemAttr 域通过 2 位索引值(Bit[5:4])指向 MAIR(Memory Attribute Indirection Register)中预定义的属性条目。MAIR 寄存器包含 8 个槽位,每个槽位定义一种内存类型和缓存策略。

对于驱动开发,最常用的两种内存类型是:

  • Normal Memory:用于常规 RAM,支持缓存、写合并、预取等优化。驱动中的数据缓冲区应使用 Normal 类型以获取最佳性能。
  • Device Memory:用于外设寄存器,禁止缓存、禁止写合并、禁止预取。所有 MMIO 区域必须映射为 Device 类型,否则会导致寄存器读写顺序错乱或数据丢失。
Device 内存的三种子类型:
  1. Device-nGnRnE:最严格模式,不合并、不重排、不提前应答。适用于控制寄存器。
  2. Device-nGnRE:允许提前应答,适用于 FIFO 数据寄存器。
  3. Device-nGRE:允许合并写,适用于帧缓冲区等对顺序要求较低的场景。
MemAttr 索引与 MAIR 映射关系 MemAttr 索引与 MAIR 映射关系 页表描述符 MemAttr[1:0] = 0b01 MAIR_EL1 寄存器 Attr0 Attr1 Attr2 Attr3 Attr4 Attr5 Attr6 Attr7 示例:MemAttr=0b01 指向 MAIR 的 Attr1 槽位 Attr1 预定义值:0xFF(Normal Memory, Write-Back, Read-Allocate, Write-Allocate) Attr0 预定义值:0x00(Device-nGnRnE)— 用于 MMIO 区域

3.7 属性域组合配置示例

以下是一个典型的设备驱动页表描述符配置示例,用于映射一个 4KB 的 UART 寄存器区域:

// 伪代码:配置 Stage 1 页表描述符属性域
// 目标:映射 UART 基址 0x1C090000,虚拟地址 0xFC090000

uint64_t descriptor = 0;

// 类型:页(Page),4KB 粒度
descriptor |= (0b10 << 0);   // Bit[1:0] = 10

// 输出物理地址(高 36 位)
descriptor |= (0x1C090000 >> 12) << 12;

// AP:仅内核读写
descriptor |= (0b00 << 6);   // AP[7:6] = 00

// MemAttr:Device-nGnRnE(索引 0)
descriptor |= (0b00 << 4);   // MemAttr[5:4] = 00

// AF:已访问
descriptor |= (1 << 10);     // AF = 1

// XN:禁止执行
descriptor |= (1 << 54);     // XN = 1

// PXN:禁止特权执行
descriptor |= (1 << 53);     // PXN = 1

// 写入页表条目
page_table[va_index] = descriptor;
配置检查清单:
  • 设备 MMIO 区域:AP=00, MemAttr=Device-nGnRnE, XN=1, PXN=1, AF=1
  • DMA 数据缓冲区:AP=00, MemAttr=Normal WB, XN=1, PXN=1, AF=1
  • 用户共享内存:AP=01, MemAttr=Normal WB, XN=1, PXN=0, AF=1