U-boot编译成功后会在源码目录下生成链接脚本u-boot.lds,本文档从链接脚本开始逐步分析启动u-boot的过程。
vi u-boot.lds
ENTRY(_start):程序的入口点为_start_start定义在了arch/arm/lib/vectors.S:
SECTIONS中的对象按顺序依次存放在内存中。
.text
: 代码段。
*(.__image_copy_start)
标签不占内存空间,
*(.vectors)
初始化异常向量表,存放在开始位置,
arch/arm/cpu/armv7/start.o (.text*)
存放start.o
*(.text*)
最后存放其他的可执行文件。
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
只读数据段,使用SORT_BY_ALIGNMENT将rodata里的对象文件相应段对齐排列。
data
:data存储已经初始化的全局变量。
u_boot_list
存放了一些命令,在System.map中搜索“u_boot_list”可以看到。
后面的标签在u-boot.map和System.map中可以看到。
1 汇编阶段
从链接脚本可以知道,程序首先执行arch/arm/lib/vectors.S 的内容,
在vectors.S中使用“.globl _start”声明“_start”是全局变量,外部可访问,然后初始化异常向量表,54行跳转到reset函数,reset函数在arch/arm/cpu/armv7/start.S中声明,
在reset函数中跳转到save_boot_params ,save_boot_params定义在100行,功能是跳转到save_boot_params_ret,
save_boot_params_ret主要功能是保证 CPU在SVC模式,并禁止FIQ和IRQ中断。
往下是向量表重定位和SPL相关的设置,然后依次跳转到cpu_init_cp15,cpu_init_crit,_main。
在113行定义了cpu_init_cp15函数,此函数主要功能是关闭cache,MMU,TLB等,这些都是虚拟内存转化相关功能,uboot阶段使用的是物理内存。
关闭虚拟内存映射后跳转到cpu_init_crit函数,
cpu_init_crit跳转到lowlevel_init,查找函数”lowlevel_init”
lowlevel_init定义在arch/arm/cpu/armv7/lowlevel_init.S文件,
在lowlevel_init.S中,设置栈顶指针,CONFIG_SYS_INIT_SP_ADDR 加载Uboot第二阶段(C语言阶段)代码代码到RAM,文件头部写着如下信息,说明设置堆栈,为调用C函数进行进一步初始化,:
s_init为空函数
cpu_init_cp15和cpu_init_crit函数执行完后,开始执行_main函数,通过在u-boot.map找到查找,_main函数在arch/arm/lib/crt0.S
打开crt0.S:
函数_main中清除bss段,跳转到board_init_f,
然后跳转到board_init_r
2 C语言阶段
uboot程序在汇编阶段跳转到board_init_f函数,此函数在common/board_f.c 中有如下定义:
void board_init_f(ulong boot_flags)
{
#ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA
/*
* For some archtectures, 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 vaule 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
}
在函数board_init_f中依次调用init_sequence[]中的函数,init_sequence[],此数组存放着一些初始化函数,在common/board_f.c中定义:
static init_fnc_t init_sequence_f[] = {
#ifdef CONFIG_SANDBOX
setup_ram_buf,
#endif
setup_mon_len,
#ifdef CONFIG_OF_CONTROL
fdtdec_setup,
#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
setup_machine,
reserve_global_data,
reserve_fdt,
reserve_arch,
reserve_stacks,
setup_dram_config,
show_dram_config,
display_new_sp,
#ifdef CONFIG_SYS_EXTBDINFO
setup_board_extra,
#endif
INIT_FUNC_WATCHDOG_RESET
reloc_fdt,
setup_reloc,
.............................