Linux内存管理学习3 —— head.S中的段页表的建立

Posted 摩斯电码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux内存管理学习3 —— head.S中的段页表的建立相关的知识,希望对你有一定的参考价值。

作者

彭东林

[email protected]

 

平台

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。

 

完。

以上是关于Linux内存管理学习3 —— head.S中的段页表的建立的主要内容,如果未能解决你的问题,请参考以下文章

Linux内存管理2---段机制

操作系统-3.2-内存

关于Linux下的段错误

80386学习 80386分页机制与虚拟内存

内存扩充的覆盖与交换以及内存空间的分配和回收的连续分配管理方式

内存扩充的覆盖与交换以及内存空间的分配和回收的连续分配管理方式