學習 ARM 系列 -- FS2410 開發板上啓用 MMU 實現虛擬內存管理

 

 

學習 ARM 系列 -- FS2410 開發板上啓用 MMU 實現虛擬內存管理

一、背景
   FS2410 開發板上的 ARM 核心爲 ARM920T, ARM920T 代表着什麼呢? 其實
ARM920T = ARM9 core + MMU + Cache,也就是說 ARM920T 爲實現虛擬內存管理提供了硬件
條件,這個硬件條件就是 MMU -- 內存管理單元。前面的實驗我們程序裏的地址都是直接對應物理地
址,也就是說虛擬地址等同於物理地址,而今藉助 MMU 我們可以實現虛擬內存管理,程序裏面的地址
不再被直接送到地址總線,而是先通過 MMU,由 MMU 來實現虛地址到物理地址的映射。這有什麼意義
呢?想象有這麼兩個程序,它們有相同的虛擬地址,但由於運行時其虛地址分別被映射到不同的物理地址
,所以它們各行其道、和平共處,而不會產生衝突...有了 MMU 的支持我們可以設計出高級的作業系統。


二、目的
   如何啓用 MMU, 並實現虛擬地址到物理地址映射正是這次實驗的目的。呵呵,你也許已經迫不及待...
那現在我們就去探個究竟!


三、代碼分析
   程序的整個執行流程都體現在 start.S 文件裏(以前不是 head.s文件嗎? 呵呵,我把以前的代碼進
行了重構,現在代碼看上去更清析--好的架構是很重要的,更便於以後的擴充),start.S裏調用的函數有的
是在 .c 文件實現的,必要時我會做相應解釋。

1  .text
2  .global _start
3  _start:
4   b reset
5   NOP
6   NOP
7   NOP
8   NOP
9   NOP
10  ldr pc, handle_irq_addr
11  NOP
12  handle_irq_addr: 
13  .long handle_irq
14 reset:
15  ldr r0, =0x53000000  @ Close Watch-dog Timer
16  mov r1, #0x0
17  str r1, [r0]
18
19 @ init stack
20 ldr sp,=4096
21 
22 @ disable all interrupts
23 mov r1, #0x4A000000
24 mov r2, #0xffffffff
25 str r2, [r1, #0x08]
26 ldr r2, =0x7ff
27 str r2, [r1, #0x1c]
28
29 bl  memory_setup  @ Initialize memory setting
30 bl  flash_to_sdram  @ Copy code to sdram
31
32 ldr pc, =run_on_sdram
33 run_on_sdram:
34 ldr sp, =0x33000000
35  bl init_mmu_tlb   @ setup page table
36  bl init_mmu         @ MMU enabled
37
38 msr cpsr_c, #0xd2  @ set the irq mode stack
39 ldr sp, =0x31000000
40 msr cpsr_c, #0xdf  @ set the system mode stack
41 ldr sp, =0x32000000
42 bl  init_irq           
43 msr cpsr_c, #0x5f  @ set the system mode open the irq
44 
45 ldr sp, =0x33000000  @ Set stack pointer
46 bl  main
47 loop:
48 b loop


(1) 設置中斷跳轉指令
    可以看到程序 4~13 行用來設置中斷跳轉指令,目前我們只實現了響應 IRQ 中斷的代
    碼,所以在第 10 行處放了一條 ldr 加載指令,它的意思是當發生 IRQ 中斷時,把
    用於響應 IRQ 中斷的函數 handle_irq 的地址加載進 pc 寄存器讓程序跳轉那裏進
    行相應處理
(2) 關閉看門狗,程序第 15~17 行
(3) 初始化堆棧寄存器體現在第 20 行,之所這麼做因爲下面會調用一些 C 函數,而 C函
    數裏的變量當然要保存在堆棧裏了
(4) 暫時不響應所有中斷: 22~27 行
(5) 第 29 行,初始化內存(內存在這裏就是 SDRAM) 慢着...程序不是已經運行在內存裏
    了嗎? 非也,準確點說是運行在 SRAM 裏。ARM 啓動時會將 Nand Flash(相當於硬
    盤)裏前 4K 代碼加載進 SRAM 裏並運行之。那程序大於 4K 怎麼辦? 呵呵,這正是
    下一點要說明的
(6) 第 30 行,程序自身到內存的般移。我們的程序大於 4K, 只靠 SRAM 的那可憐的 4K
    是運行不開的
(7) 第 32~33 行,跳轉到 SDRAM 裏執行。我們的代碼已經搬到內存了,64M 的空間夠用
     的了
(8) 第 34~36 行,設置頁表,啓用 MMU。這是今天的主角。函數
       init_mmu_tlb
       init_mmu
    定義在 mmu.c 文件裏,我們去看看這個文件裏有些什麼?

     1  /* init MMU page table*/
     2  void init_mmu_tlb() {
     3    unsigned long vm_addr, idx;
     4    unsigned long *tb_base = (unsigned long *)MMU_TBL_BASE;
     5 
     6    for (vm_addr = MEM_START; vm_addr < MEM_END; vm_addr += PAGE_SIZE) {
     7      idx = vm_addr >> 20;
     8      /* entry: section base, AP=0b11, domain=0b00,cached,write-through*/
     9      *(tb_base + idx) = vm_addr|(0x3<<10)|(0<<5)|(1<<4)|(1<<3)|0x02;
     10    }
     11 
     12    /* set IO mapped-memory addr for function register*/
     13    for (vm_addr = MEM_IO_MAPPED_START; vm_addr < MEM_IO_MAPPED_END; vm_addr += PAGE_SIZE) {
     14      idx = vm_addr >> 20;
     15      /* entry: section base, AP=0b11, domain=0b00, NCNB*/
     16      *(tb_base + idx) = vm_addr|(0x03<<10)|(0<<5)|(1<<4)|0x02;
     17    }
     18 
     19    /*
     20     * exception vectors
     21     * entry: AP=0b11, domain=0b00, cached, write-through
     22     */
     23    *(tb_base + 0x00000000) = (0x00000000)|(0x03<<10)|(0<<5)|(1<<4)|(1<<3)|0x02;
     24    *(tb_base + (0xffff0000>>20)) = VECTORS_PHY_BASE|(0x03<<10)|(0<<5)|(1<<4)|(0<<3)|0x02;
     25  }
     26
     27 void init_mmu() {
     28   unsigned long ttb = (unsigned long)MMU_TBL_BASE;
     29   __asm__(
     30    "mov r0, #0/n"
     31
     32    /* disable ICache, DCache*/
     33    "mcr p15, 0, r0, c7, c7, 0/n"
     34
     35    /* clear wirte buffer*/
     36    "mcr p15, 0, r0, c7, c10, 4/n"
     37
     38    /* disable ICache, Dcache, TLBs*/
     39    "mcr p15, 0, r0, c8, c7, 0/n"
     40
     41    /* load page table pointer*/
     42    "mov r4, %0/n"
     43    "mcr p15, 0, r4, c2, c0, 0/n"
     44
     45    /*
     46     * write domain id (cp15_r13)
     47     * domain=0b11, manager mode, no check for permission
     48     */
     49    "mvn r0, #0/n"
     50    "mcr p15, 0, r0, c3, c0, 0/n"
     51
     52    /* set control register*/
     53    "mrc p15, 0, r0, c1, c0, 0/n"
     54
     55    /* clear out 'unwanted' bits*/
     56    "ldr r1, =0x1384/n"
     57    "bic r0, r0, r1/n"
     58
     59    /*
     60     * turn on what we want
     61     * base location of exception = 0xffff0000
     62     */
     63    "orr r0, r0, #0x2000/n"
     64    /* fault checking enabled*/
     65    "orr r0, r0, #0x0002/n"
     66 #ifdef CONFIG_CPU_DCACHE_ON
     67    "orr r0, r0, #0x0004/n"
     68 #endif
     69 #ifndef CONFIG_CPU_ICACHE_ON
     70    "orr r0, r0, #0x1000/n"
     71 #endif
     72    /* MMU enabled*/
     73    "orr r0, r0, #0x0001/n"
     74
     75    /* write control register*/
     76    "mcr p15, 0, r0, c1, c0, 0/n"
     77    : /* no output*/
     78    : "r"(ttb));
     79 }
     
    程序第 1~25 行是函數 init_mmu_tlb 的實現。其實就是建立一級頁表。s3c2410 有四
    種內存映射模式: Fault、Coarse Page、Section、Fine Page. 爲了簡單起見我們用
    Section 模式。ARM920T 是 32 位的 CPU,其虛擬地址空間爲 2^32 即 4G。 我們用
    Section 模式來劃分這 4G 址址空間,每一個 Section 大小爲 1M,這樣就可得到 4K
    個 Section。怎樣管理這些 Section 呢?通過一張表來記錄它們,而這張表被稱做頁表。
    在頁表裏,用 4 個字節來記錄一個 Section 的信息。總共有 4K 個 Section,這樣就
    要花費 4x4K = 16K 的內存。這用來描述 Section 的 4 個字節也有個形象的名字,叫
    作描述符。描述符的結構又是什麼樣的呢。來看一下:

   

    Section base address: 段基地址
    AP: Access Permission 訪問控制位
    Domain: 訪問控制器的索引
    C: 被置位時爲 write-through (WT)模式
    B: 被置位時爲 write-back (WB)模式

    s3c2410 的 SDRAM 爲 64M,其物理地址範圍是 0x30000000~0x33ffffff,可劃分成
    64 個 Section。我們要實現虛址到物理地址的映射,虛地址是如何被轉換的呢?其實 MMU
    將虛地址分成兩部分: 索引(index) 和 偏移(offset)。index 就是虛地址的高 12 位,
    偏移就是虛地址的低 20 位, MMU 通過 index 在頁表裏取到相應描述符,從描述符裏取
    到對應 Section 的基地址,再由這個基地址加上偏移 offset 來找出真正的物理地址。

    明白了地址映射的基本原理,我們來分析上面的代碼:
    第 6~13 行令 SDRAM 的虛地址和物理地址相等,從 0x30000000 至 0x33ffffff
    第 12~17 行設置特殊功能寄存器的虛地址,也讓它們的虛地址與物理地址相等
    第 23~24 行設置中斷向量的虛地址,其中高端中斷向量地址 0xffff0000 對應到物理
    地址0x33f00000

    代碼中有幾個常數,定義如下:
      #define MEM_START 0x30000000UL
      #define MEM_END   0x34000000UL
      #define PAGE_SIZE 0x00100000UL /* page size: 1M*/

      #define MEM_IO_MAPPED_START 0x48000000UL
      #define MEM_IO_MAPPED_END   0x60000000UL
      #define MMU_TBL_BASE        0x30000000UL
      #define VECTORS_PHY_BASE    0x33f00000UL


    爲了理解第 27~79 行的內聯彙編到底做了些什麼,我們先來了解一下協處理器:
    在基於 ARM 的嵌入式應用系統中,存儲系統通過是通過系統控制協處理器 CP15 來完
    成的。如何設置/讀取協處理器的寄存器呢?藉助 MCR/MRC 指令。例如:

        MCR P15, 0, R4, C1, C0, 0

    將寄存器 R4 中的數據傳送到協處理器 CP15 的寄存器 C1 中,其中 R4 爲 ARM 寄存
    器,存放源操作數; C1,C0 爲協處理器寄存器,爲目的寄存器; 操作碼1爲0,操作碼2爲0
    相應的, MRC 指令將協處理器的寄存器中的數值傳送到 ARM 處理器的寄存器中。

    好了,我們來分析上面的內聯彙編代碼:
    第 32~33 行使數據Cache 和 指令Cache 無效。呵呵, 你沒明白過來? 其中原由如下:

      CP15 中的寄存器 C7 用於控制 cache 和寫緩衝區。它是一個只寫的寄存器,使用 MCR 指令
      來寫該寄存器,具體格式如下:

          MCR P15, 0, <Rd>, <c7>, <CRm>, <opcode_2>

      其中, <Rd> 中爲將寫入 C7 中的數據; <CRm>, <opcode_2> 的不同組合決定執行不同的操作:

      ----------------------------------------------------------------------------------
      <CRm>        <opcode_2>        含義                                   數據
      ----------------------------------------------------------------------------------
      C0           4                 等待中斷激活                              0
      C5           0                 使用無效整個Cache                     0
      C5           1                 使無效指令Cache 中的某塊           虛地址
      C5           2                 使無效指令Cache 中的某塊           組號/組內序號
      C5           4                 清空預取緩衝區                          0
      C5           6                 清空整個跳轉目標Cache                0
      C5           7                 清空跳轉目標Cache中的某塊        生產商定義
      C6           0                 使無效整個數據Cache                    0
      C6           1                 使無效數據Cache 中的某塊           虛地址
      C6           2                 使無效數據Cache 中的某塊           組號/組內序號
      C7           0                 使數據Cache 和指令Cache 無效    0
      C7           1                 使無效整個Cache 中的某塊           虛地址
      C7           2                 使無效整個Cache 中的某塊           組號/組內序號
      C8           2                 等待中斷激活                                  0
      C10          1                 清空數據Cache 中某塊                  虛地址
      C10          2                 清空數據Cache 中某塊                  組號/組內序號
      C10          4                 清空寫緩衝區                                 0
      C11          1                 清空整個Caceh 中某塊                  虛地址
      C11          2                 清空整個Caceh 中某塊                  組號/組內序號
      C13          1                 預取指令Cache 中某塊                  虛地址
      C14          1                 清空並使無效數據Cache中某塊   虛地址
      C14          2                 清空並使無效數據Cache中某塊   組號/組內序號
      C15          1                 清空並使無效整個Cache中某塊   虛地址
      C15          2                 清空並使無效整個Cache中某塊   組號/組內序號
      ----------------------------------------------------------------------------------

    第 35~36 行: 清空寫緩衝區
    第 38~39 行,使DCache, ICache 及頁表的內容無效。系統控制協處理器 CP15 的寄存器 C8就
    是用來控制清除 TLB內容相關操作的。指令格式如下:
     
      MCR P15, 0, <Rd>, <C8>, <CRm>, <opcode_2>

    其中 <Rd> 中爲將寫入 C8中的數據; <CRm>, <opcode_2> 的不同組合決定指令執行不同的操作
   
      ----------------------------------------------------------------------------------
      指令                           <opcode_2>   <CRm>   <Rd>    含義
      ----------------------------------------------------------------------------------
      MCR P15,0,Rd,C8,C7,0  0b0000     0b0111   0            DCache,ICache 無效   
      MCR P15,0,Rd,C8,C7,1  0b0000     0b0111   虛地址  整個Cache 中單個地址變換條目無效
      MCR P15,0,Rd,C8,C5,0  0b0000     0b0101   0            整個Cache無效
      MCR P15,0,Rd,C8,C5,1  0b0000     0b0101   虛地址  指令Cache 中單個地址變換條目無效
      MCR P15,0,Rd,C8,C6,0  0b0000     0b0110   0            整個數據Cache無效
      MCR P15,0,Rd,C8,C6,1  0b0000     0b0110   虛地址  數據Cache 中單個地址變換條目無效
      ----------------------------------------------------------------------------------

    第 41~43 行:加載頁表的首地址到 CP15 協處理器的寄存器 C2
    第 45~53 行:設置訪問控制權限。協處理器 CP15 中 C3 爲 DOMAIN ACCESS CONTROL REGISTER,
    該寄存器有效位爲32,被分成16個區域,每個區域由兩個位組成,含義如下:

      00:當前級別下,該內存區域不允許被訪問,任何的訪問都會引起一個 domain fault
      01:當前級別下,該內存區域的訪問必須配合該內存區域的段描述符中AP位進行權檢查
      10:保留狀態
      11:當前級別下,對該內存區域的訪問都不進行權限檢查

    注意第 49 行我們用的是 "mvn r0, #0" 而非 "mov r0, #0"

    第 59~76 行, 設置並啓用 MMU。這幾行代碼主要是設置了 CP15 的寄存器 C1。C1 是一個控制寄
    存器它包括以下功能:
     
      禁止/使能 MMU 以及其它的與存儲系統相關的功能
      配置存儲系統以及 ARM 處理器中的相關部分的工作方式

    來看一下 C1 寄存器具體是什麼樣子:
      
     

      各控制位含義如下表:

      ----------------------------------------------------------------------------------
        C1中的控制位  含義
      ----------------------------------------------------------------------------------
        M             禁止/使能 MMU
        A             禁止/使能地址對齊檢查功能
        C             禁止/使能整個 Cache
        W             禁止/使能寫緩衝
        P             32/26地址模式
        D             禁止/使能26地址異常檢查
        L             早期/後期中止模型
        B             little-endian/big-endian
        S             在 MMU 啓用時用作系統保護
        R             在 MMU 啓用時用作系統保護
        F             由生產商定義
        Z             禁止/使能跳轉預測指令
        I             禁止/使能 Cache
        V             低端/高端異常中斷向量表
        RR            對系統中的 Cache 選擇淘汰算法
        L4            提供對以前的 ARM 的版本兼容
        bits[31:16]   保留
      ----------------------------------------------------------------------------------


    第 77~79 行: 這是使用嵌入彙編的方式,第 78 行的 "r"(ttb) 表示變量 ttb 的值賦給一個寄
    存器作爲輸入參數,這個寄存器由編譯器自動分配。我們看到第 42 行的 "%0" 被用來表示這個
    寄存器。

    ......呵呵,總算講完 MMU 這一塊了,程序不多,可引出的內容不少!


(9) 第 38~48 行設置 irq 模式和 system 模式下的堆棧寄存器,然後程序運行在 system 模式下,
    調用 main 函數後返回, 循環並等待中斷髮生......

這就是 start.S 程序的整個流程,關鍵是 MMU 如何設置和啓用。其它代碼都有詳細的註釋。我在下
面提供了所有代碼,有興趣的同道自己下載看吧。快過年了,預祝大家新年愉快!

 

轉載於:http://www.cppblog.com/jb8164/archive/2008/02/01/42345.aspx

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章