从前面可知由CPU的初始化函数里把MMU相关寄存器的值保存到r0寄存器,这样就传送给函数__enable_mmu,以便设置到MMU的寄存器里。函数__enable_mmu的代码如下:
__enable_mmu:
#ifdefCONFIG_ALIGNMENT_TRAP
orr r0, r0, #CR_A
#else
bic r0, r0, #CR_A
#endif
#ifdefCONFIG_CPU_DCACHE_DISABLE
bic r0, r0, #CR_C
#endif
#ifdefCONFIG_CPU_BPREDICT_DISABLE
bic r0, r0, #CR_Z
#endif
#ifdefCONFIG_CPU_ICACHE_DISABLE
bic r0, r0, #CR_I
#endif
这段代码是设置r0寄存器里MMU相关的参数。
mov r5,#(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
domain_val(DOMAIN_IO, DOMAIN_CLIENT))
mcr p15, 0, r5, c3,c0, 0 @ load domain access register
这段代码是设置页的访问权限。
mcr p15, 0, r4, c2,c0, 0 @ load page table pointer
这里是把页目录地址r4寄存器的设置到页目录寄存器C2。
b __turn_mmu_on
这里跳到MMU打开的函数运行。
ENDPROC(__enable_mmu)
这个函数主要修改r0寄存器里MMU相关的参数,设置页目录访问的权限,设置页目录的地址到C2寄存器,最后跳到函数__turn_mmu_on里继续执行打开MMU的操作。函数__turn_mmu_on的代码如下:
__turn_mmu_on:
mov r0, r0
mcr p15, 0, r0, c1,c0, 0 @ write control reg
mrc p15, 0, r3, c0,c0, 0 @ read id reg
mov r3, r3
mov r3, r3
mov pc, r13
ENDPROC(__turn_mmu_on)
这段代码比较简单,主要把r0的MMU参数设置到C1寄存器里,然后再读取到r3寄存器,这样就把CPU设置为打开MMU了,进入虚拟地址变换处理。在设置后面进行两条无用的指令,以便TLB可以加载页目录到缓存里,最后通过mov pc,r13指令,就把__mmap_switched函数的虚拟地址切换到当前执行指令寄存器里,因为__switch_data结构的第一个参数就是函数__mmap_switched的地址。
由前面可知,已经把函数__mmap_switched地址给PC寄存器,那么就会运行这个函数的代码,这个函数的代码如下:
.type __switch_data,%object
__switch_data:
.long __mmap_switched
.long __data_loc @r4
.long __data_start @r5
.long __bss_start @r6
.long _end @ r7
.long processor_id @r4
.long __machine_arch_type @r5
.long __atags_pointer @r6
.long cr_alignment @r7
.long init_thread_union+ THREAD_START_SP @ sp
这段代码是定义切换分页的数据结构。
/*
* The followingfragment of code is executed with the MMU on in MMU mode,
* and uses absoluteaddresses; this is not position independent.
*
* r0 = cp#15control register
* r1 = machine ID
* r2 = atagspointer
* r9 = processorID
*/
__mmap_switched:
adr r3,__switch_data + 4
ldmia r3!, {r4, r5,r6, r7}
获取数据段和全局数据段的开始位置。
cmp r4, r5 @ Copydata segment if needed
1: cmpne r5, r6
ldrne fp, [r4], #4
strne fp, [r5], #4
bne 1b
如果数据段需要重定位,就拷贝数据段的内容。
mov fp, #0 @Clear BSS (and zero fp)
1: cmp r6, r7
strcc fp, [r6],#4
bcc 1b
清空BSS段的内容。
ldmia r3, {r4, r5,r6, r7, sp}
str r9, [r4] @Save processor ID
str r1, [r5] @Save machine type
str r2, [r6] @Save atags pointer
保存参数到__switch_data结构里。
bic r4, r0,#CR_A @ Clear 'A' bit
stmia r7, {r0,r4} @ Save control register values
保存控制寄存器。
b start_kernel
这里就跳到C语言的开始函数start_kernel运行了。
ENDPROC(__mmap_switched)
从这个函数就开始进入C语言初始化函数start_kernel,终于完成汇编代码的初始化动作,并且进入简单的分页内存管理机制。而start_kernel函数是在文件kernel/init/mail.c里定义,下一次继续这个文件里开始分析。