原创 linux内核启动解析(二)

2012-3-31 16:12 3842 13 15 分类: 消费电子

1.1 __lookup_processor_type()

话说内核映像解压后,又跳到c0008000这个地址。这个地址指向内核代码的什么地方,我们肯定很想知道。在arch/arm/kernel/vmlinux.lds.S中,可以发现这样的代码:

SECTIONS

{

#ifdef CONFIG_XIP_KERNEL

       . = XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);

#else

       . = PAGE_OFFSET + TEXT_OFFSET;

#endif

       .text.head : {

              _stext = .;

              _sinittext = .;

              *(.text.head)

       }

}

一般内核都不配置成XIP方式的,所以这段脚本等同于:

SECTIONS

{

       . = PAGE_OFFSET + TEXT_OFFSET;

       .text.head : {

              _stext = .;

              _sinittext = .;

              *(.text.head)

       }

}

这段脚本告诉我们SECTIONS的起始地址是 .text.head的起始地址_stext,且

_stext= PAGE_OFFSET + TEXT_OFFSET;

PAGE_OFSET在.config文件中设置:

PAGE_OFFSET=0xC0000000;

TEXT_OFFSET在主目录下的Makefile文件中设置:

textofs-y   := 0x00008000

TEXT_OFFSET := $(textofs-y)

 

结合arch/arm/kernel/head.S,会发现如下代码:

.section ".text.head", "ax"

ENTRY(stext)

       msr  cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode

                                          @ and irqs disabled

       mrc p15, 0, r9, c0, c0          @ get processor id

       bl     __lookup_processor_type             @ r5=procinfo r9=cupid

ENDPROC(stext)

第一句代码的意思是表示下面的内容都属于.text.head 段的,”ax”表示这段内容是可分配且可执行的(allocable and executable) 。

所以c0008000处放的代码就是stext的入口地址。接下来的

msr  cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE

就是把fiq_mask(快速中断屏蔽位)和irq_mask(快速中断屏蔽位)都置位,同时把处理器模式设置为svc模式。这就是要告诉闲杂人等不要来打扰,这里要办重要的事。cpsr_c 代表当前状态寄存器;鉴于当前状态寄存器的重要性,arm特意开发了msr指令,专门用来设置当前状态寄存器。

mrc p15, 0, r9, c0, c0

是将协处理器cp15 c0的值赋值到r9中。接下来的

bl     __lookup_processor_type

是长跳转到__lookup_processor_type,查看processor ID是否被内核支持。

__lookup_processor_type:

       adr   r3, 3f

       ldmda      r3, {r5 - r7}

       sub  r3, r3, r7               @ get offset between virt&phys

       add  r5, r5, r3               @ convert virt addresses to

       add  r6, r6, r3               @ physical address space

1:     ldmia       r5, {r3, r4}                   @ value, mask

       and  r4, r4, r9               @ mask wanted bits

       teq   r3, r4

       beq  2f

       add  r5, r5, #PROC_INFO_SZ             @ sizeof(proc_info_list)

       cmp r5, r6

       blo   1b

       mov r5, #0                           @ unknown processor

2:     mov pc, lr

ENDPROC(__lookup_processor_type)

adr是条伪指令,作用就是把标号为3位置的地址赋值给r3寄存器。3后面加f是表示这是个长距离(far)的标号。有同学可能就要问了,ldr也能起到这个作用,为什么不用ldr?首先 ldr r3, 3f取的是标号3这个地址的内容,而不是地址本身;其次,可以用ldr r3,=3f来取地址本身,但这是一个绝对地址;而adr取得的是相对地址。如果要保证程序在任何内存都能运行,就必须保证代码是地址无关的,也就是PIC(position independent code)。显然adr伪指令很对PIC的胃口,它的取相对地址方式符合PIC的设定。

       .long       __proc_info_begin

       .long       __proc_info_end

3:     .long       .

       .long       __arch_info_begin

       .long       __arch_info_end

我们接着往下看。

ldmda      r3, {r5 - r7}

       sub  r3, r3, r7               @ get offset between virt&phys

       add  r5, r5, r3               @ convert virt addresses to

       add  r6, r6, r3               @ physical address space

1:     ldmia       r5, {r3, r4}                   @ value, mask

       and  r4, r4, r9               @ mask wanted bits

       teq   r3, r4

       beq  2f

       add  r5, r5, #PROC_INFO_SZ             @ sizeof(proc_info_list)

       cmp r5, r6

       blo   1b

       mov r5, #0                           @ unknown processor

2:     mov pc, lr

ldmada r3,(r5-r7) 是把标签3所指的地址的内容(也就是标签3的虚拟地址)赋值给r7,把比标签3所指的地址小4的地址的内容(也就是__proc_info_end)赋值给r6,把比标签3所指的地址小8的地址的内容(__proc_info_begin)赋值给r5。这里的虚拟地址是线性逻辑地址,它和物理地址之间有着一一映射关系。因为__proc_info_begin 和__proc_info_end都是虚拟地址,此时我们MMU还没有打开,就必须要使用物理地址。这就需要我们先把它们转换为物理地址。接下来的三句代码就是完成这样的工作。

__proc_info_begin和__proc_info_end是在vlinux.lds.S中定义的。

__proc_info_begin = .;

                     *(.proc.info.init)

              __proc_info_end = .;

这说明在__proc_info_begin和__proc_info_end之间的是所有的.proc.info.init段。我们可以在arch/arm/mm/proc_*.S中找到相应的.proc.info.init段。Smdk6410属于armv6,我们可以在proc_v6.S找到armv6处理器的id和id掩码。

之后的代码就是把处理器的id和id掩码赋值到r3,r4中;把r9与处理器掩码做与操作,然后与处理器id(r3)比较,看是否相等;如不相等,就取下一个处理器id进行比较;如果到最后都没有处理器id相符,就将r5赋值为0:

1:     ldmia       r5, {r3, r4}                   @ value, mask

       and  r4, r4, r9               @ mask wanted bits

       teq   r3, r4

       beq  2f

       add  r5, r5, #PROC_INFO_SZ             @ sizeof(proc_info_list)

       cmp r5, r6

       blo   1b

       mov r5, #0                           @ unknown processor

2:     mov pc, lr

最后一句是跳出__lookup_processor_type函数。跳出之后会对处理器id是否有效做一个判断;如果不是有效的处理器,就进行相应的错误处理;如果是有效的处理器,就进行机器类型查找:

       movs      r10, r5                         @ invalid processor (r5=0)?

       beq  __error_p                     @ yes, error 'p'

bl     __lookup_machine_type        @ r5=machinfo

linux内核启动解析(三)

PARTNER CONTENT

文章评论2条评论)

登录后参与讨论

用户1315892 2012-4-2 10:27

c0008000处放的代码就是stext的入口地址。接下来的 msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE 就是把fiq_mask(快速中断屏蔽位)和irq_mask(快速中断屏蔽位)都置位,同时把处理器模式设置为svc模式。这就是要告诉闲杂人等不要来打扰,这里要办重要的事。cpsr_c 代表当前状态寄存器;鉴于当前状态寄存器的重要性,arm特意开发了msr指令,专门用来设置当前状态寄存器。 mrc p15, 0, r9, c0, c0 是将协处理器cp15 c0的值赋值到r9中。接下来的 bl __lookup_processor_type 是长跳转到__lookup_processor_type,查看processor ID是否被内核支持。

用户1546833 2012-4-2 10:09

学习中。。。
相关推荐阅读
用户1181832 2012-03-31 16:15
linux内核启动解析(五)
1.5 __enable_mmu()        在建好一页表之后,后面有几句这样的代码:        ldr   r13, __switch_data        @ addres...
用户1181832 2012-03-31 16:14
linux内核启动解析(四)
1.4 __create_page_tables()        __create_page_tables()函数同样也是位于arch/arm/kernel/head.S中,代码如下: ...
用户1181832 2012-03-31 16:13
linux内核启动解析(三)
  1.2 __lookup_machine_type() 机器类型的查找代码如下: __lookup_machine_type:        adr   r3, 3b   ...
用户1181832 2012-03-31 16:09
linux内核启动解析(一)
1 linux内核启动过程分析        嵌入式linux系统从软件角度来看可分为四部分:bootloader,linux内核,文件系统和应用程序。在这里我选取的内核版本是linux2.6....
用户1181832 2012-03-16 11:22
起帆
       我的主要关注点是linux内核和驱动和bootloader。在将来的一段日子里,我会不定期的就以上几点写一些东西,以和大家共同探讨。         linux的真谛在于开放和共...
EE直播间
更多
我要评论
2
13
关闭 站长推荐上一条 /3 下一条