在Linux移植之make uImage编译过程分析中已经提到了uImage是一个压缩的包并且内含压缩程序,可以进行自解压。自解压完成之后内核代码从物理地址为0x30008000处开始运行。下面分析在进入C之前内核做的一些工作,以下是内核启动过程中打印出来的信息,其中Uncompressing Linux就是在自解压代码。make uImage编译的最后也给出了链接脚本arch/arm/kernel/vmlinux.lds,以及链接的顺序arch/arm/kernel/head.o 是第一个。
分析arch/arm/kernel/vmlinux.lds可以知道程序入口的地址是stext,并且是.text.head段
277 OUTPUT_ARCH(arm)
278 ENTRY(stext)
291 . = (0xc0000000) + 0x00008000;
292
293 .text.head : {
294 _stext = .;
295 _sinittext = .;
296 *(.text.head)
297 }
打开arch/arm/kernel/head.s。可见内核运行的第一条代码就是第79行的代码,从这条开始分析,首先将CPU设置为管理模式,并且关闭所有中断;然后获得CPU的id。
76 .section '.text.head', 'ax' //.text.head段
77 .type stext, %function
78 ENTRY(stext) //入口地址stext
79 msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode//确保进入了管理模式
80 @ and irqs disabled //并且禁止中断
81 mrc p15, 0, r9, c0, c0 @ get processor id //获得处理器的CPU id,并且存入 r9中
82 bl __lookup_processor_type @ r5=procinfo r9=cpuid //调用函数,输入参数r9=cpuid。返回值r5=procinfo
83 movs r10, r5 @ invalid processor (r5=0)?//如果不支持当前CPU,则r5=0
84 beq __error_p @ yes, error 'p' //如果r5=0,则打印错误
85 bl __lookup_machine_type @ r5=machinfo //调用函数,r5=返回值machinfo
86 movs r8, r5 @ invalid machine (r5=0)? //如果不支持当前单板,则返回r5=0
87 beq __error_a @ yes, error 'a' //如果r5=0,则打印错误
88 bl __create_page_tables//创建一级页表以建立虚拟地址到物理地址的映射关系,后面再研究
接着调用__lookup_processor_type,它位于archarmkernelhead-common.S。它的功能是比较当前CPU的id与内核支持的CPU的id是否相符合。这段代码在.proc.info.init段中从__proc_info_begin开始到__proc_info_end结束,寻找符合当前CPU的ID号的proc_info_list结构
145 .type __lookup_processor_type, %function
146 __lookup_processor_type:
147 adr r3, 3f //r3 = 第178行代码的物理地址
148 ldmda r3, {r5 - r7} //将r3地址开始的3个地址的内容赋给 r5、r6、r7 ;r5=__proc_info_begin,r6=__proc_info_end
149 sub r3, r3, r7 @ get offset between virt&phys//r3=r3-r7,即物理地址与虚拟地址的差值
150 add r5, r5, r3 @ convert virt addresses to//r5=__proc_info_begind对应的物理地址
151 add r6, r6, r3 @ physical address space //r6=__proc_info_end对应的物理地址
152 1: ldmia r5, {r3, r4} @ value, mask//r3、r4等于proc_info_list结构中的cpu_val、cpu_mask
153 and r4, r4, r9 @ mask wanted bits//r4=r4&r9=cpu_mask&传入的cpuid
154 teq r3, r4 //比较
155 beq 2f //如果相等,则找到对应的proc_info_list结构,跳到160行
156 add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list)//r5指向下一个proc_info_list结构
157 cmp r5, r6 //是否已经比较完所有proc_info_list
158 blo 1b //没有则继续比较
159 mov r5, #0 @ unknown processor//比较完毕,但是没有找到匹配的proc_info_list结构,r5=0
160 2: mov pc, lr//返回,返回的值为r5=proc_info_list
176 .long __proc_info_begin
177 .long __proc_info_end
178 3: .long .//.表示当前这条代码链接后的虚拟地址
179 .long __arch_info_begin
180 .long __arch_info_end
其中__proc_info_begin、__proc_info_end被定义在archarmkernelvmlinux.lds中,它的意思是内核源码中有被定义为.proc.info.init的内容,它的起始地址是__proc_info_begin,结束地址为__proc_info_end。
299 .init : { /* Init code and data */
230 *(.init.text)
231 _einittext = .;
232 __proc_info_begin = .;
233 *(.proc.info.init)
234 __proc_info_end = .;
接着看到proc_info_list结构的内容,它被定义在includeasm-armProcinfo.h中
29 struct proc_info_list {
30 unsigned int cpu_val;
31 unsigned int cpu_mask;
32 unsigned long __cpu_mm_mmu_flags; /* used by head.S */
33 unsigned long __cpu_io_mmu_flags; /* used by head.S */
34 unsigned long __cpu_flush; /* used by head.S */
35 const char *arch_name;
36 const char *elf_name;
37 unsigned int elf_hwcap;
38 const char *cpu_name;
39 struct processor *proc;
40 struct cpu_tlb_fns *tlb;
41 struct cpu_user_fns *user;
42 struct cpu_cache_fns *cache;
43 };
接着找到对于当前内核支持的proc_info_list 定义,它在archarmmmproc-arm920.S 中。对于S3C2410、S3C2440芯片来说CPU ID都是0x41129200。cpu_val的值为0x41009200、cpu_mask的值为0xff00fff0,刚好匹配。
.section '.proc.info.init', #alloc, #execinstr
448 .type __arm920_proc_info,#object
449 __arm920_proc_info:
450 .long 0x41009200//cpu_val值
451 .long 0xff00fff0//cpu_mask值
452 .long PMD_TYPE_SECT |
453 PMD_SECT_BUFFERABLE |
454 PMD_SECT_CACHEABLE |
455 PMD_BIT4 |
456 PMD_SECT_AP_WRITE |
457 PMD_SECT_AP_READ
458 .long PMD_TYPE_SECT |
459 PMD_BIT4 |
460 PMD_SECT_AP_WRITE |
461 PMD_SECT_AP_READ
462 b __arm920_setup
463 .long cpu_arch_name
464 .long cpu_elf_name
465 .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB
466 .long cpu_arm920_name
467 .long arm920_processor_functions
468 .long v4wbi_tlb_fns
469 .long v4wb_user_fns
470 #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
471 .long arm920_cache_fns
472 #else
473 .long v4wt_cache_fns
474 #endif
475 .size __arm920_proc_info, . - __arm920_proc_info
继续回到arch/arm/kernel/head.s往下分析,看到第83行,调用完__lookup_processor_type后r5的值变为执向找到的proc_info_list 结构的地址。所以第83行与第84行比较r5是否为0,如果为0说明没有找到符合当前CPU的ID号,则打印错误。接着到85行,调用__lookup_machine_type,它同样位于archarmkernelhead-common.S中,它的功能是比较当前单板的id与内核支持的单板的id是否相符合。这段代码在.arch.info.init段中从__arch_info_begin开始到__arch_info_end结束,寻找符合当前单板的ID号的machine_desc结构
176 .long __proc_info_begin
177 .long __proc_info_end