arm linux kernel 从入口到start_kernel 的代码分析
下面仅对__create_page_tables进行简单注释:
1: __create_page_tables:
2: pgtbl r4 @ page table address
3:
4: /*
5: * Clear the 16K level 1 swapper page table
6: */
7: mov r0, r4
8: mov r3, #0
9: add r6, r0, #0x4
10: 1: str r3, [r0], #4
11: str r3, [r0], #4
12: str r3, [r0], #4
13: str r3, [r0], #4
14: teq r0, r6
15: bne 1b
16:
17: ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
18:
19: /*
20: * Create identity mapping for first MB of kernel to
21: * cater for the MMU enable. This identity mapping
22: * will be removed by paging_init(). We use our current program
23: * counter to determine corresponding section base address.
24: 下面三句完成:
25: 以tq2440为例:
26:
27: 将虚拟机地址0x30~0x30100映射到物理地址的0x30~0x30100-1
28:
29: */
30: mov r6, pc, lsr #20 @ start of kernel section 此时pc在0x38附近,r6=0x300
31: orr r3, r7, r6, lsl #20 @ flags + kernel base 构造段描述符的内容,为什么是20,参见《ARM体系结构与编程》
32: str r3, [r4, r6, lsl #2] @ identity mapping 填写页表项,完成映射
33:
34:
35: /*
36: * Now setup the pagetables for our kernel direct
37: * mapped region.
38: KERNEL_START = 0xC8
39: KERNEL_END = _end 在链接脚本中,它的地址是kernel镜像后面的一个byte的地址
40:
41: */
42: add r0, r4, #(KERNEL_START & 0xff) >> 18
43: @为什么是18,因为一级页表每个描述符4个字节,r4是一个字节一个字节的加
44: str r3, [r0, #(KERNEL_START & 0x00f00) >> 18]!
45: @上面完成的任务:将虚拟地址0xC0~0xC0100-1映射到物理地址的0x30~0x30100-1,因为r3
46: @中还是上次的值
47:
48: ldr r6, =(KERNEL_END - 1) @可以知道r6是一个虚拟地址,0xC8+解压后的内核大小-1
49: add r0, r0, #4 @r0指向下一个待填写的页表项
50: add r6, r4, r6, lsr #18 @r6指向最后一个页表项的地址 ls后缀:无符号数小于等于
51: 1: cmp r0, r6
52: add r3, r3, #1 < 20
53: strls r3, [r0], #4
54: bls 1b
55: @通过循环,将内核所在的虚拟地址空间(0xC8+解压内核大小-1)映射到物理内存
56: @0x38+解压内核大小-1,接下来,mmu开启后,就不用考虑是不是位置无关码了。
57:
58:
59: /*
60: * Then map first 1MB of ram in case it contains our boot params.
61: 个人感觉:
62: 对于tq2440将内核加载到距离物理内存起始地址32KiB的地方时,也就是0x38,下面的代码
63: 不要也可以,因为下面的目的就是将虚拟地址0xC0映射到物理地址的0x30,这个
64: 上面的代码已经完成了。
65:
66: 但是,如果没有将内核加载到距离物理内存起始地址32KiB的地方,比如加载到0x30300,即距离
67: 物理内存起始地址3MiB的地方,下面的代码就有必要了,这种情况下,上面的代码仅仅完成了将:
68:
69: 虚拟地址0xC0300~解压内核大小-1映射到物理内存0x30300~解压内核大小-1,没有将uboot传给
70: 内核的参数所在的内存区域(一般在距离物理内存起始地址16KiB范围内)进行映射。下面的代码完成了
71: 这个任务,此时PAGE_OFFSET=0xc0 PHYS_OFFSET=0x30
72: 完成将虚拟地址0xC0~0xC0100-1映射到物理地址的0x30~0x30100-1
73: */
74: add r0, r4, #PAGE_OFFSET >> 18
75: orr r6, r7, #(PHYS_OFFSET & 0xff)
76: .if (PHYS_OFFSET & 0x00f00)
77: orr r6, r6, #(PHYS_OFFSET & 0x00f00)
78: .endif
79: str r6, [r0]
80:
81: mov pc, lr
82: ENDPROC(__create_page_tables)
83: .ltorg
4. 调用平台特定的 __cpu_flush 函数
当 __create_page_tables 返回之后
此时,一些特定寄存器的值如下所示:
r4 = pgtbl (page table 的物理基地址)
r8 = machine info (struct machine_desc的基地址)
r9 = cpu id (通过cp15协处理器获得的cpu id)
r10 = procinfo (struct proc_info_list的基地址)
在我们需要在开启mmu之前,做一些必须的工作:清除ICache, 清除 DCache, 清除 Writebuffer, 清除TLB等.
这些一般是通过cp15协处理器来实现的,并且是平台相关的. 这就是 __cpu_flush 需要做的工作.
在 arch/arm/kernel/head.S中
91: ldr r13, __switch_data @ address to jump to after
92: @ mmu has been enabled
93: adr lr, __enable_mmu @ return (PIC) address
94: add pc, r10, #PROCINFO_INITFUNC
第91行: 将r13设置为 __switch_data 的地址
第92行: 将lr设置为 __enable_mmu 的地址
第93行: r10存储的是procinfo的基地址, PROCINFO_INITFUNC是在 arch/arm/kernel/asm-offset.c 中107行定义.
则该行将pc设为 proc_info_list的 __cpu_flush 函数的地址, 即下面跳转到该函数.
armlinuxkernel代码分 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)