Uboot启动流程
链接脚本uboot.lds内容如下:
进入.text段(代码段)
(1)设置_image_copy_start地址(uboot),用户代码的起始地址,也是uboot.imx去掉IVT,DCD,bootdata之后的起始地址。
IVT:保存一系列地址信息,如uboot开始地址
DCD:保存硬件初始化信息,如DDR3初始化,时钟初始化
bootdata:保存uboot的启动参数
(2)设置中断向量表起始地址
(3)设置start.o起始地址
(4)设置rel地址,地址偏移量??
(5)设置bss段开始、结束地址。bss段:未初始化数据段,存放未初始化的全局变量和静态变量,并清零
按顺序调用接下来的各个函数来启动uboot。
1._start
查看start.s:
(1)设置SVC模式(超级用户模式),只有满足SVC模式才能启动kernel关闭IRQ和FIQ(快速中断)
save_boot_params_ret:
/*
* 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
(2)将SCTLR中的第13位清零,即V=0。V=1的时候不可以重定位中断向量表。之后重定位中断向量表,将中断向量表设置在_start(程序起始地址)
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
/* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */
mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTLR Register
bic r0, #CR_V @ V = 0
mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTLR Register
/* Set vector address in CP15 VBAR register */
ldr r0, =_start
mcr p15, 0, r0, c12, c0, 0 @Set VBAR
#endif
(3)初始化CP15,初始化部分很长,失能MMU(内存管理单元)和TLBs(页表)
MMU:将虚拟地址映射到物理地址,管理物理存储器,虚拟存储器的单元。如果要取一个虚拟地址的值也需要MMU进行转换。
TLBs:快表,会将最近的页表保存下来,方便下一次查找。
页表:页表一般大小为4K,存放了下一级页表的地址,最后一级页表存放物理地址
可以看这篇文章:https://blog.csdn.net/KUNPLAYBOY/article/details/121157550
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15
bl cpu_init_crit
#endif
bl _main
(4)进入lowlevel_init:bl cpu_init_crit直接跳到lowlevel_init
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)
2.lowlevel_init
设置SP指针,堆栈指针,这个堆栈不是保存用户运行的栈,只是临时堆栈,初始化SP指针后就可以使用C函数了,这里的SP指针并不指向uboot启动后的位置。只是为了使用s_init函数
ENTRY(lowlevel_init)
/*
* Setup a temporary stack. Global data is not available yet.
*/
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
之后跳转至s_init:设置DRAM和global_data结构体,清除bss段
/*
* Call the very early init function. This should do only the
* absolute bare minimum to get started. It should not:
*
* - set up DRAM
* - use global_data
* - clear BSS
* - try to start a console
*
* For boards with SPL this should be empty since SPL can do all of
* this init in the SPL board_init_f() function which is called
* immediately after this.
*/
bl s_init
pop {ip, pc}
ENDPROC(lowlevel_init)
3.s_init
在soc.c中,基本算是空函数
void s_init(void)
{
struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR;
struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
u32 mask480;
u32 mask528;
u32 reg, periph1, periph2;
if (is_cpu_type(MXC_CPU_MX6SX) || is_cpu_type(MXC_CPU_MX6UL) ||
is_cpu_type(MXC_CPU_MX6ULL) || is_cpu_type(MXC_CPU_MX6SLL))
return;
4._main
从s_init跳出来之后,会跳回到lowlevel_init之后因为lowlevel_init没内容执行一直跳回至cpu_init_crit中,之后执行_main
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15
bl cpu_init_crit
#endif
bl _main
在crt0.S中
(1)初始化C运行环境,包括重新设置sp指针。
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr sp, =(CONFIG_SPL_STACK)
#else
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
#if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */
mov r3, sp
bic r3, r3, #7
mov sp, r3
#else
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
#endif
(2设置sp指针,为板子初始化进行配置。
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
#endif
mov r0, sp
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
(3)调用 board_init_f_alloc_reserve保存一个名为global_data的结构体。global_data这个结构体,定义在了r9上
ulong board_init_f_alloc_reserve(ulong top)
{
/* Reserve early malloc arena */
#if defined(CONFIG_SYS_MALLOC_F)
top -= CONFIG_SYS_MALLOC_F_LEN;
#endif
/* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
top = rounddown(top-sizeof(struct global_data), 16);
return top;
}
(4)调用board_init_f_init_reserve,更新gd指针,指向下一个内存区
#ifdef _USE_MEMCPY
memset(gd_ptr, '\0', sizeof(*gd));
#else
for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); )
*ptr++ = 0;
#endif
/* set GD unless architecture did it already */
#if !defined(CONFIG_ARM)
arch_setup_gd(gd_ptr);
#endif
gd里面存了:各个块的内存大小,RAM的大小和位置,malloc分配的内存在哪里,bss段在哪里,当前是boot模式还是kernel模式。目的就是从0x80000000开始将内存区分配好。
(5)board_init_f函数。用于初始化开发板,会使用initcall_run_list调用一系列函数:
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();
下面是init_sequece_f中的内容,用于配置开发板:
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
#if defined(CONFIG_X86) && defined(CONFIG_HAVE_FSP)
x86_fsp_init,
#endif
arch_cpu_init, /* basic arch cpu dependent 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,
后面还有很多函数,这里只截取了一小部分。主要就是配置开发板初始化的函数。
包括:
1.设置内存长度,根据uboot的配置来分配bss段的大小,代码段大小
2.设置malloc动态存储区的大小
3.设置控制台的记录
4.初始化cpu,和设备模式
5.配置波特率,用于初始化控制台
6.设置监测代码,RAM保护区的位置,为uboot保留4kb的内存,为malloc内存确定分配地址,分配一个board_info结构体的内存,配置RAM的大小,配置中断向量表位置和大小。
(6)relocate_vectors函数,重新定位中断向量表
ENTRY(relocate_vectors)
#ifdef CONFIG_CPU_V7M
/*
* On ARMv7-M we only have to write the new vector address
* to VTOR register.
*/
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
ldr r1, =V7M_SCB_BASE
str r0, [r1, V7M_SCB_VTOR]
#else
#ifdef CONFIG_HAS_VBAR
/*
* If the ARM processor has the security extensions,
* use VBAR to relocate the exception vectors.
*/
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
mcr p15, 0, r0, c12, c0, 0 /* Set VBAR */
#else
/*
* Copy the relocated exception vectors to the
* correct address
* CP15 c1 V bit gives us the location of the vectors:
* 0x00000000 or 0xFFFF0000.
*/
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
mrc p15, 0, r2, c1, c0, 0 /* V bit (bit[13]) in CP15 c1 */
ands r2, r2, #(1 << 13)
ldreq r1, =0x00000000 /* If V=0 */
ldrne r1, =0xFFFF0000 /* If V=1 */
ldmia r0!, {r2-r8,r10}
stmia r1!, {r2-r8,r10}
ldmia r0!, {r2-r8,r10}
stmia r1!, {r2-r8,r10}
#endif
#endif
bx lr
ENDPROC(relocate_vectors)
(7)led_on和board_init_r函数,点亮板子上的led灯,和运行uboot
README中的描述
board_init_f():
- purpose: set up the machine ready for running board_init_r():
i.e. SDRAM and serial UART
- global_data is available
- stack is in SRAM
- BSS is not available, so you cannot use global/static variables,
only stack variables and global_data
Non-SPL-specific notes:
- dram_init() is called to set up DRAM. If already done in SPL this
can do nothing
SPL-specific notes:
- you can override the entire board_init_f() function with your own
version as needed.
- preloader_console_init() can be called here in extremis
- should set up SDRAM, and anything needed to make the UART work
- these is no need to clear BSS, it will be done by crt0.S
- must return normally from this function (don't call board_init_r()
directly)
Here the BSS is cleared. For SPL, if CONFIG_SPL_STACK_R is defined, then at
this point the stack and global_data are relocated to below
CONFIG_SPL_STACK_R_ADDR. For non-SPL, U-Boot is relocated to run at the top of
memory.
boad_init_f:在board_init_f中不需要清除bss段,主要就是加载DRAM,使用SPL(二级程序加载器)将程序加载进DRAM中,相当于uboot的boot。
board_init_r():
- purpose: main execution, common code
- global_data is available
- SDRAM is available
- BSS is available, all static/global variables can be used
- execution eventually continues to main_loop()
Non-SPL-specific notes:
- U-Boot is relocated to the top of memory and is now running from
there.
SPL-specific notes:
- stack is optionally in SDRAM, if CONFIG_SPL_STACK_R is defined and
CONFIG_SPL_STACK_R_ADDR points into SDRAM
- preloader_console_init() can be called here - typically this is
done by defining CONFIG_SPL_BOARD_INIT and then supplying a
spl_board_init() function containing this call
- loads U-Boot or (in falcon mode) Linux
board_init_r:主要用于执行开发板的初始化,加载uboot或者linux,清除bss段,并且进入main_loop
main_loop:即uboot的界面程序,是移植好的uboot程序。
通过board_init_f和board_init_r我们就能将uboot初始化和运行,至此uboot启动流程结束