您的位置:首页 > 运维架构 > 网站架构

ARM架构内核启动分析-head.S(1.4、stext分析之打开MMU并跳到start kernel)

2013-07-22 20:36 609 查看


1.2.5、打开MMU:

[b]接下来,调用__enable_mmu来打开MMU,在该函数的最后会使用这里保存在R13中的__switch_data函数地址并调用它,函数__switch_data定义在head-common.S中,它的函数指针__mmap_switched最终会调用第一个C函数start_kernel!

ldr r13, __switch_data @ address to jump to after

@ mmu has been enabled

/*然后将lr设为__enable_mmu的地址*/

adr lr, BSYM(__enable_mmu) @ return (PIC) address

/*前面代码已经让R10作为struct proc_info_list变量procinfo的基地址,宏PROCINFO_INITFUNC值为16(在arch/arm/kernel/asm-offset.c中107行定义),所以pc最终指向了procinfo的成员__cpu_flush函数的地址(参考数据结构proc_info_list),这将导致调用该函数,要寻找该函数的实现,需要找到procinfo变量的值,由前面代码已知在文件proc-feroceon.S中(我们的marvell芯片是这个文件,和平台相关),可以找到".proc.info.init"段的实现;

从顶层的.config文件中,注意没有定义宏CONFIG_CPU_FEROCEON_OLD_ID,其__cpu_flush成员都是函数__feroceon_setup,所以将要执行函数__feroceon_setup(也在该文件定义);

调用该函数主要是清除I/D-cache、write buffer、I/D-TLB,并初始设置一下c1控制寄存器*/

ARM( add pc, r10, #PROCINFO_INITFUNC )

1.2.5.1、__cpu_flush:
__cpu_flush函数,主要是清除I/D-cache、write buffer、I/D-TLB,并初始设置一下c1控制寄存器,在marvell里的实现是函数__feroceon_setup(在arch/arm/mm/proc-feroceon.S),这里涉及的arm寄存器的具体含义及用途用法,是下一阶段重点;

.type __feroceon_setup, #function

__feroceon_setup:

mov r0, #0

mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4

清空(invalidate)I-cache和D-cache

mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4

清空(drain)write buffer

#ifdef CONFIG_MMU

mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4

清空(invalidate)I-TLB和D-TLB

#endif

adr r5, feroceon_crval

获取函数feroceon_crval相对地址到R5

ldmia r5, {r5, r6}

将clear和mmuset的值分别存到了r5, r6中(这些东西都在proc-marco.S文件中定义)

mrc p15, 0, r0, c1, c0 @ get control register v4

获取控制寄存器c1的值

bic r0, r0, r5

将r0中的clear(r5) 对应的位都清除掉

orr r0, r0, r6

设置r0中mmuset(r6) 对应的位

mov pc, lr

返回

1.2.5.2、__enable_mmu:
这时要真正去开启MMU了!首先注意下这时的arm寄存器状态:

R4: 页表起始处物理地址

R8: machine info,struct machine_desc的基地址

R9: cpu id

R10: procinfo,struct proc_info_list的基地址

R0: c1 parameters,用来配置控制寄存器的参数,MMU分析的重点

__enable_mmu:

/*定义了该宏,置位该bit(CR_A,到底是哪位是何作用,可见文件arch/arm/include/asm/system.h,具体用途还需深入分析)*/

#ifdef CONFIG_ALIGNMENT_TRAP

orr r0, r0, #CR_A

#else

bic r0, r0, #CR_A

#endif

#ifdef CONFIG_CPU_DCACHE_DISABLE @@未定义该宏,无需关注

bic r0, r0, #CR_C

#endif

#ifdef CONFIG_CPU_BPREDICT_DISABLE @@未定义该宏,无需关注

bic r0, r0, #CR_Z

#endif

#ifdef CONFIG_CPU_ICACHE_DISABLE @@未定义该宏,无需关注

bic r0, r0, #CR_I

#endif

/*下面的指令的意思是用于设置domain参数的值给R5,domain_val和里面各个宏的定义均在文件arch/arm/include/asm/domain.h*/

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

配置 domain(详细需要看arm寄存器手册)

mcr p15, 0, r4, c2, c0, 0 @ load page table pointer

配置临时页表在存储器中的位置(set ttb).临时页表的基地址是R4,通过写cp15的c2寄存器来设置临时页表基地址,这里就是把临时页表的内容写到MMU了!

b __turn_mmu_on

即将真正打开mmu!

1.2.5.3、__turn_mmu_on:
首先是一个.algin 5,这句意思是cache line对齐,之所以这么做的原因是: 下面我们要进行真正的打开mmu操作了, 我们要把打开mmu的操作放到一个单独的cache line上, 而在之前已经说了,I Cache是无所谓的,即可以打开也可以关闭,这里这么做的原因是要保证在I- Cache打开的时候,打开mmu的操作也能正常执行;

__turn_mmu_on:

mov r0, r0

上面是这是一个空操作,相当于nop,之所以这么做可能和arm9的流水线有关

mcr p15, 0, r0, c1, c0, 0 @ write control reg

上面是写cp15的控制寄存器c1,这里是打开mmu的操作,同时会打开cache等(根据r0相应的配置),终于打开MMU了!

mrc p15, 0, r3, c0, c0, 0 @ read id reg

上面是读取id寄存器

mov r3, r3

上面是nop操作

mov r3, r13

mov pc, r3

上面是,R13在之前存储的是__switch_data,这里最终将它赋值给PC,将跳到__switch_data

1.2.5.4、__switch_data:
在开启MMU后,将最终跳到此处执行;

.align 2

.type __switch_data, %object

/*1、首先定义了一些地址*/

__switch_data:

.long __mmap_switched

.long __data_loc @ r4

.long _data @ r5

.long __bss_start @ r6

.long _end @ r7

.long processor_id @ r4 @@--->R3

.long __machine_arch_type @ r5 @@--->R4

.long __atags_pointer @ r6 @@--->R5 @@注意,很多arm设备的实现,是没有__atags_pointer的(而是由内核machine_desc变量直接指定),这个东西是用于bootloader把参数传递给内核,

@@在stext运行之前,由R2保存这个物理地址值,这里定义变量__atags_pointer就是为了让它等于那个物理地址值,

@@后续C代码页可以用这个变量,这就让C代码获取到了bootloader传递的参数 .long cr_alignment @ r7 @@--->R6


.long init_thread_union + THREAD_START_SP @ sp @@--->R7,sp

/*

* The following fragment of code is executed with the MMU on in MMU mode,

* and uses absolute addresses; this is not position independent.

*

* r0 = cp#15 control register

* r1 = machine ID

* r2 = atags pointer

* r9 = processor ID

*/

上面的当前arm寄存器的注释还是应该注意下的。

/*2、进入函数__mmap_switched*/

__mmap_switched:

adr r3, __switch_data + 4

取__data_loc的地址赋值给R3

ldmia r3!, {r4, r5, r6, r7}

执行后结果: R3: processor_id,R4: __data_loc(数据段存放处),

R5: __data_start(数据段起始处),R6: __bss_start(bss段起始处),

R7: _end(bss段结束处同时也是内核镜像结束处)

这几个符号都是在 arch/arm/kernel/vmlinux.lds.S 中定义的变量

cmp r4, r5 @ Copy data segment if needed

@@比较__data_loc和__data_start

1: cmpne r5, r6

判断数据存储的位置和数据的开始的位置是否相等,如果不相等,则需要搬运数据,从__data_loc将数据搬到__data_start;

ldrne fp, [r4], #4

其中__bss_start是bss段的开始的位置,也标志了数据段结束的位置,因而用其作为判断数据是否搬运完成

strne fp, [r5], #4

bne 1b

/*小节:地址变量__data_start 从此可以标识数据段*/

mov fp, #0 @ Clear BSS (and zero fp)

清除 bss 段的内容,将其都置成0. 这里使用 _end 来判断 bss 的结束位置

1: cmp r6, r7

strcc fp, [r6],#4

bcc 1b

/*小节:初始化清零bss段,地址变量_end从此可以标识bss段结尾*/

ARM( ldmia r3, {r4, r5, r6, r7, sp})

R3已经在processor_id处,执行后,R3: processor_id,R4: __machine_arch_type

THUMB( ldmia r3, {r4, r5, r6, r7} )

R5: __atags_pointer,R6: cr_alignment,

THUMB( ldr sp, [r3, #16] )

R7 = SP: init_thread_union + THREAD_START_SP

/*小节:核心内容放入arm的寄存器中,包括R3、R4、R5、R6、R7、SP*/

str r9, [r4] @ Save processor ID

将r9中存放的processor id赋值给变量 processor_id

str r1, [r5] @ Save machine type

将r1中存放的machine id赋值给变量__machine_arch_type

str r2, [r6] @ Save atags pointer

将r2中存放的atags pointer赋值给变量cr_alignment

bic r4, r0, #CR_A @ Clear 'A' bit

清除r0中的 CR_A位并将值存到r4中(前面曾经置位过该位)

stmia r7, {r0, r4} @ Save control register values

存储控制寄存器的值,将r0存储到了cr_alignment中,将r4存储到了cr_no_alignment中

/*小节:核心内容存入内存变量,今后C代码一样可以操作*/

b start_kernel

跳到C函数start_kernel!!!!!!!

ENDPROC(__mmap_switched)

ENDPROC(__turn_mmu_on)

ENDPROC(__enable_mmu)

THUMB( add r12, r10, #PROCINFO_INITFUNC)

THUMB( mov pc, r12)

ENDPROC(stext)[/b]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: