歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> 基於PowerPC的Linux內核之旅:early_init

基於PowerPC的Linux內核之旅:early_init

日期:2017/3/1 11:08:36   编辑:Linux內核

很早之前就有寫基於PowerPC架構的Linux源代碼分析的文章的想法,但無奈於Linux源碼量太大,邏輯也很復雜,再加上本身對PowerPC匯編了解不多,閒暇時間也沒有太多,一直都沒有什麼機會。上個月,工作上的事情因為硬件的耽誤稍微少了些,再加上自己之前分析U-Boot的源碼時學了不少PowerPC匯編的知識,又移植了Linux中的SPI和Nand Flash的驅動源碼到vxWorks,感覺時機比較成熟了,踉踉跄跄的開始了嘗試性的分析,一點點的來,還望不足之處高手能指出。本次的文章將只致力於PowerPC平台,在加上本人對CPU內部機制比較感興趣,所以大部分內容都是在與具體的CPU初始化相關的.S文件中,之後會根據我的經歷選擇性的分析內部中斷機制和外設的驅動等內容。另外,由於在對啟動代碼的分析階段中,大多數源代碼都在Arch/Powerpc/Kernel文件夾下,如果不是我會給出相對路徑的。

相關閱讀:

Linux PowerPC I2C驅動 之 I2C設備層的注冊過程 http://www.linuxidc.com/Linux/2011-08/41104.htm

Linux PowerPC I2C驅動 之 I2C Adapter層的注冊過程 http://www.linuxidc.com/Linux/2011-08/41103.htm

PowerPC體系下的Linux啟動步驟和其他大多數架構都是類似的,系統引導將從arch/powerpc/kernel/head_32.s開始執行,再轉到init/main.c中的start_kernel函數初始化內核。首先來看下入口點文件head_32.S,其中的r1~r5這五個寄存器的內容及含義還不是很清楚,但可以確定r5的初始值為0,進而在start函數中跳轉執行Setup_32.c中的early_init函數,查看該函數的定義,代碼如下,很簡單:

  1. /*notrace為函數屬性:禁止trace,定義為__attribute__((no_instrument_function)),
  2. 不懂的可以看我之前對此關鍵字的介紹*/
  3. notrace unsigned long __init early_init(unsigned long dt_ptr)
  4. {
  5. /*reloc_offset定義於misc.s文件,用於返回(當前運行地址-鏈接地址)的值*/
  6. unsigned long offset = reloc_offset();
  7. struct cpu_spec *spec;
  8. /* 首先清空bss段,這裡使用的是memset_io,因為暫時沒有cache可用*/
  9. memset_io((void __iomem *)PTRRELOC(&__bss_start), 0,
  10. __bss_stop - __bss_start);
  11. /*確定cpu類型,mfspr(SPRN_PVR)指令獲取CPU的版本號*/
  12. spec = identify_cpu(offset, mfspr(SPRN_PVR));
  13. do_feature_fixups(spec->cpu_features,
  14. PTRRELOC(&__start___ftr_fixup),
  15. PTRRELOC(&__stop___ftr_fixup));
  16. do_feature_fixups(spec->mmu_features,
  17. PTRRELOC(&__start___mmu_ftr_fixup),
  18. PTRRELOC(&__stop___mmu_ftr_fixup));
  19. do_lwsync_fixups(spec->cpu_features,
  20. PTRRELOC(&__start___lwsync_fixup),
  21. PTRRELOC(&__stop___lwsync_fixup));
  22. return KERNELBASE + offset;
  23. }

在early_init中,首先需要注意的是在此階段,內核運行時所在的地址可能與其連接地址有所不同,所以在訪問靜態數據時都要使用reloc和ptrreloc函數,這兩個函數的定義在misc.s文件中,具體代碼如下:

  1. /*該函數返回(當前運行地址)減去(程序鏈接地址)的值,用於程序和數據
  2. 未映射到KERNELBASE時使用*/
  3. _GLOBAL(reloc_offset)
  4. mflr r0 /*鏈接寄存器的值*/
  5. bl 1f /*跳轉到1所在的地址,這就是當前代碼所在的實際地址,這樣通過mflr r3就將當前bl 1f的當前運行地址保存在r3中*/
  6. 1: mflr r3
  7. PPC_LL r4,(2f-1b)(r3) /* PPC_LL意思為lwz,裝載立即數,得到的r4為1f的鏈接地址*/
  8. subf r3,r4,r3 /*二者相減,獲取當前運行地址和鏈接地址的偏移*/
  9. mtlr r0 /*恢復保存的函數地址*/
  10. blr
  11. .align 3
  12. 2: PPC_LONG 1b
Copyright © Linux教程網 All Rights Reserved