一、MMU功能解析
1.1虚拟地址的使用
p1.c
-
#include <stdio.h>
-
-
int a = 1;
-
-
void main()
-
{
-
-
while(1)
-
{
-
printf("&a = %p, a = %d\n", &a,a);
-
}
-
sleep(3);
-
}
输出:&a = 0x8049664, a = 1
p2.c
-
#include <stdio.h>
-
-
int b = 2;
-
-
void main()
-
{
-
-
while(1)
-
{
-
printf("&b = %p, b = %d\n", &b,b);
-
}
-
sleep(3);
-
}
输出:&b = 0x8049664, b = 2
为什么a,b地址一样呢,明明值都不一样。
软件在使用物理地址时,需要通过mmu来实现管理。同样的用户空间,通过MMU后实际上对应了不同的物理地址。
1.2 MMU的作用
1、将虚拟地址转化为物理地址
2、访问权限管理
二、深入剖析内存转化
2.1 地址转化流程总体分析
ARM920T(核手册) ———> memory management unit
一级转换(Level one fetch)-->TTB(translation table base address)
二级转换(Level two fetch)-->Section、Corase page table、Fine page table
①地址转化类型
上图可以整理出ARM系统支持3种类型的地址转化:
1. 段式转化
2. 粗粒度页面转化
3. 细粒度页面转化
2.2 TTB配置
MMU要自动进行虚拟地址到物理地址的转化,
首先要找到一级页表,而一级页表的基地址(TTB:translation table base)则是保存在CP15的C2寄存器中。因此,当程序员创建好相应的页表后,需要将页表基地址写入该寄存器。
2.3 段式转化分析
如果一级页表项中最后两位为’10’,接下的来
转化过程将按照段方式来进行。
具体的转换方式:
TTB的基地址保存在cp15中的c2寄存器中,VA(virtual address)的高12位作为TTB表中的偏移量,找到对应的一级页表。
一级页表后2位是'10',知道这是一个段式转化。然后将TTB中所对应的段基地址,加上MVA中的Section index。就得到了PA(physical address)的地址。(个人理解)
2.4 细页转化分析
如果一级页表项中最后两位为’
11’,接下的来转化过程将按照细页方式来进行。(见上面Figure3-4)
转换方式:前12位找到TTB偏移后的地址,找到的地址中存放的是PA的基地址。这里多了10位的二级表index,同样的找到二级表中的偏移量对应的地址。
三个表相加得到PA的地址。
三、MMU的配置与使用
1.任务分解
建立一级页表
写入TTB
打开MMU
-
#define GPBCON (volatile unsigned long*) 0xA0000010
-
#define GPBDAT (volatile unsigned long*) 0xA0000014
-
/*
-
* 用于段描述符的一些宏定义
-
*/
-
#define MMU_DOMAIN (0<<5) /*属于哪个域*/
-
#define MMU_FULL_ACCESS (3<<10) /*访问权限*/
-
#define MMU_SPECIAL (1<<4) /*必须使1*/
-
#define MMU_CACHEABLE (1<<3) /*使能cache*/
-
#define MMU_BUFFERABLE (1<<2) /*使能buffer*/
-
#define MMU_SECTION (2<<0) /*段描述符*/
-
-
#define MMU_SECDESC (MMU_SECTION|MMU_SPECIAL|MMU_DOMAIN|MMU_FULL_ACCESS)
-
#define MMU_SECDESC_WB (MMU_SECTION|MMU_SPECIAL|MMU_DOMAIN|MMU_FULL_ACCESS|MMU_CACHEABLE|MMU_BUFFERABLE)
-
-
void create_page_table()
-
{
-
//设置ttb的起始位置
-
unsigned long *ttb = (unsigned long *)0x30000000;
-
//定义虚拟地址、物理地址
-
unsigned long vaddr, paddr;
-
//定义虚拟地址起始地址、物理起始地址
-
vaddr = 0xA0000000;
-
paddr = 0x56000000;
-
/* ttb加上虚拟地址高12位所指的地址就是PA的基地址位置,物理地址取前12位,依次设置后面的值(见AMR9核手册)
-
* 然后我们就建立的从vaddr的地址对应于paddr
-
*/
-
-
*(ttb + (vaddr>>20) ) = (paddr & 0xfff00000)|MMU_SECDESC;
-
-
// 上面是对GPIO的映射,这里是对内存的映射
-
vaddr = 0x30000000;
-
paddr = 0x30000000;
-
//循环了64M的内存空间
-
while(vaddr < 0x34000000)
-
{
-
//建立内存映射表
-
*(ttb + (vaddr>>20)) = (paddr & 0xfff00000)|MMU_SECDESC_WB;
-
//加上1M,这个1M使手册中MVA[19:0]的大小
-
vaddr += 0x100000;
-
paddr += 0x100000;
-
}
-
}
-
-
void mmu_init()
-
{
-
__asm__(
-
/*设置TTB*/
-
"ldr r0, =0x30000000\n" 这里获取TTB对应的起始物理地址
-
"mcr p15, 0, r0, c2, c0, 0\n" 将这个地址写进p15的c2寄存器(在register2)
-
/*不进行权限检查*/
-
"mvn r0, #0 \n" 设置r0为全1
-
"mcr p15, 0, r0, c3, c0, 0\n" 将r0写入到c3(cp15中的r3),全1就是仍何人都可读
-
/*使能mmu*/
-
"mrc p15, 0, r0, c1, c0, 0\n" 读取c1中的值
-
"orr r0, r0, #0x0001\n"
-
"mcr p15, 0, r0, c1, c0, 0\n" 设置开启mmu
-
:
-
:
-
);
-
}
-
-
int gboot_main()
-
{
-
//建立页表、写入ttb
-
create_page_table();
-
//使能mmu
-
mmu_init();
-
-
*(GPBCON) = 0x15400;
-
*(GPBDAT) = 0x6bf;
-
-
return 0;
-
}
扩展阅读:
http://blog.csdn.net/jianchi88/article/details/6988145
阅读(902) | 评论(0) | 转发(0) |