一、MMU的介绍
MMU全称Memory Management Unit,中文称内存管理单元。
主要有两个功能:
A.将虚拟地址转换成实际的物理地址
B.对物理内存设置访问权限
二、MMU的工作过程
在s3c2410中MMU是由协处理器(cp15)控制的,s3c2410/s3c2440最多会用到两级页表:以段(Section,1MB)的方式进行转换时只用到一级页表,以页(page)的方式进行转换时用到两级页表。页的大小有3种:大页(64KB),小页(4KB),极小页(1KB)。
明确一个概念:
条目也称为"描述符"(Descriptor),有:段描述符,大页描述符,小页描述符,极小页描述符----它们保存段、大页、小页或极小页的起始物理地址;粗页表描述符、细页表描述符---他们保存二级页表的物理地址
转换过程如下:
(1) 根据给定的虚拟地址找到一级页表中的条目
(2)如果此条目是段描述符,则返回物理地址,转换结束
(3)如果此条目是二级页表描述符,继续利用虚拟地址在二级页表中找到下一个条目;
(4)如果这第二个条目是叶描述符,则返回物理地址,转换结束;
(5)其他情况出错
注意:这里面所有的转换过程都是由MMU完成的
以段的方式映射实例说明:
例如:虚拟地址 0xa0004000
注意:当MMU打开以后,所有的地址都会被MMU拦截,然后将其转换,cpu是不管虚拟地址还是实际物理地址的。
转换如下:
先来看看TTB
简单的来说,它保存了一级页表所存放的实际物理地址,要求16KB对齐,以段的方式映射,4GB的虚拟地址空间,需要段描述符4096个(每个段描述符映射1M空间),没个描述符占用4byte,所以一段的方式映射一级页表占用的空间为16KB。
在这里我们假设,我们的一级页表存放在物理地址:0x30000000.
第一步:
获得虚拟地址所对应的段描述符所在的地址
addr = TTB&0xffffc000 | ((viraddr >> 20) << 2 ) = 0x30000000 & 0xfffc000 | ((0xa0004000 >> 20) << 2)
= 0x30000000 | (0xa00 << 2) = 0x30002800
第二步:
从0x30002800取出虚拟地址所对应的段描述符
段描述的构造我们到后面再来讲解,这里我们假设我们把0xa0004000映射到实际的物理地址0x30004000,则这里的[31:20]为0x300
第三步:
组合成实际的物理地址
phyaddr = 0x300 << 20 | (0xa0004000 & 0xfffff) = 0x30004000
三.实验
目标:以段的方式映射s3c2410的地址空间,一级页表存放在0x30000000
流程:
A.计算每个虚拟地址对应段描述符所在的地址(addr),方法如下:
B.构造段描述符
注意:Section base address 存放的是实际的物理地址的[31:20]
C.存放段描述符
(unsigned int *)addr = section descriptor
D.使能MMU
整个流程比较复杂的就是段描述符的构造,具体的流程大家可以直接看芯片手册,写的很详细
实例代码:
- /*Nand 启动sdram的起始地址*/
- #define SRAM_START_ADDR 0x00000000
- /*内存空间地址*/
- #define VMRAM_ADDR_START 0xa0000000
- #define SDRAM_ADDR_START 0x30000000
- #define SDRAM_ADDR_END 0x34000000
- /*IO空间地址*/
- #define VMIO_ADDR_START 0xb0000000
- #define PHIO_ADDR_START 0x56000000
- #define PHIO_ADDR_END 0x56010000
- /*用SDRAM起始地址开始的16KB,存放页表*/
- #define PAGE_TABLE_BASE 0x30000000
- /*MASK*/
- #define PAGE_TABLE_BASE_MASK 0xffffc000
- #define VIRADDR_MASK 0xfff00000
- #define PHYADDR_MASK 0xfff00000
- /*页表项内容*/
- #define PAGE_TABLE_SECTION_AP (0x01 << 10)
- #define APGE_TABLE_SECTION_DOMAIN (0x0 << 5)
- #define PAGE_TABLE_SECTION_CACHE_WB (0x0 << 2)
- #define PAGE_TABLE_SECTION_4BIT (1 << 4)
- #define PAGE_TABLE_SECTION_TYPE (0x2)
- /*段大小*/
- #define SECTION_SIZE 0x100000
- //根据虚拟地址和页表基地址确定页表项所在的物理地址
- unsigned int get_pgtindex_addr(unsigned int viraddr,unsigned int pgtaddr)
- {
- unsigned int addr;
-
- /*[31:14]页表基地地址
- *[13: 2]虚拟地址>>20位得到的page index
- *[1 : 0]总是为0,因为每一项占用4byte
- */
- addr = (pgtaddr & PAGE_TABLE_BASE_MASK) | (((viraddr & VIRADDR_MASK) >> 20) << 2);
-
- return addr;
- }
- //获取页表项
- unsigned int get_page_entry(unsigned int phyaddr)
- {
- unsigned int entry_value;
-
- /*[31:20]section base address
- *[19:12]
- *[11:10]AP
- *[9]
- *[8:5]Domain
- *[4]:1
- *[3]:C
- *[2]:B
- *[1:0]:Type
- */
- entry_value = (phyaddr & PHYADDR_MASK) | PAGE_TABLE_SECTION_AP |\
- PAGE_TABLE_SECTION_CACHE_WB | PAGE_TABLE_SECTION_4BIT|\
- PAGE_TABLE_SECTION_TYPE;
-
- return entry_value;
- }
- /*创建一级页表:段描述符*/
- void create_page_table()
- {
- int i;
- unsigned int pgt_index_addr;
- unsigned int viraddr,phyaddr,pgtaddr;
-
- /*我们代码的起始运行地址0x00000000
- *在这里需要注意的是:当我们开启MMU后,
- *cpu发出的地址都会被MMU拦截,要想程序
- *正常运行,pc所用的的地址必须是虚拟地址。
- *然而此时cpu执行下一条指令实际运行的地址是
- *物理地址,但是MMU会将此物理地址当作虚拟虚拟地址
- *处理。晕,乱套了。为了解决这个问题,我们通常的做法
- *是,让开启MMU的附近地址指令的虚拟地址和物理地址空间
- *做一个等价的映射。在这里我们将0x00000000开始的1M物理空间
- *映射到0x00000000开始的虚拟地址空间。
- */
- phyaddr = SRAM_START_ADDR;
- viraddr = phyaddr;
- pgtaddr = PAGE_TABLE_BASE;
- pgt_index_addr = get_pgtindex_addr(viraddr,pgtaddr);
- *(volatile unsigned int *)pgt_index_addr = get_page_entry(phyaddr);
- #if 1
- /*映射64MSDRAM*/
- for(phyaddr = SDRAM_ADDR_START,viraddr = VMRAM_ADDR_START;\
- phyaddr < SDRAM_ADDR_END;phyaddr += SECTION_SIZE,\
- viraddr += SECTION_SIZE)
- {
- pgtaddr = PAGE_TABLE_BASE;
- pgt_index_addr = get_pgtindex_addr(viraddr,pgtaddr);
- *(volatile unsigned int *)pgt_index_addr = get_page_entry(phyaddr);
- }
- #endif
- /*映射IO地址空间*/
- phyaddr = PHIO_ADDR_START;
- viraddr = VMIO_ADDR_START;
- pgtaddr = PAGE_TABLE_BASE;
- pgt_index_addr = get_pgtindex_addr(viraddr,pgtaddr);
- *(volatile unsigned int *)pgt_index_addr = get_page_entry(phyaddr);
- return;
- }
- /*
- Care must be taken if the translated address differs from the
- untranslated address as several instructions following the
- enabling of the MMU may have been prefetched with the MMU off
- (using physical = virtual address - flat translation) and enabling
- the MMU may be considered as a branch with delayed execution. A similar
- situation occurs when the MMU is disabled. Consider the following code
- sequence:
- MRC p15, 0, R1, c1, C0, 0: Read control rejectio
- ORR R1, #0x1
- MCR p15,0,R1,C1, C0,0 ; Enable MMUS
- Fetch Flat
- Fetch Flat
- Fetch Translated
- */
- void init_mmu()
- {
- unsigned long mmu_table_base = PAGE_TABLE_BASE;
-
- asm(
- /*set Translation Table Base(TTB) register*/
- "mrc p15,0,r0,c2,c0,0\n"
- "mov r0,%0\n"
- "mcr p15,0,r0,c2,c0,0\n"
-
- /*set Domain Access Control register*/
- "mrc p15,0,r0,c3,c0,0\n"
- "mvn r0,#0\n"
- "mcr p15,0,r0,c3,c0,0\n"
-
- /*Enable MMU*/
- "mrc p15,0,r0,c1,c0,0\n"
- "orr r0, #0x1\n"
- "mcr p15,0,r0,c1,c0,0\n"
- "mov r0,r0\n"
- "mov r0,r0\n"
- "mov r0,r0\n"
- :
- :"r"(mmu_table_base)
- :"r0"
- );
-
- return;
- }
最后附上整个实验的源码:
阅读(1253) | 评论(0) | 转发(0) |