主要功能
虚拟地址(VA)到物理地址(PA)映射
内存保护
概述
一个程序在运行前,没有必要全部装入内存,而仅需要将那些当前需要运行的部分先装入内存,其余部分在用到时再从磁盘读入。当内存耗光时,再将暂时不用的部分调出到磁盘。虚拟存储器从逻辑上对物理内存进行了扩充,对于32位的CPU而言,其虚拟内存为4G,这里以CortexA7内核为例。
MMU的开启是由用户通过配置CP15协处理器实现的,后面会说到。在没有开启MMU之前,内核发出的地址都是物理地址,通过总线传输到DDR控制器以及外设控制器上,实现对物理地址的读写操作,而对于开启了MMU的处理器而言,CPU和发出的地址都是虚拟地址,需要经过MMU的控制转换为实际的物理地址,再传送到DDR控制器实现对物理内存的读写以及对外设控制器等的操作。
地址转换过程
虚拟地址到物理地址的转换是通过地址映射表实现的,系统在初始化内存时,会通过一些指令在内存中填写地址映射表,开启MMU,告诉MMU映射表的位置(基地址,地址映射表的基地址保存在CP15协处理器的C2寄存器中),对于虚拟到物理的转换,MMU通过读取地址映射表来实现最终的转换。
地址映射表又名页表,页表由多级条目(描述符)组成,描述符是32bit的数据组成的,保存物理地址或下一级页表的基地址。对于地址的转换,有段转换方式,页转换方式
段转换
段式转换用到一级页表,也就是存放在内存中的地址映射表有一个。对于32位的CPU,页表中存放了4096个条目(描述符:32bit的数据),也就是这个表格将虚拟内存分为了4096份(4096*1MB=4GB),每份保存一个描述符,每个条目对应1MB的内存地址
此图为一级页表描述符组成格式,由图可知当bit[1][0]为0b10时,使用的是段转换方式,在段转换方式下
-
bit[1:0] 转换方式位
-
bit[2] 是否使用write buffer
-
bit[3] 表示一段内存是否可以被Cache,即使能后的Cache是否有效
-
bit[8:5] 域选择位,内存保护,决定是否对某块内存进行权限检查
-
bit[15,11:10] AP内存保护,如何对某块内存进行权限检查
-
bit[31:20] 保存物理地址PA的bit[31][20],与VA的bit[19:0]组成转换后的物理地址PA
以虚拟地址 VA = 0x87800000为例,地址转换的大致过程:
-
内核发出地址0x8700000,传输到MMU
-
MMU读取CP15协处理器的C2寄存器bit[31:14]得到页表基地址(16K对齐)
-
取读取到的虚拟地址0x87800000 bit[31:20],得到地址偏移0x878*4
-
读取一级页表地址(页表基地址+0x878*4)的内容,此内容保存的是段描述符
-
PA = 段描述符bit[31:20] + VA bit[19:0]
所以最终的物理地址就是PA。以段进行映射时,通过VA的bit[31:20]结合页起始地址,得到一段1MB的起始物理地址,通过VA的bit[19:0]在段中寻址
页转换
页转换最少需要用到两级页表,如上图所示,当一级页表描述符bit[1:0]为0b01时,实现的是页转换方式。在该讲解中,没有划分粗页表,细页表,默认的二级页表含有1024个条目,页表的大小有大页(64KB)小页(4KB)之分。因为一级页表一个条目代表1MB的内存空间,所以所以一个二级页表将1MB的内存空间划分为了1024个条目,每个条目为1KB,即每个描述符保存了1KB的物理内存起始地址。
当以页方式转换时,一级页表条目bit[31:10]保存二级页表物理基地址部分位,它和VA的bit[19:12]组成低两位为0的二级页表物理地址,据此可以找到二级页表描述符
此图为二级页表描述符组成格式,通过bit[1:0]选择大页,小页。大页为64KB,小页为4KB
- 大页bit[1:0] = 0b01
因为大页单位是64KB,二级页表每个条目1KB,所以每64个描述符的值相同,表示该64KB的起始地址,bit[31:16]保存PA的bit[31:16],它与VA的bit[15:0]组成最终的32bit物理地址 - 小页bit[1:0] = 0b10
因为小页单位是4KB,二级页表每个条目1KB,所以每4个描述符的值相同,表示该4KB的起始地址,bit[31:12]保存PA的bit[31:12],它与VA的bit[11:0]组成最终的32bit物理地址
以虚拟地址 VA = 0x87800001为例,大页地址转换的大致过程如下:
-
内核发出地址0x8700001,传输到MMU
-
MMU读取CP15协处理器的C2寄存器bit[31:14]得到页表基地址(16K对齐)
-
取读取到的虚拟地址0x87800001 bit[31:20],得到地址偏移,0x878*4
-
读取一级页表地址(页表基地址+0x878*4)的内容,此内容保存的是页描述符,假设为0x80000000
-
一级页表描述符bit[31:10]和VA的bit[19:12]组成低两位为0的二级页表物理地址=0x80000000
-
根据二级列表物理地址,读取二级列表描述符,假设为0x65511000
-
PA=VA bit[15:0] | 二级列表描述符bit[31:16] = 0x65510001
内存保护
内存保护是MMU的主要功能之一,它决定了一块内存的读写权限。CP15协处理器的c3寄存器(域访问控制)+描述符的域(Domain)+描述符的AP决定了内存的访问权限
-
c3寄存器(域访问控制)
每两个位决定一个域,一共有16个域,从0~15 ;含义如下 -
Domain
描述符的Domain位(4bit)对应查找C3寄存器的指定域,如Domain(0b0001),则访问域1,如果域1的值为0b00,则会产生异常;如果值为0b11,如果是段描述符,则通过描述符的AP位进行权限检查,如果是页描述符,则通过二级页表描述符AP位进行权限检查
-
AP
更多精彩内容,请关注公众号“LLP学嵌入式”,相互学习!
1/*start.S,代码入口*/
2.global _start
3_start:
4 mrs r0,cpsr
5 bic r0,r0,#0xf1
6 orr r0,r0,#0x13
7 msr cpsr,r0 /*工作在SVC模式*/
8 ldr sp,=0x86000000 /分配栈空间/
9 b main
10
11/*MMU.lds链接脚本*/
12SECTIONS{
13 . = 0x87800000;
14 .text :
15 {
16 start.o
17 main.o
18 *(.text)
19 }
20 .rodata ALIGN(4) : {*(.rodata*)}
21 .data ALIGN(4) : { *(.data) }
22 _bss_start = .;
23 .bss ALIGN(4) :{ *(.bss) *(COMMON)}
24 _bss_end = .;
25}
26
27/*main.c*/
28static void create_tlb(unsigned int *ttb,unsigned int va,unsigned pa,unsigned char WB)
29{
30 if(WB==1)
31 {
32 *(ttb+(va>>20))=(pa&0xfff00000)|MMU_SECDESC_WB;
33 }
34 else
35 {
36 *(ttb+(va>>20))=(pa&0xfff00000)|MMU_SECDESC;
37 }
38}
39
40void create_page_table(void) /*创建页表*/
41{
42 /*512M DDR MAP*/
43 unsigned int *ttb = (unsigned int *)0x80000000;
44 unsigned int va = 0x80000000;
45 unsigned int pa = 0x80000000;
46 /*DDR映射的虚拟地址,物理地址不变,512MB*/
47 for(;va<0xA0000000;va+=0x100000,pa+=0x100000)
48 {
49 create_tlb(ttb,va,pa,1);/*Cache ,write buffer有效*/
50 }
51 /*register map*/
52 /*只映射了1MB的物理内存,包含了时钟使能,GPIO控制寄存器地址*/
53 create_tlb(ttb,0x02100000,0x02000000,0);
54}
55
56static void mmu_init(void)
57{
58 __asm__(
59 "ldr r0,=0x80000000\n"
60 "mcr p15,0,r0,c2,c0,0\n" /*设置页表基址寄存器*/
61 "ldr r0,=0xffffffff\n"
62 "mcr p15,0,r0,c3,c0,0\n" /*域访问控制寄存器为0xffffffff,不进行权限检查,允许任何访问*/
63 "mrc p15,0,r0,c1,c0,0\n" /*读取控制寄存器的值*/
64 "orr r0,r0,#0x0004\n" /*开启Dcache*/
65 "orr r0,r0,#0x1000\n" /*开启Icache*/
66 "orr r0,r0,#0x0001\n" /*使能MMU*/
67 "mcr p15,0,r0,c1,c0,0\n" /*写入c1,使之有效*/
68 );
69}
70
71void delay_short(volatile unsigned int n)
72{
73 while(n--){}
74}
75
76void delay(volatile unsigned int ms)
77{
78 while(ms--)
79 {
80 delay_short(0x7ff*20);
81 }
82}
83
84void clk_enable(void)
85{
86 *((volatile unsigned int *) VA_CCM_CCGR0) = 0xffffffff;
87 *((volatile unsigned int *) VA_CCM_CCGR1) = 0xffffffff;
88 *((volatile unsigned int *) VA_CCM_CCGR2) = 0xffffffff;
89 *((volatile unsigned int *) VA_CCM_CCGR3) = 0xffffffff;
90 *((volatile unsigned int *) VA_CCM_CCGR4) = 0xffffffff;
91 *((volatile unsigned int *) VA_CCM_CCGR5) = 0xffffffff;
92 *((volatile unsigned int *) VA_CCM_CCGR6) = 0xffffffff;
93}
94
95void led_init(void)
96{
97 *((volatile unsigned int *)VA_SW_MUX_GPIO1_IO03) = 0x5;
98 *((volatile unsigned int *)VA_SW_PAD_GPIO1_IO03) = 0x10B0;
99 *((volatile unsigned int *)VA_GPIO1_GDIR) = 0x0000008;
100 *((volatile unsigned int *)VA_GPIO1_DR)|= (1<<3);//关闭LED
101}
102void led_off()
103{
104 *((volatile unsigned int *)VA_GPIO1_DR) |= (1<<3);//关闭LED
105}
106void led_on()
107{
108 *((volatile unsigned int *)VA_GPIO1_DR) &= ~(1<<3);//打开LED
109}
110
111int main(void)
112{
113 create_page_table();/*创建页表*/
114 mmu_init(); /*MMU使能*/
115 clk_enable(); /*操作虚拟地址,使能时钟*/
116 led_init(); /*led_init 打开led*/
117 while(1)
118 {
119 led_off();
120 delay(1000);
121 led_on();
122 delay(1000);
123 }
124 return 0;
125}
126
127
128
129/*宏定义*/
130#define CCM_CCGR0 0X020C4068
131#define CCM_CCGR1 0X020C406C
132#define CCM_CCGR2 0X020C4070
133#define CCM_CCGR3 0X020C4074
134#define CCM_CCGR4 0X020C4078
135#define CCM_CCGR5 0X020C407C
136#define CCM_CCGR6 0X020C4080
137
138#define SW_MUX_GPIO1_IO03 0X020E0068
139#define SW_PAD_GPIO1_IO03 0X020E02F4
140
141#define GPIO1_DR 0X0209C000
142#define GPIO1_GDIR 0X0209C004
143#define GPIO1_PSR 0X0209C008
144#define GPIO1_ICR1 0X0209C00C
145#define GPIO1_ICR2 0X0209C010
146#define GPIO1_IMR 0X0209C014
147#define GPIO1_ISR 0X0209C018
148#define GPIO1_EDGE_SEL 0X0209C01C
149
150
151#define VA_CCM_CCGR0 (0X020C4068+0x100000)
152#define VA_CCM_CCGR1 (0X020C406C+0x100000)
153#define VA_CCM_CCGR2 (0X020C4070+0x100000)
154#define VA_CCM_CCGR3 (0X020C4074+0x100000)
155#define VA_CCM_CCGR4 (0X020C4078+0x100000)
156#define VA_CCM_CCGR5 (0X020C407C+0x100000)
157#define VA_CCM_CCGR6 (0X020C4080+0x100000)
158
159#define VA_SW_MUX_GPIO1_IO03 (0X020E0068+0x100000)
160#define VA_SW_PAD_GPIO1_IO03 (0X020E02F4+0x100000)
161
162#define VA_GPIO1_DR (0X0209C000+0x100000)
163#define VA_GPIO1_GDIR (0X0209C004+0x100000)
164#define VA_GPIO1_PSR (0X0209C008+0x100000)
165#define VA_GPIO1_ICR1 (0X0209C00C+0x100000)
166#define VA_GPIO1_ICR2 (0X0209C010+0x100000)
167#define VA_GPIO1_IMR (0X0209C014+0x100000)
168#define VA_GPIO1_ISR (0X0209C018+0x100000)
169#define VA_GPIO1_EDGE_SEL (0X0209C01C+0x100000)
170
171#define MMU_SECTION_AP (0x3<<10) /*访问权限*/
172#define MMU_SECTION_DOMAIN (0<<5) /*属于哪个域*/
173#define MMU_SECTION_SPECIAL (1<<4) /*必须是1,决定该区域是否可以执行*/
174#define MMU_SECTION_CACHEEABLE (1<<3) /*cacheable*/
175#define MMU_SECTION_BUFFERABLE (1<<2) /*bufferable*/
176#define MMU_SECTION_SECTION (2) /*段映射*/
177#define MMU_SECDESC (MMU_SECTION_AP|MMU_SECTION_DOMAIN|MMU_SECTION_SPECIAL|MMU_SECTION_SECTION)
178#define MMU_SECDESC_WB (MMU_SECTION_AP|MMU_SECTION_DOMAIN|MMU_SECTION_SPECIAL|MMU_SECTION_SECTION| \
179 MMU_SECTION_CACHEEABLE|MMU_SECTION_BUFFERABLE)