电子大神的日记本,供应链专家的功夫茶盘,在这里记录、分享与共鸣。

登录以开始

linux内核启动解析(四)

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, [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为单位)填入到每一个一级页表描述符的地址。

 

linux内核启动解析(五)

博主
freshtree@163.com
茶园岗
梦里的家园 随意嬉闹着 追逐着
点击跳转