歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> 基於PowerPC的Linux內核之旅:__secondary_start(start_here)-上

基於PowerPC的Linux內核之旅:__secondary_start(start_here)-上

日期:2017/3/1 11:08:36   编辑:Linux內核
前面一篇的early_init(見 http://www.linuxidc.com/Linux/2011-11/46581.htm )執行完成後,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. lis r1,init_thread_union@ha
  14. addi r1,r1,init_thread_union@l
  15. li r0,0
  16. stwu r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1)
  17. /* 平台相關的初始化操作和配置MMU */
  18. mr r3,r31
  19. mr r4,r30
  20. bl machine_init
  21. bl __save_cpu_setup
  22. bl MMU_init
Copyright © Linux教程網 All Rights Reserved