MACHINE-START / MACHINE-END

Table of Contents

Overview

对于不同架构的CPU和不同的开发板,系统的初始化参数和函数不同,但Linux启动的整个流程是不变的,那么如何在Linux启动流程中为不同CPU和开发板修改或嵌入它们各自的函数与参数呢?

  • 像netfiler一样定义启动时可嵌入的hook函数,然后不同设备注册相应的hook 函数。
  • 或像 struct file_operation 定义好一组函数指针或变量的静态表,由不同设备去赋值相应的具体实现。
  • 还是由Linux提供一个全是空函数的预定义的文件,不同设备去实现相应具体函数。

为了可维护性,并不漏掉相应的系统初始化要实现的接口,Linux如 struct file_operation 定义好一组函数指针或变量的静态表,集中式管理,以sm2410 板子如下:

// /arch/arm/mach-s3c2410/mach-smdk2410.c
MACHINE_START(SMDK2410, "SMDK2410") 
        .phys_io        = S3C2410_PA_UART,
        .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
        .boot_params    = S3C2410_SDRAM_PA + 0x100,
        .map_io         = smdk2410_map_io,
        .init_irq       = s3c24xx_init_irq,
        .init_machine   = smdk2410_init,
        .timer          = &s3c24xx_timer,
MACHINE_END

MACHINE-START and MACHINE-END

MACHINE-STARTMACHINE-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                             \
};

用上面的smdk2410展开这个宏,如下:

static const struct machine_desc __mach_desc_SMDK2410 
__used                                                 \
__attribute__((__section__(".arch.info.init"))) = {
 .nr = MACH_TYPE_SMDK2410, /* architecture number */
 .name = "SMDK2410", /* architecture name */
 .phys_io        = S3C2410_PA_UART,
 .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
 .boot_params    = S3C2410_SDRAM_PA + 0x100,
 .map_io         = smdk2410_map_io,
 .init_irq       = s3c24xx_init_irq,
 .init_machine   = smdk2410_init,
 .timer          = &s3c24xx_timer,
}

这样就变的很清晰了,它就定义了一个类型是 strcut machine_desc如下参考)的结构体 __mach_desc_SMDK2410 。它被放在 __section__(".arch.info.init") 中, 是初始化数据,kernel启动起来后将被丢弃。

MACH_TYPE_SMDK2410 是目标板的类型值,定义在 arch/include/asm-arm/mach-types.h 中( #include <asm/mach-types.h> ),文件 mach-types.h 在未编译前是不存在的,它由目录 /arch/arm/tools/ 中的脚本 gen-mach-types 和 文件 mach-types 生成,在 mach-types 可以查找到

smdk2410                ARCH_SMDK2410 SMDK2410          193

其他字段如下:

  • phys_io :物理IO空间起始地址.
  • io_pg_offst :IO页表入口的偏移字节.
  • boot_params :bootloader产生的tagged list的开始地址.
  • map_io :IO mapping的函数.
  • init_irq :初始化板子IRQ系统的函数.
  • init_machine :初始化板子各个设备与接口的函数.
  • timer :系统tick timer的结构体.
  • other: 其他 struct machine_desc 中的成员一般用不到.

Linux启动使用或调用各个字段顺序

Linux kernel启动的整个流程k可见这篇Linux startup process,这里针对大部分板子特定 struct machine_desc 里参数的顺序:

static const struct machine_desc __mach_desc_SMDK2410 = {
 .nr = MACH_TYPE_SMDK2410, /* architecture number */
 .name = "SMDK2410", /* architecture name */
 .phys_io        = S3C2410_PA_UART,
 .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
 .boot_params    = S3C2410_SDRAM_PA + 0x100,
 .map_io         = smdk2410_map_io,
 .init_irq       = s3c24xx_init_irq,
 .init_machine   = smdk2410_init,
 .timer          = &s3c24xx_timer,
}

init_machinecustomize_machine 中被调用,而它被放在 arch_initcall 中,也就是被 do_initcalls(); 依次调用 initcall 段的函数实现,是在 kernel_init 的中部调用 do_basic_setup(); 时调用.

arch_initcall(customize_machine);

nr 作为板子的特定编号,在 setup_archmdesc = setup_machine(machine_arch_type); 传入 machine_arch_type 与各个 nr 对比找到对应的 struct machine_desc *mdesc; 结构,也就是这里的 __mach_desc_SMDK2410.

然后在 setup_arch() 中:

/*
 * Set up various architecture-specific pointers
 */
init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
  1. start_kernel() -> setup_arch(&command_line); -> paging_init(&meminfo, mdesc); -> devicemaps_init(mdesc); -> mdesc->map_io();.
  2. start_kernel() -> init_IRQ();(arch/arm/kernel/irq.c) -> init_arch_irq(); .
  3. start_kernel() -> time_init(); -> system_timer->init();.
  4. start_kernel() -> rest_init(); -> kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); -> do_basic_setup().

struct machine_desc

struct machine_desc {
        /*
         * Note! The first four elements are used
         * by assembler code in head-armv.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);
};

Author: Shi Shougang

Created: 2015-03-05 Thu 23:20

Emacs 24.3.1 (Org mode 8.2.10)

Validate