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

1.2.5、打开MMU:

接下来,调用__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-cachewrite bufferI/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,通过写cp15c2寄存器来设置临时页表基地址,这里就是把临时页表的内容写到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_idR4: __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)
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值