uboot step 11 内存一下子变大了 ----MMU的配置与使用
本文结构如下:
MMU 介绍
MMU 相关寄存器
MMU 内存访问权限控制
MMU 使能
-
MMU 页表描述
- 一级页表描述符地址
- 一级页表描述符格式
- 二级页表描述符格式
- 虚拟地址到物理地址转化 (重要)
- 1M段式
- 16M段式
- 64KB大页
- 4KB小页
代码实现:使用虚拟地址去控制LED的例子
参考
http://www.it165.net/embed/html/201408/2633.html
http://comm.chinaaet.com/adi/blogdetail/40052.html
在前面初始化的过程中,由于MMU与Cache会影响我们进行初始化的工作,因此关闭了MMU与Cache,在那篇文章中已经说过了MMU的作用,这里再重新说下:
- 1.完成虚拟地址到物理地址的转换
- 2.对内存的访问权限进行控制
首先还是先来简要说下相关与MMU相关的寄存器,先有个大概印象,后面用到了再去详细的看:
名称 | 描述 | 简写 |
---|---|---|
Translation Table Base Register 0 | 保存一级转换表的物理地址 | TTBR0 |
Translation Table Base Register 1 | 保存一级转换表的物理地址 | TTBR1 |
Translation Table Base Control Register | 一级转换表控制相关 | TTBCR |
Domain Access Control Register | 域访问权限控制 | |
C1 Control Register | MMU和cache使能 |
MMU使能
- 1.编程所有相关的CP15寄存器正确的值
- 2.编程建立所要求的一级页表,二级页表
- 3.关闭I-cache
- 4.使能MMU
MMU 内存访问权限控制
-
Domains 域控制
一个域被联系到一些内存区域,在页表描述符中有个Domain域来标识此区域内存属于哪一个域Domian来控制,ARM1176 支持16个Domains,域访问权限控制是由C3来设定的,如下图:
每个域由两位来控制,32位寄存器,总共16个域
- 00 无权限,任何访问都会产生domain fault
- 01 Client 检查TLB页表描述符的权限位是否允许访问
- 10 保留,产生domain fault
- 11 Manager 不检查访问权限,不会产生权限错误
-
访问权限控制
在域控制中,提到会检查TLB页表描述符的权限位,这个权限位就是APX,AP位,在页表描述符中有这几个域
-
可执行区域XN位
- 如果含有可执行区域,XN=0
- XN=1时,在此内存区域任何试图去执行指令的行为会产生权限错误
MMU 页表描述
为了支持段和页的映射方式,MMU使用两级页表描述符,一级页表描述符决定访问的是一个分段还是一个分页式的表,如果访问的是一个分页式的页表,处理器MMU决定页表类型是大页还是小页并找到二级页表。
一级页表描述符地址
ARM1176 包含 两个转换表基地址寄存器TTBR0和TTBR1,一个转换表基地址控制寄存器,当一个TLB未命中时,虚拟地址最高位决定使用的基地址寄存器是哪一个,采用两个转换表基地址期望去减少OS上下文切换的花费,每个独立的任务或进程,有他自己的页表而不用消耗大量内存。整个虚拟内存空间被分为两个部分,用户空间和内核空间
0x0 -> 1<<(32-N) that TTBR0 controls
1<<(32-N) -> 4GB that TTBR1 controls.
N的值在TTBCR寄存器中进行设定,N的大小决定了内核空间和用户空间的分界线,当N=0时,表示只使用了TTBR0寄存器
一级页表描述符格式分析
如上图所示:
- bit[1:0]: 映射类型,分段式还是分页式。
- 00 忽略
- 11 无效,返回Translation fault
- 10 分段式
- 01 分页式
- nG : 0 转换表被标记为全局的; 1 转换表属于特定进程
- S : 共享位,0 非共享; 1 共享内存
- XN: 0 包含可执行代码; 1 不包含可执行代码
- APX,AP位: 权限访问控制位
- Doman : 域标识,属于哪个域
- P: ECC校验
- TEX,C,B: 此区域是否采用缓冲buffer,cache,还是直接访问,一般外设采用无缓冲,内存采用缓冲方式(个人理解)
- NS:No-Secure 属性
二级页表描述符格式分析
如上图所示,和一级页表描述符格式相似,相应的位的功能是一样的。不同的是最后两位代表了两种不同的分页类型。
- bit[1:0] : 01 时,采用粗粒度大页64K进行映射 1X时,采用小(细)页4K进行映射
虚拟地址到物理地址的转化
MMU对于刚接触的人可能会感觉到有些不知所措,因为它有那么多相关的寄存器,还有什么TLB,一级页表,二级页表,粗细粒度,等等,完全一个大写的懵,认为MMU的使用是个很难的过程,当你从了解地址转化流程开始入手学习,慢慢了解了之后,便会觉得其实还是蛮简单的,下面来看下S3c6410中MMU的地址转化流程:
- TLB:Translation Lookaside Buffer,可以称为快表,当要进行虚拟地址到物理地址转化的时候,MMU便会去查询这个表来确定虚拟地址所对应的物理地址是多少,而这个表也被我们保存在了内存的某个地址,这个地址称为转换表基地址,我们需要将这个基地址写到转换表基地址寄存器中。
下面分四种情况对映射转化过程作下说明:
- 分段式映射,大小1M
- 分段式映射,大小16M
- 分页式映射,大小64KB 粗页
- 分页式映射,大小4KB 细页
1M分段式映射
- 页表基地址(TTBRx寄存器中)18bits[31:14]+虚拟地址12bits[31:20] +2[00] 构成了一级页表描述符的地址
- 取得了一级页表描述符的地址,访问这个地址从中可以得到真实的物理段基地址[31:20]
- 将得到的物理段基地址[31:20]加上虚拟地址中的[19:0]位偏移地址构成了真正的物理地址
每个虚拟地址可以索引 2^12 个一级描述符地址,每个一级描述符可以包含 2^20 个物理地址,总共可以索引4G空间
16M分段映射
与1M的段映射相似,只是有些地址线的范围发生了改变,另外一级描述符的第18位为1表示16M的段映射,0表示1M段映射
64KB分页映射
- 页表基地址+虚拟地址的[31:20]位得到了一级描述符的地址
- 一级描述符的[31:10]位为二级描述符的基地址
- 二级描述符的基地址+虚拟地址的[19:16]位,最后几位补0构成了二级页表描述符的地址
- 二级页表描述符的第[31:16]位为物理基地址+虚拟地址的[15:0]页内偏移地址构成了真正的物理地址
注:上图映射是从内核手册中截取的,有些错误,二级页表描述符的Second-level tabel index 应该为96位,50位应为0
4KB分页映射
- 页表基地址+虚拟地址的[31:20]位得到了一级描述符的地址
- 一级描述符的[31:10]位为二级描述符的基地址
- 二级描述符的基地址+虚拟地址的[19:12]位,最后几位补0构成了二级页表描述符的地址
- 二级页表描述符的第[31:12]位为物理基地址+虚拟地址的[11:0]页内偏移地址构成了真正的物理地址
代码实现-控制led
#define GPKCON (volatile unsigned long*)0xA0008800
#define GPKDAT (volatile unsigned long*)0xA0008808
/*
* 用于段描述符的一些宏定义
*/
#define MMU_FULL_ACCESS (3 << 10) // 访问权限 AP
#define MMU_DOMAIN (0 << 5) // 属于哪个域 Domain
#define MMU_SPECIAL (1 << 4) // 必须是1 XN
#define MMU_CACHEABLE (1 << 3) // cacheable C
#define MMU_BUFFERABLE (1 << 2) // bufferable B
#define MMU_SECTION (2) // 表示这是段描述符 10
#define MMU_SECDESC (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_SECTION)
#define MMU_SECDESC_WB (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)
void create_page_table(void)
{
unsigned long *ttb = (unsigned long *)0x50000000;
unsigned long vaddr, paddr;
vaddr = 0xA0000000;
paddr = 0x7f000000;
*(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC; //将虚拟地址A000映射到led所对应的物理地址区间
vaddr = 0x50000000;
paddr = 0x50000000;
while (vaddr < 0x54000000)//64M的区间地址虚拟地址与物理地址映射相同, 或者说相当于没有进行地址转化
{
*(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC_WB;
vaddr += 0x100000;
paddr += 0x100000;
}
}
void mmu_init()
{
__asm__(
/*设置TTB Base addr*/
"ldr r0, =0x50000000\n"
"mcr p15, 0, r0, c2, c0, 0\n"
/*不进行权限检查 Domain*/
"mvn r0, #0\n"
"mcr p15, 0, r0, c3, c0, 0\n"
/*使能MMU*/
"mrc p15, 0, r0, c1, c0, 0\n"
"orr r0, r0, #0x0001\n"
"mcr p15, 0, r0, c1, c0, 0\n"
:
:
);
}
int main()
{
create_page_table();
mmu_init();
*(GPKCON) = 0x11110000;
*(GPKDAT) = 0xa0;
return 0;
}
此去经年
zhaiyk@sina.cn
August 10, 2016