前面介绍了start.S到_main之间的代码的功能,接下来代码会执行到_main函数,本文将会_main函数部分进行分析。_main 函数在文件 arch\arm\lib\crt0.S 中定义,具体代码如下所示。在 _main 函数中主要做了三件事情,分别为 board_init_f(主要初始化SDRAM)、relocate_code(将uboot拷贝到外部DDR内存中)和 board_init_f(初始化硬件外设,加载内核)。接下来将详细分析这三大部分的具体实现。
ENTRY(_main)
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
#if 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
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_BD] /* r9 = gd->bd */
sub r9, r9, #GD_SIZE /* new GD is below bd */
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) || defined(CONFIG_SPL_FRAMEWORK)
# ifdef CONFIG_SPL_BUILD
/* Use a DRAM stack for the rest of SPL, if requested */
bl spl_relocate_stack_gd
cmp r0, #0
movne sp, r0
movne r9, r0
# endif
ldr r0, =__bss_start /* this is auto-relocated! */
#ifdef CONFIG_USE_ARCH_MEMSET
ldr r3, =__bss_end /* this is auto-relocated! */
mov r1, #0x00000000 /* prepare zero to clear BSS */
subs r2, r3, r0 /* r2 = memset len */
bl memset
#else
ldr r1, =__bss_end /* this is auto-relocated! */
mov r2, #0x00000000 /* prepare zero to clear BSS */
clbss_l:cmp r0, r1 /* while not at end of BSS */
#if defined(CONFIG_CPU_V7M)
itt lo
#endif
strlo r2, [r0] /* clear 32-bit BSS word */
addlo r0, r0, #4 /* move to next */
blo clbss_l
#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
第一部分的代码如下。首先将 CONFIG_SYS_INIT_SP_ADDR 的值赋值给r0寄存器,然后将r0寄存器中的值按照8字节对齐,最后将r0的值赋值给sp指针。
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
#if 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
mov r0, #0
bl board_init_f
2 board_init_f_alloc_reserve
将r0寄存器的值赋值给sp指针后,调用了 board_init_f_alloc_reserve 函数留出早期的 malloc 内存区域和 gd 内存区域。具体代码如下:
/* common\init\board_init.c */
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;
}
board_init_f_alloc_reserve 函数传入了一个参数top,该参数是寄存器r0的值,首先将宏定义 CONFIG_VAL 展开如下:
CONFIG_VAL(SYS_MALLOC_F_LEN)
#define CONFIG_VAL(option) config_val(option)
#define config_val(cfg) _config_val(_IS_SPL, cfg) (include\linux\kconfig.h)
#define _config_val(x, cfg) __config_val(x, cfg) (include\linux\kconfig.h)
#define __config_val(x, cfg) ___config_val(__ARG_PLACEHOLDER_##x, cfg) (include\linux\kconfig.h)
#define ___config_val(arg1_or_junk, cfg) ____config_val(arg1_or_junk CONFIG_SPL_##cfg, CONFIG_##cfg) (include\linux\kconfig.h)
#define ____config_val(__ignored, val, ...) val (include\linux\kconfig.h)
最终 CONFIG_VAL(SYS_MALLOC_F_LEN) 的展开结果为:
CONFIG_VAL(SYS_MALLOC_F_LEN) ____config_val(__ARG_PLACEHOLDER_1 CONFIG_SPL_SYS_MALLOC_F_LEN, CONFIG_SYS_MALLOC_F_LEN)
即:
CONFIG_VAL(SYS_MALLOC_F_LEN) = CONFIG_SYS_MALLOC_F_LEN
在 uboot顶层目录下的 Kconfig 文件中定义了 CONFIG_SYS_MALLOC_LEN 的值,其大小默认为0x400(即1KB的大小,通过修改这个值在 make xxx_defconfig后可以看到CONFIG_SYS_MALLOC_F_LEN的大小有变化)。Kconfig相关部分如下所示:
其中Kconfig的语法简单介绍如下:
- config 是关键字,表示一个配置选项的开始;,省略了前缀"CONFIG_"
- bool 表示变量类型,即"CONFIG_ TMPFS_POSIX_ACL "的类型,有5种类型:bool、tristate、string、hex和int,其中tristate和string是基本的类型
bool变量的值:y和n
tristate变量的值:y、n和m
string变量的值: 字符串
bool之后的字符串“Enable malloc() pool before relocation”是提示信息 - depends on:表示依赖于XXX,“ddepends on SYS_MALLOC_F”表示只有当SYS_MALLOC_F配置选项被选中时,当前配置选项的提示信息才会出现,才能设置当前配置选项
- default 表示默认值
代码第6行,将top的值减去CONFIG_SYS_MALLOC_F_LEN(0x400),然后代码第9行又减去了global_data(gd结构体的大小),然后将计算后的值返回,存在寄存器r0中。计算后的内部RAM的内存分配如下:
3 board_init_f_init_reserve
分配预留好 malloc 和 gd 结构体的空间后,接着执行的代码如下。将 board_init_f_alloc_reserve 返回的值赋值给sp指针,然后将 board_init_f_alloc_reserve 返回的值放入r9 寄存器中,接着调用 board_init_f_init_reserve 函数。
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
board_init_f_init_reserve 函数内容如下,此函数用于初始化gd结构体,
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)); /* 将gd结构体清0 */
/* set GD unless architecture did it already */
#if !defined(CONFIG_ARM)
arch_setup_gd(gd_ptr);
#endif
/* 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;
/* next alloc will be higher by one 'early malloc arena' size */
base += CONFIG_VAL(SYS_MALLOC_F_LEN);
#endif
}