基于PowerPC的Linux内核之旅:__secondary_start(start_here)-上
基于PowerPC的Linux内核之旅:__secondary_start(start_here)-上
前面一篇的early_init(见 )执行完成后,CPU启动早期的基本初始化工作算是做完了,这时内核会开始重定向并复制运行,代码如下:- bl reloc_offset
- mr r26,r3
- addis r4,r3,KERNELBASE@h /* current address of _start */
- lis r5,PHYSICAL_START@h
- cmplw 0,r4,r5 /* already running at PHYSICAL_START? */
- bne relocate_kernel /*Juan内核重定向,经典启动必备*/
这里的第一句mr是将当前偏移量保存在r26中,后面relocate_kernel会使用。之后内核会判断是否需要重定向,KERNELBASE为内核的虚拟起始地址,PHYSICAL_START为内核的实际起始地址,而内核则必须要从物理地址运行start函数。下面是relocate_kernel的详细代码:
- relocate_kernel:
- addis r9,r26,klimit@ha /* fetch klimit */
- lwz r25,klimit@l(r9) /*r25 = kilmit + offset*/
- addis r25,r25,-KERNELBASE@h /*最后得到的r25为内核大小*/
- lis r3,PHYSICAL_START@h /* 拷贝目标基地址 */
- li r6,0 /* 实际地址,不偏移 */
- li r5,0x4000 /* 先拷贝 16K字节*/
- bl copy_and_flush
- addi r0,r3,4f@l /* 跳到4f */
- mtctr r0 /* in copy and do the rest. */
- bctr /* jump to the copy */
- 4: mr r5,r25
- bl copy_and_flush /* copy the rest */
- b turn_on_mmu /*打开MMU*/
机制很简单,就是获取内核大小后,先拷16K,再把剩下的拷过去,然后打开MMU,打开MMU的代码和关闭的类似,这里就不再列举了,看一下拷贝函数copy_and_flush,实现的是拷贝内核到内存物理起始处,并关闭cache。代码如下:
- _ENTRY(copy_and_flush)
- addi r5,r5,-4
- addi r6,r6,-4
- 4: li r0,L1_CACHE_BYTES/4 /*L1_CACHE_BYTES:0b10000=16*/
- mtctr r0
- 3: addi r6,r6,4 /* copy a cache line */
- lwzx r0,r6,r4 /*读单字(4Byte),通过Cache*/
- stwx r0,r6,r3 /*写单字,从r4加载,存在r3*/
- bdnz 3b /*递减计数器,循环每次拷4个字*/
- dcbst r6,r3 /*Data Cache Block Store,再将r3的值写到内存*/
- sync
- icbi r6,r3 /*Instruction Cache Block Invalidate,强制清空指令Cache */
- cmplw 0,r6,r5
- blt 4b /*循环写内存,直到写完(r6>=r5)*/
- sync /* additional sync needed on g4 */
- isync
- addi r5,r5,4
- addi r6,r6,4
- blr
这里的r4是在上面调用relocate_kernel的时候赋的值,为虚拟起始地址-偏移量(偏移量是负的,remember?),即拷贝的源地址。执行完拷贝后,内核会跳转到trun_on_mmu中,该函数在SRR0中写入了start_here的地址,执行完使能MMU后,中断返回指令自动将SRR1更新为MSR,并在新的MSR控制下将SRR0更新为PC指针,实现绝对跳转,处理器即正式跳到start_here中。在此之后,就不再有前面说的链接地址与实际运行地址不同的事情了,即访问变量时也不用加上reloc_offset了。
辛辛苦苦跳了这么久,终于到了执行内核代码的时候了!!这个函数叫start_here,代码比较长,分两段来分析,先看第一段:
- start_here:
- /* ptr to current */
- lis r2,init_task@h
- ori r2,r2,init_task@l /*默认初始化的task_struct结构体*/
- /* Set up for using our exception vectors */
- tophys(r4,r2) /*获取物理地址*/
- addi r4,r4,THREAD /* 初始化线程的CPU相关的状态,THREAD为thread在task_struct中的偏移 */
- CLR_TOP32(r4) /*空的??*/
- mtspr SPRN_SPRG_THREAD,r4 /*将当前线程信息写入SPRG3*/
- li r3,0
- mtspr SPRN_SPRG_RTAS,r3 /* 写SPRG2为0,使其不在RTAS中 */
- /* 堆栈初始化 */
- lis r1,init_thread_union@ha
- addi r1,r1,init_thread_union@l
- li r0,0
- stwu r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1)
- /* 平台相关的初始化操作和配置MMU */
- mr r3,r31
- mr r4,r30
- bl machine_init
- bl __save_cpu_setup
- bl MMU_init
|
评论暂时关闭