在ARMv8/v9架构中,内存管理单元(MMU)通过多级页表转换机制实现虚拟地址到物理地址的映射。与传统x86架构不同,ARM采用了一种间接的内存属性定义方式——通过Memory Attribute Indirection Registers(MAIR)来管理内存区域的访问特性。这种设计带来了显著的灵活性优势。
MAIR寄存器包含MAIR0和MAIR1两个32位寄存器,每个寄存器定义了4组8位内存属性编码(Attr0-Attr7)。在页表项中,仅需通过3位的AttrIndx字段即可索引对应的内存属性,这种间接寻址方式使得:
MAIR0和MAIR1具有完全相同的结构:
code复制31 24 23 16 15 8 7 0
+-----------------+-----------------+-----------------+-----------------+
| Attr7 | Attr6 | Attr5 | Attr4 | MAIR0
+-----------------+-----------------+-----------------+-----------------+
| Attr3 | Attr2 | Attr1 | Attr0 | MAIR1
+-----------------+-----------------+-----------------+-----------------+
每个Attr[n]字段(8位)分为高4位(Attr[n][7:4])和低4位(Attr[n][3:0]),分别控制内存区域的外部和内部属性。
当高4位为0b0000时,表示设备内存,低4位定义具体设备类型:
| Attr[3:0] | 类型 | 特性说明 |
|---|---|---|
| 0b0000 | Device-nGnRnE | 无聚集(No gather)、无重排序(No reorder)、无早期应答(No Early Write Acknowledgement) |
| 0b0100 | Device-nGnRE | 无聚集、无重排序、允许早期应答 |
| 0b1000 | Device-nGRE | 无聚集、允许重排序、允许早期应答 |
| 0b1100 | Device-GRE | 允许聚集、允许重排序、允许早期应答 |
实际开发中,对串口等严格有序的设备应使用nGnRnE,而帧缓冲区等可考虑nGRE类型
对于普通内存,高低4位分别控制外部和内部缓存策略:
外部属性(Attr[n][7:4]):
code复制0b0000: 设备内存(见上)
0b00RW: Outer Write-through transient
0b0100: Outer Non-Cacheable
0b01RW: Outer Write-back transient
0b10RW: Outer Write-through non-transient
0b11RW: Outer Write-back non-transient
内部属性(Attr[n][3:0]):
code复制0b00RW: Inner Write-through transient
0b0100: Inner Non-Cacheable
0b01RW: Inner Write-back transient
0b1000: Inner Write-through non-transient (RW=00)
0b10RW: Inner Write-through non-transient
0b1100: Inner Write-back non-transient (RW=00)
0b11RW: Inner Write-back non-transient
其中RW位表示:
以下是一个嵌入式系统中常见的MAIR配置:
c复制// 设备内存配置
#define DEVICE_nGnRnE 0x00 // 0000 0000
#define DEVICE_nGnRE 0x04 // 0000 0100
// 普通内存配置
#define NORMAL_NC 0x44 // 0100 0100
#define NORMAL_WB_WA 0xFF // 1111 1111
#define NORMAL_WT_RA 0xAA // 1010 1010
// 组合配置MAIR0
uint64_t mair = (DEVICE_nGnRnE << 0) | // Attr0
(DEVICE_nGnRE << 8) | // Attr1
(NORMAL_NC << 16) | // Attr2
(NORMAL_WB_WA << 24) | // Attr3
(NORMAL_WT_RA << 32) | // Attr4
...;
// 写入MAIR寄存器
__set_MAIR(mair);
Linux内核中MAIR配置位于arch/arm64/mm/proc.S:
assembly复制/*
* Default MAIR值
* index attribute
* 0b000 00000000 0 DEVICE_nGnRnE
* 0b001 00000100 1 DEVICE_nGnRE
* 0b010 00001100 2 DEVICE_GRE
* 0b011 01000100 3 NORMAL_NC
* 0b100 11111111 4 NORMAL_WB_WA
* 0b101 10111011 5 NORMAL_WT
*/
#define MAIR_EL1_SET \
(MAIR_ATTRIDX(MAIR_ATTR_DEVICE_nGnRnE, 0) | \
MAIR_ATTRIDX(MAIR_ATTR_DEVICE_nGnRE, 1) | \
MAIR_ATTRIDX(MAIR_ATTR_DEVICE_GRE, 2) | \
MAIR_ATTRIDX(MAIR_ATTR_NORMAL_NC, 3) | \
MAIR_ATTRIDX(MAIR_ATTR_NORMAL_WB_WA, 4) | \
MAIR_ATTRIDX(MAIR_ATTR_NORMAL_WT, 5))
在支持TrustZone的系统中,MAIR寄存器存在安全和非安全两个实例:
这种分离设计使得:
典型配置策略:
c复制// 安全世界配置(EL3)
MAIR_EL3 = (STRICT_DEVICE << 0) | (SECURE_WB << 8);
// 非安全世界配置(EL1)
MAIR_EL1 = (RELAXED_DEVICE << 0) | (NORMAL_NC << 8);
根据内存区域用途选择最优策略:
| 内存类型 | 推荐配置 | 适用场景 |
|---|---|---|
| 代码区 | WB-WA | 高缓存命中率 |
| DMA缓冲区 | Non-Cacheable | 避免缓存一致性问题 |
| 频繁写入数据 | WT-RA | 写操作需立即可见 |
| 设备寄存器 | nGnRnE | 严格保序 |
对于共享内存区域,可采用"内部WB+外部WT"的混合策略:
c复制#define MIXED_ATTRIBUTE 0xBB // 外部WT(1011) + 内部WB(1011)
这种配置在多核系统中可减少缓存一致性流量。
设备访问异常:
性能下降:
一致性问題:
通过读取MAIR_EL1/MAIR_EL3验证当前配置:
c复制uint64_t curr_mair = __get_MAIR();
在MMU Fault处理中检查AttrIndx值:
c复制uint8_t attr_idx = (fault_addr >> 2) & 0x7;
使用ARM DS-5等工具实时监控内存访问特性
紧耦合内存(TCM)通常配置为:
code复制AttrIndx = 0b100 (NORMAL_WB_WA)
同时需在系统控制寄存器中启用TCM:
assembly复制mrc p15, 0, r0, c9, c1, 0 @ 读取TCM状态
orr r0, r0, #1 << 2 @ 启用ITCM
mcr p15, 0, r0, c9, c1, 0 @ 写入配置
运行时更新MAIR的注意事项:
assembly复制dsb ish
mcr p15, 0, r0, c10, c2, 0 @ 写入MAIR0
isb
在Linux中可通过set_memory_attributes()接口实现:
c复制int set_memory_attrs(unsigned long addr, int numpages, ...);
掌握MAIR机制需要结合具体芯片手册实践,不同ARM实现可能有个别差异。建议在开发板上实际验证各种配置,通过性能计数器对比不同策略的效果。对于安全关键系统,应特别关注设备内存的严格排序要求,避免因属性配置不当引入潜在漏洞。