《嵌入式linux应用程序开发完全手册》MMU硬件编程学习笔记
MMU是什么东东?什么原理?干什么用的?我就不废话了,只讲一些关键的,我个人认为比较重要的东西。
一般来讲,MMU将虚拟地址转化成物理地址有两种方式:第一种就是用一个确定的数学公式进行转化,第二种就是用“表格”(page table)存储虚拟地址对应的物理地址。而在ARM CPU中则采用的是第二种方式。
在MMU启动之前,CPU和各个外设使用的都是物理地址,启动MMU后,CPU核发出虚拟地址VA(Virtual Address),VA被转换成MVA(Modified Virtual Address)供Cache,MMU使用,在这里MVA被转换成PA(Physical Address);最后使用PA与实际设备打交道。
在启动MMU后,CPU只能看到VA,也不理会VA如何最终转换成PA的。而Cache和MMU看不见VA,它们利用由MVA转换得到PA,所以Cache和 MMU看得到MVA和PA。实际设备只能看到PA.
实际上MVA就是除CPU外其它部分看眼中的“VA“。
由VA转换成MVA由硬件完成,如果VA>32M,则MVA=VA。如果VA<32M时,考虑到不同进程所用VA有可能重叠的情况,一旦发生重叠,那后面的地址转换工作量将将变得非常复杂,故将VA与进程PID进行运算后作为MVA,再用MVA作为转换基地址,再转换成PA。
S3C2440最多会用到两级页表,以段(Section)的方式进行转换时只用到一级页表,以页(page)的方式进行转换时用到两级页表。
页表由一个个条目(Entry)组成,每个条目存储了一段虚拟地址对应的物理地址及其访问权限,或者下一级页表地址。条目也称为“描述符(Descriptor)”,包括段描述符,大页描述符,小页描述符,极小页描述符,它们依次保存段,大页,小页或级小页的起始物理地址。粗页表描述符,细页描述符用来保存二级页表的物理地址。
MVA->PA地址转换过程如下:
(1)根据给定的虚拟地址找到一级页表中的条目。
(2)如果此条目是段描述符,则返回物理地址,转换结束。
(3)否则,如果此条目是二级页表描述符,继续利用虚拟地址在此二级页表中找到下一条目。
(4)如果第二个条目是页描述符,则返回物理地址,转换结束。
(5)其它情况出错。
对于S3C2440详细的MMU地址转换过程,这里就不啰嗦,可以参考手册进行深入了解。其中需要注意的几个概念和原理,包括内存访问权限检查,TLB和Cache。程序设计的时候需要特别注意这些操作细节。
这里,我们将虚拟地址空间0xB0000000 ~0xB3FFFFFF映射到物理地址空间0x30000000~0x33ffffff上,并在程序连接时指定代码运行地址为0xB0004000。本示例使用SDRAM开始的16KB来存放一极页表,所以剩下的内存开始物理地址为0x30004000。例子程序分为两部分,第一部分运行地址为0,用以初始化SDRAM,复制第二部分代码到SDRAM中(存放在0x30004000开始处),设置页表,启动MMU,最后跳到SDRAM中(0XB0004000)继续执行。第二部分代码运行地址为0xb0004000,用以驱动LED。
整个程序的流程就是这样:开始->关闭watchdog->设置栈指针->初始化SDRAM->复制第二部分代码到SDRAM中->设置页表->启动MMU->重设栈指针->跳到0xb0004000处执行->led操作。
下面我们就来分析一下整个代码的流程和结构以及详细的处理方法:
create_page_table实现3个地址区域的转换。
1>将虚拟地址0~(1M-1)映射到同样的物理地址,steppingstone为4KB,处于这个范围,故VA=PA。这样,steppingstone中的程序在开启mmu前后不需要考虑地址问题。
2>GPIO寄存器起始物理地址为0x56000000,将虚拟地址0xA0000000~0xA000000+1M-1映射到物理地址0x56000000 ~0x56000000+1M-1。
3>本开发板中SDRAM的物理地址范围为0x30000000~0x33ffffff,将虚拟地址0xB0000000~0xB3ffffff映射到物理地址0x30000000~0x33ffffff。
(1)主体程序:
(2)disable_watch_dog 和memsetup 的实现与前面的一样这里就不啰嗦了。
(3)copy_2th_to_sdram 主要用来将第二部分的代码(led驱动操作相关的),从steppingstone中复制到SDRAM中,在连接程序时,第二部分代码的加载地址被指定为2048,重定位地址为0xB0004000。所以系统从nand flash启动后,第二部分代码就存储在steppingstone中地址2048之后,需要把它复制到0x30004000处(MMU开启之前,VA(0xB0004000)==PA(0x30004000))。Steppingstone总大小为4KB,我们将2048之后的所有数据复制到SDRAM中,所以源数据结束地址就为4096。
(4)其实上面的内容在前面已经都讲过了,这里只是温习一下,下面就要看新的内容了,就是本节的create_page_table ,mmu_init。
Mmu_init用来将页表地址通知给CPU,在开启MMU前做一些准备工作,比如使无效ICache,DCache,设置域访问控制寄存器等。
(5) LED驱动代码