版本比较
标识
- 该行被添加。
- 该行被删除。
- 格式已经改变。
h1. 专题11-MMU初始化
[TOC]
h2.MMU概述
MMU用于完成虚拟地址和物理地址的转换,以及为地址空间分配访问权限。MMU使进程访问的同一虚拟地址对应不同的物理地址,避免进程间的地址冲突。
!./1495603101383.png|alt=Alt text!h2. Image Added
MMU地址转换
h3.转换过程
ARM核手册《ARM920T_TRM1_S.pdf》第3章介绍了ARM9的MMU转换过程,此过程同样适用于ARM11,Cortex-A8。
!./1495603821248.png|alt=Alt text!Image Added
MMU的地址转换过程分为两个阶段。第一阶段发生在转换表(一级页表)中,转换表有2^12=4096个入口,从虚拟地址的高12位获得转换表的索引值,根据该入口处的最低两位确定二级转换进行何种转换,有以下几种情况:
00:无效转换
01:二级转换按照粗页转换进行转换
10:二级转换按照段的方式进行
11:二级转换按照细页转换进行转换
第一阶段的转换表存储在内存中,需要工程师来建立,并且把表的起始地址TTB(Translation Table Base)传递给MMU。传递的方式是给cp15协处理器的c2寄存器进行赋值。
第二阶段有段转换、粗页转换、细页转换三种转换方式。h3.
段式转换
当转换表中的最后两位为10时表示二级转换按照段式进行转换,如下图所示:
!./1495605566321.png|alt=Alt text!
!./1495605224329.png|alt=Alt text!Image Added
Image Added
进行段式转换时,MMU会把TTB的高12位取出作为二级转换的物理地址基地址,然后把虚拟地址的低20位取出,作为偏移值offset,由基地址和偏移植可以直接获得物理地址。(2^12=4096,2^20=1MB,刚好寻址能力是4GB)h3.
细页转换
!./1495606409249.png|alt=Alt text!Image Added
细页转换同样使用虚拟地址的高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
| |||||||
#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
{ unsigned long vaddr, paddr; |
unsigned long
unsigned long *ttb = (unsigned long *)0x50000000; //ttb放到内存的开始位置 |
vaddr = 0xA0000000;
paddr = 0x7f000000;
*(ttb +
vaddr = 0xA0000000; paddr = 0x7f000000; *(ttb + ((vaddr) >> 20)) = (paddr & 0xfff00000) | SECDESC; |
vaddr = 0x50000000;
paddr = 0x50000000;
while(vaddr < 0x54000000)
{
*(ttb +
vaddr = 0x50000000; paddr = 0x50000000; while(vaddr < 0x54000000) { *(ttb + ((vaddr) >> 20)) = (paddr & 0xfff00000) | SECDESC_WB; |
vaddr
vaddr += 0x100000; |
paddr
paddr += 0x100000; |
}
}
{code}
写入TTB和使用MMU需要操作cp15协处理器,使用C语言内嵌汇编代码来完成,为了完整地初始化MMU还需要设置内存中各个域的权限,代码如下:
{code:collapse=false|language=none|linenumbers=true|theme=Confluence}void
}
}
|
写入TTB和使用MMU需要操作cp15协处理器,使用C语言内嵌汇编代码来完成,为了完整地初始化MMU还需要设置内存中各个域的权限,代码如下:
代码块 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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两个寄存器的定义,将其修改成虚拟地址:
代码块 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
#define GPMCON (*(volatile unsigned long *)0xA0008820)
#define GPMDAT (*(volatile unsigned long *)0xA0008824)
|
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}
目录 |
---|