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行一些的內容就好理解了:獲取結構的valuemask變量值,然後將maskcpuid進行與操作,將結果和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          */     

問題三:

根據以上信息r1r2已經有相應的值了,那它們是如何得到的(我們從入口開始跟蹤,並沒有看到r1r2的賦值),

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

 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內核入口點之前,必須滿足的條件是r1r2已經有相應的值了,這說明是在解壓縮程序裏面進行賦值的或者可能是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清零,然後將proidmachid 以及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)執行了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章