ubootバージョン 2016.03
前のセクションで、uboot が _main 関数の実行を開始したことを学びました. _main 関数は /arch/arm/lib/crt0.S で定義されています. _main 関数のコードは比較的長いです. コード内では主に次の関数が呼び出されます. :
① board_init_f_alloc_reserve
② board_init_f_init_reserve
③board_init_f
④relocate_code
⑤ relocate_vectors
⑦board_init_r
board_init_f_alloc_reserve 関数
関数コードは次のとおりです。
ulong board_init_f_alloc_reserve(ulong top)
{
/* Reserve early malloc arena */
#if defined(CONFIG_SYS_MALLOC_F)
top -= CONFIG_SYS_MALLOC_F_LEN; //top -= 0x400
#endif
/* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
top = rounddown(top-sizeof(struct global_data), 16); //留出gd空间大小
return top;
}
この関数にはパラメータがあり, レジスタ r0 (0x0091FF00) を介して渡されます. この関数の機能は, 初期の malloc 関数とグローバル変数 gd のためにスペースを確保し, トップ値を返すことです. 関数が実行された後,メモリは次の図に示されています。
board_init_f_init_reserve
board_init_f_alloc_reserve 関数の実行後、レジスタ r0 は関数の戻り値 (0x0091FA00) を保持し、r0 レジスタの値を r9 レジスタに格納します。uboot は、r9 レジスタがグローバル変数 gd を指すアドレスを格納することを規定しているためです。
gd は gd_t type の構造体で、/include/asm-generic/global_data.h で定義されています。
次に、board_init_f_init_reserve 関数を呼び出します。この関数のパラメーターは r0 (0x0091FA00) です。
void board_init_f_init_reserve(ulong base)
{
struct global_data *gd_ptr;
#ifndef _USE_MEMCPY
int *ptr;
#endif
/*
* 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 */
#ifdef _USE_MEMCPY
memset(gd_ptr, '\0', sizeof(*gd));
#else
for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); )
*ptr++ = 0;
#endif
/* 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 defined(CONFIG_SYS_MALLOC_F)
/* go down one 'early malloc arena' */
gd->malloc_base = base;
/* next alloc will be higher by one 'early malloc arena' size */
base += CONFIG_SYS_MALLOC_F_LEN;
#endif
}
この関数の主な機能は、gd 構造体の内容をクリアし、gd->malloc_base アドレスの値を 0X0091FB00 (16 バイト アラインメント) に設定することです。
board_init_f 関数
board_init_f_init_reserve 関数を実行した後、引き続き実行されます。
①. シリアル ポート、タイマーなどの一連の周辺機器を初期化し、いくつかのメッセージを出力します。
②、初始化 gd 的各个成员变量, uboot 会将自己拷贝到 DRAM 最后面的内存区域中。在拷贝之前需要通过gd结构体的内容分配,最终形成一个完整的内存“分配图”,在后面重定位 uboot 的时候就会用到这个内存“分配图”。
void board_init_f(ulong boot_flags)
{
#ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA
/*
* For some archtectures, global data is initialized and used before
* calling this function. The data should be preserved. For others,
* CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack
* here to host global data until relocation.
*/
gd_t data;
gd = &data;
/*
* Clear global data before it is accessed at debug print
* in initcall_run_list. Otherwise the debug print probably
* get the wrong vaule of gd->have_console.
*/
zero_global_data();
#endif
gd->flags = boot_flags;
gd->have_console = 0;
if (initcall_run_list(init_sequence_f))
hang();
#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
!defined(CONFIG_EFI_APP)
/* NOTREACHED - jump_to_copy() does not return */
hang();
#endif
}
由于未定义宏定义CONFIG_SYS_GENERIC_GLOBAL_DATA,条件编译之间的代码无效。
第25行,通过函数initcall_run_list初始化序列init_sequence_f中的函数。initcall_run_list代码如下:
int initcall_run_list(const init_fnc_t init_sequence[])
{
const init_fnc_t *init_fnc_ptr;
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
unsigned long reloc_ofs = 0;
int ret;
if (gd->flags & GD_FLG_RELOC)
reloc_ofs = gd->reloc_off;
#ifdef CONFIG_EFI_APP
reloc_ofs = (unsigned long)image_base;
#endif
debug("initcall: %p", (char *)*init_fnc_ptr - reloc_ofs);
if (gd->flags & GD_FLG_RELOC)
debug(" (relocated to %p)\n", (char *)*init_fnc_ptr);
else
debug("\n");
ret = (*init_fnc_ptr)();
if (ret) {
printf("initcall sequence %p failed at call %p (err=%d)\n",
init_sequence,
(char *)*init_fnc_ptr - reloc_ofs, ret);
return -1;
}
}
return 0;
}
在initcall_run_list函数的for循环中,会依次执行函数指针数组各表项指向的内容。
init_sequence函数指数组定义在common/board_f.c中。函数指针数组部分代码如下:
static init_fnc_t init_sequence_f[] = {
#ifdef CONFIG_SANDBOX
setup_ram_buf,
#endif
setup_mon_len,
#ifdef CONFIG_OF_CONTROL
fdtdec_setup,
#endif
#ifdef CONFIG_TRACE
trace_early_init,
#endif
initf_malloc,
initf_console_record,
#if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx)
/* TODO: can this go into arch_cpu_init()? */
probecpu,
#endif
#if defined(CONFIG_X86) && defined(CONFIG_HAVE_FSP)
x86_fsp_init,
#endif
arch_cpu_init, /* basic arch cpu dependent setup */
initf_dm,
arch_cpu_init_dm,
mark_bootstage, /* need timer, go after init dm */
#if defined(CONFIG_BOARD_EARLY_INIT_F)
board_early_init_f,
/******省略大部分********/
}
列举部分重要函数:
initf_malloc函数设置了gd->malloc_limit = 0x400,也就是内存池的大小。
board_early_init_f 初始化串口的IO配置
timer_init初始化定时器,通过此定时器给uboot提供时钟
env_init 设置gd的成员环境变量
init_baud_rate根据gd的环境变量设置波特率
serial_init初始化串口
console_init_f设置控制台,初始化完串口和控制台这也是为什么能在屏幕上看到打印信息的原因
dram_init设置gd->ram_size的值,我的板子为512MB
setup_dest_addr设置目的地址为以下三个值:
gd->ram_size = 0X20000000 //ram 大小为 0X20000000=512MB
gd->ram_top = 0XA0000000 //ram 最高地址为 0X80000000+0X20000000=0XA0000000
gd->relocaddr = 0XA0000000 //重定位后最高地址为 0XA0000000
还有很多内容暂时省略。。。。。。
在所有的配置函数执行完毕以后,会形成以下在ddr中的内存分配图!!
relocate_code函数
在内存分配图形成以后,就会执行relocate_code函数实现对uboot的拷贝工作。
ENTRY(relocate_code)
ldr r1, =__image_copy_start /* r1 <- SRC &__image_copy_start */
subs r4, r0, r1 /* r4 <- relocation offset */
beq relocate_done /* skip relocation */
ldr r2, =__image_copy_end /* r2 <- SRC &__image_copy_end */
copy_loop:
ldmia r1!, {r10-r11} /* copy from source address [r1] */
stmia r0!, {r10-r11} /* copy to target address [r0] */
cmp r1, r2 /* until source end address [r2] */
blo copy_loop
/*
* fix .rel.dyn relocations
*/
ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */
ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */
fixloop:
ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */
and r1, r1, #0xff
cmp r1, #23 /* relative fixup? */
bne fixnext
/* relative fix: increase location by offset */
add r0, r0, r4
ldr r1, [r0]
add r1, r1, r4
str r1, [r0]
fixnext:
cmp r2, r3
blo fixloop
relocate_done:
#ifdef __XSCALE__
/*
* On xscale, icache must be invalidated and write buffers drained,
* even with cache disabled - 4.2.7 of xscale core developer's manual
*/
mcr p15, 0, r0, c7, c7, 0 /* invalidate icache */
mcr p15, 0, r0, c7, c10, 4 /* drain write buffer */
#endif
/* ARMv4- don't know bx lr but the assembler fails to see that */
#ifdef __ARM_ARCH_4__
mov pc, lr
#else
bx lr
#endif
ENDPROC(relocate_code)
第2行,将uboot拷贝的起始地址保存到r1寄存器
第3行,寄存器r0保存的是uboot拷贝的目标首地址,计算出偏移地址保存到r4寄存器
第4行,如果r0-r1=0,直接跳过拷贝
第5行,寄存器r2保存拷贝之前的uboot代码结束地址。
第7行,通过函数copy_loop完成拷贝。拷贝函数流程:每次将r1寄存器值地址上的内容拷贝到r0和r11,拷贝完后r1会更新下一个要拷贝的数据地址。然后将r10和r11的数据写到r0寄存器值地址上。直到r1寄存器值=r2寄存器值,跳出循环。至此完成了uboot的拷贝。
relocate_vectors函数
此函数的作用是用来重定位异常向量表,主要完成了以下工作:
将gd->relocaddr(uboot重定位后首地址)赋值给r0寄存器,将r0寄存器的值写入到VBAR寄存器,此寄存器用于向量表重定位。
board_init_r函数
void board_init_r(gd_t *new_gd, ulong dest_addr)
{
#ifdef CONFIG_NEEDS_MANUAL_RELOC
int i;
#endif
#ifdef CONFIG_AVR32
mmu_init_r(dest_addr);
#endif
#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
gd = new_gd;
#endif
#ifdef CONFIG_NEEDS_MANUAL_RELOC
for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
init_sequence_r[i] += gd->reloc_off;
#endif
if (initcall_run_list(init_sequence_r))
hang();
/* NOTREACHED - run_main_loop() does not return */
hang();
}
在board_init_f主要是调用initcall_run_list函数完成对函数指针数组init_sequence_r各表项的调用,最终实现了剩余外设的初始化,并且设置gd->flags表示uboot和向量表的重定位已经完成。
至此外设初始化完毕。
uboot有关命令行解析等函数先不分析。。