在ARMv8/ARMv9架构中,内存属性寄存器(Memory Attribute Indirection Register,简称MAIR)扮演着内存管理的关键角色。作为体系结构工程师,我经常需要深入理解这些寄存器的配置细节。MAIR寄存器通过预定义的内存属性编码(Attr)来配置TLB条目,从而控制处理器对内存区域的访问特性。
MAIR寄存器存在于三个异常级别:
这些寄存器都是64位宽,分为8个8位的Attr字段(Attr0-Attr7)。在页表描述符中,AttrIndx字段用于索引这些预定义的属性。这种间接寻址方式带来了极大的灵活性——我们可以在不修改页表的情况下,通过更新MAIR寄存器来改变内存属性。
关键提示:在启用FEAT_AIE(属性索引扩展)时,AttrIndx[3]为1时会使用MAIR2_ELx寄存器中的扩展属性,这为系统设计提供了更大的配置空间。
设备内存属性通过dd字段进行编码,共支持四种类型:
| dd值 | 内存类型 | 特性描述 |
|---|---|---|
| 0b00 | Device-nGnRnE | 无聚集、无重排序、无早期应答 |
| 0b01 | Device-nGnRE | 无聚集、无重排序、允许早期应答 |
| 0b10 | Device-nGRE | 无聚集、允许重排序、允许早期应答 |
| 0b11 | Device-GRE | 允许聚集、允许重排序、允许早期应答 |
在嵌入式系统开发中,对设备寄存器的访问必须严格遵循内存类型规范。例如,UART等外设寄存器通常配置为Device-nGnRnE,确保操作的严格顺序性。
普通内存属性由oooo(Outer属性)和iiii(Inner属性)共同定义,每个4位字段又包含RW分配策略:
code复制Attr[7:0] = ooooiiii
Outer和Inner属性采用相同的编码格式:
| 编码 | 内存类型 | 描述 |
|---|---|---|
| 0b00RW | Write-Through Transient | 透写、临时性 |
| 0b0100 | Non-cacheable | 不可缓存 |
| 0b01RW | Write-Back Transient | 回写、临时性 |
| 0b10RW | Write-Through Non-transient | 透写、非临时性 |
| 0b11RW | Write-Back Non-transient | 回写、非临时性 |
其中R和W位控制分配策略:
在手机SoC设计中,我们通常将:
MAIR寄存器通过MRS/MSR指令访问,操作编码如下:
assembly复制MRS <Xt>, MAIR_EL1 // 读取MAIR_EL1
MSR MAIR_EL1, <Xt> // 写入MAIR_EL1
对应的系统寄存器编码空间:
code复制op0=0b11, op1=0b000, CRn=0b1010, CRm=0b0010, op2=0b000
不同EL下的访问权限存在严格限制:
| 异常级别 | 读权限 | 写权限 |
|---|---|---|
| EL0 | 禁止 | 禁止 |
| EL1 | 允许* | 允许* |
| EL2 | 允许 | 允许 |
| EL3 | 允许 | 允许 |
*注:EL1访问可能受EL2的陷阱控制(如HCR_EL2.TVM)
在虚拟化场景中,Hypervisor需要通过MAIR_EL2配置客户机的第二阶段转换属性,而客户机OS则使用MAIR_EL1管理自己的页表。
在ARM64 Linux内核中,典型的MAIR配置如下(arch/arm64/include/asm/sysreg.h):
c复制#define MAIR_ATTR_DEVICE_nGnRnE UL(0x00)
#define MAIR_ATTR_DEVICE_nGnRE UL(0x04)
#define MAIR_ATTR_DEVICE_GRE UL(0x0c)
#define MAIR_ATTR_NORMAL_NC UL(0x44)
#define MAIR_ATTR_NORMAL_WT UL(0xbb)
#define MAIR_ATTR_NORMAL UL(0xff)
对应的初始化代码会将这些属性组合到MAIR_EL1寄存器中:
c复制static void init_mair(void)
{
u64 mair = MAIR_ATTRIDX(MAIR_ATTR_DEVICE_nGnRnE, 0) |
MAIR_ATTRIDX(MAIR_ATTR_DEVICE_nGnRE, 1) |
MAIR_ATTRIDX(MAIR_ATTR_NORMAL_NC, 2) |
MAIR_ATTRIDX(MAIR_ATTR_NORMAL, 3);
write_sysreg(mair, mair_el1);
}
根据我在移动芯片开发的经验,这些MAIR配置原则值得关注:
gdb复制(gdb) maintenance packet Qqemu.sregisters
shell复制cat /sys/kernel/debug/kernel_page_tables
当启用以下扩展功能时,MAIR行为会发生变化:
在调试内存问题时,我总是先确认这些特性是否启用,以及它们如何影响现有的内存属性配置。特别是在移植操作系统到新平台时,这些细节往往成为最难排查的问题根源。