ARM Linux 核心啟動總結 之 建立臨時頁表
硬體平臺:S5PV210 核心版本:Linux2.6.32 檔案:head.S(linux/arch/arm/kernel/)
#include <**********>
#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
#define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET)
//PAGE_OFFSET是核心虛擬地址空間的起始地址,一般為0xC000_0000
//PHYS_OFFSET 是硬體實體記憶體的起始地址,在檔案memory.h(linux/arch/arm/mach-s5pv210/include/mach/)中定義,為0x2000_0000
//TEXT_OFFSET是核心程式碼相對於起始地址的偏移量,一般為0x8000
//KERNEL_RAM_VADDR=0xC000_8000; KERNEL_RAM_PADDR=0x2000_8000
//剛開始的這32k(0x8000)空間用來存放頁表,啟動引數等
.globl swapper_pg_dir
.equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000
//equ相當於C語言中的#define
.macro pgtbl, rd
ldr \rd, =(KERNEL_RAM_PADDR - 0x4000)
.endm
//這裡定義了一個巨集,把實體地址0x2000_4000賦值給rd暫存器,一般這個地址用來存放臨時頁表
#ifdef CONFIG_XIP_KERNEL(未定義)
#define KERNEL_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
#define KERNEL_END _edata_loc
#else
#define KERNEL_START KERNEL_RAM_VADDR
#define KERNEL_END _end
#endif
//KERNEL_START=0xC000_8000, KERNEL_END=具體是多少沒搞懂
.section ".text.head", "ax"
ENTRY(stext)
*********************************
bl __create_page_tables
//跳過其他的啟動過程,直接看建立頁表的過程__create_page_tables
__create_page_tables:
pgtbl r4 //巨集展開,把實體地址0x2000_4000賦值給r4暫存器
mov r0, r4 //r0=r4=0x2000_4000
mov r3, #0 //r3=0
add r6, r0, #0x4000 //r6=0x2000_8000; r0和r6一起限定了頁表的範圍
1: str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
teq r0, r6
bne 1b //將0x2000_4000開始到0x2000_8000之間的實體記憶體初始化為0,為後面的填充頁表做準備
ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] //r10存放的是procinfo結構的地址,裡面包含了記憶體管理相關的引數
mov r6, pc //這時候PC值是物理值,因為還沒有開MMU。核心啟動階段,PC值應該處於實體記憶體開始的部分,是0x200*_****
mov r6, r6, lsr #20 //右移20位,r6=0x200,用r6來指示核心所處的段。
orr r3, r7, r6, lsl #20 //把記憶體管理的一些引數加進去賦值給r3。注意r6還是0x200,r3=0x2000_****(flags)
str r3, [r4, r6, lsl #2]
//從0x2000_4000開始存放頁表,這裡使用的是段頁表,4G=4K*1M,所以一共有4096個頁表項,每個頁表項代表1M空間。虛擬地址的高12位用來在這個頁表中找到具體的頁表項,2^12=4096,也就是0x000~0xfff。每個頁表項佔4個位元組,剛剛好是4*4096=16K,也就是0x2000_4000到(0x2000_8000-1)這個區間大小。虛擬地址的低20位用來在1M的空間裡定位某個具體的位元組2^20=1M。
//為什麼還要把實體地址進行對映呢?我在其他高手的部落格中看到過,好像是因為:CPU流水線取指令,在開MMU後,PC地址應該全部是虛擬地址,但是預取指令功能導致開MMU的時候既有虛擬地址,又有實體地址,如果我們只建立虛擬地址的對映,那麼PC實體地址送到MMU之後就會找不到相應的頁表,會出錯。大概是這樣,這部分理解還有待深入確認。
//注意一下這裡的 r6, lsl #2,就是r6*4=0x200*4=0x800,0x200相對於0x000的偏移量是0x200,每個頁表項佔4個位元組,所以這個1M的段對應的頁表項地址就是0x2000_4800,最後把r3的值存進這個頁表項中。
add r0, r4, #(KERNEL_START & 0xff000000) >> 18 //KERNEL_START=0xC000_8000,所以r0=0x2000_4000+0xC00*4=0x2000_7000
//提取核心虛擬地址空間的段地址,右移18位就是右移20位,然後左移2位,找到對應的頁表項
str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]! //KERNEL_START & 0x00f00000=0,對0x2000_7000處的頁表項進行填充
ldr r6, =(KERNEL_END - 1) //r6存放要對映的虛擬地址的終點,假設r6=0xC03*_****
add r0, r0, #4 //r0=0x2000_7000+4=0x2000_7004
add r6, r4, r6, lsr #18 //找到虛擬地址終點所在段對應的頁表項地址r6=0x2000_4000+0xC03*4=0x2000_700C
1: cmp r0, r6
add r3, r3, #1 << 20 //0xC00段對應0x200段,0xC01段對應0x201段,0xC02段對應0x202段。。。
strls r3, [r0], #4
bls 1b //對整個核心程式碼段(表述不確切,這個範圍由KERNEL_END 決定)進行對映,填充相應的頁表項
add r0, r4, #PAGE_OFFSET >> 18 //PAGE_OFFSET=0xC000_0000,r0=0x2000_7000
orr r6, r7, #(PHYS_OFFSET & 0xff000000) //r7裡面存放的是記憶體管理引數,r6=0x2000_****
.if (PHYS_OFFSET & 0x00f00000)
orr r6, r6, #(PHYS_OFFSET & 0x00f00000)
.endif
str r6, [r0] //對0x2000_7000處的頁表項再次進行填充。填充的數值是一樣的,都是0x2000_****(flags)
通常kernel的啟動引數由bootloader放到了實體記憶體的第1個M上,所以需要為RAM上的第1個M建立對映。
* 上面已為PHYS_OFFSET + TEXT_OFFSET建立了對映,如果TEXT_OFFSET小於0x00100000的話,
* 上面程式碼應該也為SDRAM的第一個M建立了對映,但如果大於0x0010000則不會。
* 所以這裡無論如何均為SDRAM的第一個M建立對映(不知分析對否,還請指正)。
mov pc, lr //結束
轉自 :http://blog.163.com/q_n_b/blog/static/214798216201301495346679/