热度 13
2012-3-31 16:14
2808 次阅读|
2 个评论
1.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, , #4 str r3, , #4 str r3, , #4 str r3, , #4 teq r0, r6 bne 1b ldr r7, @ 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, @ identity mapping /* * Now setup the pagetables for our kernel direct * mapped region. */ add r0, r4, #(KERNEL_START 0xff000000) 18 str r3, ! 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, , #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, ! 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, , #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, #ifdef CONFIG_DEBUG_LL ldr r7, @ 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, 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, orr r3, r3, r7 1: str r3, , #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, #endif #ifdef CONFIG_ARCH_RPC add r0, r4, #0x02000000 18 orr r3, r7, #0x02000000 str r3, add r0, r4, #0xd8000000 18 str r3, #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为单位)填入到每一个一级页表描述符的地址。 linux内核启动解析(五)