ARM V7体系架构MMU原理与实践
【摘要】
在ARM系统中,存储器管理单元MMU起着非常重要的作用,主要表现在以下三个方面:虚拟存储空间到物理存储空间的映射;存储器访问权限的控制;设置虚拟存储空间的缓冲的特性。MMU单元在嵌入式系统中应用非常广泛,但在软件设计中如何使用MMU,大家未必了解的很详细。本文结合在mcs项目AMP架构下从核小boot的开发中所学知识和实践经验,对MMU单元的使用做一个详细的总结。
【关键词】
mmu;初始化;映射
一、问题的提出
在ARM系统中,MMU的使用非常广泛,以至于好多同事都会遇到mmu配置方面的问题,为了节省大家的时间,提高工作效率,我将我在开发从核小boot过程中的所学整理出来,希望能给用到mmu的同事一点参考。
二、解决思路
本文首先简单介绍一下mmu的基本概念,使同事有一个简单的理解,然后深入cpu内部处理机制讲述cpu如何使用mmu访问内存,最后从地址变换过程、初始化和使用流程三个方面讲述如何对mmu进行编程,使之为我们效劳。
- 实践情况
3.1 MMU简介及相关概念
MMU是存储器管理单元,它主要完成以下工作:
- 虚拟存储空间到物理存储空间的映射。在ARM中采用了页式虚拟存储管理,它把虚拟地址空间分成一个个固定大小的块,每一块称为一页,把物理内存的地址空间也分成同样大小的页。页的大小可以分为粗粒度和细粒度两种,mmu就是来实现从虚拟地址到物理地址的转换。
- 控制存储器的访问权限,是否可读或可写。
- 设置虚拟存储空间的缓冲特性,是否允许cache。
页表是mmu实现上述功能的重要手段,它是位于内存中的一张表。表的每一行对应虚拟存储空间的一个页,该行包含了该虚拟内存页对应的物理内存页的地址、该页的读写权限和该页的缓存特性等。页表中的每一行称为一个地址变换条目(entry),页表中每一行的内容又称为描述符,每一行的地址称为描述符地址。
TLB又称快表,相当于页表的cache。从虚拟地址到物理地址的变换过程其实就是一个查询页表的过程,由于页表存放在内存中,这个查询过程代价太大。由于程序的局部性,在一段时间内,程序对页表的访问只是局限在少数几个单元中,根据这一特点,采用一个容量更小,访问速度和cpu中寄存器相当的存储器件来存放当前访问需要的地址变换条目,这个小容量的页表就是TLB。
3.2 cpu是如何通过mmu访问内存的
如图所示,当Arm处理器请求存储访问时,首先在TLB中查找虚拟地址。如果系统中数据TLB和指令TLB是分开的,在取指令时,从指令TLB中查找相应的虚拟地址,对于其他内存访问操作,从数据TLB中查找相应的虚拟地址。如果该虚拟地址对应的地址变换条目不在TLB中,cpu通过页表遍历硬件系统(TTW)从位于内存的页表中查询对应于该虚拟地址的地址变换条目,并把相应的结果添加到TLB中。如果TLB已经满了,还需要根据一定的淘汰算法进行替换。这样,当cpu下一次需要访问该虚拟地址时,就可以从TLB中直接得到,从而使地址变换的速度大大提高。
当得到了该地址的地址变换条目后,将进行以下操作:
(1)得到该虚拟地址对应的物理地址。
(2)根据条目中的C(cachable)控制位和B(bufferable)控制位决定是否缓存该内存访问的结果。
(3)根据存取权限控制位AP和域控制位确定该内存访问是否被允许。如果该内存访问不被允许,CP15通过访问权限控制硬件向ARM处理器报告存储访问终止。
(4)对于不允许缓存(uncachable)的存储访问,使用上一步中得到的物理地址访问内存。对于允许缓存(cachable)的存储访问,如果在cache命中,则忽略物理地址;如果cache没有命中,则使用上一步中得到的物理地址访问内存,并把该块数据读取到cache中。
3.3 MMU中地址变换过程
页表是存在内存中的一张表,在此以一级页表为例进行说明,该张表可以看成是一个有4096个元素的数组,每个元素用4字节来表示,其包含的内容是:“段对应的物理基地址+物理空间属性”,设该数组是PageTable[4096]。
假设我们有一台可以生成32位地址的机器,它的虚拟地址范围从0x00000000~0xFFFFFFFF (4G),而这台机器只有512M的物理地址,因此他可以运行4G的程序,但该程序不能一次性调入内存运行。这台机器必须有一个达到可以存放4G程序的外部存储器(例如磁盘或是FLASH),以保证程序片段在需要时可以被调用。在这个例子中,页的大小为1M,页框大小与页相同(这点是必须保证的,内存和外围存储器之间的传输总是以页为单位的),对应4G的虚拟地址和512M的物理存储器,他们分别包含了4096个页和512个页框,如下图所示。
如上图有一个映射关系:物理地址0x00000000~0x00100000(0~1M)的1M空间对应虚拟地址0xc0000000~0xc0100000(3072~3072M)的1M空间,则应该进行如下操作:
首先计算0xc0000000对应在页表数组中的位置,计算方法是0xc0000000>>20,等于0xc00,即十进制的3072,故该段映射关系应放置在PageTable[3072]元素中。PageTable[3072]的值即该段映射关系对应的一级描述符地址。
其次组装一级描述符,参照手册P1282中Section项。
最后两个bit用来区分该表项是否有效,且为粗表还是细表或者段表。
00:表示的是此表项无效,直接返回访问失败
在各种表项中SBZ表示未用,置0。
段页表section:表示1MB大小的段,bit[31:20]为物理地址高12bit,C,B分别表示cacheable和write bufferable属性,Domain字段表示所属的区域,AP字段定义访问属性(在domain所属区域定义为需要AP决定时有效)。IMP由运行环境决定。
示例中段对应的物理基地址是0x00000000>>20,即0x000,设置该1M空间可读可写、cache属性是write-back、write-allocate,安全、共享等,故根据对应的位可组装出一个段描述符0x00015c26。
最后把段描述符赋给对应的页表数组元素即完成了该条映射关系的配置,示例中设置为:PageTable[3072]= 0x00015c26。
上面配置页表的思路和过程,可以用一个函数SetMmuPage来实现,方便以后调用。
但是这个函数有个缺点就是可维护性不高,因为并不是所有内存的属性都是0x00015c26,因此最好是将组装一级描述符的过程也添加到程序中,而不是手动组装,即用下面的代码表示。
其中,ptMmuPageTableEntryCfg为一个T_HAL_MMU_PageTableEntryCfg类型的指针,T_HAL_MMU_PageTableEntryCfg结构体声明如下:
typedef struct
{
UINT32 udPABaseAddr; /**< 物理块基地址*/
UINT32 udExecuteNever; /**< 是否为可执行代码memory*/
UINT32 udNotGlobal; /**< 是否为全局页表项 */
UINT32 udNonSecure; /**< 是否为非安全表项 */
UINT32 udShareable; /**< 共享属性设置*/
UINT32 udDomainNumber; /**< Domain号设置*/
UINT32 udAccessPermission; /**< 访问权限设置 */
UINT32 udMemoryAttribute; /**< memory 属性设置*/
UINT32 udPageType; /**< 页大小 */
}T_HAL_MMU_PageTableEntryCfg;
一级页表的地址变换过程简单介绍如上,手册中相关的描述为:
同样地,用户可以根据手册中描述的二级页表变换过程对二级页表进行组装和填充,二级页表变换过程如下图所示:
3.4 mmu初始化
初始化mmu的流程如下:
(1)关闭mmu,其核心思想是将寄存器SCTLR的bit[0]置0,用到的指令为:
mrc p15, 0, r0, c1, c0, 0 和mcr p15, 0, r0, c1, c0, 0
(2)失效所有TLB,用到的指令为
mcr p15, 0, r0, c8, c7, 0
(3)设置域访问控制器,用到的指令为
mcr p15 , 0, r0, c3, c0, 0
(4)通过设置TTBCR寄存器来禁止TTBR0/TTBR1,用到的指令为
mcr p15, 0, r0, c2, c0, 2
(5)设置页表基址寄存器TTBR0/TTBR1,用到的指令为
mcr p15, 0, r0, c2, c0, 0
(6)通过设置TTBCR寄存器来使能TTBR0/TTBR1,用到的指令为
mcr p15, 0, r0, c2, c0, 2
3.5 mmu使用流程
在小boot中使用mmu配置页表的流程如下:
(1)首先将页表所在的16k空间清0,相关代码如下:
ULONG* pPageTable=(ULONG*)(CORE_START_PAGEADDR);
memset(pPageTable, 0, PAGE_TABLE_ENTRY_NUM*sizeof(ULONG));
(3)配置物理地址空间和虚拟地址空间的映射关系(方法参加第三章Mmu中地址变换过程)
(4)初始化mmu(方法参见第四章)
(5)开启mmu,其核心思想是将寄存器SCTLR的bit[0]置1,用到的指令为
mrc p15, 0, r0, c1, c0, 0 和mcr p15, 0, r0, c1, c0, 0
- 效果评价
通过以上分析和实际代码编写测试表明,依照上面所述配置mmu,可以达到我们期望mmu实现的功能,并且代码编写也很条理不繁琐。
- 推广建议
本文主要讲述了在使用ARMv7架构下的处理器时,软件编程中使用mmu需遵循的基本原则,对其它处理器的mmu单元应用也有一定的参考性,建议在希望学习和使用mmu的同事中推广。
参考资料
《ARM体系结构与编程》 杜春雷编著
《ARM v7_TRM.pdf》 ARM公司