3. MMU初始化流程:系统寄存器配置(TTBR0/TTBR1)、MAIR属性设置、SCTLR使能MMU
MMU(内存管理单元)的初始化是嵌入式系统启动过程中最关键的步骤之一。在ARMv8-A架构中,MMU的启用必须遵循严格的寄存器配置顺序:首先设置内存属性寄存器(MAIR),然后配置页表基地址寄存器(TTBR0/TTBR1),最后通过系统控制寄存器(SCTLR)使能MMU。任何步骤的遗漏或顺序错误都将导致系统异常或不可预测的行为。
3.1 MMU初始化总体流程
下图展示了MMU初始化的标准三阶段流程,每个阶段对应一组关键寄存器的配置:
3.2 MAIR寄存器配置:定义内存属性
MAIR(Memory Attribute Indirection Register)是MMU初始化时第一个必须配置的寄存器。它定义了8个内存属性编码(Attr0~Attr7),每个编码对应一个内存区域的缓存策略和访问特性。页表描述符中的AttrIndex字段通过索引MAIR编码来决定该页面的实际内存属性。
3.2.1 MAIR寄存器格式
| 位段 | 字段名 | 描述 |
|---|---|---|
| [63:56] | Attr7 | 属性编码7,用于页表AttrIndex=7 |
| [55:48] | Attr6 | 属性编码6,用于页表AttrIndex=6 |
| [47:40] | Attr5 | 属性编码5,用于页表AttrIndex=5 |
| [39:32] | Attr4 | 属性编码4,用于页表AttrIndex=4 |
| [31:24] | Attr3 | 属性编码3,用于页表AttrIndex=3 |
| [23:16] | Attr2 | 属性编码2,用于页表AttrIndex=2 |
| [15:8] | Attr1 | 属性编码1,用于页表AttrIndex=1 |
| [7:0] | Attr0 | 属性编码0,用于页表AttrIndex=0 |
3.2.2 常用内存属性编码
每个Attr字段的8位编码中,低4位定义设备内存类型,高4位定义正常内存的缓存策略。以下是嵌入式系统中常用的属性配置:
| 属性名称 | Attr编码 | 内存类型 | 缓存策略 | 典型用途 |
|---|---|---|---|---|
| Normal_WBWA | 0xFF | Normal | Write-Back, Write-Allocate | DDR内存、堆栈 |
| Normal_NCNB | 0x44 | Normal | Non-Cacheable, Non-Bufferable | DMA缓冲区(一致性) |
| Device_nGnRnE | 0x00 | Device | nG(非聚集), nR(非重排), nE(早写) | MMIO寄存器 |
| Device_nGnRE | 0x04 | Device | nG(非聚集), nR(非重排), E(早写允许) | 以太网MAC寄存器 |
MAIR配置示例代码(ARMv8-A汇编):
/* 配置MAIR_EL1寄存器 */
/* Attr0: 0xFF - Normal, WB, WA, R/W Allocate */
/* Attr1: 0x44 - Normal, Non-Cacheable, Non-Bufferable */
/* Attr2: 0x00 - Device, nGnRnE */
/* Attr3: 0x04 - Device, nGnRE */
/* Attr4~Attr7: 0x00 (未使用) */
ldr x0, =0x00040044FF
msr mair_el1, x0
isb
3.3 TTBR0/TTBR1寄存器配置:页表基地址
TTBR(Translation Table Base Register)寄存器保存了页表的物理基地址。ARMv8-A架构使用两个独立的页表基地址寄存器:
- TTBR0_EL1:用于低地址空间(用户空间),虚拟地址范围由TCR.T0SZ决定
- TTBR1_EL1:用于高地址空间(内核空间),虚拟地址范围由TCR.T1SZ决定
3.3.1 TTBR寄存器格式
| 位段 | 字段名 | 描述 |
|---|---|---|
| [63:48] | ASID | 地址空间标识符,用于TLB区分进程 |
| [47:1] | BADDR | 页表物理基地址(64KB对齐) |
| [0] | Reserved | 保留位,必须为0 |
3.3.2 页表基地址配置步骤
- 确定页表层级:根据TCR配置选择页表层级(L0~L3)
- 分配页表内存:在物理内存中分配页表空间(必须64KB对齐)
- 填充页表条目:写入页表描述符,设置物理地址映射和访问权限
- 写入TTBR:将页表基地址写入TTBR0/TTBR1
- 执行DSB+ISB:确保页表写入完成并同步
实践建议:在配置TTBR之前,建议先通过TCR寄存器设置页表大小和粒度。典型的配置是TCR使用4KB粒度、40位输入地址空间(T0SZ=24),此时TTBR指向L1页表基地址。
3.4 SCTLR寄存器:使能MMU
SCTLR(System Control Register)是MMU初始化的最后一步。该寄存器的M位控制MMU的全局使能,C位控制数据缓存,I位控制指令缓存。
3.4.1 SCTLR关键位域
| 位 | 名称 | 描述 | 推荐值 |
|---|---|---|---|
| [0] | M | MMU使能位:0=禁用,1=使能 | 1 |
| [1] | A | 对齐检查使能 | 0 |
| [2] | C | 数据缓存使能 | 1 |
| [3] | WXN | 写权限不可执行 | 1 |
| [12] | I | 指令缓存使能 | 1 |
| [25] | EE | 端序配置(0=小端,1=大端) | 0 |
3.4.2 使能MMU的完整代码序列
/* 步骤1:配置TCR寄存器(页表控制) */
ldr x0, =0x80823520 /* T0SZ=24, TG0=4KB, SH0=Inner, ORGN=WBWA, IRGN=WBWA */
msr tcr_el1, x0
isb
/* 步骤2:配置MAIR寄存器(内存属性) */
ldr x0, =0x00040044FF
msr mair_el1, x0
isb
/* 步骤3:配置TTBR0(页表基地址) */
/* 假设页表基地址在x1寄存器中 */
msr ttbr0_el1, x1
isb
/* 步骤4:读取当前SCTLR值,修改M/C/I位 */
mrs x0, sctlr_el1
orr x0, x0, #(1 << 0) /* M位:使能MMU */
orr x0, x0, #(1 << 2) /* C位:使能数据缓存 */
orr x0, x0, #(1 << 12) /* I位:使能指令缓存 */
msr sctlr_el1, x0
isb
/* 步骤5:使能后立即执行地址转换验证 */
/* 读取当前PC的物理地址(可选调试步骤) */
at s1e1r, x30 /* 将x30(链接寄存器)的虚拟地址转换为物理地址 */
isb
⚠ 重要安全提示:
- 使能MMU前,必须确保当前执行地址(PC)所在的页面在页表中已正确映射,且映射属性允许执行
- 使能MMU后,所有指令地址立即通过MMU转换,如果页表映射错误将立即触发异常
- 建议在使能MMU前先建立恒等映射(identity mapping),确保转换过程平滑过渡
- 使能MMU后,必须使用ISB指令刷新流水线,确保后续指令使用新的MMU配置
3.5 完整初始化流程总结
下表总结了MMU初始化的三个关键阶段及其寄存器配置要点:
| 阶段 | 寄存器 | 配置内容 | 同步指令 | 常见错误 |
|---|---|---|---|---|
| 1. 属性定义 | MAIR_EL1 | 设置8个Attr编码,定义内存类型和缓存策略 | ISB | Attr编码与页表描述符不匹配 |
| 2. 页表基址 | TTBR0_EL1 / TTBR1_EL1 | 写入页表物理基地址(64KB对齐) | DSB + ISB | 基地址未对齐或页表未初始化 |
| 3. 使能MMU | SCTLR_EL1 | 设置M位=1,可选设置C/I位 | ISB | 未建立恒等映射导致跳飞 |
3.6 调试与验证方法
MMU初始化完成后,建议通过以下方法验证配置的正确性:
- 读取寄存器回读:通过
mrs x0, mair_el1等指令读取寄存器值,确认写入正确 - 地址转换测试:使用AT指令(Address Translation)将已知虚拟地址转换为物理地址,验证映射正确性
- TLB刷新验证:使能MMU后执行TLBI指令刷新TLB,确保旧映射被清除
- 异常向量验证:故意访问未映射地址,确认系统能正确触发Page Fault异常
调试代码示例:验证地址转换
/* 验证虚拟地址0x80000000的物理映射 */
ldr x0, =0x80000000 /* 待转换的虚拟地址 */
at s1e1r, x0 /* 执行地址转换 */
isb
mrs x1, par_el1 /* 读取转换结果寄存器 */
/* 检查PAR_EL1[0]位:0=转换成功,1=转换失败 */
tbnz x1, #0, fault_handler