U-Boot _main函数

_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()函数。
ENTRY(_main)

 

/*  * 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的空间上

 

 
上一篇:内核启动流程-初探


下一篇:【故障处理】 DBCA建库报错CRS-2566