版本比较
标识
- 该行被添加。
- 该行被删除。
- 格式已经改变。
h1. 专题11-MMU初始化
[TOC]
h2. MMU概述
MMU用于完成虚拟地址和物理地址的转换,以及为地址空间分配访问权限。MMU使进程访问的同一虚拟地址对应不同的物理地址,避免进程间的地址冲突。
!./1495603101383.png|alt=Alt text!
h2. MMU地址转换
h3. 转换过程
ARM核手册《ARM920T_TRM1_S.pdf》第3章介绍了ARM9的MMU转换过程,此过程同样适用于ARM11,Cortex-A8。
Image Removed
!./1495603821248.png|alt=Alt text!
MMU的地址转换过程分为两个阶段。第一阶段发生在转换表(一级页表)中,转换表有2^12=4096个入口,从虚拟地址的高12位获得转换表的索引值,根据该入口处的最低两位确定二级转换进行何种转换,有以下几种情况:
00:无效转换
01:二级转换按照粗页转换进行转换
10:二级转换按照段的方式进行
11:二级转换按照细页转换进行转换
第一阶段的转换表存储在内存中,需要工程师来建立,并且把表的起始地址TTB(Translation Table Base)传递给MMU。传递的方式是给cp15协处理器的c2寄存器进行赋值。
第二阶段有段转换、粗页转换、细页转换三种转换方式。
h3. 段式转换
当转换表中的最后两位为10时表示二级转换按照段式进行转换,如下图所示:
Image RemovedImage Removed
!./1495605566321.png|alt=Alt text!
!./1495605224329.png|alt=Alt text!
进行段式转换时,MMU会把TTB的高12位取出作为二级转换的物理地址基地址,然后把虚拟地址的低20位取出,作为偏移值offset,由基地址和偏移植可以直接获得物理地址。(2^12=4096,2^20=1MB,刚好寻址能力是4GB)
h3. 细页转换
Image Removed!./1495606409249.png|alt=Alt text!
细页转换同样使用虚拟地址的高12位确定一级页表的索引值,然后将该索引处的高12位取出,用于定位二级页表。定位到二级页表后,使用虚拟地址的中间8位作为二级页表的索引值,找到二级页表的位置后将该位置的高20位取出,作为物理页的基地址,物理页一般是4KB或64KB大小,最后使用虚拟地址的低12位作为物理页的偏移,获得最终物理地址的位置。
h2. MMU配置与使用
要求将MMU配置好后,使用虚拟地址去完成控制LED灯的功能。为了简单演示MMU的作用,只使用MMU的段式转换,将控制LED的两个寄存器地址所在的0x7f000000所在的段映射到0xa000000这个虚拟地址。
MMU的配置包含以下几件事:
# 建立一级页表
# 写入 TTB到cp15协处理器
# 打开MMU
我们把一级页表建立在内存开始的起始地址,根据ARM核心手册《ARM920T_TRM1_S.pdf》3.3.4小节中对于段描述符格式的介绍对页表的每一项进行赋值,除了要对0x7f000000这个段进行映射外,还要对内存进行映射,因为程序的链接地址也必须是虚拟地址,此处对内存的映射非常简单,就是让内存的虚拟地址和物理地址直接相等,建立页表的代码如下:
#define{code:collapse=false|language=c++|linenumbers=true|theme=Confluence}
#define MMU_SECTION
(2
<<
0)
#define
MMU_CACHEABLE
(1
<<
3)
#define
MMU_BUFFERABLE
(1
<<
2)
#define
MMU_SPECIAL
(1
<<
4)
#define
MMU_DOMAIN
(0
<<
5)
#define
MMU_FULL_ACCESS
(3
<<
10)
#define
SECDESC
(MMU_SECTION
|
MMU_SPECIAL
|
MMU_DOMAIN
|
MMU_FULL_ACCESS)
#define
SECDESC_WB
SECDESC
|
MMU_CACHEABLE
|
MMU_BUFFERABLE
void
create_page_table()
{
unsigned long
vaddr,
paddr;
unsigned long
*ttb
=
(unsigned
long
*)0x50000000;
//ttb放到内存的开始位置
vaddr =
0xA0000000;
paddr =
0x7f000000;
*(ttb
+
((vaddr)
>>
20))
=
(paddr
&
0xfff00000)
|
SECDESC;
vaddr =
0x50000000;
paddr =
0x50000000;
while(vaddr
<
0x54000000)
{
*(ttb
+
((vaddr)
>>
20))
=
(paddr
&
0xfff00000)
|
SECDESC_WB;
vaddr
+=
0x100000;
paddr
+=
0x100000;
}
}
{code}
写入TTB和使用MMU需要操作cp15协处理器,使用C语言内嵌汇编代码来完成,为了完整地初始化MMU还需要设置内存中各个域的权限,代码如下:
{code:collapse=false|language=none|linenumbers=true|theme=Confluence}
void
mmu_init()
{
__asm__(
/*设置TBB,参考手册2.3.6小节*/
"ldr
r0,
=0x50000000\n"
"mcr
p15,
0,
r0,
c2,
c0,
0\n"
/*不进行域权限检查,参考手册2.3.7小节*/
"mvn
r0,
#0x0\n"
"mcr
p15,
0,
r0,
c3,
c0,
0\n"
/*使能MMU,参考手册2.3.5小节*/
"mrc
p15,
0,
r0,
c1,
c0,
0\n"
"orr
r0,
r0,
#0x1\n"
"mcr
p15,
0,
r0,
c1,
c0,
0\n"
:
:
);
}
{code}
MMU配置完成后,还需要修改GPMCON和GMPDAT两个寄存器的定义,将其修改成虚拟地址:
{code:collapse=false|language=c++|linenumbers=true|theme=Confluence}
#define
GPMCON
(*(volatile
unsigned
long
*)0xA0008820)
#define
GPMDAT
(*(volatile
unsigned
long
*)0xA0008824)
{code}
目录 |
---|