mmu的主要作用就是实现进程隔离
主要功能是
1 虚拟地址到物理地址的转换
访问某个进程1的虚拟地址A。mmu转换为物理地址B访问。
访问某个进程2的虚拟地址A。mmu转换为物理地址C的访问。
保证不同空间上的同一个虚拟地址访问映射到不同的物理内存。
2 内存权限控制的实现
在页表项中将某些不需要的bit项变成访问控制位,控制内存的访问。
在ARM中使用CP15协处理来控制MMU。
CR0 (有两个物理寄存器) 分别用于记录ARM的架构以及CACHE类型等等信息
CR1 控制寄存器,控制各种行为,包括开启关闭 MMU ,CACHE控制,控制异常向量的位置(0X0还是0XFFFF0000)
CR2 一级页表基地址
CR3 域的访问权限
...
....
以及其他CRx寄存器
UBOOT 部分:
下面的主要参考树莓派的BCM2835芯片,arm的架构是arm1176。
参考的手册是
arm1176jzfs.pdf ARM手册
BCM2835-ARM-Peripherals.pdf 芯片手册
MMU提供4K 16K 1M 16M段管理
首先来看以1M作为映射。
每一个页表条目(4字节)代表的每一个页面是1M大小,为了表示4GB的空间,所以一共需要 4096MB/1MB * 4字节页表条目=4KB的大小。
也就是从page_table开始的4KB大小的内存每四个字节存放一个页表条目,一共4096项,每一项代表1MB的空间。如下图
(对于arm1176jzfs CP15的c1寄存器的bit23控制AP位,AP位开,地址转换兼容ARMV4 V5,AP关,地址转换采用ARMV6架构 )
具体的每个条目的每一项参看芯片手册比较好,这里只讲两个重要的。
[0:1] 表示当前条目的类型 00表示无效映射 10表示段页1M或者16M,具体是16M还是1M由第18bit控制
[2:3]表示缓冲缓存类型
[11:10] 表示当前页面的访问权限
[31:20] 表示这个条目映射的 物理空间的内存地址,因为条目是1M为单位,因此后20bit一定是0,恰好可以用来做一些标记位
用树莓派uboot初始化的时候页表操作来演示,如果开启MMU,则调用了
for (i = 0; i < 4096; i++)
set_section_dcache(i, DCACHE_OFF);
执行第一次的初始化
其中set_section_dcache函数如下
void set_section_dcache(int section, enum dcache_option option)
{
u32 *page_table = (u32 *)gd->arch.tlb_addr;
u32 value = TTB_SECT_AP;//0XC00
/* Add the page offset */
//#define MMU_SECTION_SHIFT 20
value |= ((u32)section << MMU_SECTION_SHIFT);
/* Add caching bits */
value |= option;
/* Set PTE */
page_table[section] = value;
}
首先value=TTB_SEC_AP=0xC00
DCACHE_OFF =0x12=0b 1(4bit) 0(3bit) 0(2bit) 1(1bit) 0(0bit)
0x0000c12 0x0010c12 0x0020c12 0x0030c12 0x0040c12...... 0xFFE0c12 0xFFF0c12
一共4096项
设置完毕以后。又调用dram_bank_mmu_setup这次主要是将有效的内存设置为DCACHE_WRITEBACK
前面我们设置页表的时候,一共设置了4GB大小的内存空间,但是这其中很多是无效或者其他IO空间。
比如树莓派一共有512M内存,除去 64MB大小的GPU空间,那么剩下448MB的内存,因此将这448M空间设置为DCACHE_WRITEBACK。
随后直接开启了MMU,这样就进入了虚拟地址空间,因为物理地址和虚拟地址是一一对应的,因此不需要考虑代码重定位的问题。
-
虚拟地址的[31:20]位存放一级页表的入口index,[19:0]位存放段偏移;
-
从TTBR(translation table base register,协处理器CP15中的一个寄存器,用于存放一级页表的基址)寄存器中获取一级页表的基址;
-
一级页表基址+ VA[31:20] = 该虚拟地址对应的页表描述符的入口地址;
-
页表描述符的[31:20]位为该虚拟地址对应的物理段基址;
-
物理段基址+ VA[19:0]段偏移= 物理地址
比如当前的虚拟地址是 0X123ABCDE。
1 那么0X123就是一级页表的索引 ABCDE就是段内的偏移
2 取得c2寄存器,一级页表的地址0XA0000000。
3 一级页表的地址0XA0000000+VA[31:20](0X123)=0XA0000123(物理地址)
4 0XA0000ABC物理地址中得到 关于该段的四字节描述 0X1230C12。 得到页表描述的[31:20]为0X123
5 物理段基地址+va[19:0]偏移 得到真实访问的物理地址 0X123+0XABCDEF得到最终的物理地址 0X123ABCDEF
实际上 虚拟地址和物理地址 是一样的。
那既然虚拟地址和物理地址是一样的,为什么又要开启MMU多此一举呢?我认为可能是为了使用DCACHE和ICACHE功能,这样能够加速UBOOT的启动和一些执行。
对于多级页表,道理是差不多的。
在UBOOT里面由于是一对一映射,所以就和访问物理地址是一样的。比如有一些IO地址,只需要直接访问就可以了。
所以UBOOT里面直接操作串口输出是没有问题的。