之前我们说到u-boot的最终目的是将内核从flash中读出,并且启动内核。那么内核一旦开始启动之后,就没有u-boot什么事情了,控制权就交给了内核,那么先来明确一下内核要做的事情:运行用户程序,而要想运行用户程序,就得先挂接到文件系统上。下面分析内核的启动流程:
head.S主要做的事情是处理u-boot传入的参数,具体如下:
1、判断是否支持这个CPU
2、判断是否支持这个单板(对比机器ID)
3、建立一级页表
4、使能MMU
5、跳到start_kernel函数
下面具体分析这个流程:
这是在内核文件下arch\arm\kernel目录下的head.S文件,也是内核启动的第一个文件。
79行,通过设置CPSR寄存器来确保处理器进入管理模式,并且禁止中断。
81行,通过读取CP15寄存器C0获得CPUid
82调用__lookup_processor_type这个函数确认内核是否支持当前CPU,如果支持,r5寄存器会返回一个用来描述这个处理器的结构体的地址,否则r5=0
83行,先把这个r5的值保存到r10中
85行,调用__lookup_machine_type这个函数确认内核是否支持当前开发板,如果支持,r5寄存器会返回一个用来描述这个开发板的结构体的地址,否则r5=0
__lookup_processor_type和__lookup_machine_type这两个判断任何一个出现错误,内核都会停止启动,并且跳转到显示错误的函数如54、87
而一旦两个判断都通过,那么就会执行88行,生成页表。
下面主要来看一下__lookup_processor_type和__lookup_machine_type这个两个函数是如何具体实现的。
147行,r3=178代码的物理地址
148行,r5=__proc_info_begin,r6=__proc_info_end,虚拟地址,r7=178行代码的虚地
149行,物理地址和虚拟地址的差值
150行,__proc_info_begin对应的物理地址
151行,__proc_info_end对应的物理地址
152行,r3、r4=__proc_info_list结构中的cpu_val和cpu_mask
153行,r4=r4&r9=cpu_mask&传入的CPU ID
154行,比较
155行,如果相等,表示找到匹配的__proc_info_list结构,跳到160行
156行,r5指向下一个__proc_info_list结构
157行,是否已经比较完所有的__proc_info_list结构?
158行,没有则继续比较
159行,比较完毕,但是没有匹配的__proc_info_list结构,r5=0
160行,返回
在启动mmu之前,使用的都是物理地址,而内核却是用虚拟地址连接的,所以在访问__proc_info_list结构之前,先将它的虚拟地址转换成物理地址,147~151代码实现
r3=178行代码的物理地址
R4=178行代码的虚拟地址,
148行,r5=__arch_info_begin,r6=__arch_info_end,虚拟地址,r4=178行代码的虚地
149行,物理地址和虚拟地址的差值
150行,__arch_info_begin对应的物理地址
151行,__arch_info_end对应的物理地址
152行,r5是machine_desc结构地址
153行,r4=r4&r9=cpu_mask&传入的CPU ID
154行,比较
155行,如果相等,表示找到匹配的__proc_info_list结构,跳到160行
156行,r5指向下一个__proc_info_list结构
157行,是否已经比较完所有的__proc_info_list结构?
158行,没有则继续比较
159行,比较完毕,但是没有匹配的__proc_info_list结构,r5=0
160行,返回
当这个两个判断都满足的时候,建立页表,接着启动MMU我们可以看到下面的这条语句:97~99行,启动MMU之后会跳转到_swich_data这个函数,具体的代码就不一一分析了,不过可以跟踪流程如下:
_swich_data——>__mmap_switched——>start_kernel其中最重要的是start_kernel这个函数,这是内核启动的第一个C语言函数。
到这里可以先来总结一下head.S主要做的事情:
1、通过分析u-boot启动流程,我们知道u-boot在启动内核之前,会吧一些参数传给内核(具体过程请参考u-boot启动流程的分析)。而这些传入的是机器ID和其他参数。在head.S中,就是利用u-boot传入的机器ID来判断内核是否支持这个CPU和这个开发板。
2、如果1中的两个条件都满足,则会建立页表,并且启动MMU。
在这里简单介绍一下什么是MMU:
MMU也就是memory management uint,也就是内存管理单元。它负责虚拟地址到物理地址的映射,并且提供内存机制的内存访问权限检查。
为什么要启动MMU?
由于内核使用的是虚拟地址连接,所以启动MMU,更方便地实现虚拟地址到物理地址的映射。
3、启动MMU之后,内核流程会跳转到start_kernel函数,在这个函数中将会处理u-boot传入的其他参数。
下次接着分析