#最小嵌入式Linux系統#uboot啓動分析



u-boot啓動分析


1.完成配置任務後,再一次make

$(obj)u-boot:  depend \
        $(SUBDIR_TOOLS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds

//start.o是第一個依賴文件,因此先分析start.s
OBJS  = $(CPUDIR)/start.o 
//u-boot.lds作爲最後一個依賴項,負責鏈接任務


2.分析/arch/arm/cpu/armv7/start.s

/* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f:
    ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
    bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
    ldr r0,=0x00000000
#ifdef CONFIG_NO_CODE_RELOC 
    bl relocate_data
    ldr r0,=0x00000000
#endif  
    bl  board_init_f

2.函數board_init_f在/arch/arm/lib/board.c,分析board_init_f     //CONFIG_SYS_TEXT_BASE = 0xF0000000 (in board/broadcom/bcm95340x)

board_init_f
    bootstage_mark_name //進行標記,若失敗,可定位錯誤
    gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07) //給gd分配空間
    /*
        給gd賦值,用於存儲各種資源信息,如串口等
    */

    /*以下爲一系列的初始化函數*/
    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
        if ((*init_fnc_ptr)() != 0) {
            hang ();
        }
    }

    展開可得:
    ...
    serial_init
    ...
    display_banner
        debug("U-Boot code: %08lX -> %08lX  BSS: -> %08lX\n",_TEXT_BASE,_bss_start_ofs + _TEXT_BASE, _bss_end_ofs + _TEXT_BASE); //U-Boot code: F0000000 -> F0066A10  BSS: -> F0073048
    ...
    ...
    ...
    dram_init();
            ddr_init2();
                    printf("DEV ID = 0x%x\n", dev_id);
                    printf("SKU ID = 0x%x\n", sku_id);
                    printf("DDR type: DDR%d\n", (ddr_type == 1)? 3 : 4);
                    printf("MEMC 0 DDR speed = %dMHz\n", ddr_clk);
                    printf("PHY revision version: 0x%08x\n", val);  ///* Wait for DDR PHY up */
                    printf("ddr_init2: Calling soc_and28_shmoo_dram_info_set\n");
                    soc_and28_shmoo_dram_info_set();
                    printf("ddr_init2: Calling soc_and28_shmoo_phy_init\n");
                    soc_and28_shmoo_phy_init();
                    switch(ddr_clk) {
                        ddr_init_regs
                    }
                    printf("DDR Interface Ready\n");    

    /*以上爲一系列的初始化函數*/

    dram_init_banksize();   /* Ram ist board specific, so move it to board code ... */
            //in /arch/arm/lib
            void dram_init_banksize(void)
            __attribute__((weak, alias("__dram_init_banksize")));

            void __dram_init_banksize(void)
            {
                gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE;
                gd->bd->bi_dram[0].size =  gd->ram_size;
            }
    display_dram_config();

    relocate_code();
    board_init_r
        bootstage_mark_name
        board_init  //芯片相關初始化
            gd->bd->bi_arch_number = CONFIG_MACH_TYPE;      /* board id for linux */
            gd->bd->bi_boot_params = LINUX_BOOT_PARAM_ADDR; /* adress of boot parameters */  //#define LINUX_BOOT_PARAM_ADDR 0x60200000 (in greyhound.h)
        serial_initialize
        debug("Now running in RAM - U-Boot at: %08lx\n", dest_addr);

        puts("Flash: ");
        flash_size = flash_init();
        puts("PNOR flash is not present - switch decoding bit back for NAND\n");

        #if defined(CONFIG_CMD_NAND)
        puts("NAND:  ");
        nand_init();        /* go init the NAND */  
            nand_init_chip
                board_nand_init
                    iproc_nand_init
                        iproc_nand_config
                            for (i = 0; i < IPROC_NAND_CHIP_LIST_COUNT; i++) {   //選擇Micron下不同型號的芯片
                                uint32_t devid;
                                struct iproc_nand_chips *ch;

                                ch = &iproc_chip_list[i];
                                devid = ch->id[0] << 24 | ch->id[1] << 16 | ch->id[2] << 8 | ch->id[3];
                                if (devid == inand[cs].device_id) {
                                    inand[cs].chip_ptr = &iproc_chip_list[i];
                                    break;
                                }
                            }
            printf("%lu MiB\n", total_nand_size / 1024);
        #endif

        #ifdef CONFIG_GENERIC_MMC  //MMC卡 multi-media card 類似於SD卡
        puts("MMC:   ");
        mmc_initialize(gd->bd);
        #endif

        env_relocate();
            env_relocate_spec
                spi_flash_probe 
                    for (i = 0; i < ARRAY_SIZE(flashes); ++i)   //選擇SPI_FLASH芯片廠商
                        if (flashes[i].shift == shift && flashes[i].idcode == *idp) {
                            /* we have a match, call probe */
                            flash = flashes[i].probe(spi, idp);  //將進入對應廠商的xxx_probe函數,裏面又將選擇不同的芯片型號
                            if (flash)
                                break;
                    }
                    printf("SF: Detected %s with page size ", flash->name);

        stdio_init(); //標準輸入輸出初始化
        console_init_r();
            stdio_print_current_devices
                puts("In:    ");
                puts("Out:   ");
                puts("Err:   ");    

        #if defined(CONFIG_USE_IRQ)
        /* set up exceptions */
        interrupt_init();
        /* enable exceptions */
        enable_interrupts();
        #endif

        #ifdef CONFIG_BOARD_LATE_INIT
        board_late_init();
            iproc_clk_enum();
                printf("arm_clk=%dMHz, axi_clk=%dMHz, apb_clk=%dMHz, arm_periph_clk=%dMHz\n", 
            save_shmoo_to_flash();
                soc_and28_shmoo_ctl
                 printf("DDR Tuning Complete\n");       
        #endif

        #if defined(CONFIG_CMD_NET)
        puts("Net:   ");
        eth_initialize(gd->bd);

        printf("========== relocate address: 0x%lx, offset 0x%lx ==========\n",id->relocaddr, id->relocaddr - 0x1e000000);

        /* main_loop() can return to retry autoboot, if so just run it again. */
        for (;;) {
            main_loop();
        }

3.分析main_loop

main_loop()
    s = getenv ("bootcmd");

    if (bootdelay != -1 && s && !abortboot(bootdelay)) {
    # ifdef CONFIG_AUTOBOOT_KEYED
            int prev = disable_ctrlc(1);    /* disable Control C checking */
    # endif

    run_command_list(s, -1, 0);   //執行bootcmd對應的處理函數

    # ifdef CONFIG_AUTOBOOT_KEYED
            disable_ctrlc(prev);    /* restore Control C checking */
    # endif
    }       

    for (;;) {   //等待輸入命令,然後執行對應的處理函數
        len = readline (CONFIG_SYS_PROMPT); 
        ...
        strcpy (lastcommand, console_buffer);
        ...
        rc = run_command(lastcommand, flag);  
    }

4.分析run_command

run_command     
    builtin_run_command
        cmd_process


5.分析cmd_process()是如何根據輸入的命令找到對應的處理函數的,下面以ping命令爲例   

cmd_process
    find_cmd
        int len = &__u_boot_cmd_end - &__u_boot_cmd_start;
        return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
    cmd_call


#if defined(CONFIG_CMD_PING)
int do_ping (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    ....
}

U_BOOT_CMD(   //將U_BOOT_CMD展開可知,其屬性爲.u-boot.cmd,在u-boot.lds時,把他們放在從__u_boot_cmd_start到__u_boot_cmd_end的存儲空間
    ping,   2,  1,  do_ping,
    "send ICMP ECHO_REQUEST to network host",
    "pingAddress"
);
#endif  

6.分析 bootm

do_bootm
    bootm_start
        boot_get_kernel
            image_get_kernel
                image_print_contents
                    printf("%sImage Name:   %.*s\n"
                    printf("%sData Size:
                    ...
                puts("   Verifying Checksum ... ");  puts("OK\n");
        boot_get_ramdisk //虛擬硬盤
    bootm_load_os
        printf("   XIP %s ... ", type_name);   puts("OK\n");
    boot_fn = boot_os[images.os.os];   //根據os匹配得 boot_fn = do_bootm_linux
    boot_fn(0, argc, argv, &images); //即do_bootm_linux(0, argc, argv, &images);
        boot_prep_linux(images);    
            #ifdef CONFIG_CMDLINE_TAG
            char *commandline = getenv("bootargs");
            printf("%s commandline: %s\n", __FUNCTION__, commandline);
            #endif

            debug("using: ATAGS\n");
            setup_start_tag(gd->bd);

            #ifdef CONFIG_CMDLINE_TAG
            setup_commandline_tag(gd->bd, commandline);
            #endif

            setup_end_tag(gd->bd);                  
        boot_jump_linux(images);
            kernel_entry = (void (*)(int, int, uint))images->ep;  //Kernel入口地址
            s = getenv("machid"); //機器碼
            announce_and_cleanup();
                printf("\nStarting kernel ...\n\n");
            cleanup_before_linux();     
            r2 = gd->bd->bi_boot_params;
            kernel_entry(0, machid, r2);  //轉向Kernel執行,在arm架構中,函數傳遞參數的用寄存器r1、r2、r3保存

7.以下開始分析Kernel啓動過程,同樣的,首先分析 kernel/Makefile,觀察先後順序,發現$(head-y)在最前面,通過grep,找到arch/arm/kernel/head.s ,開始分析head.s

head.s  //分析寄存器r1、r2、r3中保存的函數參數
    start_kernel

8.分析 start_kernel (in linux/init/main.c)

start_kernel
    printk(KERN_NOTICE "%s", linux_banner);
    setup_arch(&command_line);
        setup_processor();
            lookup_processor_type //得到cpu相關信息
        machine_desc = setup_machine_tags(machine_arch_type);
            /*通過輪詢由MACHINE_START定義的內容,得到對應的板級信息*/
                        for (p = __arch_info_begin; p < __arch_info_end; p++) /*由MACHINE_START定義 __arch_info_begin */
                        ... 
                        strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
        strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
        *cmdline_p = cmd_line;   //此時command_line被賦值爲了uboot傳過來的Bootargs 
        parse_early_param();        
            parse_early_options
                do_early_param
                    for (p = __setup_start; p < __setup_end; p++) {   //執行__setup屬性的函數
                        root_dev_setup、mtdpart_setup等
    parse_early_param();  //若以上已經完成,則跳過
    parse_args("Booting kernel", static_command_line, __start___param,  
           __stop___param - __start___param,
           -1, -1, &unknown_bootoption);    //對unknown_bootoption進行處理,怎麼處理還未細看
    ...
    console_init();
    ... 
    rest_init();
        kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
        ...
        cpu_idle(); //把cpu讓給kernel_init

9.分析 kernel_init

kernel_init
    do_basic_setup();
        ...
        driver_init();
        ...
        do_initcalls(); //根據優先級
                for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
                    do_initcall_level(level);       //
                    包括:late_initcall(northstar_mtd_setup);   arch_initcall(customize_machine)等
                            northstar_mtd_setup
                                brcm_setup_spi_master
                                brcm_setup_spi_flash
                                    master = spi_busnum_to_master(bus_num);  //bus_num爲1,所有maser返回的是spi1
                                    spi_new_device
                                        spi_add_device
                                            dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),spi->chip_select); //該處組合成spi1.0

    if (!ramdisk_execute_command)
    ramdisk_execute_command = "/init";

    if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
        ramdisk_execute_command = NULL;
        prepare_namespace();
            if (saved_root_name[0]) {
                if (!strncmp(root_device_name, "mtd", 3) ||
                    !strncmp(root_device_name, "ubi", 3)) {
                    mount_block_root(root_device_name, root_mountflags);
                        get_fs_names(fs_names);
                            get_filesystem_list  //從已經註冊的filesytems中選擇對用的
                        do_mount_root
                            sys_mount(name, "/root", fs, flags, data);
                                printk(KERN_INFO"VFS: Mounted root (%s filesystem)%s on device %u:%u.\n",
                goto out;
            ...
            mount_root();
                #ifdef CONFIG_ROOT_NFS
                if (ROOT_DEV == Root_NFS) {
                    if (mount_nfs_root())
                        return;

                    printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
                    ROOT_DEV = Root_FD0;
                }
                }   
                #endif
    ...     
    if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
        printk(KERN_WARNING "Warning: unable to open an initial console.\n");

    (void) sys_dup(0);
    (void) sys_dup(0);
    ...
    init_post();
        if (execute_command) {   //execute_command在init_setup()中賦值  __setup("init=", init_setup); 
            run_init_process(execute_command);

        //如果沒有指定Init=,則運行默認腳本   
        run_init_process("/sbin/init");
        run_init_process("/etc/init");
        run_init_process("/bin/init");
        run_init_process("/bin/sh");
        panic("No init found.  Try passing init= option to kernel. "
          "See Linux Documentation/init.txt for guidance.");


m25p_probe //此處爲何時調用尚不可知
    mtd_device_parse_register   
        parse_mtd_partitions
            parse_cmdline_partitions  //分析bootargs中“mtdparts=”內容,構建傳入的flash分區表
                if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id)))  //與dev_set_name組合成的spi1.0進行匹配


//iproc         MACH_IPROC      IPROC           4735(0x127f)        
MACHINE_START(IPROC, "Broadcom iProc")      

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章