您的位置:首页 > 运维架构 > Linux

linux之MACHINE_START和 MACHINE_END

2013-03-27 13:43 351 查看
一、

注:下面的内容是以linux-2.6.38和mini6410为例进行学习的。

玩过或者移植过arm-linux的都应该知道在/arch/arm目录下有许多与具体处理器相关的目录,当然对于6410的话所对应的目录就是mach-s3c64xx,在里面找到与具体板子相关的文件mach-mini6410.c,没错,就是它。无论是出于想移植到新的内核还是出于想深入学习某一款arm等,对这个文件的学习是必不可少的。这个文件大部分内容是对平台设备(例如串口,LCD,Nand falsh等)的结构体初始化,在这个文件的最后有一个非常重要的宏:

1 MACHINE_START(MINI6410, "MINI6410")
2         /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
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这个成员,它是一个函数指针,值为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()

二、

原文地址:Linux中的MACH定义之MACHINE_START / MACHINE_END 作者:kangjunping

(写在转载之前)
在linux中machine对应一个设备,对于开发者而言对应一个新的项目,项目开展的初始需要对应定义一个MACH,在arch/arm/mach-xxx/目录下,此文件中定义了GPIO的使用,外设,i2c总线等系统信息,所以在参加一个项目的开始,应该认真的分析machine定义文件,了解项目的情况。
在arch/arm/下定义了很多mach-xxx的文件夹,一般是按照厂商或者平台命名,例如高通平台的为mach-msm,marvell的为mach-mmp,mach-pxa。

本文讲解LINUX中用MACHINE_START/MACHINE_END定义的MACH,并给出定义的各个成员函数在初始化过程中被调用的时机。

1. 定义一个MACH

LINUX中MACHINE定义是用MACHINE_START()/MACHINE_END两个宏来实现的,比如MSM的实现(arch/arm/mach-msm/board-halibut.c):

1.MACHINE_START(HALIBUT,"Halibut Board (QCT SURF7200A)")

2. .boot_params = 0x10000100,

3. .map_io = halibut_map_io,

4. .init_irq = halibut_init_irq,

5. .init_machine = halibut_init,

6. .timer = &msm_timer,

7.MACHINE_END

2. MACHINE_START / MACHINE_END定义

上面的定义中,用到了这两个宏MACHINE_START/MACHINE_END,下面是它们具体的定义(在arch/arm/include/asm/mach/arch.h中):

1.#defineMACHINE_START(_type,_name) \

2.static const structmachine_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也是定义在arch/arm/include/asm/mach/arch.h

1.struct machine_desc {

2. /*

3. * Note! The firstfour elements are used

4. * by assembler codein head.S, head-common.S

5. */

6. unsigned int nr; /* architecture number */

7. unsigned int phys_io; /* start of physical io */

8. unsigned int io_pg_offst; /* byte offset for io

9. * page tabe entry */

10.

11. const char *name; /* architecture name */

12. unsigned long boot_params; /* tagged list */

13.

14. unsigned int video_start; /* start of video RAM */

15. unsigned int video_end; /* end of video RAM */

16.

17. unsigned int reserve_lp0:1; /* never has lp0 */

18. unsigned int reserve_lp1:1; /* never has lp1 */

19. unsigned int reserve_lp2:1; /* never has lp2 */

20. unsigned int soft_reboot:1; /* soft reboot */

21. void (*fixup)(struct machine_desc *,

22. struct tag *, char **,

23. struct meminfo *);

24. void (*map_io)(void); /* IO mapping function */

25. void (*init_irq)(void);

26. struct sys_timer *timer; /* system tick timer */

27. void (*init_machine)(void);

28.};

3. MACH HALIBUT的定义

把1中定义的MACH展开之后,得到:

1.struct machine_desc __mach_desc_HALIBUT{

2.__used

3.__attribute__((__section__(".arch.info.init")))= {

4. .nr = MACH_TYPE_HALIBUT,

5. .name = "HalibutBoard (QCT SURF7200A)",

6. .boot_params = 0x10000100,

7. .map_io = halibut_map_io,

8. .init_irq = halibut_init_irq,

9. .init_machine = halibut_init,

10. .timer = &msm_timer,

11.};

总结一下:MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,Kernel 起来之后将被丢弃。

4. 成员函数被调用的时机

在setup_arch() [setup.c#758~760]中init_irq, timer & init_machine分别被赋值给下列变量:

init_arch_irq = mdesc->init_irq;

system_timer = mdesc->timer;

init_machine = mdesc->init_machine;

而这三个函数指针是在下列场景中被调用的:

1) start_kernel()[main.c#589]-> init_IRQ() [irq.c] ->init_arch_irq();

2) start_kernel()[main.c#595]->time_init () [time.c] ->system_time->init();

3) customize_machine()[setup.c#692] -> init_machine();

customize_machine是被放在arch_initcall段的,按照顺序被调用。xxx_initcall段内的函数是按下列顺序被调用的:start_kernel() [main.c#682] -> rest_init() [启动内核线程]-> kernel_init() –> do_basic_setup()-> do_initcalls();

map_io是在下列顺序中被调用

4) start_kernel()[main.c#546]-> setup_arch () [setup.c#745] -> paging_init() [mmu.c#1028] -> devicemaps_init()[mmu.c#993] -> map_io()

从它们在start_kernel()中被调用的顺序,可知它们执行的先后为:map_io; init_irq; timer->time_init; init_machine。
本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2011-09/43351p2.htm

三、

一、定义

[cpp]
#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结构体的定义
[cpp]
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); /* 初始化函数 */
};
使用例子:
[cpp]
MACHINE_START(SMDKC110, "SMDKC110")
/* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
.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中
[cpp]
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
在linux启动函数start_kernel中调用了setup_arch(&command_line);
[cpp]
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.
[cpp]
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.板级初始化函数
[cpp]
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中定义
[cpp]
#define arch_initcall(fn) __define_initcall("3",fn,3)
__define_initcall的定义
[cpp]
#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中
[cpp]
__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
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
[cpp]
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: