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,
        };
    我们发现这个是个类似数组的玩意。通过函数名,我们大概可以知道,就是一个个按顺序去调用这个数组里面的函数。

你可能感兴趣的:(SPL,Uboot)