MMU内存管理单元

2/22/2017来源:ASP.NET技巧人气:1268

1、MMU(memory management unit)功能解析

将虚拟地址转化为物理地址

//在p1.c #include<stdio.h> int a = 1; void main() { while(1) { PRintf("&a = %p,a = %d",&a,a); sleep(3); } }

这里写图片描述

//在p2.c #include<stdio.h> int b= 2; void main() { while(1) { printf("&b = %p,b = %d",&b,b); sleep(3); } }

这里写图片描述

从上面的运行结果来看,两个不同的程序,在相同的地址处,读取的数据不一样。 因为这个地址是虚拟地址,不是实际的物理地址,MMU的功能是对于不同的进程的地址(可能是相同的虚拟地址)进行不同的解析,在实际的内存中给相应的变量分配相应的实际物理地址。

在前面的讲解中,我已将mmu关闭了,在后续要将mmu打开,进行地址的转化。除此之外,mmu还有“访问权限管理”的作用,即控制相应地址的空间是可读还是可写的。

2、深入剖析地址转化

段转换方式(当一级转换列表的最后两位是:10): 虚拟地址的高12位(31:20)作为Translatation table的基地址索引,在这个基地址索引处,它的高12位为段的物理基地址,(0:19)位保存了偏移地址

粗页转换方式(…:01) 细页转换方式 (…:11) 错误(…:00)

引用> http://blog.sina.com.cn/s/blog_63ac1cef0100vd95.html 这里写图片描述

3、MMU的配置与使用

前面点亮一个LED,是采用物理地址的方式,本节采用MMU虚拟地址的方式来点亮LED。 (虚拟地址空间—–》物理地址空间—(GPIO寄存器等)—》led)

首先MMU要去查一级页表(由工程师建立),并将表的基地址(2440:0x30000000 6410:0x50000000)写到CP15的C2寄存器(需要用的时候在从里面读取),再打开MMU。 所以配置MMU有三项工作: (1)建立一级页表 (2)写入TTB(Translation Table Base) (3)使能MMU:由CP15的C1寄存器的第0位(置1)来实现的(通过C嵌入汇编来实现)。

根据段转化的方式,来建立一级页表: 这里写图片描述 【31:20】为实际物理地址的高12位。 这里写图片描述

代码如下:

#define GPBCON (volatile unsigned long*)0xA0000010 //使用虚拟地址,真正的物理地址是0x56000010 #define GPBDAT (volatile unsigned long*)0xA0000014 /* * 用于段描述符的一些宏定义 */ #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) void create_page_table(void) { unsigned long *ttb = (unsigned long *)0x30000000; //页表的基地址 unsigned long vaddr, paddr; vaddr = 0xA0000000; paddr = 0x56000000; *(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC; //建立内存的映射,因为程序是存储在内存中的,也需要通过虚拟地址的方式来访问 vaddr = 0x30000000; paddr = 0x30000000; while (vaddr < 0x34000000) { *(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC_WB;//相对于GPIO增加了对缓存cache和write buffer的使用 vaddr += 0x100000; // + 1M paddr += 0x100000; } } void mmu_init() { __asm__( /*设置TTB,将页表的基地址写到CP15的C2寄存器*/ "ldr r0, =0x30000000\n" "mcr p15, 0, r0, c2, c0, 0\n" /*不进行权限检查*/ "mvn r0, #0\n" "mcr p15, 0, r0, c3, c0, 0\n" /*使能MMU*/ "mrc p15, 0, r0, c1, c0, 0\n" "orr r0, r0, #0x0001\n" "mcr p15, 0, r0, c1, c0, 0\n" : : ); } int gboot_main() { create_page_table(); mmu_init(); //采用虚拟地址方式点亮LED *(GPBCON) = 0x400; *(GPBDAT) = 0x0; return 0; }