从上面已经看到movpc,r4语句,就知道把解压后内核的执行地址r4给了pc程序计数器,意味着解压的内核已经把运行控制权交给解压后的内核代码进行运行了。这时内核已经在正确的物理地址上运行,但是它还是未长大的孩子,还有很多东西都没有准备好,比如内存管理、中断管理等等。同时内核还需要再次映射合适的虚拟地址,以便打开MMU访问内核所有的代码和数据。从这里开始正式进入内核运行起点,是从文件kernel/arch/arm/kernel/head.S开始。为什么是从这个文件开始,而不是别的文件开始呢?其实这是从连接文件vmlinux.lds里决定的,它里面指定如下内容:
OUTPUT_ARCH(arm)
ENTRY(stext)
jiffies= jiffies_64;
SECTIONS
{
.= 0xC0000000 + 0x00008000;
.text.head : {
_stext = .;
_sinittext = .;
*(.text.head)
}
入口点的标号是stext,只需要找到这个标号的位置,就会找内核第一行执行的代码位置了。顺着这条线索,就可以找到文件head.S里,然后就会看到下面这段代码:
.section".text.head", "ax"
ENTRY(stext)
msr cpsr_c,#PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
这行代码确保CPU关闭FIRQ、IRQ的中断关闭,并且处理SVC模式。
@ and irqsdisabled
mrc p15, 0, r9, c0,c0 @ get processor id
bl __lookup_processor_type @r5=procinfo r9=cpuid
这两行代码是通过协处理器获取CPU的ID,然后查找处理器类型并返回到r5里。
movs r10, r5 @invalid processor (r5=0)?
beq __error_p @yes, error 'p'
这两行代码通过返回处理器类型是否为0进行错误处理。
bl __lookup_machine_type @r5=machinfo
这行代码是查找CPU的架构类型,并返回在r5里。
movs r8, r5 @invalid machine (r5=0)?
beq __error_a @yes, error 'a'
这两行代码是判断是否已经找到合适的架构,如果找不到就跳到出错位置。
bl __vet_atags
这行代码是分析参数标记。
bl __create_page_tables
这行代码是创建页表目录。
/*
* The followingcalls CPU specific code in a position independent
* manner. Seearch/arm/mm/proc-*.S for details. r10 = base of
* xxx_proc_infostructure selected by __lookup_machine_type
* above. Onreturn, the CPU will be ready for the MMU to be
* turned on, and r0will hold the CPU control register value.
*/
ldr r13,__switch_data @ address to jump to after
这行代码是加载切换数据到r13寄存器。
@ mmu has beenenabled
adr lr,__enable_mmu @ return (PIC) address
这行代码是加载MMU执行函数到lr寄存器。
add pc, r10,#PROCINFO_INITFUNC
这行代码是执行架构里的初始化函数。
ENDPROC(stext)
在这个函数里,主要先检查CPU是否关闭FIRQ、IRQ的中断,因为还没有建立任何中断机制,一定要确保中断是关闭的,同时也需要确认CPU还在SVC模式,否则特权指令没有办法执行。接着获取处理器的ID,调用__lookup_processor_type函数获取到内核是否支持的处理器,如果支持,就返回大于0的编号,如果返回0,说明内核不支持这个处理器。接着调用__lookup_machine_type函数来判断内核是否支持这个处理器架构,如果支持返回大于0的值,否则返回0。调用函数__vet_atags来分析引导程序传送的参数,调用函数__create_page_tables来创建内核临时使用的页目录。最后把跳到内核的函数地址__switch_data加载到r13里,把打开MMU的函数__enable_mmu地址放到lr寄存器,紧跟着就调用CPU架构里的初始化函数。在代码最后执行顺序里,其实是使用倒序的方式执行的,是先执行架构初始化函数,然后执行__enable_mmu,最后才执行__switch_data函数,最后在__switch_data函数里,就会跳到C语言入口函数start_kernel里继续执行。