微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > arm linux kernel 从入口到start_kernel 的代码分析

arm linux kernel 从入口到start_kernel 的代码分析

时间:11-09 来源:互联网 点击:

下面仅对__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 函数的地址, 即下面跳转到该函数.

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top