版本比较

标识

  • 该行被添加。
  • 该行被删除。
  • 格式已经改变。

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。

Alt textImage Removed


!./1495603821248.png|alt=Alt text!

MMU的地址转换过程分为两个阶段。第一阶段发生在转换表(一级页表)中,转换表有2^12=4096个入口,从虚拟地址的高12位获得转换表的索引值,根据该入口处的最低两位确定二级转换进行何种转换,有以下几种情况:
00:无效转换
01:二级转换按照粗页转换进行转换
10:二级转换按照段的方式进行
11:二级转换按照细页转换进行转换

第一阶段的转换表存储在内存中,需要工程师来建立,并且把表的起始地址TTB(Translation Table Base)传递给MMU。传递的方式是给cp15协处理器的c2寄存器进行赋值。

第二阶段有段转换、粗页转换、细页转换三种转换方式。

h3. 段式转换

当转换表中的最后两位为10时表示二级转换按照段式进行转换,如下图所示:

Alt textImage RemovedAlt textImage Removed


!./1495605566321.png|alt=Alt text!
!./1495605224329.png|alt=Alt text!

进行段式转换时,MMU会把TTB的高12位取出作为二级转换的物理地址基地址,然后把虚拟地址的低20位取出,作为偏移值offset,由基地址和偏移植可以直接获得物理地址。(2^12=4096,2^20=1MB,刚好寻址能力是4GB)

h3. 细页转换

Alt textImage 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

 unsigned long

vaddr,

paddr;

unsigned

 unsigned long

*ttb

=

(unsigned

long

*)0x50000000;

//ttb放到内存的开始位置

 

vaddr

 vaddr =

0xA0000000;

paddr

 paddr =

0x7f000000;

 

 *(ttb

+

((vaddr)

>>

20))

=

(paddr

&

0xfff00000)

|

SECDESC;

 

vaddr

 vaddr =

0x50000000;

paddr

 paddr =

0x50000000;

while

 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}


目录