arm linux 内核启动 create_page_tables,Linux内核启动代码之__create_page_tables函数分析

在分析__create_page_tables函数之前,需要知道以下的知识。

1、head.S首先确定了processor type和machine type,之后就是创建页表。通过前面的两步,我们已经确定了processor type和machine type。此时,一些特定寄存器的值如下所示:

r8 = machine info(struct machine_desc的基地址)

r9 = cpu id(通过cp15协处理器获得的cpu id)

r10 = procinfo(struct proc_info_list的基地址)

2、由于CPU要开启MMU进入虚地址执行模式,因此必须先通过__create_page_tables建立一个临时的page table(将来这个table会被抛弃,重新建立)。

3、函数中出现的宏及其解释

默认值

定义

KERNEL_RAM_VADDR

0xC0008000

内核在内存中的虚拟地址

PAGE_OFFSET

0xC0000000

内核虚拟地址空间的起始地址

TEXT_OFFSET

0x00008000

内核起始位置相对于内存起始位置的偏移

PHYS_OFFSET

构架相关

物理内存的起始地址

4、因为在ARM数据处理指令中,当参与操作的第二操作数为立即数时,每个立即数都是采用一个8位的常数循环右移偶数位而间接得到,所以

addr0, r4,#(KERNEL_START & 0xff000000) >> 18

strr3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!

这样分开写是由于arm的立即数只能是8位表示。

.type__create_page_tables, %function

__create_page_tables:

pgtblr4@通过宏pgtbl将r4设置成页表的基地址(物理地址)

/*页表将4GB的地址空间分成若干个1MB的段(section),因此页表包含4096个页表项(section entry)。每个页表项是32bits(4 bytes),因而页表占用4096*4=16k的内存空间。下面的代码是将这16k的页表清0。

*/

movr0, r4

movr3, #0

addr6, r0, #0x4000

1:strr3, [r0], #4

strr3, [r0], #4

strr3, [r0], #4

strr3, [r0], #4

teqr0, r6

bne1b

ldrr7, [r10, #PROCINFO_MM_MMUFLAGS] @获得proc_info_list的__cpu_mm_mmu_flags的值,并存储到r7中。

/*

*下面三行设置了kernel的section的页表项,将起始物理地址为0x30008000

*的1M内存空间映射到虚拟地址为0x30008000。

*/

movr6, pc, lsr #20@通过PC值的高12位(右移20位),得到kernel的section。并存储在r6中,因为当前是通过运行时地址得到的kernel的section,因而是物理地址。

orrr3, r7, r6, lsl #20@得到页表需要设置的值。

strr3, [r4, r6, lsl #2]@设置页表:mem[r4+r6*4]=r3,因为页表的每一项是32bits(4字节),所以要乘以4(<<2)。

/*

*因为KERNEL_START是内核的起始虚拟地址(0xC0008000),KERNEL_END为内核的结束虚拟地址,所以下面的代码实际上是将物理地址为kernel的起始地址(0x30008000)的一段内存空间(大小为内核映像文件的大小)映射到虚拟地址0xC0008000。

*/

addr0, r4,#(KERNEL_START & 0xff000000) >> 18

strr3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!

ldrr6, =(KERNEL_END - 1)

addr0, r0, #4

add r6, r4, r6, lsr #18

1:cmp r0, r6

addr3, r3, #1 << 20

strls r3, [r0], #4

bls1b

/*

*如果是XIP技术的内核,上面的映射只能映射内核代码和只读数据部分

*这里我们再映射一些RAM来作为.data and .bss空间。

*/

#ifdef CONFIG_XIP_KERNEL

orrr3, r7, #(KERNEL_RAM_PADDR & 0xff000000)

.if(KERNEL_RAM_PADDR & 0x00f00000)

orrr3, r3, #(KERNEL_RAM_PADDR & 0x00f00000)

.endif

addr0, r4,#(KERNEL_RAM_VADDR & 0xff000000) >> 18

strr3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!

ldrr6, =(_end - 1)

addr0, r0, #4

addr6, r4, r6, lsr #18

1:cmpr0, r6

addr3, r3, #1 << 20

strlsr3, [r0], #4

bls1b

#endif

/*

*下面的代码用来设置RAM中起始地址为0x30000000、大小为1M虚拟地址的页表,之所以要设置这个页表项的原因是该区域起始地址为0x30000100存

*储着boot params。因此需要为它建立map,这样开启MMU后就可以访

*问这些参数了。

*/

addr0, r4, #PAGE_OFFSET >> 18

orrr6, r7, #(PHYS_OFFSET & 0xff000000)

.if(PHYS_OFFSET & 0x00f00000)

orrr6, r6, #(PHYS_OFFSET & 0x00f00000)

.endif

strr6, [r0]

movpc, lr@建好页表后返回

通过__create_page_tables函数可知,启动时虚拟地址和物理地址映射关系如下图所示:

acc71e75deca3ae1ecec8bc1570307db.png 

问题:为什么要把物理地址0x30008000开始的一段内存映射到虚拟地址0x30008000?

这是因为当开启分页机制后,在执行以某一个符号为转移目标的指令之前,PC还是使用的物理地址取指令,所以把0x30008000开始的内存也临时地做相应的映射就不会有问题了。

由于笔者能力有限,文中难免存在不足,如果发现错误之处,希望能帮助我及时指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值