我分析的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)執行了