ARM中MMU使用实例
am函数的代码如下:
/*
*将第二部分代码复制到SDRAM
*/
void copy_2th_to_sdram(void)
{
unsigned int *pdwSrc= (unsigned int *)2048;
unsigned int *pdwDest = (unsigned int *)0x30004000;
while (pdwSrc < (unsigned int *)4096)
{
*pdwDest = *pdwSrc;
pdwDest++;
pdwSrc++;
}
}
剩下的create_page_table、mmu_init就是本章的重点了,前者用来设置页表,后者用来开启MMU。
先看看create_page_table函数。它用于设置3个区域的地址映射关系。
(1)将虚拟地址0 - (1M - 1)映射到同样的物理地址去,Steppingstone(从0地址开始的4KB内存)就处于这个范围中。使虚拟地址等于物理地址,可以让Steppingstone中的程序(head.s和init.c)在开启MMU前后不需要考虑太多的事情。
(2)GPIO寄存器的起始物理地址范围为0x56000000,将虚拟地址0xA0000000 - (0xA0000000 + 1M - 1)映射到物理地址0x56000000 - (0x56000000 + 1M - 1)。
(3)本开发板中SDRAM的物理地址范围为0x30000000 - 0x33FFFFFF,将虚拟地址0xB0000000 - 0xB3FFFFFF映射到物理地址0x30000000 - 0x33FFFFFF。
create_page_table函数代码如下:
/*
*设置页表
*/
void create_page_table(void)
{
/*
*用于段描述符的一些宏定义
*/
#define MMU_FULL_ACCESS(3 < 10)/*访问权限*/
#define MMU_DOMAIN(0 < 5)/*属于哪个域*/
#define MMU_SPECIAL(1 < 4)/*必须是1 */
#define MMU_CACHEABLE(1 < 3)/* cacheable */
#define MMU_BUFFERABLE(1 < 2)/* bufferable */
#define MMU_SECTION(2)/*表示这是段描述符*/
#define MMU_SECDESC(MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
MMU_SECTION)
#define MMU_SECDESC_WB(MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)
#define MMU_SECTION_SIZE0x00100000
unsigned long virtuladdr, physicaladdr;
unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;
/*
* Steppingstone的起始物理地址为0,第一部分程序的起始运行地址也是0,
*为了在开启MMU后仍能运行第一部分的程序,
*将0~1M的虚拟地址映射到同样的物理地址
*/
virtuladdr = 0;
physicaladdr = 0;
*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
MMU_SECDESC_WB;
/*
* 0x56000000是GPIO寄存器的起始物理地址,
* GPBCON和GPBDAT这两个寄存器的物理地址0x56000010、0x56000014,
*为了在第二部分程序中能以地址0xA0000010、0xA0000014来操作GPBCON、GPBDAT,
*把从0xA0000000开始的1M虚拟地址空间映射到从0x56000000开始的1M物理地址空间
*/
virtuladdr = 0xA0000000;
physicaladdr = 0x56000000;
*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
MMU_SECDESC;
/*
* SDRAM的物理地址范围是0x30000000~0x33FFFFFF,
*将虚拟地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF上,
*总共64M,涉及64个段描述符
*/
virtuladdr = 0xB0000000;
physicaladdr = 0x30000000;
while (virtuladdr < 0xB4000000)
{
*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
MMU_SECDESC_WB;
virtuladdr += 0x100000;
physicaladdr += 0x100000;
}
}
mmu_tlb_base被定义为unsigned long指针,所指向的内存为4字节,刚好是一个描述符的大小。在SDRAM的开始存放页表——第84行令mmu_tlb_base指向SDRAM的起始地址0x30000000。其中最能体现页表结构的代码是第93、104、116行。
*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
MMU_SECDESC_WB;
*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
MMU_SECDESC;
*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
MMU_SECDESC_WB;
虚拟地址的位[31:20]用于索引一级页表,找到它所对应的描述符,对应于“(virtuladdr >> 20)”。
如图7.13所示,段描述符中位[31:20]中保存段的物理地址,对应于“physicaladdr & 0xFFF00000”。
位[11:0]中用来设置段的访问权限,包括所属的域、AP位、C位(是否可Cache)、B位(是否使用Write buffer)——这对应于“MMU_SECDESC”或“MMU_SECDESC_WB”,它们的域都被设置为0,AP位被设为0b11(根据表7.2可知,它所在的域进行权限检查,则读写操作都被允许)。“MMU_SECDESC”中C/B位都没有设置,表示不使用Cache和Write buffer,所以映射寄存器空间时使用“MMU_SECDESC”。“
ARMMM 相关文章:
- ARM·MMU(11-24)
- ARM MMU工作原理剖析(11-23)
- ARM中MMU的作用(11-11)
- ARM中MMU之地址转换(11-09)
- ARM中MMU地址转换理解(11-09)
- Windows CE 进程、线程和内存管理(11-09)