MAIR_EL1 是 ARMv8 架构内存管理的核心,它通过一个索引机制来定义内存的"性格"。简单来说,页表项通过一个3位索引(AttrIndx)来引用 MAIR_EL1 中预先定义好的8种内存属性之一,从而避免了在每个页表项中都重复描述这些复杂的属性。
寄存器结构
MAIR_EL1 是一个 64位 寄存器,它被均匀地划分为 8个字段 ,每个字段宽度为 8位,用于定义一种内存属性。
assembly
MAIR_EL1 的位域分布如下(63-0 位):
+---------------+---------------+-----+---------------+
| Attr7 (63-56) | Attr6 (55-48) | ... | Attr0 (7-0) |
+---------------+---------------+-----+---------------+
-
字段索引 (
n): 8个字段的索引从0到7。 -
选择机制 : 页表描述符的
bit[4:2](即AttrIndx字段) 用于选择Attr<n>。例如,若页表项的AttrIndx为0b001,则 CPU 会使用MAIR_EL1中Attr1字段的值。
支持的属性值
ARMv8 架构将内存主要分为两大类:设备内存 (Device Memory) 和 普通内存 (Normal Memory) 。MAIR_EL1 的每个8位字段必须填入符合特定编码规则的值。
1. 设备内存 (Device Memory)
-
编码格式 :
0b0000dd00(8位) -
属性说明: 用于映射外设地址空间(如UART、GPIO),其访问行为是严格受控的,通常不可缓存。
-
dd编码详解:
| 二进制 | 类型 | 行为说明 |
|---|---|---|
0b00 |
Device-nGnRnE | 最强限制:不允许聚合(Gathering)、不允许重排序(Re-ordering)、不提前写应答(Early Write Acknowledge)。这是最安全的设备内存配置,适用于UART等敏感外设。 |
0b01 |
Device-nGnRE | 允许提前写应答,但不允许聚合和重排序。 |
0b10 |
Device-GRE | 允许聚合、重排序和提前写应答,性能更高,但需设备支持。 |
2. 普通内存 (Normal Memory)
-
编码格式 :
0booooiiii(8位) -
属性说明: 用于映射RAM等常规内存。CPU可对其进行缓存、预取和乱序执行以提升性能。
-
编码详解:
-
oooo(Outer) 和iiii(Inner): 分别控制外部和内部共享域(如多核集群内/间)的缓存策略。 -
RW编码 : 表内RW字段共同决定策略:
-
二进制 (oooo 或 iiii) |
策略 |
|---|---|
0b0000 |
Non-cacheable (不可缓存) |
0b0001 |
Write-Through (写透) |
0b0010 |
Write-Back, Write-Allocate (回写,写分配) |
0b0011 |
Write-Back, no Write-Allocate (回写,非写分配) |
Linux 内核的配置参考
Linux 内核中预定义了几种常用的内存类型和对应的 MAIR_EL1 编码,可以直接参考使用。
| 属性名 | 描述 | MAIR_EL1 编码值 (8位) |
|---|---|---|
| MT_DEVICE_nGnRnE | 强限制设备内存,用于 UART 等 | 0b00000000 (0x00) |
| MT_DEVICE_nGnRE | 允许提前应答的设备内存 | 0b00000100 (0x04) |
| MT_DEVICE_GRE | 允许聚合和重排序的设备内存 | 0b00001000 (0x08) |
| MT_NORMAL_NC | 普通内存,不可缓存 | 0b01000100 (0x44) |
| MT_NORMAL | 普通内存,回写、写分配 | 0b11111111 (0xFF) |
| MT_NORMAL_WT | 普通内存,写透 | 0b10101010 (0xAA) |
使用示例
假设需要配置一个支持回写的普通内存(索引0)和一个严格的设备内存(索引1),代码片段如下:
// 定义宏
#define NORMAL_MEM_ATTR 0xFF // 用于索引 0
#define DEVICE_MEM_ATTR 0x00 // 用于索引 1
// 组装 MAIR_EL1 的值
unsigned long mair_val = (unsigned long)DEVICE_MEM_ATTR << 8 | NORMAL_MEM_ATTR;
// 写入寄存器
__asm__ volatile("msr mair_el1, %0" : : "r" (mair_val));
__asm__ volatile("isb"); // 确保配置生效
在页表项中,设置 AttrIndx 字段(bit[4:2])为 0b000 即选择普通内存,为 0b001 则选择设备内存。
配置的关键步骤
-
计算并写入 : 根据需求计算出64位值,通过
MSR MAIR_EL1, Xn写入。 -
刷新 TLB : 修改后必须刷新TLB,确保旧属性被清除,使新配置生效。使用
TLBI *和DSB/ISB指令。 -
使能 MMU : 确保 MMU 已使能 (
SCTLR_EL1.M = 1)。 -
与页表协同 : 页表项中的
AttrIndx必须与MAIR_EL1中的索引配置相匹配