Linux內核啓動代碼之__create_page_tables函數分析

 在分析__create_page_tables函數以前,須要知道如下的知識。

一、head.S首先肯定了processor type和 machine type,以後就是建立頁表。經過前面的兩步,咱們已經肯定了processor type 和 machine type。此時,一些特定寄存器的值以下所示:
r8 = machine info       (struct machine_desc的基地址)
r9 = cpu id             (經過cp15協處理器得到的cpu id)
r10 = procinfo          (struct proc_info_list的基地址)

二、因爲CPU要開啓MMU進入虛地址執行模式,所以必須先經過__create_page_tables創建一個臨時的page table(未來這個table會被拋棄,從新創建)。

三、函數中出現的宏及其解釋

默認值
定義
KERNEL_RAM_VADDR
0xC0008000
   
內核在內存中的虛擬地址
PAGE_OFFSET
0xC0000000
內核虛擬地址空間的起始地址

TEXT_OFFSET
0x00008000
內核起始位置相對於內存起始位置的偏移
PHYS_OFFSET

構架相關
物理內存的起始地址

四、由於在ARM數據處理指令中,當參與操做的第二操做數爲當即數時,每一個當即數都是採用一個8位的常數循環右移偶數位而間接獲得,因此           

   add  r0, r4,  #(KERNEL_START & 0xff000000) >> 18
   str    r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!

這樣分開寫是因爲arm的當即數只能是8位表示。

.type      __create_page_tables, %function
__create_page_tables:
       pgtbl      r4       @經過宏pgtbl將r4設置成頁表的基地址(物理地址)

       /*頁表將4GB的地址空間分紅若干個1MB的段(section),所以頁表包含4096個頁表項(section entry)。每一個頁表項是32bits(4 bytes),於是頁表佔用4096*4=16k的內存空間。下面的代碼是將這16k的頁表清0。

   */
       mov       r0, r4
       mov       r3, #0
       add r6, r0, #0x4000
1:    str   r3, [r0], #4
       str   r3, [r0], #4
       str   r3, [r0], #4
       str   r3, [r0], #4
       teq  r0, r6
       bne 1b
       ldr   r7, [r10, #PROCINFO_MM_MMUFLAGS] @得到proc_info_list的__cpu_mm_mmu_flags的值,並存儲到r7中。
   /*

*下面三行設置了kernel的section的頁表項,將起始物理地址爲0x30008000

*的1M內存空間映射到虛擬地址爲0x30008000。

    */
   mov r6, pc, lsr #20             @經過PC值的高12位(右移20位),獲得kernel的section。並存儲在r6中,由於當前是經過運行時地址獲得的kernel的section,於是是物理地址。
       orr   r3, r7, r6, lsl #20         @獲得頁表須要設置的值。
       str   r3, [r4, r6, lsl #2]        @設置頁表:mem[r4+r6*4]=r3,由於頁表的每一項是32bits(4字節),因此要乘以4(<<2)。
       /*

        * 由於KERNEL_START是內核的起始虛擬地址(0xC0008000),KERNEL_END爲內核的結束虛擬地址,因此下面的代碼其實是將物理 地址爲kernel的起始地址(0x30008000)的一段內存空間(大小爲內核映像文件的大小)映射到虛擬地址0xC0008000。

        */
       add  r0, r4,  #(KERNEL_START & 0xff000000) >> 18
       str   r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
       ldr   r6, =(KERNEL_END - 1)
       add r0, r0, #4
       add       r6, r4, r6, lsr #18
1:    cmp      r0, r6
       add r3, r3, #1 << 20
       strls r3, [r0], #4
       bls   1b
/*

 * 若是是XIP技術的內核,上面的映射只能映射內核代碼和只讀數據部分
 * 這裏咱們再映射一些RAM來做爲.data and .bss空間。

 */
#ifdef CONFIG_XIP_KERNEL
       orr   r3, r7, #(KERNEL_RAM_PADDR & 0xff000000)
       .if    (KERNEL_RAM_PADDR & 0x00f00000)
       orr   r3, r3, #(KERNEL_RAM_PADDR & 0x00f00000)
       .endif
       add r0, r4,  #(KERNEL_RAM_VADDR & 0xff000000) >> 18
       str   r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
       ldr   r6, =(_end - 1)
       add r0, r0, #4
       add r6, r4, r6, lsr #18
1:    cmp       r0, r6
       add r3, r3, #1 << 20
       strls r3, [r0], #4
       bls   1b
#endif

       /*
        *下面的代碼用來設置RAM中起始地址爲0x30000000、大小爲1M虛擬地址的頁表,之因此要設置這個頁表項的緣由是該區域起始地址爲0x30000100存
     *儲着boot params。所以須要爲它創建map,這樣開啓MMU後就能夠訪
     *問這些參數了。
       */
       add r0, r4, #PAGE_OFFSET >> 18
       orr   r6, r7, #(PHYS_OFFSET & 0xff000000)
       .if    (PHYS_OFFSET & 0x00f00000)
       orr   r6, r6, #(PHYS_OFFSET & 0x00f00000)
       .endif
       str   r6, [r0]
       mov       pc, lr    @建好頁表後返回

經過__create_page_tables函數可知,啓動時虛擬地址和物理地址映射關係以下圖所示:




問題:爲何要把物理地址0x30008000開始的一段內存映射到虛擬地址0x30008000?
    這是由於當開啓分頁機制後,在執行以某一個符號爲轉移目標的指令以前,PC仍是使用的物理地址取指令,因此把0x30008000開始的內存也臨時地作相應的映射就不會有問題了。
本文版權歸屬:銑挖機www.no1tool.com 轉載請註明,肆意刪除連接,咱們將保留追責權利。函數

相關文章
相關標籤/搜索