MMU概述

MMU用于完成虚拟地址和物理地址的转换,以及为地址空间分配访问权限。MMU使进程访问的同一虚拟地址对应不同的物理地址,避免进程间的地址冲突。
Alt text

MMU地址转换

转换过程

ARM核手册《ARM920T_TRM1_S.pdf》第3章介绍了ARM9的MMU转换过程,此过程同样适用于ARM11,Cortex-A8。
Alt text

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

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

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

段式转换

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

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

细页转换

Alt text

细页转换同样使用虚拟地址的高12位确定一级页表的索引值,然后将该索引处的高12位取出,用于定位二级页表。定位到二级页表后,使用虚拟地址的中间8位作为二级页表的索引值,找到二级页表的位置后将该位置的高20位取出,作为物理页的基地址,物理页一般是4KB或64KB大小,最后使用虚拟地址的低12位作为物理页的偏移,获得最终物理地址的位置。

MMU配置与使用

要求将MMU配置好后,使用虚拟地址去完成控制LED灯的功能。为了简单演示MMU的作用,只使用MMU的段式转换,将控制LED的两个寄存器地址所在的0x7f000000所在的段映射到0xa000000这个虚拟地址。

MMU的配置包含以下几件事:

  1. 建立一级页表
  2. 写入 TTB到cp15协处理器
  3. 打开MMU

我们把一级页表建立在内存开始的起始地址,根据ARM核心手册《ARM920T_TRM1_S.pdf》3.3.4小节中对于段描述符格式的介绍对页表的每一项进行赋值,除了要对0x7f000000这个段进行映射外,还要对内存进行映射,因为程序的链接地址也必须是虚拟地址,此处对内存的映射非常简单,就是让内存的虚拟地址和物理地址直接相等,建立页表的代码如下:

#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;
    }
}

写入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"
        :
        :
    );
}

MMU配置完成后,还需要修改GPMCON和GMPDAT两个寄存器的定义,将其修改成虚拟地址:

#define GPMCON (*(volatile unsigned long *)0xA0008820)
#define GPMDAT (*(volatile unsigned long *)0xA0008824) 

  • 无标签