《kernel源码分析(二)内核启动流程分析》

1.内核启动参数

  当uboot启动内核时,调用的是armlinux.c中的theKernel (0, bd->bi_arch_number, bd->bi_boot_params),传入了两个参数。

  bi_arch_number:机器码(以2410为例,可以通过sourceinsight中的全局搜索。在smdk2410.c中:gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;  #define MACH_TYPE_SMDK2410      193,所以uboot会将193传给kernel。193会存入R1寄存器中)

  bd->bi_boot_params:启动参数(同一个文件中 gd->bd->bi_boot_params = 0x30000100;)

  所以接下来分析内核启动第一个运行的文件/arch/arm/kernel/head.S  

 

2.heas.S分析

    mrc    p15, 0, r9, c0, c0        @ get processor id
//获取CPU处理器并对比,如果没有这个CPU。就跳转到error_p
bl __lookup_processor_type @ r5=procinfo r9=cpuid   
movs r10, r5 @ invalid processor (r5
=0)? beq __error_p @ yes, error 'p'
//检测是不是支持该单板,参数由uboot传入 bl __lookup_machine_type @ r5=machinfo movs r8, r5 @ invalid machine (r5=0)? beq __error_a @ yes, error 'a' bl __vet_atags bl __create_page_tables

lookup_machine_type分析:

/*
 * Look in and arch/arm/kernel/arch.[ch] for
 * more information about the __proc_info and __arch_info structures.
 */
 .align 2
3: .long __proc_info_begin
 .long __proc_info_end
4: .long .
 .long __arch_info_begin
 .long __arch_info_end

1
__lookup_machine_type: 2 adr r3, 4b //r3=4b的地址 3 ldmia r3, {r4, r5, r6} 4 sub r3, r3, r4 @ get offset between virt&phys 5 add r5, r5, r3 @ convert virt addresses to 6 add r6, r6, r3 @ physical address space 7 1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type 8 teq r3, r1 @ matches loader number? 9 beq 2f @ found 10 add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc 11 cmp r5, r6 12 blo 1b 13 mov r5, #0 @ unknown machine 14 2: mov pc, lr 15 ENDPROC(__lookup_machine_type)

第二行:

    adr  r3,4b   ---->r3=4b的地址(物理地址)

    4b               ---->4: .long .     .long __arch_info_begin     .long __arch_info_end

第三行:

    r4=.(“.”代表虚拟地址,当内核编译的时候,这个段的地址)    r5=__arch_info_begin   r6=__arch_info_end

    在vmlinux.lds中有相关定义:

__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;

    查找.arch.info.init,可以发现在arch/arm/include/asm/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_START在哪里使用,可以找到在相关单板有定义(mach-smdk2410.c)  

MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch* to SMDK2410 */
/* Maintainer: Jonas Dietsche */
.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

   跟uboot命令那样,对宏进行分析

#define MACHINE_START(_type,_name)            \
static const struct machine_desc __mach_desc_SMDK2410    \
 __used                            \
 __attribute__((__section__(".arch.info.init"))) = {    \    //将这个段的属性设置在.arch.info.init中
    .nr        = MACH_TYPE_SMDK2410,        \
    .name        = "SMDK2410",
  /* Maintainer: Jonas Dietsche */
  .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_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    */ 机器ID
    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);
};

总结:

  当添加相对应单板进行编译时,就会将MACHINE_START(SMDK2410, "SMDK2410")这一段的内容进行编译,并且强制将这段放在".arch.info.init"中。uboot通过theKernel (0, bd->bi_arch_number, bd->bi_boot_params)启动内核,会将bd->bi_arch_number和

bd->bi_boot_params两个参数传递给内核。然后内核将获取__arch_info_begin和__arch_info_end地址,在这两个地址之间(也就是".arch.info.init"段)进行对比,查看该内核是否支持该单板。

 

head.S总结:

  由于head.S源码相对较长,就不全部贴出一一分析。

  head.S中主要做的事情:

  1.      判断是否支持该cpu

  2.      判断是否支持这个单板

  3.      创建页表

  4.      使能mmu

  5.      跳转到start_kernel

   在head.S中已经对uboot传入的参数bd->bi_arch_number进行的对比使用,接下来分析bd->bi_boot_params参数。

 

3.bi_boot_params解析

  在/init/mian.c中,setup_arch(&command_line)和setup_command_line(command_line),这两个函数就是对bi_boot_params参数进行解析。

 

4.内核挂载根文件系统

  start_kernel---->

      parse_early_param---->

        parse_early_options---->

          do_early_param    //从__setup_start到__setup_end,进行对比。

        unknown_bootoption---->

          obsolete_checksetup  //从__setup_start到__setup_end,进行对比。

      rest_init--->

        kernel_init---->

          prepare_namespace---->

            mount_root       //挂载根文件系统

          init_post                //执行以下的应用程序

             run_init_process("/sbin/init");
             run_init_process("/etc/init");
             run_init_process("/bin/init");
             run_init_process("/bin/sh");

其中__setup_start和__setup_end,就是和上面分析的一样。

  __setup_start = .;
   *(.init.setup)
  __setup_end = .;

 

do_mounts.c中

static int __init root_dev_setup(char *line)
{
    strlcpy(saved_root_name, line, sizeof(saved_root_name));
    return 1;
}

__setup("root=", root_dev_setup);

 

init.h中

#define __setup_param(str, unique_id, fn, early)            \
    static const char __setup_str_##unique_id[] __initconst    \
        __aligned(1) = str; \
    static struct obs_kernel_param __setup_##unique_id    \
        __used __section(.init.setup)            \
        __attribute__((aligned((sizeof(long)))))    \
        = { __setup_str_##unique_id, fn, early }

#define __setup(str, fn)                    \
    __setup_param(str, fn, fn, 0)

分析过程和上面一样。这边就不再重复分析了。其中一个early参数和其他不一样。后面再详细分析。

 

 

转载于:https://www.cnblogs.com/zhuangquan/p/11490126.html

你可能感兴趣的:(《kernel源码分析(二)内核启动流程分析》)