标签:ref 保存 while 调用 虚拟内存 分代 memory image 数学
内存管理单元(Memory Management Unit)简称MMU,它负责虚拟地址到物理地址的映射,并提供硬件机制的内存访问权限检查。现在的多用户多进程操作系统通过 MMU 使得各个用户进程都拥有自己独立的地址空间。
地址映射功能使得各进程拥有“看起来”一样的地址空间,内存访问权限的检查可以保护每个进程所用的内存不会被其他进程破坏。
S3C2440/2410 有如下特性:
一个程序在运行之前,没由必要全部装入内存,而仅需要将那些当前要运行的部分先装入内存,其余部分在用到的时候再从磁盘调入,而当内存耗光时再将暂时不用的部分调出到磁盘。
再 32 位的CPU系统中,虚拟内存地址范围位 0~ 0xFFFFFFFF,这个地址范围称为虚拟地址空间,其中的某个地址称为虚拟地址。
与虚拟地址空间、虚拟地址对应的使物理地址空间、物理地址,他们对应实际的内存
虚拟地址最终需要转换为物理地址才能读写实际的数据,这通过将虚拟地址空间、物理地址空间划分为同样大小的一块块小空间(称为段或页),然后为这两类小空间建立映射关系。由于虚拟地址空间远大于物理空间,有可能多块虚拟地址空间映射到同一块物理地址空间,或者有些虚拟地址空间没有映射到具体的物理地址空间上去(可以在使用到时再映射)。
未启动 MMU 时,CPU核、cache、MMU、外设等所有部件使用的都使物理地址
启动MMU后,CPU核对外发出虚拟地址VA;VA被转换为MVA供cache、MMU使用,再这里 MVA 被转换为 PA;最后使用PA读写实际设备:
MVA 是除 CPU 核外的其他部分看见的虚拟地址
将一个虚拟地址转换为物理地址,由两个办法,用一个确定的数学公式进行转换或用表格存储虚拟地址对应的物理地址。这类表格称为页表(Page table),页面由一个个条目(Entry)组成;每个条目存储了一段虚拟地址对应的物理地址及其访问权限,或者下一级页表的地址。ARM CPU 中使用的是表格。
S3C2440/2410 最多用到两级页表:以段(Section,1MB)的方式进行转换时只用到一级页表,以页(Page)的方式进行转换时用到两级页表。
页的大小由 3 种,大页(64KB)、小页(4KB)、极小页(1KB)。
条目也称为”描述符“(Descriptor),有段描述符、大页描述符、小页描述符、极小页描述符,主要是用来保存相应的段或页的起始物理地址;粗页描述符、细页描述符用来保存二级页表的物理地址。
转换过程为:
TTB base 代表一级页表的地址,将它写入协处理器 CP15 的寄存器 C2(称为页表基址寄存器)即可。
MMU对段和页面进行保护,对段和页面进行保护是由几个因素造成的。它由域的访问控制字段和一级描述符或二级描述符中的 AP 字段,以及 C1 寄存器的 S(表示system),R(表示rom)控制位来共同决定的。
MMU中的域是指的一些段,大页或者小页的集合。ARM支持最多16个域,每个域的访问控制特性由 CP15 中的寄存器 C3 中的两位来控制。CP15 中的寄存器 C3 的格式如下:
其中每两位控制一个域的访问控制特性,其编码及对应的含义如下:
当域为“用户”域时,AP,S,R控制访问权限的具体规则如下:
当域为“用户”域时,当CPU运行在“特权级”或“用户级”时,AP,S,R 控制段或页的存储访问权限;
比如:
当域为“用户”域时,当CPU运行在“用户级”时,AP=00,S=1,R=0,查表可知,这时CPU没有访问权限;
当域为“用户”域时,当CPU运行在“特权级”时,AP=00,S=1,R=0,这里CPU只能读存储内容,但不能写,如果写的话将产生错误;
注意:AP,S,R的决定访问权限的作用只用是在其域为“用户”域的状态。
CPU核只关心 发出地址、读写数据,至于是否是虚拟地址还是物理地址,CPU不关心,至于是什么地址,是后面的设备关心的
写程序的时候的链接地址,也是没有物理地址和虚拟地址之分,只是单纯的地址,链接地址是CPU看到的
mmu.lds
1 SECTIONS { 2 firtst 0x00000000 : { head.o init.o } 3 second 0xB0004000 : AT(2048) { leds.o } 4 }
head.S
1 @************************************************************************* 2 @ File:head.S 3 @ 功能:设置SDRAM,将第二部分代码复制到SDRAM,设置页表,启动MMU, 4 @ 然后跳到SDRAM继续执行 5 @************************************************************************* 6 7 .text 8 .global _start 9 _start: 10 ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈,指向 SRAM 的顶端 11 bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启 12 bl memsetup @ 设置存储控制器以使用SDRAM 13 bl copy_2th_to_sdram @ 将第二部分代码复制到SDRAM 14 bl create_page_table @ 设置页表 15 bl mmu_init @ 启动MMU 16 ldr sp, =0xB4000000 @ 重设栈指针,指向SDRAM顶端(使用虚拟地址) 17 ldr pc, =0xB0004000 @ 跳到SDRAM中继续执行第二部分代码 18 halt_loop: 19 b halt_loop
init.c
1 /* 2 * init.c: 进行一些初始化,在Steppingstone中运行 3 * 它和head.S同属第一部分程序,此时MMU未开启,使用物理地址 4 */ 5 6 /* WATCHDOG寄存器 */ 7 #define WTCON (*(volatile unsigned long *)0x53000000) 8 /* 存储控制器的寄存器起始地址 */ 9 #define MEM_CTL_BASE 0x48000000 10 11 12 /* 13 * 关闭WATCHDOG,否则CPU会不断重启 14 */ 15 void disable_watch_dog(void) 16 { 17 WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可 18 } 19 20 /* 21 * 设置存储控制器以使用SDRAM 22 */ 23 void memsetup(void) 24 { 25 /* SDRAM 13个寄存器的值 */ 26 unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON 27 0x00000700, //BANKCON0 28 0x00000700, //BANKCON1 29 0x00000700, //BANKCON2 30 0x00000700, //BANKCON3 31 0x00000700, //BANKCON4 32 0x00000700, //BANKCON5 33 0x00018005, //BANKCON6 34 0x00018005, //BANKCON7 35 0x008C07A3, //REFRESH 36 0x000000B1, //BANKSIZE 37 0x00000030, //MRSRB6 38 0x00000030, //MRSRB7 39 }; 40 int i = 0; 41 volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE; 42 for(; i < 13; i++) 43 p[i] = mem_cfg_val[i]; //32位CPU中,指针移动一次是4个字节 44 } 45 46 /* 47 * 将第二部分代码复制到SDRAM 48 */ 49 void copy_2th_to_sdram(void) 50 { 51 unsigned int *pdwSrc = (unsigned int *)2048; //链接脚本中指定了 led.o 的存放的地址为 2048 52 unsigned int *pdwDest = (unsigned int *)0x30004000; // led.o 存放在 SDRAM 中的地址 53 54 while (pdwSrc < (unsigned int *)4096) 55 { 56 *pdwDest = *pdwSrc; 57 pdwDest++; 58 pdwSrc++; 59 } 60 } 61 62 /* 63 * 设置页表 64 */ 65 void create_page_table(void) 66 { 67 68 /* 69 * 用于段描述符的一些宏定义 70 */ 71 #define MMU_FULL_ACCESS (3 << 10) /* 访问权限 */ 72 #define MMU_DOMAIN (0 << 5) /* 属于哪个域 */ 73 #define MMU_SPECIAL (1 << 4) /* 必须是1 */ 74 #define MMU_CACHEABLE (1 << 3) /* cacheable */ 75 #define MMU_BUFFERABLE (1 << 2) /* bufferable */ 76 #define MMU_SECTION (2) /* 表示这是段描述符 */ 77 #define MMU_SECDESC (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | 78 MMU_SECTION) 79 #define MMU_SECDESC_WB (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | 80 MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION) 81 #define MMU_SECTION_SIZE 0x00100000 82 83 unsigned long virtuladdr, physicaladdr; 84 unsigned long *mmu_tlb_base = (unsigned long *)0x30000000; 85 86 /* 87 * Steppingstone的起始物理地址为0,第一部分程序的起始运行地址也是0, 88 * 为了在开启MMU后仍能运行第一部分的程序, 89 * 将0~1M的虚拟地址映射到同样的物理地址 90 */ 91 virtuladdr = 0; 92 physicaladdr = 0; 93 *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | 94 MMU_SECDESC_WB; 95 96 /* 97 * 0x56000000是GPIO寄存器的起始物理地址, 98 * GPBCON和GPBDAT这两个寄存器的物理地址0x56000050、0x56000054, 99 * 为了在第二部分程序中能以地址0xA0000050、0xA0000054来操作GPFCON、GPFDAT, 100 * 把从0xA0000000开始的1M虚拟地址空间映射到从0x56000000开始的1M物理地址空间 101 */ 102 virtuladdr = 0xA0000000; 103 physicaladdr = 0x56000000; 104 *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | 105 MMU_SECDESC; 106 107 /* 108 * SDRAM的物理地址范围是0x30000000~0x33FFFFFF, 109 * 将虚拟地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF上, 110 * 总共64M,涉及64个段描述符 111 */ 112 virtuladdr = 0xB0000000; 113 physicaladdr = 0x30000000; 114 while (virtuladdr < 0xB4000000) 115 { 116 *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | 117 MMU_SECDESC_WB; 118 virtuladdr += 0x100000; 119 physicaladdr += 0x100000; 120 } 121 } 122 123 /* 124 * 启动MMU 125 */ 126 void mmu_init(void) 127 { 128 unsigned long ttb = 0x30000000; 129 130 __asm__( 131 "mov r0, #0\n" 132 "mcr p15, 0, r0, c7, c7, 0\n" /* 使无效ICaches和DCaches */ 133 134 "mcr p15, 0, r0, c7, c10, 4\n" /* drain write buffer on v4 */ 135 "mcr p15, 0, r0, c8, c7, 0\n" /* 使无效指令、数据TLB */ 136 137 "mov r4, %0\n" /* r4 = 页表基址 */ 138 "mcr p15, 0, r4, c2, c0, 0\n" /* 设置页表基址寄存器 */ 139 140 "mvn r0, #0\n" 141 "mcr p15, 0, r0, c3, c0, 0\n" /* 域访问控制寄存器设为0xFFFFFFFF, 142 * 不进行权限检查 143 */ 144 /* 145 * 对于控制寄存器,先读出其值,在这基础上修改感兴趣的位, 146 * 然后再写入 147 */ 148 "mrc p15, 0, r0, c1, c0, 0\n" /* 读出控制寄存器的值 */ 149 150 /* 控制寄存器的低16位含义为:.RVI ..RS B... .CAM 151 * R : 表示换出Cache中的条目时使用的算法, 152 * 0 = Random replacement;1 = Round robin replacement 153 * V : 表示异常向量表所在的位置, 154 * 0 = Low addresses = 0x00000000;1 = High addresses = 0xFFFF0000 155 * I : 0 = 关闭ICaches;1 = 开启ICaches 156 * R、S : 用来与页表中的描述符一起确定内存的访问权限 157 * B : 0 = CPU为小字节序;1 = CPU为大字节序 158 * C : 0 = 关闭DCaches;1 = 开启DCaches 159 * A : 0 = 数据访问时不进行地址对齐检查;1 = 数据访问时进行地址对齐检查 160 * M : 0 = 关闭MMU;1 = 开启MMU 161 */ 162 163 /* 164 * 先清除不需要的位,往下若需要则重新设置它们 165 */ 166 /* .RVI ..RS B... .CAM */ 167 "bic r0, r0, #0x3000\n" /* ..11 .... .... .... 清除V、I位 */ 168 "bic r0, r0, #0x0300\n" /* .... ..11 .... .... 清除R、S位 */ 169 "bic r0, r0, #0x0087\n" /* .... .... 1... .111 清除B/C/A/M */ 170 171 /* 172 * 设置需要的位 173 */ 174 "orr r0, r0, #0x0002\n" /* .... .... .... ..1. 开启对齐检查 */ 175 "orr r0, r0, #0x0004\n" /* .... .... .... .1.. 开启DCaches */ 176 "orr r0, r0, #0x1000\n" /* ...1 .... .... .... 开启ICaches */ 177 "orr r0, r0, #0x0001\n" /* .... .... .... ...1 使能MMU */ 178 179 "mcr p15, 0, r0, c1, c0, 0\n" /* 将修改的值写入控制寄存器 */ 180 : /* 无输出 */ 181 : "r" (ttb) ); 182 }
标签:ref 保存 while 调用 虚拟内存 分代 memory image 数学
原文地址:https://www.cnblogs.com/kele-dad/p/8909933.html