跳转到start_kernel中,与本题相关的主要是setup_arch()和rest_init(),其中前者执行的较早,而后者做为start_kernel执行的最后一个函数。
函数start_kernel()和rest_init()定义在kernel/init/main.c中,函数setup_arch()定义在kernel/arch/arch_name/kernel/setup.c中。
1.setup_arch()在.init.text中,会执行machine_desc.init_very_early和machine_desc.init_early,代码如下,
915void __init setup_arch(char **cmdline_p) 916{ 917 struct machine_desc *mdesc; 918 919 unwind_init(); 920 921 setup_processor(); 922 mdesc = setup_machine_fdt(__atags_pointer); 923 if (!mdesc) 924 mdesc = setup_machine_tags(machine_arch_type); 925 machine_desc = mdesc; 926 machine_name = mdesc->name; 927 928 if (mdesc->soft_reboot) 929 reboot_setup("s"); 930 931 init_mm.start_code = (unsigned long) _text; 932 init_mm.end_code = (unsigned long) _etext; 933 init_mm.end_data = (unsigned long) _edata; 934 init_mm.brk = (unsigned long) _end; 935 936 /* populate cmd_line too for later use, preserving boot_command_line */ 937 strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); 938 *cmdline_p = cmd_line; 939 940 parse_early_param(); 941 942 if (mdesc->init_very_early) 943 mdesc->init_very_early(); 944 945 sanity_check_meminfo(); 946 arm_memblock_init(&meminfo, mdesc); 947 948 paging_init(mdesc); 949 request_standard_resources(mdesc); 950 951 unflatten_device_tree(); 952 953#ifdef CONFIG_SMP 954 if (is_smp()) 955 smp_init_cpus(); 956#endif 957 reserve_crashkernel(); 958 959 cpu_init(); 960 tcm_init(); 961 962#ifdef CONFIG_MULTI_IRQ_HANDLER 963 handle_arch_irq = mdesc->handle_irq; 964#endif 965 966#ifdef CONFIG_VT 967#if defined(CONFIG_VGA_CONSOLE) 968 conswitchp = &vga_con; 969#elif defined(CONFIG_DUMMY_CONSOLE) 970 conswitchp = &dummy_con; 971#endif 972#endif 973 early_trap_init(); 974 975 if (mdesc->init_early) 976 mdesc->init_early(); 977}
2. rest_init()执行后,创建内核线程kernel_init,这也是内核第一个线程,kernel_init做的与本题相关的初始化是do_pre_smp_initcalls()和do_basic_setup(),然后变成用户态进程init。代码如下,
841static int __init kernel_init(void * unused) 842{ 843 /* 844 * Wait until kthreadd is all set-up. 845 */ 846 wait_for_completion(&kthreadd_done); 847 /* 848 * init can allocate pages on any node 849 */ 850 set_mems_allowed(node_states[N_HIGH_MEMORY]); 851 /* 852 * init can run on any cpu. 853 */ 854 set_cpus_allowed_ptr(current, cpu_all_mask); 855 856 cad_pid = task_pid(current); 857 858 smp_prepare_cpus(setup_max_cpus); 859 860 do_pre_smp_initcalls(); 861 lockup_detector_init(); 862 863 smp_init(); 864 sched_init_smp(); 865 866 do_basic_setup(); 867 868 /* Open the /dev/console on the rootfs, this should never fail */ 869 if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) 870 printk(KERN_WARNING "Warning: unable to open an initial console.\n"); 871 872 (void) sys_dup(0); 873 (void) sys_dup(0); 874 /* 875 * check if there is an early userspace init. If yes, let it do all 876 * the work 877 */ 878 879 if (!ramdisk_execute_command) 880 ramdisk_execute_command = "/init"; 881 882 if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { 883 ramdisk_execute_command = NULL; 884 prepare_namespace(); 885 } 886 887 /* 888 * Ok, we have completed the initial bootup, and 889 * we're essentially up and running. Get rid of the 890 * initmem segments and start the user-mode stuff.. 891 */ 892 893 init_post(); 894 return 0; 895}
do_pre_smp_initcalls()做.initcallearly.init section函数的执行,早于.initcall0.init-.initcall7.init;在初始化完smp调度后,.initcalln.init函数在do_basic_setup()被调用。
do_pre_smp_initcalls()代码如下,
786static void __init do_pre_smp_initcalls(void) 787{ 788 initcall_t *fn; 789 790 for (fn = __initcall_start; fn < __initcall0_start; fn++) 791 do_one_initcall(*fn); 792}
do_basic_setup()及调用.initcalln.init的代码如下,
743static void __init do_initcall_level(int level) 744{ 745 extern const struct kernel_param __start___param[], __stop___param[]; 746 initcall_t *fn; 747 748 strcpy(static_command_line, saved_command_line); 749 parse_args(initcall_level_names[level], 750 static_command_line, __start___param, 751 __stop___param - __start___param, 752 level, level, 753 repair_env_string); 754 755 for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) 756 do_one_initcall(*fn); 757} 758 759static void __init do_initcalls(void) 760{ 761 int level; 762 763 for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) 764 do_initcall_level(level); 765} 766 767/* 768 * Ok, the machine is now initialized. None of the devices 769 * have been touched yet, but the CPU subsystem is up and 770 * running, and memory and process management works. 771 * 772 * Now we can finally start doing some real work.. 773 */ 774static void __init do_basic_setup(void) 775{ 776 cpuset_init_smp(); 777 usermodehelper_init(); 778 shmem_init(); 779 driver_init(); 780 init_irq_proc(); 781 do_ctors(); 782 usermodehelper_enable(); 783 do_initcalls(); 784}
各.initcalln.init的宏定义如下
168/* initcalls are now grouped by functionality into separate 169 * subsections. Ordering inside the subsections is determined 170 * by link order. 171 * For backwards compatibility, initcall() puts the call in 172 * the device init subsection. 173 * 174 * The `id' arg to __define_initcall() is needed so that multiple initcalls 175 * can point at the same handler without causing duplicate-symbol build errors. 176 */ 177 178#define __define_initcall(level,fn,id) \ 179 static initcall_t __initcall_##fn##id __used \ 180 __attribute__((__section__(".initcall" level ".init"))) = fn 181 182/* 183 * Early initcalls run before initializing SMP. 184 * 185 * Only for built-in code, not modules. 186 */ 187#define early_initcall(fn) __define_initcall("early",fn,early) 188 189/* 190 * A "pure" initcall has no dependencies on anything else, and purely 191 * initializes variables that couldn't be statically initialized. 192 * 193 * This only exists for built-in code, not for modules. 194 */ 195#define pure_initcall(fn) __define_initcall("0",fn,0) 196 197#define core_initcall(fn) __define_initcall("1",fn,1) 198#define core_initcall_sync(fn) __define_initcall("1s",fn,1s) 199#define postcore_initcall(fn) __define_initcall("2",fn,2) 200#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s) 201#define arch_initcall(fn) __define_initcall("3",fn,3) 202#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s) 203#define subsys_initcall(fn) __define_initcall("4",fn,4) 204#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s) 205#define fs_initcall(fn) __define_initcall("5",fn,5) 206#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s) 207#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs) 208#define device_initcall(fn) __define_initcall("6",fn,6) 209#define device_initcall_sync(fn) __define_initcall("6s",fn,6s) 210#define late_initcall(fn) __define_initcall("7",fn,7) 211#define late_initcall_sync(fn) __define_initcall("7s",fn,7s) 212 213#define __initcall(fn) device_initcall(fn) 214 215#define __exitcall(fn) \ 216 static exitcall_t __exitcall_##fn __exit_call = fn 217 218#define console_initcall(fn) \ 219 static initcall_t __initcall_##fn \ 220 __used __section(.con_initcall.init) = fn 221 222#define security_initcall(fn) \ 223 static initcall_t __initcall_##fn \ 224 __used __section(.security_initcall.init) = fn
machine_desc.init_machine在customize_machine() @ kernel/arch/arm/kernel/setup.c中被调用,而customize_machine指针引用在.initcall3.init中。customize_machine()代码如下,
842static int __init customize_machine(void) 843{ 844 /* customizes platform devices, or adds new ones */ 845 if (machine_desc->init_machine) 846 machine_desc->init_machine(); 847 return 0; 848} 849arch_initcall(customize_machine);
各module_init(fn)设备驱动在.initcall6.init中被调用,由以下module_init的宏定义可以看出,
259/** 260 * module_init() - driver initialization entry point 261 * @x: function to be run at kernel boot time or module insertion 262 * 263 * module_init() will either be called during do_initcalls() (if 264 * builtin) or at module insertion time (if a module). There can only 265 * be one per module. 266 */ 267#define module_init(x) __initcall(x);
而__initcall(x)宏定义为
213#define __initcall(fn) device_initcall(fn)
综上,题中提及各初始化的顺序是machine_desc.init_very_early, machine_desc.init_early, .initcallearly.init, machine_desc.init_machine(.initcall3.init), module_init(.initcall6.init) 。
[End]
[参考文章]
http://blog.csdn.net/paomadi/article/details/8611408
一、定义
#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 \ };
MACHINE_START和MACHINE_END框起了一个machine_desc结构体的声明并根据MACHINE_START宏的参数初始化其.nr和.name成员
并将该结构体标记编译到.arch.info.init段
在MACHINE_START和MACHINE_END宏之间可以初始化machine_desc结构体的剩余成员
machine_desc结构体的定义
struct machine_desc { unsigned int nr; /* architecture number 编号 */ const char *name; /* architecture name 名字 */ unsigned long boot_params; /* tagged list */ unsigned int nr_irqs; /* number of IRQs 中断数 */ unsigned int video_start; /* start of video RAM */ unsigned int video_end; /* end of video RAM */ unsigned int reserve_lp0 :1; /* never has lp0 */ unsigned int reserve_lp1 :1; /* never has lp1 */ unsigned int reserve_lp2 :1; /* never has lp2 */ unsigned int soft_reboot :1; /* soft reboot */ void (*fixup)(struct machine_desc *,struct tag *, char **,struct meminfo *); void (*reserve)(void); /* reserve mem blocks */ void (*map_io)(void); /* IO mapping function io映射函数 */ void (*init_irq)(void); /* 中断初始化函数 */ struct sys_timer *timer; /* system tick timer 滴答定时器 */ void (*init_machine)(void); /* 初始化函数 */ };
使用例子:
MACHINE_START(SMDKC110, "SMDKC110") /* Maintainer: Kukjin Kim <[email protected]> */ .boot_params = S5P_PA_SDRAM + 0x100, .init_irq = s5pv210_init_irq, .map_io = smdkc110_map_io, .init_machine = smdkc110_machine_init, .timer = &s3c24xx_timer, MACHINE_END
这里smdkc110_machine_init就是对应的板级初始化函数,s5pv210_init_irq就是板级中断初始化函数,smdkc110_map_io就是板级io初始化函数...
二、调用关系
MACHINE_START宏将machine_desc标记编译到.arch.info.init段, 而/arch/arm/kernel/vmlinux.lds中
__arch_info_begin = .; *(.arch.info.init) __arch_info_end = .;
当系统启动时在linux启动函数start_kernel中调用了setup_arch(&command_line);
void __init setup_arch(char **cmdline_p) { struct tag *tags = (struct tag *)&init_tags; struct machine_desc *mdesc; //声明了一个machine_desc结构体指针 char *from = default_command_line; init_tags.mem.start = PHYS_OFFSET; unwind_init(); setup_processor(); mdesc = setup_machine(machine_arch_type); //0根据machine_arch_type获取machine_desc machine_name = mdesc->name; //设置名字 if (mdesc->soft_reboot) //需要软重启? reboot_setup("s"); if (__atags_pointer) tags = phys_to_virt(__atags_pointer); else if (mdesc->boot_params) { //处理启动参数 #ifdef CONFIG_MMU if (mdesc->boot_params < PHYS_OFFSET || mdesc->boot_params >= PHYS_OFFSET + SZ_1M) { printk(KERN_WARNING"Default boot params at physical 0x%08lx out of reach\n",mdesc->boot_params); } else #endif { tags = phys_to_virt(mdesc->boot_params); } } #if defined(CONFIG_DEPRECATED_PARAM_STRUCT) if (tags->hdr.tag != ATAG_CORE) convert_to_tag_list(tags); #endif if (tags->hdr.tag != ATAG_CORE) tags = (struct tag *)&init_tags; if (mdesc->fixup) //若存在fixup方法则调用其方法 mdesc->fixup(mdesc, tags, &from, &meminfo); if (tags->hdr.tag == ATAG_CORE) { if (meminfo.nr_banks != 0) squash_mem_tags(tags); save_atags(tags); parse_tags(tags); } init_mm.start_code = (unsigned long) _text; init_mm.end_code = (unsigned long) _etext; init_mm.end_data = (unsigned long) _edata; init_mm.brk = (unsigned long) _end; /* parse_early_param needs a boot_command_line */ strlcpy(boot_command_line, from, COMMAND_LINE_SIZE); /* populate cmd_line too for later use, preserving boot_command_line */ strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); *cmdline_p = cmd_line; parse_early_param(); arm_memblock_init(&meminfo, mdesc); //这里可能会调用reserve方法 paging_init(mdesc); //->devicemaps_init(mdesc)->map_io方法 request_standard_resources(&meminfo, mdesc); //这里可能会调用video_start方法 #ifdef CONFIG_SMP if (is_smp()) smp_init_cpus(); #endif reserve_crashkernel(); cpu_init(); tcm_init(); arch_nr_irqs = mdesc->nr_irqs; //1设置全局变量 中断个数 init_arch_irq = mdesc->init_irq; //2设置全局变量 中断初始化函数 system_timer = mdesc->timer; //3设置全局变量 sys_timer结构体 init_machine = mdesc->init_machine; //4设置全局变量 板级初始化函数 #ifdef CONFIG_VT #if defined(CONFIG_VGA_CONSOLE) conswitchp = &vga_con; #elif defined(CONFIG_DUMMY_CONSOLE) conswitchp = &dummy_con; #endif #endif early_trap_init(); }
0. 结构machine_desc安装
static struct machine_desc * __init setup_machine(unsigned int nr) { extern struct machine_desc __arch_info_begin[], __arch_info_end[]; struct machine_desc *p; for (p = __arch_info_begin; p < __arch_info_end; p++)//遍历__arch_info_begin和__arch_info_end之间的machine_desc结构体 if (nr == p->nr) { //找到对应的板 printk("Machine: %s\n", p->name);//打印板级信息 return p; } early_print("\n" "Error: unrecognized/unsupported machine ID (r1 = 0x%08x).\n\n" "Available machine support:\n\nID (hex)\tNAME\n", nr); for (p = __arch_info_begin; p < __arch_info_end; p++) early_print("%08x\t%s\n", p->nr, p->name); early_print("\nPlease check your kernel config and/or bootloader.\n"); while (true) /* can't use cpu_relax() here as it may require MMU setup */; }
1.中断个数
start_kernel->early_irq_init->arch_probe_nr_irqs函数中nr_irqs = arch_nr_irqs ? arch_nr_irqs : NR_IRQS;设置全局nr_irqs变量
2.中断初始化函数
start_kernel->init_IRQ->init_arch_irq()
3.sys_timer结构体
start_kernel->time_init()调用system_timer->init()方法既sys_timer->init()
4.板级初始化函数
static void (*init_machine)(void) __initdata; static int __init customize_machine(void) { /* customizes platform devices, or adds new ones */ if (init_machine)//全局函数init_machine存在 init_machine();//则调用,既mdesc->init_machine() return 0; } arch_initcall(customize_machine);//用arch_initcall修饰customize_machine函数
arch_iniitcall函数在/include/linux/init.h中定义
#define arch_initcall(fn) __define_initcall("3",fn,3)
__define_initcall的定义
#define __define_initcall(level,fn,id) \ static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" level ".init"))) = fn
展开就是static initcall_t __initcall_customize_machine3 __used __attribute__((__section__(".initcall3.init")))=customize_machine
在vmlinux.lds中
__initcall_start = .; *(.initcallearly.init) __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) __initcall_end = .;
标注为.initcall3.init的函数编译进__initcall_start和__initcall_end框起的section中
而在系统启动的时候start_kernel->rest_init()->kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);创建了kernel线程
kernel_init->do_pre_smp_initcalls()
static void __init do_pre_smp_initcalls(void) { initcall_t *fn; for (fn = __initcall_start; fn < __early_initcall_end; fn++) do_one_initcall(*fn); }
该函数遍历__initcall_start和__early_initcall_end中的函数,并调用do_one_initcall
int __init_or_module do_one_initcall(initcall_t fn) { int count = preempt_count(); int ret; if (initcall_debug) ret = do_one_initcall_debug(fn); else ret = fn();//执行了fn函数也就是customize_machine msgbuf[0] = 0; if (ret && ret != -ENODEV && initcall_debug) sprintf(msgbuf, "error code %d ", ret); if (preempt_count() != count) { strlcat(msgbuf, "preemption imbalance ", sizeof(msgbuf)); preempt_count() = count; } if (irqs_disabled()) { strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf)); local_irq_enable(); } if (msgbuf[0]) { printk("initcall %pF returned with %s\n", fn, msgbuf); } return ret; }
http://blog.csdn.net/cxw3506/article/details/8475965
Machine定义以MACHINE_START开始并以MACHINE_END结束,如下mini2440开发板的移植为示例
MACHINE_START(MINI2440, "MINI2440") .phys_io = S3C2410_PA_UART, .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc, .boot_params = S3C2410_SDRAM_PA + 0x100, .map_io = mini2440_map_io, .init_machine = mini2440_init, .init_irq = s3c24xx_init_irq, .timer = &s3c24xx_timer, MACHINE_END
MACHINE_START、MACHINE_END都是定义的宏,代码如下
/*
* 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的变量,结构体定义如下
struct machine_desc {
/*
* Note! The first four elements are used
* by assembler code in head.S, head-common.S
*/
unsigned int nr; /* architecture number */
unsigned int phys_io; /* start of physical io */
unsigned int io_pg_offst; /* byte offset for io
* page tabe entry */
const char *name; /* architecture name */
unsigned long boot_params; /* tagged list */
unsigned int video_start; /* start of video RAM */
unsigned int video_end; /* end of video RAM */
unsigned int reserve_lp0 :1; /* never has lp0 */
unsigned int reserve_lp1 :1; /* never has lp1 */
unsigned int reserve_lp2 :1; /* never has lp2 */
unsigned int soft_reboot :1; /* soft reboot */
void (*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
void (*map_io)(void);/* IO mapping function */
void (*init_irq)(void);
struct sys_timer *timer; /* system tick timer */
void (*init_machine)(void);
};
这个类型的变量放在内核代码段.arch.info.init中,在内核运行初期,被函数lookup_machine_type(此函数用汇编实现,在汇编文件中)取出,读取流程为
Start_kernel() -> setup_arch() -> setup_machine() -> lookup_machine_type()
在函数setup_machine()中,利用这个结构体类型的变量初始化一些全局变量,以备内核运行时使用,比如
init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
这个结构体中,成员init_machine保存的是开发板资源注册的初始化代码,init_irq保存的是中断初始化指针,timer保存的是一个struct sys_timer类型的指针…..如果我们要给自己的开发板定制内核,那么我们必须自己实现以上成员函数,其中函数init_machine()是我们向内核传递开发板设备信息的重要的常规途径,分析mini2440开发板内核移植代码知道,在这个函数中,注册了开发板所用到的所有设备的相关硬件信息!
static void __init mini2440_init(void) { struct mini2440_features_t features = { 0 }; int i; printk(KERN_INFO "MINI2440: Option string mini2440=%s\n", mini2440_features_str); /* Parse the feature string */ mini2440_parse_features(&features, mini2440_features_str); /* turn LCD on */ s3c_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND); /* Turn the backlight early on */ WARN_ON(gpio_request(S3C2410_GPG(4), "backlight")); gpio_direction_output(S3C2410_GPG(4), 1); /* remove pullup on optional PWM backlight -- unused on 3.5 and 7"s */ s3c_gpio_setpull(S3C2410_GPB(1), S3C_GPIO_PULL_UP); s3c2410_gpio_setpin(S3C2410_GPB(1), 0); s3c_gpio_cfgpin(S3C2410_GPB(1), S3C2410_GPIO_INPUT); /* Make sure the D+ pullup pin is output */ WARN_ON(gpio_request(S3C2410_GPC(5), "udc pup")); gpio_direction_output(S3C2410_GPC(5), 0); /* mark the key as input, without pullups (there is one on the board) */ for (i = 0; i < ARRAY_SIZE(mini2440_buttons); i++) { s3c_gpio_setpull(mini2440_buttons[i].gpio, S3C_GPIO_PULL_UP); s3c_gpio_cfgpin(mini2440_buttons[i].gpio, S3C2410_GPIO_INPUT); } if (features.lcd_index != -1) { int li; mini2440_fb_info.displays = &mini2440_lcd_cfg[features.lcd_index]; printk(KERN_INFO "MINI2440: LCD"); for (li = 0; li < ARRAY_SIZE(mini2440_lcd_cfg); li++) if (li == features.lcd_index) printk(" [%d:%dx%d]", li, mini2440_lcd_cfg[li].width, mini2440_lcd_cfg[li].height); else printk(" %d:%dx%d", li, mini2440_lcd_cfg[li].width, mini2440_lcd_cfg[li].height); printk("\n"); s3c24xx_fb_set_platdata(&mini2440_fb_info); } s3c24xx_udc_set_platdata(&mini2440_udc_cfg); s3c24xx_mci_set_platdata(&mini2440_mmc_cfg); s3c_nand_set_platdata(&mini2440_nand_info); s3c_i2c0_set_platdata(NULL); i2c_register_board_info(0, mini2440_i2c_devs, ARRAY_SIZE(mini2440_i2c_devs)); platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices)); if (features.count) /* the optional features */ platform_add_devices(features.optional, features.count); }
那么成员函数init_machine什么时候被调用呢?
在函数setup_machine()中有一条语句init_machine = mdesc->init_machine;其中init_machine为全局函数指针变量,此变量在函数customize_machine()中被调用,代码如下所示:
static int __init customize_machine(void) { /* customizes platform devices, or adds new ones */ if (init_machine) init_machine(); return 0; } arch_initcall(customize_machine);