Uboot 2017.01 啓動流程分析

前言

2017.01 UBoot包含兩個階段的啓動,一個是SPL啓動,一個是正常的啓動我們稱爲第二階段Uboot。當然,我們也可以選擇使用SPL和不使用。
在編譯的過程中,是先編譯第二階段Uboot,然後在編譯SPL的。這兩個階段的編譯時分離的。
擁有不同的配置,所以許多地方的宏是和SPL的不一樣。而且鏈接的文件也不一致。
所以接下來,我們也會分爲兩個部分進行分析。

SPL啓動

在AM437X平臺中,實際的SPL啓動就是MLO啓動階段。其運行空間爲片內DRAM,所以對其大小有較爲嚴格的限制。
通常使用SPL初始化基本設備後,將第二階段UBoot加載進行SDRAM中執行。

第一階段:和架構相關的初始化

其源碼位於arch/arm/cpu/armv7/start.s中。
其中關鍵的調用關係如下:
調用:cpu_init_crit
        b   lowlevel_init
            ==>arch/arm/cpu/armv7/lowlevel_init.S
                b s_init 

第二階段:和SoC底層相關的初始化

在第一階段初始化完成後,便調用了s_init函數,其實現的源碼是arch/arm/mach-omap2/an33xx/Borad.c。
該函數就調用了一個函數rtc_only();然後便返回到lowlevel_init函數中。
此時,lowlevel_init也已經完成他的任務,就返回到cpu_init_crit中。
然而,cpu_init_crit這個函數的工作也全都幹完了,便回到start.S中,然後便是
b _main

而這個_main函數位於:
arch/arm/lib/crt0.S中
在_main函數中,就調用了一個跨時代的函數。
board_init_f (arch/arm/mach-omap2/am33xx/Borad.c)

第三階段:板級初始化

在上面,我們可以知道 _main函數會調用board_init_f。實際上,這個函數便是和板級相關連的函數了。
然而,在SPL啓動中,他指向了下面的函數:

void board_init_f(ulong dummy)
{
    early_system_init();
    board_early_init_f();
    sdram_init();
    /* dram_init must store complete ramsize in gd->ram_size */
    gd->ram_size = get_ram_size(
            (void *)CONFIG_SYS_SDRAM_BASE,
            CONFIG_MAX_RAM_BANK_SIZE);
}

其中的sdram_ini(); 便是初始化SDRAM的關鍵代碼,所以,如果我們有自己的板子,我們也可以實現sdram_init函數來完成我們板子的SDRAM的初始化。

其調用關係如下:
board_init_f
==>early_system_init()
==>set_uart_mux_conf(); // 設置相應的PIN爲串口
==>board_early_init_f(); // 不同的板子可以實現這個函數
==>
==>sdram_init(); // 初始化SDRAM

執行完borad_init_f後,回到_main
這個時候,會去執行spl_relocate_stack_gd
重新定位在棧中的global_data。

如果定義了CONFIG_SPL_STACK_R,那麼就將之前在片內內存中的global_data對象重定位到SDRAM中,並且將棧指針指向SDRAM中的某一地址
最後,就會調用borad_init_r函數:

borad_init_r(common/spl/spl.c)
    ==>spl_board_init();    // 初始化串口
    ==> if (boot_from_devices(&spl_image, spl_boot_list,
                  ARRAY_SIZE(spl_boot_list))) {
                puts("SPL: failed to boot from all boot devices\n");
                hang();
            }
            =>遍歷loader,看哪個loader和設備匹配。
            ==> spl_load_image
                ==>spl_mmc_load_image
                    ==>spl_mmc_do_fs_boot
                        ==>spl_load_image_fat(uboot.img)    // 將uboot.img加載到內存,並進行解析

將鏡像加載到內存的時候,此時SPL進行判斷,加載的鏡像是uboot還是kernel然後便跳轉到鏡像的入口中進行執行。此時,SPL的使命便完成了
==>jump_to_image_no_args(&spl_image);

第二階段Uboot

此階段的UBoot是在內存中的了,內存已經初始化完畢,並且許多的引腳複用在上一個階段就已經複用完畢了。

在編譯的過程中,是先編譯第二階段Uboot,然後在編譯SPL的。這兩個階段的編譯時分離的。擁有不同的配置,所以許多地方的宏是和SPL的不一樣。而且
鏈接的文件也不一致。

通過驗證,就board_init_r函數而言,在SPL中是鏈接向spl.c中的board_init_r函數,而在Uboot中,是鏈接向board_r.c中的borad_init_r函數。

那麼,我們就從borad_init_r函數入手,看看第二階段的Uboot是怎麼啓動的。

在分析的過程中,
我們直接在board_init_r函數實現成一個死循環,但是發現還是有信息能夠輸出,說明在board_init_r還進行了初始化的工作。

    U-Boot 2017.01-g03781bc-dirty (Jul 14 2017 - 13:55:58 +0800)

    CPU  : AM437X-GP rev 1.2
    Model: TI AM437x GP EVM
    I2C:   ready
    DRAM:  1 GiB

我們發現,其實在board_init_f中都已經鏈接在別的文件中了,他是位於board_f.c文件中
    void board_init_f(ulong boot_flags)
    {
        #ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA
            /*
             * For some architectures, global data is initialized and used before
             * calling this function. The data should be preserved. For others,
             * CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack
             * here to host global data until relocation.
             */
            gd_t data;

            gd = &data;

            /*
             * Clear global data before it is accessed at debug print
             * in initcall_run_list. Otherwise the debug print probably
             * get the wrong value of gd->have_console.
             */
            zero_global_data();
        #endif

            gd->flags = boot_flags;
            gd->have_console = 0;

            if (initcall_run_list(init_sequence_f))
                hang();

        #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
                !defined(CONFIG_EFI_APP)
            /* NOTREACHED - jump_to_copy() does not return */
            hang();
        #endif
    }
其中關鍵的函數就是 initcall_run_list(init_sequence_f);
我們來看看init_sequence_f是個什麼東西。
static init_fnc_t init_sequence_f[] = {
            #ifdef CONFIG_SANDBOX
                setup_ram_buf,
            #endif
                setup_mon_len,
            #ifdef CONFIG_OF_CONTROL
                fdtdec_setup,   // 初始化FDT
            #endif
            #ifdef CONFIG_TRACE
                trace_early_init,
            #endif
                initf_malloc,
                initf_console_record,
            #if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx)
                /* TODO: can this go into arch_cpu_init()? */
                probecpu,
            #endif
            #if defined(CONFIG_X86) && defined(CONFIG_HAVE_FSP)
                x86_fsp_init,
            #endif
                arch_cpu_init,      /* basic arch cpu dependent setup */
                mach_cpu_init,      /* SoC/machine dependent CPU setup */
                initf_dm,
                arch_cpu_init_dm,
                mark_bootstage,     /* need timer, go after init dm */
            #if defined(CONFIG_BOARD_EARLY_INIT_F)
                board_early_init_f,
            #endif
                /* TODO: can any of this go into arch_cpu_init()? */
            #if defined(CONFIG_PPC) && !defined(CONFIG_8xx_CPUCLK_DEFAULT)
                get_clocks,     /* get CPU and bus clocks (etc.) */
            #if defined(CONFIG_TQM8xxL) && !defined(CONFIG_TQM866M) \
                    && !defined(CONFIG_TQM885D)
                adjust_sdram_tbs_8xx,
            #endif
                /* TODO: can we rename this to timer_init()? */
                init_timebase,
            #endif
            #if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || \
                    defined(CONFIG_BLACKFIN) || defined(CONFIG_NDS32) || \
                    defined(CONFIG_SH) || defined(CONFIG_SPARC)
                timer_init,     /* initialize timer */
            #endif
            #ifdef CONFIG_SYS_ALLOC_DPRAM
            #if !defined(CONFIG_CPM2)
                dpram_init,
            #endif
            #endif
            #if defined(CONFIG_BOARD_POSTCLK_INIT)
                board_postclk_init,
            #endif
            #if defined(CONFIG_SYS_FSL_CLK) || defined(CONFIG_M68K)
                get_clocks,
            #endif
                env_init,       /* initialize environment */
            #if defined(CONFIG_8xx_CPUCLK_DEFAULT)
                /* get CPU and bus clocks according to the environment variable */
                get_clocks_866,
                /* adjust sdram refresh rate according to the new clock */
                sdram_adjust_866,
                init_timebase,
            #endif
                init_baud_rate,     /* initialze baudrate settings */
                serial_init,        /* serial communications setup */
                console_init_f,     /* stage 1 init of console */
            #ifdef CONFIG_SANDBOX
                sandbox_early_getopt_check,
            #endif
                display_options,    /* say that we are here */
                display_text_info,  /* show debugging info if required */
            #if defined(CONFIG_MPC8260)
                prt_8260_rsr,
                prt_8260_clks,
            #endif /* CONFIG_MPC8260 */
            #if defined(CONFIG_MPC83xx)
                prt_83xx_rsr,
            #endif
            #if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_SH)
                checkcpu,
            #endif
                print_cpuinfo,      /* display cpu info (and speed) */
            #if defined(CONFIG_MPC5xxx)
                prt_mpc5xxx_clks,
            #endif /* CONFIG_MPC5xxx */
            #if defined(CONFIG_DTB_RESELECT)
                embedded_dtb_select,
            #endif
            #if defined(CONFIG_DISPLAY_BOARDINFO)
                show_board_info,
            #endif
                INIT_FUNC_WATCHDOG_INIT
            #if defined(CONFIG_MISC_INIT_F)
                misc_init_f,
            #endif
                INIT_FUNC_WATCHDOG_RESET
            #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)
                init_func_i2c,
            #endif
            #if defined(CONFIG_HARD_SPI)
                init_func_spi,
            #endif
                announce_dram_init,
                /* TODO: unify all these dram functions? */
            #if defined(CONFIG_ARM) || defined(CONFIG_X86) || defined(CONFIG_NDS32) || \
                    defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32) || \
                    defined(CONFIG_SH)
                dram_init,      /* configure available RAM banks */ // 設置一些gd相關的參數
            #endif
            #if defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_M68K)
                init_func_ram,
            #endif
            #ifdef CONFIG_POST
                post_init_f,
            #endif
                INIT_FUNC_WATCHDOG_RESET
            #if defined(CONFIG_SYS_DRAM_TEST)
                testdram,
            #endif /* CONFIG_SYS_DRAM_TEST */
                INIT_FUNC_WATCHDOG_RESET

            #ifdef CONFIG_POST
                init_post,
            #endif
                INIT_FUNC_WATCHDOG_RESET
                /*
                 * Now that we have DRAM mapped and working, we can
                 * relocate the code and continue running from DRAM.
                 *
                 * Reserve memory at end of RAM for (top down in that order):
                 *  - area that won't get touched by U-Boot and Linux (optional)
                 *  - kernel log buffer
                 *  - protected RAM
                 *  - LCD framebuffer
                 *  - monitor code
                 *  - board info struct
                 */
                setup_dest_addr,
            #if defined(CONFIG_BLACKFIN) || defined(CONFIG_XTENSA)
                /* Blackfin u-boot monitor should be on top of the ram */
                reserve_uboot,
            #endif
            #if defined(CONFIG_SPARC)
                reserve_prom,
            #endif
            #if defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_ADDR)
                reserve_logbuffer,
            #endif
            #ifdef CONFIG_PRAM
                reserve_pram,
            #endif
                reserve_round_4k,
            #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \
                    defined(CONFIG_ARM)
                reserve_mmu,
            #endif
            #ifdef CONFIG_DM_VIDEO
                reserve_video,
            #else
            # ifdef CONFIG_LCD
                reserve_lcd,
            # endif
                /* TODO: Why the dependency on CONFIG_8xx? */
            # if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \
                    !defined(CONFIG_ARM) && !defined(CONFIG_X86) && \
                    !defined(CONFIG_BLACKFIN) && !defined(CONFIG_M68K)
                reserve_legacy_video,
            # endif
            #endif /* CONFIG_DM_VIDEO */
                reserve_trace,
            #if !defined(CONFIG_BLACKFIN) && !defined(CONFIG_XTENSA)
                reserve_uboot,
            #endif
            #ifndef CONFIG_SPL_BUILD
                reserve_malloc,
                reserve_board,
            #endif
                setup_machine,
                reserve_global_data,
                reserve_fdt,
                reserve_arch,
                reserve_stacks,
                setup_dram_config,
                show_dram_config,
            #if defined(CONFIG_M68K) || defined(CONFIG_MIPS) || defined(CONFIG_PPC) || \
                defined(CONFIG_SH)
                setup_board_part1,
            #endif
            #if defined(CONFIG_PPC) || defined(CONFIG_M68K)
                INIT_FUNC_WATCHDOG_RESET
                setup_board_part2,
            #endif
                display_new_sp,
            #ifdef CONFIG_SYS_EXTBDINFO
                setup_board_extra,
            #endif
                INIT_FUNC_WATCHDOG_RESET
                reloc_fdt,
                setup_reloc,
            #if defined(CONFIG_X86) || defined(CONFIG_ARC)
                copy_uboot_to_ram,
                clear_bss,
                do_elf_reloc_fixups,
            #endif
            #if defined(CONFIG_XTENSA)
                clear_bss,
            #endif
            #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX)
                jump_to_copy,
            #endif
                NULL,
        };
    我們發現這個是個類似數組的玩意。通過函數名,我們大概可以知道,就是一個個按順序去調用這個數組裏面的函數。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章