內存管理單元(Memory Management Unit)簡稱MMU,它負責虛擬地址到物理地址的映射,並提供硬件機制的內存訪問權限檢查。如今的多用戶多進程操做系統經過 MMU 使得各個用戶進程都擁有本身獨立的地址空間。c++
地址映射功能使得各進程擁有「看起來」同樣的地址空間,內存訪問權限的檢查能夠保護每一個進程所用的內存不會被其餘進程破壞。算法
S3C2440/2410 有以下特性:編程
一個程序在運行以前,沒由必要所有裝入內存,而僅須要將那些當前要運行的部分先裝入內存,其他部分在用到的時候再從磁盤調入,而當內存耗光時再將暫時不用的部分調出到磁盤。dom
再 32 位的CPU系統中,虛擬內存地址範圍位 0~ 0xFFFFFFFF,這個地址範圍稱爲虛擬地址空間,其中的某個地址稱爲虛擬地址。函數
與虛擬地址空間、虛擬地址對應的使物理地址空間、物理地址,他們對應實際的內存oop
虛擬地址最終須要轉換爲物理地址才能讀寫實際的數據,這經過將虛擬地址空間、物理地址空間劃分爲一樣大小的一塊塊小空間(稱爲段或頁),而後爲這兩類小空間創建映射關係。因爲虛擬地址空間遠大於物理空間,有可能多塊虛擬地址空間映射到同一塊物理地址空間,或者有些虛擬地址空間沒有映射到具體的物理地址空間上去(能夠在使用到時再映射)。編碼
未啓動 MMU 時,CPU核、cache、MMU、外設等全部部件使用的都使物理地址spa
啓動MMU後,CPU覈對外發出虛擬地址VA;VA被轉換爲MVA供cache、MMU使用,再這裏 MVA 被轉換爲 PA;最後使用PA讀寫實際設備:操作系統
MVA 是除 CPU 核外的其餘部分看見的虛擬地址3d
將一個虛擬地址轉換爲物理地址,由兩個辦法,用一個肯定的數學公式進行轉換或用表格存儲虛擬地址對應的物理地址。這類表格稱爲頁表(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 }