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

7-1、和relocate空间规划的函数

 在board_init_f中,会依次执行init_sequence_f数组里面函数。其中,和relocate空间规划的函数如下:

/*
     * Now that we have DRAM mapped and working, we can
     * relocate the code and continue running from DRAM.
     *
     * Reserve memory at end of RAM for (top down in that order):
     *  - area that won't get touched by U-Boot and Linux (optional)
     *  - kernel log buffer
     *  - protected RAM
     *  - LCD framebuffer
     *  - monitor code
     *  - board info struct
     */
    setup_dest_addr,
#ifdef CONFIG_OF_BOARD_FIXUP
    fix_fdt,
#endif
#ifdef CONFIG_PRAM
    reserve_pram,
#endif
    reserve_round_4k,
    arch_reserve_mmu,
    reserve_video,
    reserve_trace,
    reserve_uboot,
    reserve_malloc,
    reserve_board,
    reserve_global_data,
    reserve_fdt,
    reserve_bootstage,
    reserve_bloblist,
    reserve_arch,
    reserve_stacks,
    dram_init_banksize,
    show_dram_config,
    INIT_FUNC_WATCHDOG_RESET
    setup_bdinfo,
    display_new_sp,
    INIT_FUNC_WATCHDOG_RESET
    reloc_fdt,
    reloc_bootstage,
    reloc_bloblist,
    setup_reloc,
#if defined(CONFIG_X86) || defined(CONFIG_ARC)
    copy_uboot_to_ram,
    do_elf_reloc_fixups,
#endif
    clear_bss,
#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
        !CONFIG_IS_ENABLED(X86_64)
    jump_to_copy,
#endif
    NULL,

7.2 setup_dest_addr

static int setup_dest_addr(void)
{
    debug("Monitor len: %08lX\n", gd->mon_len);
// gd->mon_len表示了整个uboot代码空间的大小,如下

// gd->mon_len = (ulong)&__bss_end - (ulong)_start;

// 在uboot代码空间relocate的时候,relocate的size就是由这里决定
/* * Ram is setup, size stored in gd !! */ debug("Ram size: %08lX\n", (ulong)gd->ram_size);

// gd->ram_size表示了ram的size,也就是可使用的ddr的size,在board.c中定义如下
// int dram_init(void)
// {
// gd->ram_size = PHYS_SDRAM_1_SIZE;也就是0x2000_0000
// return 0;
// }

#if defined(CONFIG_SYS_MEM_TOP_HIDE)
    /*
     * Subtract specified amount of memory to hide so that it won't
     * get "touched" at all by U-Boot. By fixing up gd->ram_size
     * the Linux kernel should now get passed the now "corrected"
     * memory size and won't touch it either. This should work
     * for arch/ppc and arch/powerpc. Only Linux board ports in
     * arch/powerpc with bootwrapper support, that recalculate the
     * memory size from the SDRAM controller setup will have to
     * get fixed.
     */
    gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif
#ifdef CONFIG_SYS_SDRAM_BASE
    gd->ram_base = CONFIG_SYS_SDRAM_BASE;
#endif
    gd->ram_top = gd->ram_base + get_effective_memsize();
    gd->ram_top = board_get_usable_ram_top(gd->mon_len);
// gd->ram_top计算ddr的顶端地址 gd
->relocaddr = gd->ram_top;
// 从gd->
relocaddr 的位置开始分配
  debug(
"Ram top: %08lX\n", (ulong)gd->ram_top); #if defined(CONFIG_MP) && (defined(CONFIG_MPC86xx) || defined(CONFIG_E500))

/* * We need to make sure the location we intend to put secondary core * boot code is reserved and not used by any part of u-boot */

if (gd->relocaddr > determine_mp_bootpg(NULL)) {
  gd
->relocaddr = determine_mp_bootpg(NULL);
  debug(
"Reserving MP boot page to %08lx\n", gd->relocaddr); }

#endif

return 0; }

setup_dest_addr函数,设置目的地址,设置 gd->ram_sizegd->ram_topgd->relocaddr这三个的值。
设置gd->ram_top和gd->relocaddr

int dram_init(void):

gd->ram_size = sdram_size(readl(REG_SDIC_SIZE0)) + sdram_size(readl(REG_SDIC_SIZE1));

reserve_round_4k 

 reserve_round_4k 函 数 用 于 对 gd->relocaddr 4KB 对 齐 , 因 为gd->relocaddr=0X40000000,已经是 4K 对齐了,所以调整后不变。

reserve_mmu

留出 MMU TLB 表的位置,分配 MMU TLB 表内存以后会对 gd->relocaddr 64K 字节对齐。完成以后 gd->arch.tlb_sizegd->arch.tlb_addr gd->relocaddr

reserve_video

reserve_malloc

留出 malloc 区域,调整 gd->start_addr_sp 位置, malloc 区域由宏
TOTAL_MALLOC_LEN 定义,宏定义如下:
#define TOTAL_MALLOC_LEN (CONFIG_SYS_MALLOC_LEN +
CONFIG_ENV_SIZE)


reserve_uboot

留出重定位后的 uboot 所占用的内存区域, uboot 所占用大小由
gd->mon_len 所指定,留出 uboot 的空间以后还要对 gd->relocaddr 4K 字节对齐,并且重新设
gd->start_addr_sp


reserve_global_data

函数保留出 gd_t 的内存区域, gd_t 结构体大小为


setup_dram_config 函数设置 dram 信息,就是设置 gd->bd->bi_dram[0].start
gd->bd->bi_dram[0].size,后面会传递给 linux 内核,告诉 linux DRAM 的起始地址和大小。


show_dram_config 函数,用于显示 DRAM 的配置





setup_reloc

计算uboot代码空间到relocation的位置的偏移
同样在board_init_f中,调用init_sequence_f数组里面的setup_reloc实现。

static int setup_reloc(void)
{
    if (gd->flags & GD_FLG_SKIP_RELOC) {
        debug("Skipping relocation due to flag\n");
        return 0;
    }

#ifdef CONFIG_SYS_TEXT_BASE
#ifdef ARM
    gd->reloc_off = gd->relocaddr - (unsigned long)__image_copy_start;
// gd->relocaddr表示新的uboot代码空间的起始地址,__image_copy_start表示旧的uboot代码空间的起始地址,二者算起来就是偏移了。
#elif defined(CONFIG_M68K) /* * On all ColdFire arch cpu, monitor code starts always * just after the default vector table location, so at 0x400 */ gd->reloc_off = gd->relocaddr - (CONFIG_SYS_TEXT_BASE + 0x400); #elif !defined(CONFIG_SANDBOX) gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE; #endif #endif memcpy(gd->new_gd, (char *)gd, sizeof(gd_t)); debug("Relocation Offset is: %08lx\n", gd->reloc_off); debug("Relocating to %08lx, new gd at %08lx, sp at %08lx\n", gd->relocaddr, (ulong)map_to_sysmem(gd->new_gd), gd->start_addr_sp); return 0; }

 

 
posted @ 2022-02-04 18:39  liujunhuasd  阅读(776)  评论(0编辑  收藏  举报