MMU简单来说就是两个作用:
1.将虚拟地址转化为物理地址(查表映射),在逻辑上对内存容量进行了扩充(重点讲解)
2.存储访问权限控制(参看http://www.cnblogs.com/stardream/articles/1758289.html)。
ARM11中的3种地址:VA(Virtual Address,虚拟地址),MVA(ModifiedVirtual Address ,变换后虚拟地址),PA(PhysicalAddress,物理地址)。
1)VA,是程序中的逻辑地址,0x00000000~0xFFFFFFFF。CPU核心看到和用到的只是虚拟地址VA。
2)MVA,由于多个进程执行,逻辑地址会重合。所以,跟据进程号将逻辑地址分布到整个内存中。MVA = (PID << 25) | VA。Caches和MMU看不到VA,他们利用MVA转换得到PA。 PID是存放在CP15协处理器的C13寄存器的高7位。
在32位的CPU系统上,虚拟地址空间为2^32 = 4G
VA和MVA的转换公式为:
if(VA < 32M) then
MVA = VA | PID << 25;
else
MVA = VA;
这样,只要每个进程的VA都在32M之内,就可以保证每个进程的MVA不会发生重叠。2^25 = 32M。另外,可以计算出进程最多为2^(32-25) = 128个
利用PID生成MVA的目的是为了减少切换进程时的代价:不使用MVA而直接使用VA的话,当两个进程所用的虚拟地址空间重叠时,在切换进程时为了把重叠的VA映射到不同的PALBB上去,需要重建页表、使无效的caches和TIB等,代价非常大。使用MVA后,进程切换就省事多了。
3)PA,MVA通过MMU转换后的地址。实际设备看不到VA、MVA,读写它们使用的是物理地址PA
由2可知,地址位共32位,PID占7位,所以最多只能有 128 个进程。而每个进程可访问的地址位为25位,故只能分到 32MB 的地址空间。(注:不是物理内存空间)
虚拟地址到物理地址的转换过程。ARM使用页表来进行转换,S3C6410最多会用到两级页表,以段(Section,1M)的方式进行转换时只用到一级页表,以页(Page)的方式进行转换时用到两级页表。页的大小有3种:大页(64KB)、小页(4KB)和极小页(1KB)。一级页表的基地址(TTB:translation table base)则是保存在CP15的C2寄存器中。因此,当程序员创建好相应的页表后,需要将页表基地址写入该寄存器。
首先有个页表基址寄存器TTB(位置在协处理器CP15的寄存器C2),它里面写入的就是一级页表的地址,通过读取它就可以找到一级页表存放的起始位置。一级页表的地址是16K对齐(所以[13:0]为0,使用[31:14]存储页表基址)。一级页表使用4096个描述符来表示4GB空间,所以每个描述符对应1MB的虚拟地址,存储它对应的1MB物理空间的起始地址,或者存储下一级页表的地址。使用MVA[31:20]来索引一级页表(31-20一共12位,2^12=4096,所以是4096个描述符),得到一个描述符,每个描述符占4个字节。
★描述符最后两位为0B10时,即是段的方式映射。[31:20]为段基址,此描述符低20位填充0后就是一块1MB物理地址空间的起始地址。MVA[19:0]用来在这1MB空间中寻址。描述符的位[31:20]和MVA[19:0]构成了这个虚拟地址MVA对应的物理地址。以段的方式进行映射时,虚拟地址MVA到物理地址PA的转换过程如下:①页表基址寄存器位[31:14]和MVA[31:20]组成一个低两位为10的32位地址,MMU利用这个地址找到段描述符;②取出段描述符的位[31:20](段基址),它和MVA[19:0]组成一个32位的物理地址(这就是MVA对应的PA)。
★描述符最后两位为0B01时,即是粗页的方式映射。粗页描述符中存放的是粗页表二级表的基址。 将MVA的[19~12]位用来进行二级页表查寻。粗页表二级表分两种:
1)大页描述符,一个描述符可以对应64KB的内存地址,但16个二级描述符对应同一块内存。
2)小页描述符,一个描述符只对应4KB的内存地址,每个二级描述符只对应一块内存。
下面以保存在粗也表中的大页描述符为例,说明地址转化的过程。
1) 页表基址寄存器为[31:14]和MVA[31:20]组成一个低两位为0地32位地址,MMU利用这个地址找到粗页表描述符。
2) 取出粗页描述符的为[31:10]----即粗页基址,它和MVA[19:12]组成一个低两位为0的32为物理地址-----据此可以找到大页描述符。
3) 取出大页描述符的为[31:16]----即大页基址,它和MVA[15:0]组成一个32位物理地址-----这就是MVA对应的PA。
★描述符最后两位为0B11时,即是细页的方式映射。细页描述符中存放的是细页表二级表的基址。将MVA的[19~10],共计10位用于进行二级页表索引。二级页表共1024个描述符。剩下的10位作为基址,可访问空间为1024B。
使用段映射为例,将vaddr = 0xA0000000映射到paddr = 0x7f000000;
#define GPKCON (volatile unsigned long*)0xA0008820 #define GPKDAT (volatile unsigned long*)0xA0008824 /* * 用于段描述符的一些宏定义 */ #define MMU_FULL_ACCESS (3 << 10) /* 访问权限 */ #define MMU_DOMAIN (0 << 5) /* 属于哪个域 */ #define MMU_SPECIAL (1 << 4) /* 必须是1 */ #define MMU_CACHEABLE (1 << 3) /* cacheable */ #define MMU_BUFFERABLE (1 << 2) /* bufferable */ #define MMU_SECTION (2) /* 表示这是段描述符 */ #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; vaddr = 0x50000000; paddr = 0x50000000; while (vaddr < 0x54000000) { *(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC_WB; vaddr += 0x100000; paddr += 0x100000; } }
void mmu_init() { __asm__(
/*设置TTB*/ "ldr r0, =0x50000000\n" "mcr p15, 0, r0, c2, c0, 0\n"
/*不进行权限检查*/ "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) = 0x1111; *(GPKDAT) = 0xe; return 0; } |
相关资料:http://blog.csdn.net/hevake_lcj/article/details/7400751
http://www.cnblogs.com/stardream/articles/1758289.html
《嵌入式开发完全手册》