Chinaunix首页 | 论坛 | 博客
  • 博客访问: 939041
  • 博文数量: 173
  • 博客积分: 3436
  • 博客等级: 中校
  • 技术积分: 1886
  • 用 户 组: 普通用户
  • 注册时间: 2009-01-07 09:29
文章分类

全部博文(173)

文章存档

2016年(6)

2015年(10)

2014年(14)

2013年(8)

2012年(36)

2011年(63)

2010年(19)

2009年(17)

分类: LINUX

2011-02-22 21:31:06

ARM linux 内核启动分析(1) [原创 2007-06-11 10:35:46]   

Author:    jimmy.li
Date:       2007-06-08
-----------------------
head-armv.S主支分析

       head-armv.S是解压后(或未压缩)的内核最先执行的一个文件,这个文件位于arch/arm/kernel/head-armv.S,在与这个文件同目录下还有一个文件head-armo.Shead-armv.S很相似,但从arch/arm/下的Makefile中可以看到区别在哪里:

 

ifeq ($(CONFIG_CPU_26),y)

PROCESSOR       := armo

ifeq ($(CONFIG_ROM_KERNEL),y)

               DATAADDR     = 0x02080000

               TEXTADDR     = 0x03800000

               LDSCRIPT     = arch/arm/vmlinux-armo-rom.lds.in

                 else

TEXTADDR     = 0x02080000

               LDSCRIPT     = arch/arm/vmlinux-armo.lds.in

                 endif

endif

 

ifeq ($(CONFIG_CPU_32),y)

PROCESSOR        = armv

TEXTADDR         = 0xC0008000

LDSCRIPT         = arch/arm/vmlinux-armv.lds.in

endif

 

……

HEAD            :=arch/arm/kernel/head-$(PROCESSOR).o \

                  arch/arm/kernel/init_task.o

 

闲话少说,在进入分析head-armv.S之前,交待一下我所分析的内核版本号以及硬件平台,内核是2.4.19-rmk7-pxa2,对应的硬件平台为pxa 270

       开篇说到,head-armv.S是进入内核最先执行的文件,为什么呢?内核可执行文件由许多链接在一起的对象文件组成。对象文件有许多节,如文本、数据、init 数据、bass 等等。这些对象文件都是由一个称为link script的文件链接并装入的。这个link script的功能是将输入对象文件的各节映射到输出文件中;换句话说,它将所有输入对象文件都链接到单一的可执行文件中,将该可执行文件的各节装入到指定地址处。vmlinux-armv.lds就是链接内核用到的link script,它位于arch/arm/目录下,你可能注意到了同目录下还有一vmlinux-armv.lds.in文件,这两文件可是有关系的,答案就在arch/arm/Makefile里。

ifeq ($(CONFIG_CPU_32),y)     /* 对于pxa 270来说这里是True */

PROCESSOR        = armv

TEXTADDR         = 0xC0008000

LDSCRIPT         = arch/arm/vmlinux-armv.lds.in

endif

 

arch/arm/vmlinux.lds: arch/arm/Makefile $(LDSCRIPT) \

        $(wildcard include/config/cpu/32.h) \

        $(wildcard include/config/cpu/26.h) \

        $(wildcard include/config/arch/*.h)

        @echo '  Generating $@'

        @sed 's/TEXTADDR/$(TEXTADDR)/;s/DATAADDR/$(DATAADDR)/' $(LDSCRIPT) >$@

       从这个Makefile中我们可以看到,实际上arch/arm/vmlinux-armv.lds.in就是arch/arm/vmlinux-armv.lds是一个蓝本,在make的时候vmlinux-armv.lds是由sed命令来替换vmlinux-armv.lds.in文件中的TEXTADDR, DATAADDR为特定的值而生成的。TEXTADDR是内核Image的映像地址,也是内核Image所处的虚拟地址,它在系统内核空间(3G~4G)的起始位置,通常是0xC0000000(这相应于物理内存开始的地方)+32K的位置,也就是0xC0008000处;在内核映像之前的16K空间用来存放内核的页目录表,这也就是为什么TEXTADDR要把系统放置在0xC0008000的缘故,它必须留出足够的物理空间来存放页表。

       接下来就来真正看一下vmlinux-armv.lds里面的内容:

OUTPUT_ARCH(arm)

ENTRY(stext)

SECTIONS

{

        . = 0xC0008000;

        .init : {                       /* Init code and data*/

                _stext = .;

                __init_begin = .;

                        *(.text.init)

                __proc_info_begin = .;

                        *(.proc.info)

                __proc_info_end = .;

                __arch_info_begin = .;

                        *(.arch.info)

                __arch_info_end = .;

               ……

        }

        ……

}

       ENTRY(stext),就是说明了最先执行的第一条指令是从stext开始,而这个stext就是位于head-armv.S当中,它被定义于放置于.text.init section,而且.text.init sectionvmlinux.lds文件中也是被放置于输出文件的起始位置。

/* arch/arm/kernel/head-armv.S */

93                 .section ".text.init",#alloc,#execinstr

     94                 .type   stext, #function

     95 ENTRY(stext)     //内核入口点

     96 /*     

     97                 mov r0, #0

     98                 mov r1, #300                   

     99                 add r1, r1, #4

    100 */     

101                 mov     r12, r0    //保护r0, r0=0, r12=0

                 ….  /* 这中间的都是与XScale平台无关的code */

186                 mov     r0, #F_BIT | I_BIT | MODE_SVC   @ make sure svc mode

187                 msr     cpsr_c, r0                      @ and all irqs disabled

188                 bl      __lookup_processor_type

189                 teq     r10, #0                         @ invalid processor?

190                 moveq   r0, #'p'                        @ yes, error 'p'

191                 beq     __error

192                 bl      __lookup_architecture_type

193                 teq     r7, #0                          @ invalid architecture?

194                 moveq   r0, #'a'                        @ yes, error 'a'

195                 beq     __error

196                 bl      __create_page_tables

197                 adr     lr, __ret                       @ return address

198                 add     pc, r10, #12                   @ initialise processor

199                                                       @ (return control reg)

       在程序注释中有一段对于入口点的说明,说这个入口点一般是在内核自解压缩代码中被调用(关于内核的自解压缩,我将在以后的文章中进行分析),在进入这个入口点前,须满足以下条件:MMU=offD-Cache=offI-Cache=don’t carer0 =0r1=machine number (see arch/arm/tools/mach-types.h)

Line93, 定义一个section,名为.text.init#alloc表示section is allocatable, #execinstr表示section is executable。从前面vmlinux-armv.lds文件中我们可以看到,这个"text.init" section会放到0xC0008000(arch/arm/MakefileTEXTADDR指定)这个起始位置。

       Line95, 这里的ENTRY其实是一个宏,这个宏位于linux/linkage.h中,在head-armv.S中就包含了这个头文件。

/* linux/linkage.h */

#define SYMBOL_NAME(X) X

#ifdef __STDC__

#define SYMBOL_NAME_LABEL(X) X##:

#else

#define SYMBOL_NAME_LABEL(X) X/**/:

#endif

#ifdef __arm__

#define __ALIGN .align 0

#define __ALIGN_STR ".align 0"

#else

#endif

#define ALIGN __ALIGN

#define ALIGN_STR __ALIGN_STR

 

#define ENTRY(name) \

  .globl SYMBOL_NAME(name); \

  ALIGN; \

  SYMBOL_NAME_LABEL(name)

       line186~187:  r0=0b11010011,用于设置当前程序状态寄存器,以禁止FIQ, IRQ,进入supervisor模式。

       Line188:跳转到__lookup_processor_type,读取运行的cpuID值,判断此ID值是否被内核所支持,如果不支持,返回时r100

       Line189~191: 如果是无效的processor,则跳转到__error

       Line192: 跳转到__lookup_architecture_type,看r1寄存器的architecture number值是否支持,如果不支持,则返回时r7=0Bootloader引导linux kernel时,会传递给r1machine number

       Line193~195: 如果是无效的(不支持的)体系类型(r7=0),则跳转到__error

       Line196:创建核心页表。

       Line197: 将标号__ret的地址放入 lr 寄存器。__ret标号处相关源码如下:

  /* arch/arm/kernel/head-armv.S */

 216                 .type   __ret, %function

    217 __ret:           ldr     lr, __switch_data

    218                 mcr     p15, 0, r0, c1, c0

    219                 mrc     p15, 0, r0, c1, c0, 0           @ read it back.

    220                 mov     r0, r0

    221                 mov     r0, r0

    222                 mov     pc, lr

也就是说,在将来某个时候有可能会调用mov pc, lr语句(实际上会在arch/arm/mm/proc-xscale.S文件中__xscale_setup函数的line942调用,这在后面会讲到)会跳转到line217执行ldr lr, __switch_data

       Line198r10+12->pc,也即程序跳转到r10+12的地址处执行,此时的r10存储的是在执行__lookup_processor_type函数后得到的当前处理器信息、是一个proc_info_list的结构体信息,对于我们的pxa270平台,r10指向的就是arch/arm/mm/proc-xscale.S文件中line1099处那个位置,也就是说从line1099~1112的内容就是一个proc_info_list的结构体实例。

/* arch/arm/mm/proc-xscale.S */

1097     .type   __bva0_proc_info,#object

   1098 __bva0_proc_info:

   1099     .long   0x69054110          @ Bulverde A0: 0x69054110, A1 : 0x69054111.

   1100     .long   0xfffffff0          @ and this is the CPU id mask.

   1101 #if CACHE_WRITE_THROUGH

   1102     .long   0x00000c0a

   1103 #else

   1104     .long   0x00000c0e

   1105 #endif

   1106     b   __xscale_setup

   1107     .long   cpu_arch_name

   1108     .long   cpu_elf_name

   1109     .long   HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_XSCALE

   1110     .long   cpu_bva0_info

   1111     .long   xscale_processor_functions

   1112     .size   __bva0_proc_info, . - __bva0_proc_info

 

r10+12就是line1106b   __xscale_setup)。__xscale_setup函数是实际的CPU的设置子程序,它主要是操作协处理器,设置页表目录项基地址,对CACHEBUFFER的控制位进行一些操作(关于__xscale_setup的源码分析放在后面的分支分析中)。也就是说line198执行的结果是跳转到arch/arm/mm/proc-xscale.Sline1106(关于该行源码建议读者先分析完__lookup_processor_type函数再看。__xscale_setup函数返回程序会跳转到line217

       Line217:将__switch_data位置处的值放入lr(注意这里是指令ldr,不是把标号__switch_data的地址放入lr),将来在line222时会跳转到__swith_data所指向的地址。__swith_data标号相关源码如下:

/* arch/arm/kernel/head-armv.S */

201                 .type   __switch_data, %object

    202 __switch_data:  .long   __mmap_switched

    203                 .long   SYMBOL_NAME(__bss_start)

    204                 .long   SYMBOL_NAME(_end)

    205                 .long   SYMBOL_NAME(processor_id)

    206                 .long   SYMBOL_NAME(__machine_arch_type)

    207                 .long   SYMBOL_NAME(cr_alignment)

    208                 .long   SYMBOL_NAME(init_task_union)+8192

       Line222:因为line217lr的值存储为__swith_data标号处的值,即__mmap_switched标号的地址,则此行执行的结果是跳转到__mmap_switched函数(line233)处。而__mmap_switched函数相关源码如下:

/* arch/arm/kernel/head-armv.S */

224 /*

    225  * The following fragment of code is executed with the MMU on, and uses

    226  * absolute addresses; this is not position independent.

    227  *

    228  *  r0  = processor control register

    229  *  r1  = machine ID

    230  *  r9  = processor ID

    231  */

    232                 .align  5

    233 __mmap_switched:

    234 #ifdef CONFIG_XIP_KERNEL  // 对于pxa270 此处为false,故code

 

……           

243 #endif

    244

    245                 adr     r3, __switch_data + 4

    246                 ldmia   r3, {r4, r5, r6, r7, r8, sp}@ r2 = compat

    247                                                         @ sp = stack pointer

    248

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

    250 1:              cmp     r4, r5

    251                 strcc   fp, [r4],#4

    252                 bcc     1b

    253

    254                 str     r9, [r6]                        @ Save processor ID

    255                 str     r1, [r7]                        @ Save machine type

    256 #ifdef CONFIG_ALIGNMENT_TRAP

    257                 orr     r0, r0, #2                      @ ...........A.

    258 #endif

    259                 bic     r2, r0, #2                      @ Clear 'A' bit

    260                 stmia   r8, {r0, r2}                    @ Save control register values

    261                 b       SYMBOL_NAME(start_kernel)

    Line233~261这段代码的作用主要是在进入C函数前先做一些变量的初始化和保存工作。首先清空BSS区域,然后保存处理器ID和机器类型到各自变量地址,接着保存cr_alignment,最后跳转到init/main.c中的start_kernel函数运行。
  再来具体分析一下这里面的代码,line245~246,它的结果就是r4 =__bss_start,  r5=_end,  r6=processor_id,  r7=__machine_arch_type, r8=cr_alignment, sp= init_task_union+8192,这些寄存器存储的都是变量的地址。
  再看一下line254,
r9之前存储的是在__lookup_processor_type获取的processor id,这里把process id放置在r6指向的内存,r6此时由于在line246被赋予了processor_id变量的指针,所以这里把process id 保存在变量processor_id中(processor_id在arch/arm/kernel/setup.c中被定义)。
  同样,line255,r1之前存储的是在kernel booting前由bootloader传递过来的machine number,在这里把machine number放置到r7所指向的内存,r7在line246被赋予了指向__machine_arch_type变量,所以这里把machine number保存在变量__machine_arch_type中(__machine_arch_type同样也定义在arch/arm/kernel/setup.c中)。

阅读(851) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~