linux启动流程分析(一)

我分析的linux 版本是linux3.14.77

编译脚本是:arch/arm/kernel/vmlinux.lds

根据顶层Makefile可知,最开始编译的文件是arch/arm/kernel/head.S,所以这个文件是最开始执行的:

 所有arm架构的芯片都是这个入口开始执行:

arch/arm/kernel/head.S:

 80 ENTRY(stext)

 81  ARM_BE8(setend be )                    @ ensure we are in BE8 mode          

 82

 83  THUMB( adr     r9, BSYM(1f)    )       @ Kernel is always entered in ARM.   

 84  THUMB( bx      r9              )       @ If this is a Thumb-2 kernel,

 85  THUMB( .thumb                  )       @ switch to Thumb now.       

 86  THUMB(1:                       )   

.....

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

 95         bl      __lookup_processor_type         @ r5=procinfo r9=cpuid

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

 97  THUMB( it      eq )            @ force fixup-able long branch encoding

 98         beq     __error_p                       @ yes, error 'p'

94行从c0寄存器中读取cpuid,并存于r9中。95行根据cpuid获取对应的proinfo结构体地址

arch/arm/kernel/head-common.S:

139 /*

140  * Read processor ID register (CP#15, CR0), and look up in the linker-built

141  * supported processor list.  Note that we can't use the absolute addresses  

142  * for the __proc_info lists since we aren't running with the MMU on

143  * (and therefore, we are not in the correct address space).  We have to

144  * calculate the offset.        

145  *

146  *      r9 = cpuid

147  * Returns:     

148  *      r3, r4, r6 corrupted

149  *      r5 = proc_info pointer in physical address space

150  *      r9 = cpuid (preserved)

151  */

152 __lookup_processor_type:

153         adr     r3, __lookup_processor_type_data

154         ldmia   r3, {r4 - r6}                   

155         sub     r3, r3, r4                      @ get offset between virt&phys

156         add     r5, r5, r3                      @ convert virt addresses to

157         add     r6, r6, r3                      @ physical address space

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

159         and     r4, r4, r9                      @ mask wanted bits

160         teq     r3, r4

161         beq     2f

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

163         cmp     r5, r6

164         blo     1b

165         mov     r5, #0                          @ unknown processor

166 2:      mov     pc, lr

167 ENDPROC(__lookup_processor_type)

...

169 /*

170  * Look in <asm/procinfo.h> for information about the __proc_info structure.                                                                                                      

171  */

172         .align  2

173         .type   __lookup_processor_type_data, %object

174 __lookup_processor_type_data:

175         .long   .

176         .long   __proc_info_begin

177         .long   __proc_info_end

178         .size   __lookup_processor_type_data, . - __lookup_processor_type_data

 __proc_info structure定义在/arch/arm/include/asm/procinfo.h:

/*

 * Note!  struct processor is always defined if we're

 * using MULTI_CPU, otherwise this entry is unused,

 * but still exists.

 *

 * NOTE! The following structure is defined by assembly

 * language, NOT C code.  For more information, check:

 *  arch/arm/mm/proc-*.S and arch/arm/kernel/head.S                                                                                                                                   

 */

struct proc_info_list {

        unsigned int            cpu_val;

        unsigned int            cpu_mask;        

        unsigned long           __cpu_mm_mmu_flags;     /* used by head.S */

        unsigned long           __cpu_io_mmu_flags;     /* used by head.S */

        unsigned long           __cpu_flush;            /* used by head.S */

        const char              *arch_name;      

        const char              *elf_name;       

        unsigned int            elf_hwcap;       

        const char              *cpu_name;       

        struct processor        *proc;

        struct cpu_tlb_fns      *tlb;

        struct cpu_user_fns     *user;

        struct cpu_cache_fns    *cache;

};

根据以上注释,可以得知,通过cpuid,获取cpu特有的信息(cpuinfo),在153执行后,r3=175行的运行物理地址,154行运行后,r4=175行的虚拟地址,r5= _proc_info_begin,r6=_proc_info_end,所以在155行:r3-r4可知是物理地址与虚拟地址的差值,156是得到_proc_info_begin的物理地址(指针),157是得到__proc_info_end的物理地址。

158行一些的内容就好理解了:获取结构的value和mask变量值,然后将mask和cpuid进行与操作,将结果和value比较,如果相等,说明找到的所要的cpuinfo,那么就返回,否则就继续查找。

问题一:proc_info_begin,_proc_info_end变量值是哪里来的?

可以从arch/arm/kernel/vmlinux.lds中有一些内容:

. = ALIGN(4); __proc_info_begin = .; KEEP(*(.proc.info.init)) __proc_info_end = .;

所以编译时从脚本获取到的。

问题二:

各种cpuinfo是在哪里定义的呢?

主要是在arch/arm/mm/proc-*文件里。

如arch/arm/mm/proc-v7.S 文件有如下内容

_

        .section ".proc.info.init", #alloc, #execinstr

        /*

         * Standard v7 proc info content

         */

.macro __v7_proc initfunc, mm_mmuflags = 0, io_mmuflags = 0, hwcaps = 0, proc_fns = v7_processor_functions

        ALT_SMP(.long   PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \

                        PMD_SECT_AF | PMD_FLAGS_SMP | \mm_mmuflags)

        ALT_UP(.long    PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \

                        PMD_SECT_AF | PMD_FLAGS_UP | \mm_mmuflags)

        .long   PMD_TYPE_SECT | PMD_SECT_AP_WRITE | \    

                PMD_SECT_AP_READ | PMD_SECT_AF | \io_mmuflags

        W(b)    \initfunc               

        .long   cpu_arch_name           

        .long   cpu_elf_name            

        .long   HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_FAST_MULT | \

                HWCAP_EDSP | HWCAP_TLS | \hwcaps

        .long   cpu_v7_name             

        .long   \proc_fns               

        .long   v7wbi_tlb_fns           

        .long   v6_user_fns             

        .long   v7_cache_fns            

.endm

/****以上是个宏,主要应用与下面,看下面的__v7_proc就知道了*********/

#ifndef CONFIG_ARM_LPAE                 

        /*

         * ARM Ltd. Cortex A5 processor.

         */

        .type   __v7_ca5mp_proc_info, #object

__v7_ca5mp_proc_info:                   

        .long   0x410fc050              

        .long   0xff0ffff0

        __v7_proc  __v7_ca5mp_setup      

        .size   __v7_ca5mp_proc_info, . - __v7_ca5mp_proc_info    

继续跟踪,在__lookup_processor_type返回后下一条语句是:

movs    r10, r5,//r10=r5,

而继续下可以看到如下注释:

117         /*

118          * r1 = machine no, r2 = atags or dtb,

119          * r8 = phys_offset, r9 = cpuid, r10 = procinfo

120          */     

问题三:

根据以上信息r1和r2已经有相应的值了,那它们是如何得到的(我们从入口开始跟踪,并没有看到r1和r2的赋值),

在文件最上面有如下注释:

 59 /*

 60  * Kernel startup entry point.

 61  * ---------------------------

 62  *

 63  * This is normally called from the decompressor code.  The requirements

 64  * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,

 65  * r1 = machine nr, r2 = atags or dtb pointer.

 66  *

 67  * This code is mostly position independent, so if you link the kernel at

 68  * 0xc0008000, you call this at __pa(0xc0008000).

 69  *

 70  * See linux/arch/arm/tools/mach-types for the complete list of machine

 71  * numbers for r1.

 72  *

 73  * We're trying to keep crap to a minimum; DO NOT add any machine specific

 74  * crap here - that's what the boot loader (or in extreme, well justified

 75  * circumstances, zImage) is for.

 76  */

可知:在内核解压缩程序最终跳转到linux内核入口点之前,必须满足的条件是r1和r2已经有相应的值了,这说明是在解压缩程序里面进行赋值的或者可能是uboot阶段进行赋值的

跟踪继续,在到start_kernel函数之前的内容简述如下:

__create_page_tables->__enable_mmu->__turn_mmu_on->__mmap_switched

上述内容看名称就大概就知道意思了。创建页表和开启MMU,最后跳转到__mmap_switched,这些都还是汇编阶段。

 70 /*

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

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

 73  *

 74  *  r0  = cp#15 control register

 75  *  r1  = machine ID

 76  *  r2  = atags/dtb pointer

 77  *  r9  = processor ID

 78  */

 79         __INIT

 80 __mmap_switched:

 81         adr     r3, __mmap_switched_data

 82

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

 84         cmp     r4, r5                          @ Copy data segment if needed

 85 1:      cmpne   r5, r6

 86         ldrne   fp, [r4], #4

 87         strne   fp, [r5], #4

 88         bne     1b

 89

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

 91 1:      cmp     r6, r7

 92         strcc   fp, [r6],#4

 93         bcc     1b

 94

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

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

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

 98         str     r9, [r4]                        @ Save processor ID

 99         str     r1, [r5]                        @ Save machine type

100         str     r2, [r6]                        @ Save atags pointer

101         cmp     r7, #0

102         bicne   r4, r0, #CR_A                   @ Clear 'A' bit

103         stmneia r7, {r0, r4}                    @ Save control register values

104         b       start_kernel

105 ENDPROC(__mmap_switched)

106

107         .align  2

108         .type   __mmap_switched_data, %object

109 __mmap_switched_data:

110         .long   __data_loc                      @ r4

111         .long   _sdata                          @ r5

112         .long   __bss_start                     @ r6

113         .long   _end                            @ r7

114         .long   processor_id                    @ r4

115         .long   __machine_arch_type             @ r5

116         .long   __atags_pointer                 @ r6

117 #ifdef CONFIG_CPU_CP15

118         .long   cr_alignment                    @ r7

119 #else

120         .long   0                               @ r7

121 #endif

122         .long   init_thread_union + THREAD_START_SP @ sp

123         .size   __mmap_switched_data, . - __mmap_switched_data

这个函数__mmap_switched主要做了的事情是:复制数据段内容以及将bss清零,然后将proid,machid 以及atags/dtb pointer分别保存在变量processor_id,__machine_arch_type,__atags_pointer,中,而这些变量定义在/arch/arm/kernel/setup.c:

  82 unsigned int processor_id;

  83 EXPORT_SYMBOL(processor_id);

  84 unsigned int __machine_arch_type __read_mostly;

  85 EXPORT_SYMBOL(__machine_arch_type);

  86 unsigned int cacheid __read_mostly;                                                   

  87 EXPORT_SYMBOL(cacheid);

  88

  89 unsigned int __atags_pointer __initdata; 

 以上内容是最后一个汇编函数的执行,之后就跳转到C函数(start_kernel)执行了

猜你喜欢

转载自blog.csdn.net/u012681014/article/details/73612465