MMU作为一个硬件单元有把虚拟地址转换成物理地址的作用,操作系统可以让用户运行大量的程序,这些程序都需要分配内存,而一段程序重复运行的时候,如果没有MMU,一段物理地址不可能被重复使用,而有MMU的时候可以把同一虚拟地址映射到不同的物理地址段,这样就避免地址冲突了。而且大家都有体验,以linux为例,存在交换分区,它可以把硬盘的存储空间映射到虚拟地址中,这样一来相当于内存被扩容了,当然这样的便利也伴随着访问效率的降低。
mmu.c
/*mask for page table base addr*/
#define PAGE_TABLE_L1_BASE_ADDR_MASK (0xffffc000)
#define VIRT_TO_PTE_L1_INDEX(addr) (((addr)&0xfff00000)>>18)
#define PTE_L1_SECTION_NO_CACHE_AND_WB (0x0<<2)
#define PTE_L1_SECTION_DOMAIN_DEFAULT (0x0<<5)
#define PTE_ALL_AP_L1_SECTION_DEFAULT (0x1<<10)
#define PTE_L1_SECTION_PADDR_BASE_MASK (0xfff00000)
#define PTE_BITS_L1_SECTION (0x2)
#define L1_PTR_BASE_ADDR 0x30700000
#define PHYSICAL_MEM_ADDR 0x30000000
#define VIRTUAL_MEM_ADDR 0x30000000
#define MEM_MAP_SIZE 0x800000
#define PHYSICAL_IO_ADDR 0x48000000
#define VIRTUAL_IO_ADDR 0xc8000000
#define IO_MAP_SIZE 0x18000000
void start_mmu(void)
{
unsigned int ttb = L1_PTR_BASE_ADDR;
asm (
"mcr p15,0,%0,c2,c0,0\n" /* set base address of page table*/
"mvn r0,#0\n"
"mcr p15,0,r0,c3,c0,0\n" /* enable all region access*/
"mov r0,#0x1\n"
"mcr p15,0,r0,c1,c0,0\n" /* set back to control register */
"mov r0,r0\n"
"mov r0,r0\n"
"mov r0,r0\n"
:
: "r" (ttb)
:"r0"
);
}
unsigned int gen_l1_pte(unsigned int paddr)
{
return (paddr & PTE_L1_SECTION_PADDR_BASE_MASK) | PTE_BITS_L1_SECTION;
}
unsigned int gen_l1_pte_addr(unsigned int baddr, unsigned int vaddr)
{
return (baddr & PAGE_TABLE_L1_BASE_ADDR_MASK) | VIRT_TO_PTE_L1_INDEX(vaddr);
}
void init_sys_mmu(void)
{
unsigned int pte;
unsigned int pte_addr;
int j;
for(j = 0; j < MEM_MAP_SIZE>>20; j++) {
pte = gen_l1_pte(PHYSICAL_MEM_ADDR + (j<<20));
pte |= PTE_ALL_AP_L1_SECTION_DEFAULT;
pte |= PTE_L1_SECTION_NO_CACHE_AND_WB;
pte |= PTE_L1_SECTION_DOMAIN_DEFAULT;
pte_addr = gen_l1_pte_addr(L1_PTR_BASE_ADDR, VIRTUAL_MEM_ADDR + (j<<20));
*(volatile unsigned int *)pte_addr = pte;
}
for(j = 0; j< IO_MAP_SIZE>>20; j++) {
pte = gen_l1_pte(PHYSICAL_IO_ADDR+(j<<20));
pte |= PTE_ALL_AP_L1_SECTION_DEFAULT;
pte |= PTE_L1_SECTION_NO_CACHE_AND_WB;
pte |= PTE_L1_SECTION_DOMAIN_DEFAULT;
pte_addr = gen_l1_pte_addr(L1_PTR_BASE_ADDR, VIRTUAL_IO_ADDR + (j<<20));
*(volatile unsigned int *)pte_addr = pte;
}
}