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。

完。

原文地址:https://www.cnblogs.com/pengdonglin137/p/7820021.html