MACHINE_START

ARM 68浏览

玩过或者移植过arm-linux的都应该知道在/arch/arm目录下有许多与具体处理器相关的目录,当然对于s3c2440的话所对应的目录就是 arch/arm/mach-s3c2440/,在里面找到与具体板子相关的文件mach-mini2440.c,这个文件大部分内容是对平台设备(示例串口,LCD,Nand falsh等)的结构体初始化,在这个文件的最后有一个非常重要的宏:

  1. MACHINE_START(MINI2440,"MINI2440")
  2.     /* Maintainer: Michel Pollet<buserror@gmail.com>*/
  3.     .atag_offset
    =
    0x100,
  4.     .map_io = mini2440_map_io,
  5.     .init_machine
    =
    mini2440_init,
  6.     .init_irq
    =
    s3c24xx_init_irq,
  7.     .timer = &s3c24xx_timer,
  8.     .restart = s3c244x_restart,
  9. MACHINE_END

MINI2440这个宏在/arch/arm/tools/mach-types文件里定义:

  1. mini2440 MACH_MINI2440 MINI2440 1999

MACHINE_START 的定义在arch/arm/include/asm/mach/arch.h,如下:

  1. /*
  2.  * Set of macrosto define architecture features. Thisis built into
  3.  * a table by the linker.
  4.  */
  5. #define MACHINE_START(_type,_name)
  6. static const struct machine_desc __mach_desc_##_type
  7.  __used
  8.  __attribute__((__section__(".arch.info.init")))=
    {
  9.     .nr = MACH_TYPE_##_type,
  10.     .name = _name,
  11. #define MACHINE_END
  12. };

就是定义了一个struct machine_desc类型结构体变量,这个结构体还定义了其他一些成员,接下来着重关注.init_machine这个成员,它是一个函数指针,值为mini2440_init,这个函数也在mach-mini2440.c中定义。内这个结构体变量在哪里被调用,从而调用它里面的成员和成员函数呢?先来看 arch/arm/kernel/setup.c 里面的setup_arch()函数:

  1. void __init setup_arch(char**cmdline_p)
  2. {
  3.     struct machine_desc
    *
    mdesc;
  4.     setup_processor();
  5.     mdesc = setup_machine_fdt(__atags_pointer);
  6.     if (!mdesc)
  7.         mdesc = setup_machine_tags(machine_arch_type);
  8.     machine_desc = mdesc;
  9.     machine_name = mdesc->name
  10. ...........
  11. ...........

这个函数在 init/main.c 的start_kernel()函数里被调用。看到第10行, 这里的setup_machine()函数的作用就是找到我们想要的struct machine_desc类型的变量,也就是在mach-mini2440.c里定义那个变量。函数的参数machine_arch_type的值是什么呢?继续看:include/generated/mach-types.h

  1. #ifdef CONFIG_ARCH_S3C2440
  2. # ifdef machine_arch_type
  3. # undef machine_arch_type
  4. # define machine_arch_type __machine_arch_type
  5. # else
  6. # define machine_arch_type MACH_TYPE_S3C2440
  7. # endif
  8. # define machine_is_s3c2440()(machine_arch_type
    == MACH_TYPE_S3C2440)
  9. #else
  10. # define machine_is_s3c2440()(0)
  11. #endif
 #define MACH_TYPE_S3C2440              362

也就是说参数 machine_arch_type的值为362。在setup_machine()函数里主要调用了lookup_machine_type()函数来查找对应的type,应该是出于效率的原因,这个函数是通过汇编实现的,在此就不给出具体代码了。

到这里,知道了在/init/main.c的start_kernel()函数里调用了setup_arch(),在setup_arch()里找到了具体的struct machine_desc类型的变量,但是在哪里通过这个变量调用里面的成员或成员函数的呢?继续找。还是在setup.c里,看到了这样一个函数:

  1. static int __init customize_machine(void)
  2.  {
  3.          /* customizes platform devices,or adds new ones
    */
  4.          if (machine_desc->init_machine)
  5.                  machine_desc->init_machine();
  6.          return 0;
  7.  }
  8.  arch_initcall(customize_machine);
  9. #define arch_initcall(fn) __define_initcall("3",fn,3)
  10. __initcall_start =
    .
    ;
  11.  *(.initcallearly.init) __early_initcall_end=
    .;
  12.  *(.initcall0.init)*(.initcall0s.init)
  13.  *(.initcall1.init)*(.initcall1s.init)
  14.  *(.initcall2.init)*(.initcall2s.init)
  15.  *(.initcall3.init)*(.initcall3s.init)
  16.  *(.initcall4.init)*(.initcall4s.init)
  17.  *(.initcall5.init)*(.initcall5s.init)
  18.  *(.initcallrootfs.init)
  19.  *(.initcall6.init)*(.initcall6s.init)
  20.  *(.initcall7.init)*(.initcall7s.init)
  21.  __initcall_end =
    .
    ;

成员函数init_machine就是在这里被调用的。但是它没有被显式调用,而是放在了arch_initcall这个宏里,它被链接到了.initcall段里,现在简单看看 arch/arm/kernel/vmlinux.lds这个链接脚本里关于initcall的定义:可以看到customize_machine()被放到了.initcall3.init里。说了那么多定义,究竟它在哪里被调用啊?好吧,它是在/init/main.c里一个叫do_initcalls()的函数里被调用,去看看呗:

  1. extern initcall_t __initcall_start[], __initcall_end[],
    __early_initcall_end[];
  2.  
  3.  static void __init do_initcalls(void)
  4.  {
  5.          initcall_t *fn;
  6.  
  7.          for (fn= __early_initcall_end; fn< __initcall_end;
    fn++)
  8.                  do_one_initcall(*fn);
  9.  }

看到第1行,很熟悉吧。在for循环里依次调用了从__early_initcall_end开始到__initcall_end结束的所有函数。customize_machine()也是在其间被调用。

好了,到这里差不多该结束了,最后总结一下这些函数调用顺序:

start_kernel()--->setup_arch()--->do_initcalls()--->customize_machine()--->mini2440_init()