基于PowerPC的Linux内核之旅:__secondary_start(start_here)-上


前面一篇的early_init(见  )执行完成后,CPU启动早期的基本初始化工作算是做完了,这时内核会开始重定向并复制运行,代码如下:
  1. bl  reloc_offset  
  2.     mr  r26,r3  
  3.     addis   r4,r3,KERNELBASE@h  /* current address of _start */  
  4.     lis r5,PHYSICAL_START@h  
  5.     cmplw   0,r4,r5         /* already running at PHYSICAL_START? */  
  6.     bne relocate_kernel     /*Juan内核重定向,经典启动必备*/  

    这里的第一句mr是将当前偏移量保存在r26中,后面relocate_kernel会使用。之后内核会判断是否需要重定向,KERNELBASE为内核的虚拟起始地址,PHYSICAL_START为内核的实际起始地址,而内核则必须要从物理地址运行start函数。下面是relocate_kernel的详细代码:

  1. relocate_kernel:  
  2.     addis   r9,r26,klimit@ha    /* fetch klimit */  
  3.     lwz r25,klimit@l(r9)   /*r25 = kilmit + offset*/  
  4.     addis   r25,r25,-KERNELBASE@h    /*最后得到的r25为内核大小*/  
  5.     lis r3,PHYSICAL_START@h     /* 拷贝目标基地址 */  
  6.     li  r6,0            /* 实际地址,不偏移 */  
  7.     li  r5,0x4000       /* 先拷贝 16K字节*/  
  8.     bl  copy_and_flush   
  9.     addi    r0,r3,4f@l      /* 跳到4f */  
  10.     mtctr   r0          /* in copy and do the rest. */  
  11.     bctr                /* jump to the copy */  
  12. 4:  mr  r5,r25  
  13.     bl  copy_and_flush      /* copy the rest */  
  14.     b   turn_on_mmu    /*打开MMU*/  

    机制很简单,就是获取内核大小后,先拷16K,再把剩下的拷过去,然后打开MMU,打开MMU的代码和关闭的类似,这里就不再列举了,看一下拷贝函数copy_and_flush,实现的是拷贝内核到内存物理起始处,并关闭cache。代码如下:

  1. _ENTRY(copy_and_flush)  
  2.     addi    r5,r5,-4  
  3.     addi    r6,r6,-4  
  4. 4:  li  r0,L1_CACHE_BYTES/4   /*L1_CACHE_BYTES:0b10000=16*/  
  5.     mtctr   r0  
  6. 3:  addi    r6,r6,4         /* copy a cache line */  
  7.     lwzx    r0,r6,r4     /*读单字(4Byte),通过Cache*/  
  8.     stwx    r0,r6,r3     /*写单字,从r4加载,存在r3*/  
  9.     bdnz      3b     /*递减计数器,循环每次拷4个字*/  
  10.     dcbst   r6,r3           /*Data Cache Block Store,再将r3的值写到内存*/  
  11.     sync  
  12.     icbi    r6,r3           /*Instruction Cache Block Invalidate,强制清空指令Cache */  
  13.     cmplw   0,r6,r5  
  14.     blt 4b     /*循环写内存,直到写完(r6>=r5)*/  
  15.     sync                /* additional sync needed on g4 */  
  16.     isync  
  17.     addi    r5,r5,4  
  18.     addi    r6,r6,4  
  19.     blr  

    这里的r4是在上面调用relocate_kernel的时候赋的值,为虚拟起始地址-偏移量(偏移量是负的,remember?),即拷贝的源地址。执行完拷贝后,内核会跳转到trun_on_mmu中,该函数在SRR0中写入了start_here的地址,执行完使能MMU后,中断返回指令自动将SRR1更新为MSR,并在新的MSR控制下将SRR0更新为PC指针,实现绝对跳转,处理器即正式跳到start_here中。在此之后,就不再有前面说的链接地址与实际运行地址不同的事情了,即访问变量时也不用加上reloc_offset了

    辛辛苦苦跳了这么久,终于到了执行内核代码的时候了!!这个函数叫start_here,代码比较长,分两段来分析,先看第一段:

  1. start_here:  
  2.     /* ptr to current */  
  3.     lis r2,init_task@h  
  4.     ori r2,r2,init_task@l   /*默认初始化的task_struct结构体*/  
  5.     /* Set up for using our exception vectors */  
  6.     tophys(r4,r2)   /*获取物理地址*/  
  7.     addi    r4,r4,THREAD    /* 初始化线程的CPU相关的状态,THREAD为thread在task_struct中的偏移 */  
  8.     CLR_TOP32(r4)   /*空的??*/  
  9.     mtspr   SPRN_SPRG_THREAD,r4    /*将当前线程信息写入SPRG3*/  
  10.     li  r3,0  
  11.     mtspr   SPRN_SPRG_RTAS,r3   /* 写SPRG2为0,使其不在RTAS中 */  
  12.   
  13.     /* 堆栈初始化 */  
  14.     lis r1,init_thread_union@ha  
  15.     addi    r1,r1,init_thread_union@l  
  16.     li  r0,0  
  17.     stwu    r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1)  
  18. /* 平台相关的初始化操作和配置MMU */  
  19.     mr  r3,r31  
  20.     mr  r4,r30  
  21.     bl  machine_init  
  22.     bl  __save_cpu_setup  
  23.     bl  MMU_init  
  • 1
  • 2
  • 3
  • 下一页

相关内容