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

登录以开始

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一级页表的转换关系如下:

[](file:///D:/DOCUME~1/rich/LOCALS~1/Temp/msohtml1/01/clip_image002.jpg)[](file:///D:/DOCUME~1/rich/LOCALS~1/Temp/msohtml1/01/clip_image002.jpg)

从上图可以看出,一级页表描述符的内容是物理地址段(物理地址前12位)和一些MMU管理位合成的;一级页表描述符的地址是由页表地址基地址(31-14位)和虚拟地址前12位(31-20)合成的,它的最后两位都是零,满足32位地址对齐的方式。

建立一级页表的过程就是将每一个一级页表描述符(1M为单位)填入到每一个一级页表描述符的地址。

linux内核启动解析(五)

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