天天看点

ARM内存管理单元--MMU

前言

我们在学习4412的时候,我们原本的中断地址已经被IROM和IRAM占据了,并且不允许被修改,所以我们只能去选取高端地址0xffff 0000高端地址,但是这一片地址属于虚拟地址,所以我们需要开启MMU

MMU定义

MMU功能,(memory management unit) 内存管理单元,MMU是硬件的内存管理器件,使用硬件方式对内存进行映射管理。

将物理内存 0X4000 0000 - 0X8000 0000,的某些指定地址,映射到虚拟内存地址上。 虚拟内存地址总计大小 0X0000 0000 – 0X FFFF FFFF 总计4GB ,他包含了0xffff 0000这地址,我们就可以去开启异常,其中异常包含着硬件最重要的中断

同样的,MMU的存在可以让计算机多出很多的空间,他要配合协处理器寄存器等方式来实现,所以我们需要使用到内联汇编

ARM内存管理单元--MMU

他是一种多对一的方式,虚拟地址很多存储在相同的物理空间中,它通过走表的方式获得物理内存,下面我们详细介绍他的走表过程

MMU的走表过程

ARM内存管理单元--MMU

上图就是完整的走表过程,他经过非常精细的设计,下面我们来介绍一下他这个过程

  1. Input address,先获取一个虚拟地址,然后去掉后面的20位,只留前面的12位,
  2. First-level descriptor address,将他和从C2寄存器中取出来的28位拼在一起,并且在后面加上00代替,表示使用的部分流的方式,刚才虚拟地址的前12位就被当作了偏移量
  3. First-level Section descriptor,通过图中Translation flow for a Section找到了对应的地址,
  4. Output address,这时候取他的前16位就是他的真实物理地址

协处理器来查表

协助核心工作的协处理了,他的内部有16个寄存器,cp0–cp15,他的名字是c1-c15,其中我们要用到的是c1,c2,c3,起始表项写到c2,c1是用来使能mmu,c是用来设置使能内存的权限

extern void enable_mmu(u32 *ttb)
{
    __asm__ __volatile__(
        "nop\n\t"
        //将TTB基地址写入C2
        "mcr p15, 0, %[ttb], c2, c0, 0\n\t"

        //设置C3的内存访问权限为最大权限,全部设为11
        "mvn r0, #0\n\t"
        "mcr p15, 0, r0, c3, c0, 0\n\t"
        //设置C1,使能MMU,顺便设置异常向量表存储在高端地址
        
        "mrc p15, 0, r0, c1, c0, 0\n\t"
        "orr r0, r0, #(0x1 << 13)\n\t"
        "orr r0, r0, #0x1\n\t"
        "mcr p15, 0, r0, c1, c0, 0\n\t"
        "nop\n\t"
        :
        :[ttb]"r"(ttb)
        :"r0"
    );

}
           

MMU功能的实现

由于频繁使用,所以我们将走表过程写成宏函数

/*
    TTB 由C2给定的转换表的基地之,看成一个数组
    VA 要映射的虚拟地址的基地址, 取高12位,可以配合TTB基地址当成数组寻找表项
    PA 表项中需要指定的物理基地址,管理本地址上1M的物理地址空间的映射
*/
#define CREAT_DESCRIPTOR(TTB, VA, PA) \
    TTB[VA>>20] = ((PA&0xfff0000)|(0x2))
           

同时我们查手册,我们需要将板子出场的地址占用,还有真实的物理地址,不能让他映射,方法就是自己映射自己

memset(ttb, 0x0, 4096*4);

    section_map(ttb, 0x0, 0x0cd00000,0x0);

    section_map(ttb,0x0ce00000,0x14000000,0x0ce00000);

    section_map(ttb, 0x40000000,0x80000000,0x40000000);
           

同时我们应该注意,我们映射的时候使用的是段映射,所以我们的低20位要和映射地址的低20位相同,因为在走表的时候,他们的偏移量是相同的