1. 程式人生 > >三、內存管理單元---MMU

三、內存管理單元---MMU

相互 算法 res ldr ... mvn SM try step

3.1 MMU介紹

3.1.1 MMU 特性

  內存管理單元(Memory Management Unit)簡稱MMU,它負責虛擬地址到物理地址的映射,並提供硬件機制的內存訪問權限檢查。現在的多用戶多進程操作系統通過 MMU 使得各個用戶進程都擁有自己獨立的地址空間。

  地址映射功能使得各進程擁有“看起來”一樣的地址空間,內存訪問權限的檢查可以保護每個進程所用的內存不會被其他進程破壞。

  S3C2440/2410 有如下特性:

  • 與 ARM V4 兼容的映射長度、域、訪問權限檢查機制
  • 4種映射長度:段(1MB)、大頁(64KB)、小頁(4KB)、極小頁(1KB)
  • 對每段都可以設置訪問權限
  • 大頁、小頁的每個子頁(sub-page,即被映射頁的 1/4)都可以單獨設置訪問權限
  • 硬件實現的 16 個域
  • 指令 TLB(含 64 個條目)、數據TLB(含64個條目)
  • 硬件訪問頁表(地址映射、權限檢查由硬件自動進行)
  • TLB 中條目的替換采用 round-robin 算法(也稱 cyclic 算法)
  • 可以使無效整個 TLB
  • 可以單獨使無效某個 TLB 條目
  • 可以在 TLB 中鎖定某個條目,指令 TLB、數據TLB 相互獨立  

  一個程序在運行之前,沒由必要全部裝入內存,而僅需要將那些當前要運行的部分先裝入內存,其余部分在用到的時候再從磁盤調入,而當內存耗光時再將暫時不用的部分調出到磁盤。

  再 32 位的CPU系統中,虛擬內存地址範圍位 0~ 0xFFFFFFFF,這個地址範圍稱為虛擬地址空間,其中的某個地址稱為虛擬地址。

  與虛擬地址空間、虛擬地址對應的使物理地址空間、物理地址,他們對應實際的內存

  虛擬地址最終需要轉換為物理地址才能讀寫實際的數據,這通過將虛擬地址空間、物理地址空間劃分為同樣大小的一塊塊小空間(稱為段或頁),然後為這兩類小空間建立映射關系。由於虛擬地址空間遠大於物理空間,有可能多塊虛擬地址空間映射到同一塊物理地址空間,或者有些虛擬地址空間沒有映射到具體的物理地址空間上去(可以在使用到時再映射)。

  • ARM CPU 上的地址轉換過程的三個概念
    • VA:Virtual Address,虛擬地址
    • MVA:Modified Virtual Address,變換後的虛擬地址
    • PA:Physical Address,物理地址

  未啟動 MMU 時,CPU核、cache、MMU、外設等所有部件使用的都使物理地址

  啟動MMU後,CPU核對外發出虛擬地址VA;VA被轉換為MVA供cache、MMU使用,再這裏 MVA 被轉換為 PA;最後使用PA讀寫實際設備:

  1. CPU核看到的、用到的只是虛擬地址 VA,至於 VA如何最終落實到物理地址 PA 上,CPU核使不理會的
  2. caches 和 MMU 也是看不見 VA 的,他們利用由 MVA 轉換得到 PA
  3. 實際設備看不到 VA、MVA,讀寫它們時使用的是物理地址PA

  MVA 是除 CPU 核外的其他部分看見的虛擬地址

  技術分享圖片

3.1.2 地址映射過程

  技術分享圖片

  將一個虛擬地址轉換為物理地址,由兩個辦法,用一個確定的數學公式進行轉換或用表格存儲虛擬地址對應的物理地址。這類表格稱為頁表(Page table),頁面由一個個條目(Entry)組成;每個條目存儲了一段虛擬地址對應的物理地址及其訪問權限,或者下一級頁表的地址。ARM CPU 中使用的是表格。

  S3C2440/2410 最多用到兩級頁表:以段(Section,1MB)的方式進行轉換時只用到一級頁表,以頁(Page)的方式進行轉換時用到兩級頁表。

  頁的大小由 3 種,大頁(64KB)、小頁(4KB)、極小頁(1KB)。

  條目也稱為”描述符“(Descriptor),有段描述符、大頁描述符、小頁描述符、極小頁描述符,主要是用來保存相應的段或頁的起始物理地址;粗頁描述符、細頁描述符用來保存二級頁表的物理地址。

  轉換過程為:

  1. 根據給定的虛擬地址找到一級頁表中的條目
  2. 如果此條目是段描述符,則返回物理地址,轉換結束
  3. 否則如果此條目是二級頁表描述符,繼續利用虛擬地址在此二級頁表中找到下一個條目
  4. 如果這第二個條目是頁描述符,則返回物理地址,轉換結束
  5. 其他情況出錯

  技術分享圖片

  TTB base 代表一級頁表的地址,將它寫入協處理器 CP15 的寄存器 C2(稱為頁表基址寄存器)即可。

  

  

3.1.3 權限管理 

  MMU對段和頁面進行保護,對段和頁面進行保護是由幾個因素造成的。它由域的訪問控制字段和一級描述符或二級描述符中的 AP 字段,以及 C1 寄存器的 S(表示system),R(表示rom)控制位來共同決定的。
  MMU中的域是指的一些段,大頁或者小頁的集合。ARM支持最多16個域,每個域的訪問控制特性由 CP15 中的寄存器 C3 中的兩位來控制。CP15 中的寄存器 C3 的格式如下:

  技術分享圖片

   其中每兩位控制一個域的訪問控制特性,其編碼及對應的含義如下:

  技術分享圖片

  • 當控制位為00時,此域是沒有訪問權限的;
  • 當控制位為01時,此時域的屬性為“用戶”域;
    • 當其域為“用戶”域時,存儲訪問權限控制由AP,S,R來決定。
  • 當控制位為11時,此時域的屬性為“系統”域。
    • 當其域為“系統”域時,AP,S,R 的值將會忽略,CPU(無論cpu運行在用戶級還是在特權級)可以直接訪問存儲內容,將不進行存儲權限檢查。

  當域為“用戶”域時,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的決定訪問權限的作用只用是在其域為“用戶”域的狀態。

3.2 編程地址映射

  CPU核只關心 發出地址、讀寫數據,至於是否是虛擬地址還是物理地址,CPU不關心,至於是什麽地址,是後面的設備關心的

  寫程序的時候的鏈接地址,也是沒有物理地址和虛擬地址之分,只是單純的地址,鏈接地址是CPU看到的

  • MMU 操作:
    • 建立地址映射表格,即建立虛擬地址到物理地址的映射
    • 把表格地址告訴 MMU,即把內存地址告訴 MMU
    • 啟動MMU

  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 }

三、內存管理單元---MMU