2017.9版本uboot启动至命令行几个重要函数为:_start,_main,board_init_f,relocate_code,board_init_r。
对于任何程序,入口函数是在链接时决定的,uboot的入口是由链接脚本决定的。uboot下armv7链接脚本默认目录为u-boot-2017.09\arch\arm\cpu\u-boot.lds。
由链接文件u-boot.lds的ENTRY(_start)可知,程序的入口在_start。
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
......
. = 0x00000000;
. = ALIGN(4);
.text :
{
*(.__image_copy_start)
*(.vectors)
CPUDIR/start.o (.text*)
*(.text*)
}
在u-boot-2017.09版本中,入口_start在u-boot-2017.09\arch\arm\lib\vectors.S中,
_start:
#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
.word CONFIG_SYS_DV_NOR_BOOT_CFG
#endif
b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
在vectors.S中,可以看到,从_start入口进入后,立刻跳转到reset去执行。reset在u-boot-2017.09\arch\arm\cpu\armv7\start.S中。
在start.S中,顺序执行以下操作:
(1)单板保存一些boot重要参数
reset:
/* Allow the board to save important registers */
b save_boot_params
save_boot_params_ret:
#ifdef CONFIG_ARMV7_LPAE /* 未定义,因此实际上什么也没做 */
/*
* check for Hypervisor support
*/
mrc p15, 0, r0, c0, c1, 1 @ read ID_PFR1
and r0, r0, #CPUID_ARM_VIRT_MASK @ mask virtualization bits
cmp r0, #(1 << CPUID_ARM_VIRT_SHIFT)
beq switch_to_hypervisor
switch_to_hypervisor_ret:
#endif
......
ENTRY(save_boot_params)
b save_boot_params_ret @ back to my caller
ENDPROC(save_boot_params)
.weak save_boot_params
(2)屏蔽中断,设置CPU进入SVC32模式
/*
* disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
* except if in HYP mode already
*/
mrs r0, cpsr
and r1, r0, #0x1f @ mask mode bits
teq r1, #0x1a @ test for HYP mode
bicne r0, r0, #0x1f @ clear all mode bits
orrne r0, r0, #0x13 @ set SVC mode
orr r0, r0, #0xc0 @ disable FIQ and IRQ
msr cpsr,r0
(3)配置cp15,初始化mmu cache tlb;然后cpu_init_crit调用lowlevel_init(与特定开发板相关的初始化函数)函数,在这个函数里会做一些pll和memory的初始化;从cpu_init_crit返回后,_start的工作基本完成,接下来就是调用_main。
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15
#ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
bl cpu_init_crit
#endif
#endif
bl _main
......
ENTRY(cpu_init_crit)
/*
* Jump to board specific initialization...
* The Mask ROM will have already initialized
* basic memory. Go here to bump up clock rate and handle
* wake up conditions.
*/
b lowlevel_init @ go setup pll,mux,memory
ENDPROC(cpu_init_crit)
_main函数在u-boot-2017.09\arch\arm\lib\crt0.S中,代码如下,其中有三个比较重要的函数:
(1)board_init_f
从_main处顺序执行,遇到的第一个重要的函数就是board_init_f。之前部分代码的作用是设置运行环境,为调用board_init_f做准备。
board_init_f函数在common/board_f.c 里面;函数通过调用initcall_run_list(init_sequence_f)执行一系列前半部分板级的初始化工作;
ENTRY(_main)
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr r0, =(CONFIG_SPL_STACK)
#else
ldr r0, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
bic r0, r0, #7 /* 8-byte alignment for ABI compliance */
mov sp, r0
bl board_init_f_alloc_reserve
mov sp, r0
/* set up gd here, outside any C code */
mov r9, r0
bl board_init_f_init_reserve
mov r0, #0
bl board_init_f /* u-boot-2017.05\common\board_f.c */
(2)relocate_code
第二部分重定位u-boot代码,复制u-boot代码到SDRAM中;
relocate_code定义在u-boot-2017.09\arch\arm\lib\relocate.S中;
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
b relocate_code /* u-boot-2017.05\arch\arm\lib\relocate.S */
(3)board_init_r:
第三部分跳转到board_init_r,board_init_r在common\board_r.c中。
在board_init_r中通过initcall_run_list(init_sequence_r)执行一系列后半部分的板级初始化函数。在这些板级初始化函数 最后调用的是run_main_loop函数 ,经过 main_loop函数后不再返回。
main_loop函数在common\main.c中,其主要作用是进入命令行,接收并执行命令。
#if ! defined(CONFIG_SPL_BUILD)
bl coloured_LED_init
bl red_led_on
#endif
/* call board_init_r(gd_t *id, ulong dest_addr) */
mov r0, r9 /* gd_t */
ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
/* call board_init_r */
#if CONFIG_IS_ENABLED(SYS_THUMB_BUILD)
ldr lr, =board_init_r /* this is auto-relocated! */
bx lr
#else
ldr pc, =board_init_r /* u-boot-2017.05\common\board_r.c:this is auto-relocated! */
#endif
/* we should not return here. */
#endif
至此基本完成uboot的一个启动过程。
参考博文:https://blog.csdn.net/qq_38144425/article/details/73460095(2017.05版,分析过程简练)
https://blog.csdn.net/skyflying2012/article/details/25804209(2014.04版,分析得很深入)
两位写的非常好,值得好好学习!