歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux內核

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

前面一篇的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.     /* 堆棧初始化 */  
  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  
Copyright © Linux教程網 All Rights Reserved