基于PowerPC的Linux内核之旅:early_init

很早之前就有写基于PowerPC架构的Linux源代码分析的文章的想法,但无奈于Linux源码量太大,逻辑也很复杂,再加上本身对PowerPC汇编了解不多,闲暇时间也没有太多,一直都没有什么机会。上个月,工作上的事情因为硬件的耽误稍微少了些,再加上自己之前分析U-Boot的源码时学了不少PowerPC汇编的知识,又移植了Linux中的SPI和Nand Flash的驱动源码到vxWorks,感觉时机比较成熟了,踉踉跄跄的开始了尝试性的分析,一点点的来,还望不足之处高手能指出。本次的文章将只致力于PowerPC平台,在加上本人对CPU内部机制比较感兴趣,所以大部分内容都是在与具体的CPU初始化相关的.S文件中,之后会根据我的经历选择性的分析内部中断机制和外设的驱动等内容。另外,由于在对启动代码的分析阶段中,大多数源代码都在Arch/Powerpc/Kernel文件夹下,如果不是我会给出相对路径的。

相关阅读:

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.   
  9.     /* 首先清空bss段,这里使用的是memset_io,因为暂时没有cache可用*/  
  10.     memset_io((void __iomem *)PTRRELOC(&__bss_start), 0,  
  11.             __bss_stop - __bss_start);  
  12.   
  13.     /*确定cpu类型,mfspr(SPRN_PVR)指令获取CPU的版本号*/  
  14.     spec = identify_cpu(offset, mfspr(SPRN_PVR));  
  15.   
  16.     do_feature_fixups(spec->cpu_features,  
  17.               PTRRELOC(&__start___ftr_fixup),  
  18.               PTRRELOC(&__stop___ftr_fixup));  
  19.   
  20.     do_feature_fixups(spec->mmu_features,  
  21.               PTRRELOC(&__start___mmu_ftr_fixup),  
  22.               PTRRELOC(&__stop___mmu_ftr_fixup));  
  23.   
  24.     do_lwsync_fixups(spec->cpu_features,  
  25.              PTRRELOC(&__start___lwsync_fixup),  
  26.              PTRRELOC(&__stop___lwsync_fixup));  
  27.   
  28.     return KERNELBASE + offset;  
  29. }  

在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.   
  12.     .align  3  
  13. 2:  PPC_LONG 1b  

相关推荐