linux核心啟動過程分析(2)

來源:互聯網
上載者:User

.4 __create_page_tables()

__create_page_tables()函數同樣也是位於arch/arm/kernel/head.S中,代碼如下:

__create_page_tables:

pgtbl r4 @ page table address

/*

* Clear the 16K level 1 swapper page table

*/

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] @ mm_mmuflags

/*

* Create identity mapping for first MB of kernel to

* cater for the MMU enable. This identity mapping

* will be removed by paging_init(). We use our current program

* counter to determine corresponding section base address.

*/

mov r6, pc, lsr #20 @ start of kernel section

orr r3, r7, r6, lsl #20 @ flags + kernel base

str r3, [r4, r6, lsl #2] @ identity mapping

/*

* Now setup the pagetables for our kernel direct

* mapped region.

*/

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

#ifdef CONFIG_XIP_KERNEL

/*

* Map some ram to cover our .data and .bss areas.

*/

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

/*

* Then map first 1MB of ram in case it contains our boot params.

*/

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]

#ifdef CONFIG_DEBUG_LL

ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags

/*

* Map in IO space for serial debugging.

* This allows debug messages to be output

* via a serial console before paging_init.

*/

ldr r3, [r8, #MACHINFO_PGOFFIO]

add r0, r4, r3

rsb r3, r3, #0x4000 @ PTRS_PER_PGD*sizeof(long)

cmp r3, #0x0800 @ limit to 512MB

movhi r3, #0x0800

add r6, r0, r3

ldr r3, [r8, #MACHINFO_PHYSIO]

orr r3, r3, r7

1: str r3, [r0], #4

add r3, r3, #1 << 20

teq r0, r6

bne 1b

#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)

/*

* If we're using the NetWinder or CATS, we also need to map

* in the 16550-type serial port for the debug messages

*/

add r0, r4, #0xff000000 >> 18

orr r3, r7, #0x7c000000

str r3, [r0]

#endif

#ifdef CONFIG_ARCH_RPC

add r0, r4, #0x02000000 >> 18

orr r3, r7, #0x02000000

str r3, [r0]

add r0, r4, #0xd8000000 >> 18

str r3, [r0]

#endif

#endif

mov pc, lr

ENDPROC(__create_page_tables)

這段代碼是用來建立一級頁表的。這個初始頁表是給接下來要啟動並執行kernel代碼用的。因為核心代碼用的都是虛擬位址,在使用之前我們必須要建立MMU。 這裡的MMU只需要建立的頁表能識別核心代碼這部分的虛擬位址就夠了,也就是從KERNEL_START到KERNEL_END部分。

#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)

#define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET)

#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000

#error KERNEL_RAM_VADDR must start at 0xXXXX8000

#endif

.globl swapper_pg_dir

.equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000

.macro pgtbl, rd

ldr \rd, =(KERNEL_RAM_PADDR - 0x4000)

.endm

#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就是c0008000,KERNEL_END等於_end。_end我們可以從vmlinux.lds.S中找到蹤跡。

另外需要強調的是,這裡建立的MMU 頁表是一級頁表的,是以1M為單位的;二級頁表(4K)不是在這裡建立的。Arm一級頁表的轉換關係如下:

從可以看出,一級頁表描述符的內容是物理位址區段(物理地址前12位)和一些MMU管理位合成的;一級頁表描述符的地址是由頁表地址基地址(31-14位)和虛擬位址前12位(31-20)合成的,它的最後兩位都是零,滿足32位地址對齊的方式。

建立一級頁表的過程就是將每一個一級頁表描述符(1M為單位)填入到每一個一級頁表描述符的地址。

1.5 __enable_mmu()

在建好一頁表之後,後面有幾句這樣的代碼:

ldr r13, __switch_data @ address to jump to after

@ mmu has been enabled

adr lr, __enable_mmu @ return (PIC) address

add pc, r10, #PROCINFO_INITFUNC

最後一句是跳轉到處理器初始化函數執行。我們的處理器是armv6,所以處理器初始化函數可在arch/arm/mm/pro_v6.S中找到:

ENTRY(cpu_v6_proc_init)

mov pc, lr

OK,到這裡就知道,目的就是跳轉到__enable_mmu()函數執行。至於r13,另有他用,在__enable_mmu()函數的最後可以看到。

建立好一級頁表後,這時我們就可以開啟MMU,就可以放心大膽地使用虛擬位址了。使能MMU的代碼如下:

__enable_mmu:

#ifdef CONFIG_ALIGNMENT_TRAP

orr r0, r0, #CR_A

#else

bic r0, r0, #CR_A

#endif

#ifdef CONFIG_CPU_DCACHE_DISABLE

bic r0, r0, #CR_C

#endif

#ifdef CONFIG_CPU_BPREDICT_DISABLE

bic r0, r0, #CR_Z

#endif

#ifdef CONFIG_CPU_ICACHE_DISABLE

bic r0, r0, #CR_I

#endif

mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \

domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \

domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \

domain_val(DOMAIN_IO, DOMAIN_CLIENT))

mcr p15, 0, r5, c3, c0, 0 @ load domain access register

mcr p15, 0, r4, c2, c0, 0 @ load page table pointer

b __turn_mmu_on

ENDPROC(__enable_mmu)

__turn_mmu_on:

mov r0, r0

mcr p15, 0, r0, c1, c0, 0 @ write control reg

mrc p15, 0, r3, c0, c0, 0 @ read id reg

mov r3, r3

mov r3, r3

mov pc, r13

ENDPROC(__turn_mmu_on)

這段代碼很簡單,就是把一級頁表的基地址放到CP15的c2中,然後開啟MMU。執行到最後,把r13賦值給pc,就是跳轉到__swtich_data處執行。

1.6 __mmap_switched()

我們可以在arch/arm/kernel/head-common.S找到__switch_data的定義:

__switch_data:

.long __mmap_switched

.long __data_loc @ r4

.long __data_start @ r5

.long __bss_start @ r6

.long _end @ r7

.long processor_id @ r4

.long __machine_arch_type @ r5

.long __atags_pointer @ r6

.long cr_alignment @ r7

.long init_thread_union + THREAD_START_SP @ sp

可見標號__switch_data的值就等同於__mmap_switched()函數的指標地址。__mmap_switch()函數定義如下:

__mmap_switched:

adr r3, __switch_data + 4

ldmia r3!, {r4, r5, r6, r7}

cmp r4, r5 @ Copy data segment if needed

1: cmpne r5, r6

ldrne fp, [r4], #4

strne fp, [r5], #4

bne 1b

mov fp, #0 @ Clear BSS (and zero fp)

1: cmp r6, r7

strcc fp, [r6],#4

bcc 1b

ldmia r3, {r4, r5, r6, r7, sp}

str r9, [r4] @ Save processor ID

str r1, [r5] @ Save machine type

str r2, [r6] @ Save atags pointer

bic r4, r0, #CR_A @ Clear 'A' bit

stmia r7, {r0, r4} @ Save control register values

b start_kernel

ENDPROC(__mmap_switched)

這段代碼很簡單,就是拷貝資料到資料區段;清BSS;然後儲存處理器ID,機器類型和atag指標到記憶體的相應位置(因為接下來既要跳到c語言環境執行了,必須要把之前有意義的寄存器加以儲存);跳轉到start_kernel()函數,進入作業系統環境。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.