在start.S做完基础的初始化后,程序进入到main函数中,本博客主要分析main函数的框架(在crt0_64.S)
ENTRY(_main)
#if defined(CONFIG_TPL_BUILD) && defined(CONFIG_TPL_NEEDS_SEPARATE_STACK)
ldr x0, =(CONFIG_TPL_STACK)
#elif defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr x0, =(CONFIG_SPL_STACK)
#elif defined(CONFIG_SYS_INIT_SP_BSS_OFFSET)
adr x0, __bss_start
add x0, x0, #CONFIG_SYS_INIT_SP_BSS_OFFSET
#else
ldr x0, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
/*CONFIG_TPL_BUILD、CONFIG_TPL_NEEDS_SEPARATE_STACK和CONFIG_SYS_INIT_SP_BSS_OFFSET在U-BOOT中都没有定义
include/configs/imx8qxp_mek.h:134:#define CONFIG_SYS_INIT_SP_ADDR 0x80200000
include/configs/imx8qxp_mek.h:21:#define CONFIG_SPL_STACK 0x013E000
所以把CONFIG_SYS_INIT_SP_ADDR的值赋值给x0,即x0 = 0x80200000*/
bic sp, x0, #0xf //将x0的后四位清零,即16字节对齐后赋值给sp,后续可以调用c函数
mov x0, sp //存储sp的地址到x0中
bl board_init_f_alloc_reserve //调用board_init_f_alloc_reserve函数
//board_init_f_alloc_reserve 在board_init.c中定义
ulong board_init_f_alloc_reserve(ulong top) //此处top为传入的参数x0,top = sp = 0x80200000
{
#if CONFIG_VAL(SYS_MALLOC_F_LEN)
/*#define CONFIG_VAL(option) config_val(option) //等价于config_val(SYS_MALLOC_F_LEN)
#define config_val(cfg) _config_val(_IS_SPL, cfg) //等价于_config_val(_IS_SPL,SYS_MALLOC_F_LEN)
#define _config_val(x, cfg) __config_val(x, cfg) //等价于__config_val(_IS_SPL,SYS_MALLOC_F_LEN)
#define __config_val(x, cfg) ___config_val(__ARG_PLACEHOLDER_##x, cfg)
//等价于___config_val(__ARG_PLACEHOLDER__IS_SPL,SYS_MALLOC_F_LEN)
#define ___config_val(arg1_or_junk, cfg) \
____config_val(arg1_or_junk CONFIG_SPL_##cfg, CONFIG_##cfg)
//等价于____config_val( __ARG_PLACEHOLDER__IS_SPL CONFIG_SPL_SYS_MALLOC_F_LEN, CONFIG_SYS_MALLOC_F_LEN)
#define ____config_val(__ignored, val, ...) val //等价于CONFIG_SYS_MALLOC_F_LEN
//在configs/imx8qxp_mek_defconfig中CONFIG_SYS_MALLOC_F_LEN=0x2000
top -= CONFIG_VAL(SYS_MALLOC_F_LEN); //top = 0x80200000 - 0x2000 = 0x801FE000
#endif
top = rounddown(top-sizeof(struct global_data), 16);
//rounddown定义在在kernel.h中
#define rounddown(x, y) ( \ //x=top-sizeof(struct global_data), y=16
{ \
typeof(x) __x = (x); \ //__x =top-sizeof(struct global_data)
__x - (__x % (y)); \ //top = top-sizeof(struct global_data) - (top-sizeof(struct global_data))%16
} //此处相当于top-sizeof(struct global_data)16字节对齐
return top; //返回top值
}
/*此处由于struct global_data的大小受config项的影响,无法直接计算,我们可以通过看汇编代码得知
000000008002cc04 <board_init_f_alloc_reserve>:
8002cc04: 928433e1 mov x1, #0xffffffffffffde60 // #-8608
8002cc08: 8b010000 add x0, x0, x1 //x0 -8608 = 0x801FBE60
8002cc0c: 927cec00 and x0, x0, #0xfffffffffffffff0 //16字节对齐 x0 =0x801FBE60
8002cc10: d65f03c0 ret
可以看到x0减少了8608字节,即struct global_data为8608个字节*/
//此时内存的情况如下:
mov sp, x0 //将函数board_init_f_alloc_reserve的返回值赋值给sp=0x801FBE60
mov x18, x0 //将x0的值存入x18中,方便以后调用x18=0x801FBE60
bl board_init_f_init_reserve //跳到board_init_f_init_reserve 中
//board_init_f_alloc_reserve 也在board_init.c中定义
void board_init_f_init_reserve(ulong base) //base即为x0,base=0x801FBE60
{
struct global_data *gd_ptr;
gd_ptr = (struct global_data *)base; //gd_ptr指向base,即为分配的sizeof(struct global_data)空间
memset(gd_ptr, '\0', sizeof(*gd)); //将分配的sizeof(struct global_data)空间清零
#if !defined(CONFIG_ARM)
arch_setup_gd(gd_ptr);
#endif
//CONFIG_ARM并没有定义,arch_setup_gd的功能只是gd = gd_ptr
base += roundup(sizeof(struct global_data), 16);
//roundup的定义如下:
#define roundup(x, y) ( \ //x=sizeof(struct global_data), y=16
{ \
const typeof(y) __y = y; \ //__y =16
(((x) + (__y - 1)) / __y) * __y; \ //base += ((sizeof(struct global_data)+15)/16)*16 16字节对齐
}
//此时base跳到0x801FE000处
#if CONFIG_VAL(SYS_MALLOC_F_LEN)
gd->malloc_base = base; //将全局变量gd(即分配的sizeof(struct global_data)内存空间)的base指向0x801FE000处
base += CONFIG_VAL(SYS_MALLOC_F_LEN); //base=0x80200000
#endif
}
//board_init_f_alloc_reserve的主要功能是清空全局变量gd区,并初始化gd的malloc_base的地址,用于后续内存分配申请
mov x0, #0
bl board_init_f
//board_init_f为主要的初始化函数之一,这个下次单独分析
#if !defined(CONFIG_SPL_BUILD)
ldr x0, [x18, #GD_START_ADDR_SP] //x0存入gd->start_addr_sp的地址
bic sp, x0, #0xf //sp指向x0并且16字节对齐
ldr x18, [x18, #GD_NEW_GD] //x18存入gd->new_gd的地址
adr lr, relocation_return //设置lr为relocation_return的地址
#if CONFIG_POSITION_INDEPENDENT
adr x0, _start /* x0 <- Runtime value of _start */
ldr x9, _TEXT_BASE /* x9 <- Linked value of _start */
sub x9, x9, x0 /* x9 <- Run-vs-link offset */
add lr, lr, x9
#endif
//CONFIG_POSITION_INDEPENDENT在U-BOOT中未定义
ldr x9, [x18, #GD_RELOC_OFF] //x9存入gd->reloc_off 的地址
add lr, lr, x9 //设置新的返回地址为lr+x9
ldr x0, [x18, #GD_RELOCADDR] //x0存入 gd->relocaddr
b relocate_code
//重定位 代码后续博客继续分析
relocation_return:
bl c_runtime_cpu_setup
//c_runtime_cpu_setup在start.S中定义
ENTRY(c_runtime_cpu_setup)
#if defined(CONFIG_ARMV8__SPL_EXCEPTION_VECTORS) || !defined(CONFIG_SPL_BUILD)
adr x0, vectors
switch_el x1, 3f, 2f, 1f
3: msr vbar_el3, x0
b 0f
2: msr vbar_el2, x0
b 0f
1: msr vbar_el1, x0
0:
#endif
ret
ENDPROC(c_runtime_cpu_setup)
//根据当前的中断等级设置对应的中断向量表地址
#endif /* !CONFIG_SPL_BUILD */
#if defined(CONFIG_SPL_BUILD)
bl spl_relocate_stack_gd /* may return NULL */
cmp x0, #0
csel x18, x0, x18, ne
mov x1, sp
cmp x0, #0
csel x0, x0, x1, ne
mov sp, x0
#endif
//CONFIG_SPL_BUILD在U-BOOT中未定义
ldr x0, =__bss_start
ldr x1, =__bss_end
clear_loop:
str xzr, [x0], #8
cmp x0, x1
b.lo clear_loop
//清空bss段
mov x0, x18 /* gd_t */
ldr x1, [x18, #GD_RELOCADDR] /* dest_addr */
b board_init_r
// board_init_r 也是U-BOOT中比较重要的一项初始化函数,后续博客单独分析
ENDPROC(_main)
main函数的大致框架就结束了,后面会继续深入分析board_init_f、relocate_code和board_init_r 的代码