在分析__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 轉載請註明,肆意刪除連接,咱們將保留追責權利。函數