• 《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 <asm/procinfo.h> 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参数和其他不一样。后面再详细分析。

  • 相关阅读:
    二进制兼容
    non-fragile:oc2.0特性--继承结构的父类内存布局变化时子类是否需要重新编译的问题
    [objc explain]: Non-fragile ivars
    函数响应式编程(FRP)思想-Callback风格
    FRP-Functional Reactive Programming-函数响应式编程
    AWESOME SWIFT-swift.libhunt.com-swift类库网站
    iOS
    视图逻辑、应用逻辑、业务逻辑
    laravel微信自定义分享
    实现手机网页调起原生微信朋友圈分享的工具nativeShare.js
  • 原文地址:https://www.cnblogs.com/zhuangquan/p/11490126.html
Copyright © 2020-2023  润新知