_main函数主要完成的工作:
- 先设置用于调用board_init_f()函数的初始环境,该环境仅仅是提供了堆栈和存储位置GD('global data')结构,两者都是位于可以使用的RAM(SRAM,locked cache...)中,在调用board_init_f()函数前,GD应该被清0;
- 调用board_init_f()函数,该函数的功能为从system RAM(DRAM,DDR...)中执行准备硬件,当system RAM还不能够使用的时,必须要使用目前的GD存储传递到后续阶段的所有数据,这些数据包括重定位的目标,将来的堆栈和GD的位置;
- 设置中间环境,其中堆栈和GD是由board_init_f()函数在system RAM中进行分配的,但此时的bss和初始化的非常量仍然不能使用;
- 对于正常的uboot引导(非SPL),调用relocate_code()函数,该函数的功能将uboot从当前的位置重新转移到由board_init_f()函数计算的目标位置;
- 对于SPL,board_init_f()函数仅仅是返回(crt0),没有代码的重定位;
- 设置用于调用board_init_r()函数的最终环境,该环境将有bss段(初始化为0),初始化非常量数据(初始化为预期值),并入栈到system RAM中,GD保留了board_init_f()函数设置的值;
- 为了使uboot正常运行(非SPL),某些CPU还有一些关于内存的工作要做,调用c_runtime_cpu_setup()函数;
- 调用board_init_r()函数。
/* * Set up initial C runtime environment and call board_init_f(0). */
#if defined(CONFIG_TPL_BUILD) && defined(CONFIG_TPL_NEEDS_SEPARATE_STACK) ldr r0, =(CONFIG_TPL_STACK) #elif 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
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_EARLY_BSS) CLEAR_BSS #endif
mov r0, #0 bl board_init_f
#if ! defined(CONFIG_SPL_BUILD)
/* * Set up intermediate environment (new sp and gd) and call * relocate_code(addr_moni). Trick here is that we'll return * 'here' but relocated. */
ldr r0, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */ bic r0, r0, #7 /* 8-byte alignment for ABI compliance */ mov sp, r0 ldr r9, [r9, #GD_NEW_GD] /* r9 <- gd->new_gd */
adr lr, here ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */ add lr, lr, r0 #if defined(CONFIG_CPU_V7M) orr lr, #1 /* As required by Thumb-only */ #endif ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */ b relocate_code here: /* * now relocate vectors */
bl relocate_vectors
/* Set up final (full) environment */
bl c_runtime_cpu_setup /* we still call old routine here */ #endif #if !defined(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(FRAMEWORK)
#if !defined(CONFIG_SPL_BUILD) || !defined(CONFIG_SPL_EARLY_BSS) CLEAR_BSS #endif
# ifdef CONFIG_SPL_BUILD /* Use a DRAM stack for the rest of SPL, if requested */
//调用spl_relocate_stack_gd函数 重定位新的gd栈指针
//在定义了CONFIG_SPL_STACK_R等情况下,将片内SRAM中的GD复制到系统RAM中。
bl spl_relocate_stack_gd
cmp r0, #0 //如果spl_relocate_stack_gd返回为0,即未定义CONFIG_SPL_STACK_R,GD依旧在SRAM中
movne sp, r0 //如果r0不等于0,则sp=r0,即sp等于SDRAM中GD的地址
movne r9, r0 //如果r0不等于0,则r9=r0,即r9等于SDRAM中GD的地址#endif # endif
#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 /* this is auto-relocated! */ #endif /* we should not return here. */ #endif
ENDPROC(_main)
1、初始栈指针设置:
首先加载的栈地址,将其保存到r0中,对其进行16byte对齐后指向sp指针。在board_init_f_alloc_reserve之前保存sp指针栈到r0,作为函数的输入参数,函数调用结束时作为返回参数。同理 r0作为输入参数board_init_f_alloc_reserve执行后为保留区域清零,同时需要还原sp栈指针。2、board_init_f_alloc_reserve
从“top”(这里指sp指针)地址分配保留空间用作“全局变量”,返回分配空间的“top”地址(返回sp指针地址)
GD 在 16 字节边界上向下对齐。早期的 malloc area 未对齐,因此它遵循堆栈我们正在构建的架构的对齐约束。GD 是最后分配的,所以这个函数的返回值是保留区的底部和GD的地址,都应该调用上下文需要它。
top参数是汇编语言所对应的r0,作为指针为globle_data保留内存区域。内存分配在高位区域,保留区域内存指针减小。
ulong board_init_f_alloc_reserve(ulong top)
{
/* Reserve early malloc arena */
#if CONFIG_VAL(SYS_MALLOC_F_LEN)
top -= CONFIG_VAL(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;
}
3、board_init_f_init_reserve
board_init_f_alloc_reserve预留了global_data结构体空间,未进行初始化处理,board_init_f_init_reserve现在可以开始初始化gd结构了。board_init_f_init_stack_protection_addr为gd->start_addr_sp设置了栈指针一个保护边沿,然后调整向上以对齐16字节方式回到栈顶。将早期malloc空间的基地址保存到gd->malloc_base;board_init_f_init_stack_protection设置栈指针底部,保留20字节对于栈区进行memset设置。
void board_init_f_init_reserve(ulong base) { struct global_data *gd_ptr;/* * clear GD entirely and set it up. * Use gd_ptr, as gd may not be properly set yet. */
gd_ptr = (struct global_data *)base; /* zero the area */ memset(gd_ptr, '\0', sizeof(*gd)); /* set GD unless architecture did it already */ #if !defined(CONFIG_ARM) arch_setup_gd(gd_ptr); #endif
if (CONFIG_IS_ENABLED(SYS_REPORT_STACK_F_USAGE)) board_init_f_init_stack_protection_addr(base);
/* next alloc will be higher by one GD plus 16-byte alignment */ base += roundup(sizeof(struct global_data), 16);
/* * record early malloc arena start. * Use gd as it is now properly set for all architectures. */
#if CONFIG_VAL(SYS_MALLOC_F_LEN) /* go down one 'early malloc arena' */ gd->malloc_base = base; #endif
if (CONFIG_IS_ENABLED(SYS_REPORT_STACK_F_USAGE)) board_init_f_init_stack_protection(); }
4、CONFIG_SYS_MALLOC_F
如果定义了CONFIG_SYS_MALLOC_F,会先预留出early malloc所需的空间。会初始化gd->malloc_base。
5、设置初始的堆栈
基址由CONFIG_SYS_INIT_SP_ADDR定义。
6、清除BBS段
7、uboot的重定向动作
#if ! defined(CONFIG_SPL_BUILD) /* * Set up intermediate environment (new sp and gd) and call * relocate_code(addr_moni). Trick here is that we'll return * 'here' but relocated. */ ldr r0, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */ bic r0, r0, #7 /* 8-byte alignment for ABI compliance */ mov sp, r0 ldr r9, [r9, #GD_NEW_GD] /* r9 <- gd->new_gd */ @@ 把新的global_data地址放在r9寄存器中 adr lr, here ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */ add lr, lr, r0
@@ 计算返回地址在新的uboot空间中的地址。b调用函数返回之后,就跳到了新的uboot代码空间中。 #if defined(CONFIG_CPU_V7M) orr lr, #1 /* As required by Thumb-only */ #endif ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
@@ 把uboot的新的地址空间放到r0寄存器中,作为relocate_code的参数
b relocate_code
@@ 跳转到relocate_code中,在这里面实现了
@@ (1)relocate旧的uboot代码空间到新的空间上去
@@ (2)修改relocate之后全局变量的label
@@ 注意,由于上述已经把lr寄存器重定义到uboot新的代码空间中了,所以返回之后,就已经跳到了新的代码空间了!!!!!!
here:
/* * now relocate vectors */ bl relocate_vectors @@ relocate中断向量表 /* Set up final (full) environment */ bl c_runtime_cpu_setup /* we still call old routine here */ #endif
1)从global data中取出relocation之后的堆栈基址,16-byte对齐后,保存到sp中。
2)将新的global data的指针,保存在r9寄存器中。
3)计算relocation之后的执行地址(relocation_return处),计算的方法就是当前的relocation_return位置加上gd->reloc_off。
4)以relocation的目的地址(gd->relocaddr)为参数,调用relocate_code执行实际的relocation动作,就是将u-boot的代码段、data段、bss段等数据,拷贝到新的位置(gd->relocaddr)。
@@ 在board_init_f里面实现了
@@ (1)对relocate进行空间规划
@@ (2)计算uboot代码空间到relocation的位置的偏移
@@ (3)relocate旧的global_data到新的global_data的空间上