Linux内存管理学习3 —— head.S中的段页表的建立
作者
彭东林
pengdonglin137@163.com
平台
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。
完。
本文来自博客园,作者:dolinux,未经同意,禁止转载