linux内核启动过程分析

来源:互联网 发布:js导致硬件死机 编辑:程序博客网 时间:2024/06/11 06:39

linux内核启动过程分析

1.处理U-boot 传入的参数
在Linux/arch/arm/kernel/head.s中
(1)判断是否支持该CPU(uboot启动时传入的参数bd->bi_arch_number为362)

mrc p15, 0, r9, c0, c0      @ get processor idbl  __lookup_processor_type     @ r5=procinfo r9=cpuid/*theKernel (0, bd->bi_arch_number, bd->bi_boot_params);*/

g:\linux-2.6.22.6\arch\arm\kernel\head-common.S下

/* * Read processor ID register (CP#15, CR0), and look up in the linker-built * supported processor list.  Note that we can't use the absolute addresses * for the __proc_info lists since we aren't running with the MMU on * (and therefore, we are not in the correct address space).  We have to * calculate the offset. * *  r9 = cpuid * Returns: *  r3, r4, r6 corrupted *  r5 = proc_info pointer in physical address space *  r9 = cpuid (preserved) */    .type   __lookup_processor_type, %function__lookup_processor_type:    adr r3, 3f  /* R3=3f的地址,为实际地址,物理地址 */    ldmda   r3, {r5 - r7}   /* ldm为加载多个寄存器,DA 每次传送后地址减四,LDMDA指令,每次传送后地址减4,long 型每次占4个字节。r5=__proc_info_begin ,r6=__proc_info_end ,r7="."代表虚拟地址*/    sub r3, r3, r7          @ get offset between virt&phys    add r5, r5, r3          @ convert virt addresses to    add r6, r6, r3          @ physical address space1:  ldmia   r5, {r3, r4}            @ value, mask/* ldm为加载多个寄存器,iA 每次传送后地址加四*/    and r4, r4, r9          @ mask wanted bits    teq r3, r4    beq 2f    add r5, r5, #PROC_INFO_SZ       @ sizeof(proc_info_list)    cmp r5, r6    blo 1b    mov r5, #0              @ unknown processor2:  mov pc, lr/* * This provides a C-API version of the above function. */ENTRY(lookup_processor_type)    stmfd   sp!, {r4 - r7, r9, lr}    mov r9, r0    bl  __lookup_processor_type    mov r0, r5    ldmfd   sp!, {r4 - r7, r9, pc}/* * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for * more information about the __proc_info and __arch_info structures. */    .long   __proc_info_begin    .long   __proc_info_end3:  .long   .    .long   __arch_info_begin    .long   __arch_info_end

在G:\linux-2.6.22.6\arch\arm\kernel\vmlinux.lds.S中

SECTIONS{ .......    . = PAGE_OFFSET + TEXT_OFFSET;/*TEXT_OFFSET=0x00000800*/    ......init : {           /* Init code and data       */......          __arch_info_begin = .;            *(.arch.info.init)/*  *表示所有的*/        __arch_info_end = .;

PAGE_OFFSET 在Linux/include/asm-arm/Memory.c下定义

/* * Page offset: 3GB */#ifndef PAGE_OFFSET#define PAGE_OFFSET     UL(0xc0000000)#endif

在内核中查找

grep ".arch.info.init" * -nR

在include/asm-arm/mach/arch.h中,

/* * 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             \};

关于MACHINE_START(_type,_name)的定义在include/configs/100ask24x0.h中

MACHINE_START(S3C2440, "SMDK2440")    /* Maintainer: Ben Dooks <ben@fluff.org> */    .phys_io    = S3C2410_PA_UART,    .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,    .boot_params    = S3C2410_SDRAM_PA + 0x100,/*0x30000100*/    .init_irq   = s3c24xx_init_irq,    .map_io     = smdk2440_map_io,    .init_machine   = smdk2440_machine_init,    .timer      = &s3c24xx_timer,MACHINE_END

两个定义带入

static const struct machine_desc __mach_desc_S3C2440\ __used                         \ __attribute__((__section__(".arch.info.init"))) = {    \    .nr     = MACH_TYPE_S3C2440,        \    .name       = "SMDK2440",    .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     = smdk2440_map_io,    .init_machine   = smdk2440_machine_init,    .timer      = &s3c24xx_timer,    };

machine_desc 结构体在linux/asm-arm/arch/Arch.h中

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);};

(2)判断是否支持该单板

bl  __lookup_machine_type       @ r5=machinfo

(3)创建页表,由于链接地址是虚拟地址,需要创建页表与实际内存进行对应。

bl  __create_page_tables

(4)使能mmu

    adr lr, __enable_mmu        @ return (PIC) address    add pc, r10, #PROCINFO_INITFUNC

(5)跳转到secondary_start_kernel

b   secondary_start_kernel

函数在Linux/arch/arm/kernel/smp.c

asmlinkage void __cpuinit secondary_start_kernel(void){}

Linux内核启动的第二阶段在
Linux/init/main.C
主要函数

asmlinkage void __init start_kernel(void){printk(linux_banner);setup_arch(&command_line);/*解析uboot传入的启动参数*/setup_command_line(command_line);/*解析uboot传入的启动参数*/parse_early_param();console_init();.......    rest_init();}

在该函数中首先打印内核版本信息,

printk(linux_banner);

设置与体系结构相关的环境

setup_arch(&command_line);/*解析uboot传入的启动参数*/setup_command_line(command_line);/*解析uboot传入的启动参数*/

在arch/arm/kernel/setup.c中

void __init setup_arch(char **cmdline_p){    struct tag *tags = (struct tag *)&init_tags;    struct machine_desc *mdesc;/*machine_desc 为前面linux/asm-arm/arch/Arch.h中定义的结构体*/    char *from = default_command_line;    setup_processor();    mdesc = setup_machine(machine_arch_type);    machine_name = mdesc->name;    /*.name     = "SMDK2440",*/    if (mdesc->soft_reboot)        reboot_setup("s");    if (mdesc->boot_params)        tags = phys_to_virt(mdesc->boot_params);/*  .boot_params    = S3C2410_SDRAM_PA + 0x100,=0x30000100,也是uboot启动的最后theKernel (0, bd->bi_arch_number, bd->bi_boot_params);(uboot/lib_arm/Armlinux.c)告诉内核的存放参数的地址gd->bd->bi_boot_params = 0x30000100(uboot/board/100ask24x0.c);*/
parse_early_param();

该函数同样在Linux/init/main.C下

void __init parse_early_param(void){    static __initdata int done = 0;    static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];    if (done)        return;    /* All fall through to do_early_param. */    strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);    parse_args("early options", tmp_cmdline, NULL, 0, do_early_param);    done = 1;}

初始化控制台

console_init();
原创粉丝点击