2015年(3)
分类: LINUX
2015-08-24 23:00:25
内核的启动是一个复杂的过程,它几乎是“包罗万象”:内存管理、进程管理、中断管理、外设等等都需要在启动过程中进行初始化和配置。因而我们只把我们关心的部分抽取出来进行解剖,效果更加清晰明了。
这里要讨论的架构和内核都是特定的:mips 架构 + kernel 3.18。
1、内核启动前的状态
对于smp的机器,拿mips来说,有些架构4个core,有些架构有8个core,每个core又有多个(有些是2个有些是4个)称为hwthread的执行单元,这样整个系统就有core_n * hwthread_n=m个执行单元,我们就说有m个cpu或者m个核,后面为了方便说明,就称作cpux。
内核启动之前整个系统一般是在uboot或者booloader中,多数情况下这个时候只有一个主核cpu0在运行,其他的核我们称为从核,是处于一种睡眠状态的。当我们加载内核镜像并启动内核时是只有主核在统治整个过程。
2、为从核打基础
前面讲到内核是由主核cpu0来启动的。对于smp系统,主核除了完成必要的初始化还要安顿好其他的从核,这一大家子都是cpu,可是一个都不能少的。
主核从entry_point的汇编代码跳入到内核start_kernel后,开始一些准备工作,然后再进入到setup_arch 开始他为smp 从核的首次准备-----为从核的启动打基础。
首先,将nlm_reset_entry代码拷贝到内存的reset入口点-----即0x1fc00000处。系统在上电或者reset之后cpu都要跳转到reset入口点开始取指、执行-----通常在mips体系架构中这个reset内存物理地址是固定的0x1fc00000,这个物理地址需要通过CKSEG1ADDR()宏转换成程序可以使用的虚拟地址。这样reset入口点存放的是nlm_reset_entry函数的代码的拷贝,cpu在reset之后将会跳转到这里执行nlm_reset_entry函数的拷贝。
其次,reset入口准备好了,接下来就是让从核reset。这里的从核进入reset的过程分为两个步骤:第一,对于那些cpu0的sibling cpus(即core0上除了hwthread 0外的其他hwthread),那么无需进行cpu reset,主核直接跳转的reset区域,通过写thread_mod控制寄存器让这些sibling cpus 进行必要的初始化,最终这些sibling cpus会执行wait操作进入到一种类似睡眠的状态;第二,core0上的cpus都reset完毕后,主核对其他每一个core写对应core_x的cpu_reset寄存器,cpu_reset寄存器一旦被(一般是写0触发)之后对应core的第一个hwthread就跳转到reset入口点执行,这个reset入口点拷贝的nlm_reset_entry函数不仅要初始化当前执行cpu的状态和配置,如果是core_x的第一个hwthread还会像cpu0一样写thread_mod控制寄存器,以唤醒它的sibling cpus,sibling cpus也会去初始化自己,然后大家都进入到wait睡眠状态。这样一来,除了cpu0外,所有的从核cpu都几乎走了一遍reset,最后还将自己给wait睡眠起来。
从核被暂时安顿好之后,主核还要做一些善后工作。在前一阶段从核进行reset初始化的过程中,会设置一个标志位:cpu_ready[i]。这个cpu_ready可以看成一个unsigned long型数组,数组的起始位置是固定的,被设置在reset入口点后面一定的距离处(有些mip架构设置的是距离reset入口点3k处,视具体架构而定)。数组元素的个数就是smp系统中cpu的个数,每个元素代表一个标志,初始时这些标志都是0,从核reset初始化时将这些标志置1。
此时主核cpu0根据cpu_ready数组置位的情况设置一系列的mask:phys_cpu_present_mask、__cpu_number_map,__cpu_logical_map,cpu_possible_mask以及nlm_nodes[0]->cpumask。
然后还要BOOT_NMI_HANDLER设置成nlm_boot_secondary_cpus处理函数。这样smp的初始化暂时告一段落,后面会后更多的内容来真正完成smp多个核的初始化。