I.Mx6 3.0.35 MACHINE_START 分析
1、在/arch/arm 目录下有许多与具体处理器相关的目录,对于I.Mx6q 对应的目录就是 arch/arm/mach-mx6/
在里面找到与具体板子相关的文件 board-mx6q_sabresd.c ,这个文件大部分内容是对平台设备(如nand,串口,spi,nor等)
的结构体的初始化。在这个文件的最后有一个非常重要的宏:
/* * initialize __mach_desc_MX6Q_SABRESD data structure. */ MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board") /* Maintainer: Freescale Semiconductor, Inc. */ .boot_params = MX6_PHYS_OFFSET + 0x100, .fixup = fixup_mxc_board, .map_io = mx6_map_io, .init_irq = mx6_init_irq, .init_machine = mx6_sabresd_board_init, .timer = &mx6_sabresd_timer, .reserve = mx6q_sabresd_reserve, MACHINE_END
MACHINE_START 的定义在 arch/arm/include/asm/mach/arch.h 中,如下:
/* * Set of macros to define architecture features. This is built into * a table by the linker. */ #define MACHINE_START(_type,_name) \ static const struct machine_desc __mach_desc_##_type \ __used \ __attribute__((__section__(".arch.info.init"))) = { \ .nr = MACH_TYPE_##_type, \ .name = _name, #define MACHINE_END \ };
其定义了一个 struct machine_desc 类型的结构体变量。
2、那么这个结构体变量在哪里被调用,从而调用它里面的成员和成员函数呢?
在 arch/arm/kernel/setup.c 中查看 setup_arch()
void __init setup_arch(char **cmdline_p) { struct machine_desc *mdesc; unwind_init(); setup_processor(); mdesc = setup_machine_fdt(__atags_pointer); if (!mdesc) mdesc = setup_machine_tags(machine_arch_type); machine_desc = mdesc; machine_name = mdesc->name; if (mdesc->soft_reboot) reboot_setup("s"); ... }
在该函数中setup_machine_xxx() 函数的作用就是找到我们想要的 struct machine_desc 类型的变量,也就是在
board-mx6q_sabresd.c 中定义的那个变量。
3、setup_arch() 函数是在哪里调用的?
在 init/main.c 的 start_kernel() 中,定义如下:
asmlinkage void __init start_kernel(void) { ... tick_init(); boot_cpu_init(); page_address_init(); printk(KERN_NOTICE "%s", linux_banner); setup_arch(&command_line); mm_init_owner(&init_mm, &init_task); mm_init_cpumask(&init_mm); setup_command_line(command_line); setup_nr_cpu_ids(); setup_per_cpu_areas(); smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */ ... }
4、到这里,知道了在 /init/main.c 的 start_kernel() 函数里调用了setup_arch(),在 setup_arch() 里找到了具体的
struct machine_desc 类型的变量,但是在哪里通过这个变量调用里面的成员或成员函数的呢?
在 arch/arm/kernel/setup.c 中有如下定义 :
static int __init customize_machine(void) { /* customizes platform devices, or adds new ones */ if (machine_desc->init_machine) machine_desc->init_machine(); return 0; } arch_initcall(customize_machine);
在 include/linux/init.h 中有如下定义:
#define arch_initcall(fn) __define_initcall("3",fn,3)
在 arch/arm/kernel/vmlinux.lds.h 中有如下定义:
#define INITCALLS \ *(.initcallearly.init) \ VMLINUX_SYMBOL(__early_initcall_end) = .; \ *(.initcall0.init) \ *(.initcall0s.init) \ *(.initcall1.init) \ *(.initcall1s.init) \ *(.initcall2.init) \ *(.initcall2s.init) \ *(.initcall3.init) \ *(.initcall3s.init) \ *(.initcall4.init) \ *(.initcall4s.init) \ *(.initcall5.init) \ *(.initcall5s.init) \ *(.initcallrootfs.init) \ *(.initcall6.init) \ *(.initcall6s.init) \ *(.initcall7.init) \ *(.initcall7s.init)
#define INIT_CALLS \
VMLINUX_SYMBOL(__initcall_start) = .; \
INITCALLS \
VMLINUX_SYMBOL(__initcall_end) = .;
可以看到 customize_machine() 被放到了.initcall3.init里。
5、customize_machine()在哪里被调用的?
在/init/main.c 的 do_initcalls() 的函数中,定义如下:
static void __init do_initcalls(void) { initcall_t *fn; for (fn = __early_initcall_end; fn < __initcall_end; fn++) do_one_initcall(*fn); }
在for 循环里依次调用了从__early_initcall_end开始到__initcall_end结束的所有函数。customize_machine() 也是在其间被调用。
6、总体调用顺序
start_kernel()--->setup_arch()--->do_initcalls()--->customize_machine()--->mx6_sabresd_board_init()