1.mmu的介绍
当为0b01时,表示这个虚拟地址要按粗页(coarse page)的方式转化
当为0b10时(且18位为0),表示这个虚拟地址要按段(section)的方式转化
当为0b10时(且18位为1),表示徐哲虚拟地址按supersection的方式转化
mmu,即Memory Management Unit,内存管理单元。在处理器中,放在cpu和内存之间,有两个作用
(1)转换虚拟内存
(2)设置程序对内存的访问(在裸机阶段,对于处在不同域domain的虚拟地址,可设置不同的权限)
2.s3c6410中的mmu
按照虚拟地址的[31:20]位的最后两位,当为0b00时,表示这个虚拟地址是无效的
今天,我先采用段的方式来设置虚拟地址
首先,我们从一个叫Translation table base(TTB)的地方取高18位,加上虚拟地址的高12位,最后加上00;但是我看到一些其他人的代码,都是TTB的地址直接加上虚拟地址的高12位,最后没有00,我试了之后发现确实是这样,具体为什么,我也不清楚,就先按照这样来。
1.首先,我们要访问一个虚拟地址va(virtual address),然后我们预先设好一个TTB
2.我们把TTB的高20位,加上va的高12位,构成一级描述符地址(First-level descriptor address)
3.通过一级描述符的地址,得知这个虚拟地址对应物理地址的一些特性,在First-level descriptor里面的各个域,我们可以得知一些特性
4.mmu根据一级描述符进行配置后,把一级描述符的高12位加上虚拟地址的[19:0]位得到物理地址
这些过程都是mmu完成的,而我们要做的就是把这个TTB写好
3.采用段的转换方式使用虚拟内存
那要通过一个虚拟地址,实现对LED的控制
首先,我们要制作一个TTB,然后把这个TTB写到cp15协处理器中,告知cp15协处理器,我们的TTB在哪一个位置,当要进行虚拟地址转换时要去哪里找TTB
假设我们写好了TTB,则需要把我们存放TTB的地址告诉cp15协处理器(因为mmu由它管),然后使能mmu
设置TTB地址
ldr r0, =0x50000000
mcr p15, 0, r0, c2, c0, 0
然后设置域的访问控制,不进行权限检查
mvn
r0, #0
mcr
p15, 0, r0, c3, c0, 0
最后打开MMU,对c1寄存器的第0位进行配置
orr r0, r0, #0b1
mcr p15, 0, r0, c1, c0, 0
然后回过头来编写者个TTB表
在6410里面,GPIO的地址在外设区,即在0x7f******的位置,加入我们把这一块区域的物理地址的虚拟地址设为0xa0******,则根据一级描述符,我们需要写好一级描述符的内容,即里面的几个域
首先最后两位为10
[2]表示是否使用缓冲。在MMU和内存之间,为了协调高速cpu和低速内存,则加入了buffer缓冲,这里可以设置,也可以不设置
[3]表示是否使用cache。使用cache可以提高cpu取值得速度,这里可以使用,也可以不使用。
[4]这里写着SBZ(0),但是我实际操作过,写成0会无法完成转化,要写成1.
[8:5]表示对于这部分虚拟地址,要使用那个域,6410一共有16个域可以使用,我们用第0个域
[11:10][9]这几位表示访问权限
[14:12]根据C,B两位来设置
把所有的外设地址都转换成虚拟地址
#define SECTION 0b10
#define BUFFER 0b1<<2
#define CACHE 0b1<<3
#define XN 0b1<<4
#define DOMAIN 0b0<<5
#define AP 0b11<<10
va = 0xa0000000;
pa = 0x7f000000;
while(pa <= 0x7fffffff)
{
*(ttb + (va>>20)) = (pa&0xfff00000) | SECTION | BUFFER | CACHE | XN | DOMAIN | AP;
va += 0x100000;
pa += 0x100000;
}
然后还有一点值得注意的是,我们还要去转化内存所在的地址为虚拟地址,为什么呢?因为我们的程序是要在内存当中运行的,然而我们所运行的程序访问的又是物理地址,所以我们要把内存的地址映射到相同的虚拟地址去
va = 0x50000000;
pa = 0x50000000;
while(pa < 0x54000000)
{
*(ttb + (va>>20)) = (pa&0xfff00000) | SECTION | BUFFER | CACHE | XN | DOMAIN | AP;
va += 0x100000;
pa += 0x100000;
}
而在主程序中,我们就直接使用虚拟地址来配置点亮LED了
#define GPMCON *((unsigned long volatile *)0xa0008820)
#define GPMDAT *((unsigned long volatile *)0xa0008824)
void main()
{
mmu_init();
GPMCON = 0x1111;
GPMDAT = 0x39;
}
下面贴上初始化MMU的代码
#define SECTION 0b10
#define BUFFER 0b1<<2
#define CACHE 0b1<<3
#define XN 0b1<<4
#define DOMAIN 0b0<<5
#define AP 0b11<<10
void creat_TTB()
{
unsigned long *ttb = (unsigned long *)0x50000000;
unsigned long va, pa;
va = 0xa0000000;
pa = 0x7f000000;
while(pa <= 0x7fffffff)
{
*(ttb + (va>>20)) = (pa&0xfff00000) | SECTION | BUFFER | CACHE | XN | DOMAIN | AP;
va += 0x100000;
pa += 0x100000;
}
va = 0x50000000;
pa = 0x50000000;
while(pa < 0x54000000)
{
*(ttb + (va>>20)) = (pa&0xfff00000) | SECTION | BUFFER | CACHE | XN | DOMAIN | AP;
va += 0x100000;
pa += 0x100000;
}
}
void mmu_enable()
{
__asm__(
"ldr r0, =0x50000000\n"
"mcr p15, 0, r0, c2, c0, 0\n"
"mvn r0, #0\n"
"mcr p15, 0, r0, c3, c0, 0\n"
"mrc p15, 0, r0, c1, c0, 0\n"
"orr r0, r0, #0b1\n"
"mcr p15, 0, r0, c1, c0, 0\n"
:
:
);
}
void mmu_init()
{
creat_TTB();
mmu_enable();
}