原创 linux内核启动解析(四)

2012-3-31 16:14 2808 11 13 分类: 消费电子

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内核启动解析(五)

PARTNER CONTENT

文章评论2条评论)

登录后参与讨论

用户1619473 2012-4-11 08:38

图片呢

用户1637334 2012-4-2 09:02

谢谢好东西的分享,Arm一级页表的转换关系图看不到。
相关推荐阅读
用户1181832 2012-03-31 16:15
linux内核启动解析(五)
1.5 __enable_mmu()        在建好一页表之后,后面有几句这样的代码:        ldr   r13, __switch_data        @ addres...
用户1181832 2012-03-31 16:13
linux内核启动解析(三)
  1.2 __lookup_machine_type() 机器类型的查找代码如下: __lookup_machine_type:        adr   r3, 3b   ...
用户1181832 2012-03-31 16:12
linux内核启动解析(二)
1.1 __lookup_processor_type() 话说内核映像解压后,又跳到c0008000这个地址。这个地址指向内核代码的什么地方,我们肯定很想知道。在arch/arm/kerne...
用户1181832 2012-03-31 16:09
linux内核启动解析(一)
1 linux内核启动过程分析        嵌入式linux系统从软件角度来看可分为四部分:bootloader,linux内核,文件系统和应用程序。在这里我选取的内核版本是linux2.6....
用户1181832 2012-03-16 11:22
起帆
       我的主要关注点是linux内核和驱动和bootloader。在将来的一段日子里,我会不定期的就以上几点写一些东西,以和大家共同探讨。         linux的真谛在于开放和共...
EE直播间
更多
我要评论
2
11
关闭 站长推荐上一条 /3 下一条