平台
TQ2440
Qemu+vexpress-ca9
Linux-4.10.17
正文
继续分析head.S:
1 ldr r13, =__mmap_switched @ address to jump to after
2 @ mmu has been enabled
3 badr lr, 1f @ return (PIC) address
4 mov r8, r4 @ set TTBR1 to swapper_pg_dir
5 ldr r12, [r10, #PROCINFO_INITFUNC]
6 add r12, r12, r10
7 ret r12
8 1: b __enable_mmu
第1行将__mmp_switched标号的虚拟地址赋给r13,后面从__turn_mmu_on返回时会用到
第3行将1f标号的物理地址赋给lr,后面从__arm920_setup返回时会用到
第4行将段式页表的物理起始地址赋给r8,对于TQ2440来说,是0x3000_4000,对于vexpress是0x6000_4000
第5行,因为r10指向匹配到的proc_info_list结构体的首地址,对于TQ2440来说,偏移#PROCINFO_INITFUNC得到的是__arm920_setup 与__arm920_proc_info的差值,存放到r12中,此时r10存放的就是__arm920_proc_info物理地址
第6行,r10加r12就得到了__arm920_setup的物理地址,对于vexpress来说是__v7_ca9mp_setup
第7行,开始执行__arm920_setup,定义在arch/arm/mm/proc-arm920.S中
1 .type __arm920_setup, #function
2 __arm920_setup:
3 mov r0, #0
4 mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4
5 mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4
6
7 mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4
8
9 adr r5, arm920_crval
10 ldmia r5, {r5, r6}
11 mrc p15, 0, r0, c1, c0 @ get control register v4
12 bic r0, r0, r5
13 orr r0, r0, r6
14 ret lr
15 .size __arm920_setup, . - __arm920_setup
16
17 /*
18 * R
19 * .RVI ZFRS BLDP WCAM
20 * ..11 0001 ..11 0101
21 *
22 */
23 .type arm920_crval, #object
24 arm920_crval:
25 crval clear=0x00003f3f, mmuset=0x00003135, ucset=0x00001130
这个函数执行一些开启MMU之前的准备工作。上面对cache、tlb的操作可以参考手册 ARM920T Technical Reference Manual 的2.3.11 Register 7, cache operations register和2.3.12 Register 8, TLB operations register
第14行执行完毕后,会跳转到前面所说的head.S中的1f标号处,也就是 b __enable_mmu
关于MMU的操作,可以参考手册 ARM920T Technical Reference Manual 的2.3.5 Register 1, control register
回到head.S继续分析。
1 __enable_mmu:
2 #if defined(CONFIG_ALIGNMENT_TRAP) && __LINUX_ARM_ARCH__ < 6
3 orr r0, r0, #CR_A
4 #else
5 bic r0, r0, #CR_A
6 #endif
7
8 mov r5, #DACR_INIT
9 mcr p15, 0, r5, c3, c0, 0 @ load domain access register
10 mcr p15, 0, r4, c2, c0, 0 @ load page table pointer
11
12 b __turn_mmu_on
第10行将段表的物理起始地址设置到CP15的C2寄存器中,即0x30004000或者0x60004000,可以参考ARM920T Technical Reference Manual 的2.3.6 Register 2, translation table base (TTB) register
第12行准备打开MMU
下面开始打开MMU:
1 .align 5
2 .pushsection .idmap.text, "ax"
3 ENTRY(__turn_mmu_on)
4 mov r0, r0
5 instr_sync
6 mcr p15, 0, r0, c1, c0, 0 @ write control reg
7 mrc p15, 0, r3, c0, c0, 0 @ read id reg
8 instr_sync
9 mov r3, r3
10 mov r3, r13
11 ret r3
12 __turn_mmu_on_end:
13 ENDPROC(__turn_mmu_on)
14 .popsection
第6行开启MMU, 由于之前已经建立了映射这部分的段表,所以程序可以继续执行,不会出错
第10行,r13中存放的是__mmap_switched的虚拟地址
第11行,开始跳到__mmap_switched处执行,自此以后的虚拟地址就跟链接地址相同了
__mmap_switched定义在arch/arm/kernel/head-common.S中:
1 __mmap_switched:
2 adr r3, __mmap_switched_data
3
4 ldmia r3!, {r4, r5, r6, r7}
5 cmp r4, r5 @ Copy data segment if needed
6 1: cmpne r5, r6
7 ldrne fp, [r4], #4
8 strne fp, [r5], #4
9 bne 1b
10
11 mov fp, #0 @ Clear BSS (and zero fp)
12 1: cmp r6, r7
13 strcc fp, [r6],#4
14 bcc 1b
15
16 ARM( ldmia r3, {r4, r5, r6, r7, sp})
17
18 str r9, [r4] @ Save processor ID
19 str r1, [r5] @ Save machine type
20 str r2, [r6] @ Save atags pointer
21 cmp r7, #0
22 strne r0, [r7] @ Save control register values
23 b start_kernel
24 ENDPROC(__mmap_switched)
25
26 .align 2
27 .type __mmap_switched_data, %object
28 __mmap_switched_data:
29 .long __data_loc @ r4
30 .long _sdata @ r5
31 .long __bss_start @ r6
32 .long _end @ r7
33 .long processor_id @ r4
34 .long __machine_arch_type @ r5
35 .long __atags_pointer @ r6
36 .long cr_alignment @ r7
37 .long init_thread_union + THREAD_START_SP @ sp
38 .size __mmap_switched_data, . - __mmap_switched_data
这里主要关注一下第16到第23行,这里将r9中存放的CPU ID赋给processor_id, 将dtb所在的物理地址赋给__atags_pointer,将sp设置为init_thread_union + THREAD_START_SP, 这里init_thread_union定义在init/init_task.c中,THREAD_START_SP的值是(8KB-8),也就是sp指向init进程的内核栈。然后第23行跳转到init/main.c中的start_kernel。
完。