基于Linux2.6.38.8内核启动过程完全解析(4)

五:为kernel建立临时页表
前面提及到,kernel里面的所有符号在链接时,都使用了虚拟地址值。在完成基本的初始化后,kernel代码将跳到第一个C语言函数start_kernl来执行,在哪个时候,这些虚拟地址必须能够对它所存放在真正内存位置,否则运行将为出错。为此,CPU必须开启MMU,但在开启MMU前,必须为虚拟地址到物理地址的映射建立相应的面表。在开启MMU后,kernel指并不马上将PC值指向start_kernl,而是要做一些C语言运行期的设置,如堆栈,重定义等工作后才跳到start_kernel去执行。在此过程中,PC值还是物理地址,因此还需要为这段内存空间建立va = pa的内存映射关系。当然,本函数建立的所有页表都会在将来paging_init销毁再重建,这是临时过度性的映射关系和页表。
在介绍__create_table_pages前,先认识一个macro pgtbl,它将KERNL_RAM_PADDR – 0x4000的值赋给rd寄存器,从下面的使用中可以看它,该值是页表在物理内存的基础,也即页表放在kernel开始地址下的16K的地方。

 .macro pgtbl, rd
 ldr \rd, =(KERNEL_RAM_PADDR - 0x4000)
 .endm

[cpp]

/*   * Setup the initial page tables.  We only setup the barest   * amount which are required to get the kernel running, which   * generally means mapping in the kernel code.   *   * r8  = machinfo   * r9  = cpuid   * r10 = procinfo   *   * Returns:   *  r0, r3, r5-r7 corrupted   *  r4 = physical page table address   */   __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 to cater for __enable_mmu.       * This identity mapping will be removed by paging_init().       */       adr r0, __enable_mmu_loc       ldmia   r0, {r3, r5, r6}       sub r0, r0, r3          @ virt->phys offset       add r5, r5, r0          @ phys __enable_mmu       add r6, r6, r0          @ phys __enable_mmu_end       mov r5, r5, lsr #20       mov r6, r6, lsr #20      1:  orr r3, r7, r5, lsl #20     @ flags + kernel base       str r3, [r4, r5, lsl #2]        @ identity mapping       teq r5, r6       addne   r5, r5, #1          @ next section       bne 1b          /*       * Now setup the pagetables for our kernel direct       * mapped region.       */       mov r3, pc       mov r3, r3, lsr #20       orr r3, r7, r3, lsl #20       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    #ifndef CONFIG_DEBUG_ICEDCC        /*       * Map in IO space for serial debugging.       * This allows debug messages to be output       * via a serial console before paging_init.       */       addruart r7, r3          mov r3, r3, lsr #20       mov r3, r3, lsl #2          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       mov r3, r7, lsr #20       ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags       orr r3, r7, r3, lsl #20   1:  str r3, [r0], #4       add r3, r3, #1 << 20       teq r0, r6       bne 1b      #else /* CONFIG_DEBUG_ICEDCC */        /* we don't need any serial debugging mappings for ICEDCC */       ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags   #endif /* !CONFIG_DEBUG_ICEDCC */       #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        /*       * Map in screen at 0x02000000 & SCREEN2_BASE       * Similar reasons here - for debug.  This is       * only for Acorn RiscPC architectures.       */       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)  


里面涉及的代码主要就是建立虚拟地址与物理地址的转换,尤其是右移20位和18位两个地方与页表目录项的地址关系比较复杂。执行完该函数后,虚拟内存和物理内存的映射关系如下图所示:

基于Linux2.6.38.8内核启动过程完全解析

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/22526.html