在学习AP20 kernel代码的时候,看到文件kernel/arch/arm/mach-tegra/board-stingray.c
一个重要结构:
MACHINE_START(STINGRAY, "stingray")
.boot_params = 0x00000100,
.map_io = stingray_map_io,
.reserve = stingray_reserve,
.init_early = tegra_init_early,
.init_irq = tegra_init_irq,
.timer = &tegra_timer,
.init_machine = tegra_stingray_init,
MACHINE_END
MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,Kernel 起来之后将被丢弃。
其余各个成员函数在setup_arch()中被赋值到内核结构体,在不同时期被调用:
start_kernel()--->setup_arch()--->do_initcalls()--->customize_machine()--->
tegra_stingray_init
()
1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。
2. .init_irq在start_kernel() --> init_IRQ() --> init_arch_irq()中被调用
3. .map_io 在 setup_arch() --> paging_init() --> devicemaps_init()中被调用
4. .timer是定义系统时钟,定义TIMER4为系统时钟,在arch/arm/plat-s3c/time.c中体现。在start_kernel() --> time_init()中被调用。
5. .boot_params是bootloader向内核传递的参数的位置,这要和bootloader中参数的定义要一致。
可以参考这个blog
注:下面的内容是以linux-2.6.38和mini6410为例进行学习的。
玩过或者移植过arm-linux的都应该知道在/arch/arm目录下有许多与具体处理器相关的目录,当然对于6410的话所对应的目录就是mach-s3c64xx,在里面找到与具体板子相关的文件mach-mini6410.c,没错,就是它。无论是出于想移植到新的内核还是出于想深入学习某一款arm等,对这个文件的学习是必不可少的。这个文件大部分内容是对平台设备(例如串口,LCD,Nand falsh等)的结构体初始化,在这个文件的最后有一个非常重要的宏:一般在mach-mini6410.c文件的最后几行:
1 MACHINE_START(MINI6410, "MINI6410")
2 /* Maintainer: Ben Dooks <[email protected]> */
3 .boot_params = S3C64XX_PA_SDRAM + 0x100,
4
5 .init_irq = s3c6410_init_irq,
6 .map_io = mini6410_map_io,
7 .init_machine = mini6410_machine_init,
8 .timer = &s3c24xx_timer,
9 MACHINE_END
MINI6410这个宏在/arch/arm/tools/mach-types文件里定义:
mini6410 MACH_MINI6410 MINI6410 2520
MACHINE_START的定义在arch/arm/include/asm/mach/arch.h,如下:
1 #define MACHINE_START(_type,_name) \
2 static const struct machine_desc __mach_desc_##_type \
3 __used \
4 __attribute__((__section__(".arch.info.init"))) = { \
5 .nr = MACH_TYPE_##_type, \
6 .name = _name,
7
8 #define MACHINE_END \
9 };
噢,其实就是定义了一个struct machine_desc类型结构体变量,这个结构体还定义了其他一些成员。
接下来着重关注.init_machine这个成员,
init_machine是一个函数指针,值为mini6410_machine_init,这个函数也在mach-mini6410.c中定义。内容是什么呢?呵呵,因为在这里只给出大体流程,具体内容先不分析。现在最关心的是这个结构体变量在哪里被调用,从而调用它里面的成员和成员函数呢?先来看/arch/arm/kernel/setup.c里面的setup_arch()函数:
1 void __init setup_arch(char **cmdline_p)
2 {
3 struct tag *tags = (struct tag *)&init_tags;
4 struct machine_desc *mdesc;
5 char *from = default_command_line;
6
7 unwind_init();
8
9 setup_processor();
10 mdesc = setup_machine(machine_arch_type);
11 machine_desc = mdesc;
12 machine_name = mdesc->name;
......................
这个函数在/init/main.c的start_kernel()函数里被调用。
看到第10行,这里的setup_machine()函数的作用就是找到我们想要的struct machine_desc类型的变量,
也就是在mach-mini6410.c里定义那个变量。函数的参数machine_arch_type的值是什么呢?继续看:
1 #ifdef CONFIG_MACH_MINI6410
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_MINI6410
7 # endif
8 # define machine_is_mini6410() (machine_arch_type == MACH_TYPE_MINI6410)
9 #else
10 # define machine_is_mini6410() (0)
11 #endif
第6行,MACH_TYPE_MINI6410宏为:
#define MACH_TYPE_MINI6410 2520
也就是说参数machine_arch_type的值为2520。在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);
终于看到了,成员函数init_machine就是在这里被调用的。但是它没有被显式调用,而是放在了arch_initcall这个宏里,去看看它怎么定义的:
#define arch_initcall(fn) __define_initcall("3",fn,3)
再看__define_initcall宏:
1 #define __define_initcall(level,fn,id) \
2 static initcall_t __initcall_##fn##id __used \
3 __attribute__((__section__(".initcall" level ".init"))) = fn
嗯,它被链接到了.initcall段里,现在简单看看/arch/arm/kernel/vmlinux.lds这个链接脚本里关于initcall的定义:
1 __initcall_start = .;
2 *(.initcallearly.init) __early_initcall_end = .;
3 *(.initcall0.init) *(.initcall0s.init)
4 *(.initcall1.init) *(.initcall1s.init)
5 *(.initcall2.init) *(.initcall2s.init)
6 *(.initcall3.init) *(.initcall3s.init)
7 *(.initcall4.init) *(.initcall4s.init)
8 *(.initcall5.init) *(.initcall5s.init)
9 *(.initcallrootfs.init)
10 *(.initcall6.init) *(.initcall6s.init)
11 *(.initcall7.init) *(.initcall7s.init)
12 __initcall_end = .;
可以看到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()--->mini6410_machine_init()
//=======================================================
//=======================================================
//=======================================================
MACHINE_START分析
在友善mini2440提供的linux2.6.32.2内核中,有如下定义:
MACHINE_START(MINI2440, "FriendlyARM Mini2440 development board")
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = mini2440_map_io,
.init_machine = mini2440_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
下面分析一下:
在include/asm-arm/mach/arch.h中,有定义
#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_desc的定义:
struct machine_desc {
unsigned int nr;
unsigned int phys_io;
unsigned int io_pg_offst;
const char *name;
unsigned long boot_params;
unsigned int video_start;
unsigned int video_end;
unsigned int reserve_lp0 :1;
unsigned int reserve_lp1 :1;
unsigned int reserve_lp2 :1;
unsigned int soft_reboot :1;
void (*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
void (*map_io)(void);
void (*init_irq)(void);
struct sys_timer *timer;
void (*init_machine)(void);
};
按定义展开后:
static const struct machine_desc __mach_desc_MINI2440 \
__used \
__attribute__((__section__(".arch.info.init"))) = {
.nr = MACH_TYPE_MINI2440,
.name = "FriendlyARM Mini2440 development board",
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = mini2440_map_io,
.init_machine = mini2440_machine_init,
.timer = &s3c24xx_timer,
};
MACH_TYPE_MINI2440 是mini2440开发板在linux中的机器号。
"FriendlyARM Mini2440 development board"是开发板信息,在终端输入cat /proc/cpuinfo可以查看。
MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,Kernel 起来之后将被丢弃。
其余各个成员函数在setup_arch()中被赋值到内核结构体,在不同时期被调用:
1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。
2. .init_irq在start_kernel() --> init_IRQ() --> init_arch_irq()中被调用
3. .map_io 在 setup_arch() --> paging_init() --> devicemaps_init()中被调用
4. .timer是定义系统时钟,定义TIMER4为系统时钟,在arch/arm/plat-s3c/time.c中体现。在start_kernel() --> time_init()中被调用。
5. .boot_params是bootloader向内核传递的参数的位置,这要和bootloader中参数的定义要一致。
其他主要都在 setup_arch() 中用到。
//=======================================================
//=======================================================
//=======================================================
在移植Linux时,有个结构体需要填写,它以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(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都是定义的宏,代码如下
-
-
-
-
- #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 \
- };
/*
* 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 {
-
-
-
-
- unsigned int nr;
- unsigned int phys_io;
- unsigned int io_pg_offst;
-
-
- const char *name;
- unsigned long boot_params;
-
- unsigned int video_start;
- unsigned int video_end;
-
- unsigned int reserve_lp0 :1;
- unsigned int reserve_lp1 :1;
- unsigned int reserve_lp2 :1;
- unsigned int soft_reboot :1;
- void (*fixup)(struct machine_desc *,
- struct tag *, char **,
- struct meminfo *);
- void (*map_io)(void);
- void (*init_irq)(void);
- struct sys_timer *timer;
- void (*init_machine)(void);
- };
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);
-
-
- mini2440_parse_features(&features, mini2440_features_str);
-
-
- s3c_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND);
-
-
- WARN_ON(gpio_request(S3C2410_GPG(4), "backlight"));
- gpio_direction_output(S3C2410_GPG(4), 1);
-
-
- 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);
-
-
- WARN_ON(gpio_request(S3C2410_GPC(5), "udc pup"));
- gpio_direction_output(S3C2410_GPC(5), 0);
-
-
- 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)
- platform_add_devices(features.optional, features.count);
-
- }
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)
-
- {
-
-
-
- if (init_machine)
-
- init_machine();
-
- return 0;
-
- }
-
- arch_initcall(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);
在MACHINE_START与MACHINE_END之间还要填写一些参数,参照结构体注释小心填写即可,最好找个例子参考参考。