尽管有了调试跟踪手段,甚至也可以通过串口打印信息了,但是不一定能够判断出错原因。如果能够充分理解代码的启动流程,那么对准确地解决和分析问题很有帮助。
开发板上电后,执行U-Boot的第一条指令,然后顺序执行U-Boot启动函数。函数调用顺序如图6.3所示。
看一下board/smsk2410/u-boot.lds这个链接脚本,可以知道目标程序的各部分链接顺序。第一个要链接的是cpu/arm920t/start.o,那么U-Boot的入口指令一定位于这个程序中。下面详细分析一下程序跳转和函数的调用关系以及函数实现。
1.cpu/arm920t/start.S
这个汇编程序是U-Boot的入口程序,开头就是复位向量的代码。
图6.3 U-Boot启动代码流程图
_start:
b
reset
//复位向量
ldr pc,
_undefined_instruction
ldr pc,
_software_interrupt
ldr pc,
_prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc,
_irq
//中断向量
ldr pc,
_fiq
//中断向量
…
reset:
//复位启动子程序
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
#ifdef
CONFIG_INIT_CRITICAL
bl
cpu_init_crit
#endif
relocate:
adr r0,
_start
ldr r1,
_TEXT_BASE
cmp
r0,
r1
beq
stack_setup
ldr r2,
_armboot_start
ldr r3, _bss_start
sub r2, r3,
r2
add r2, r0,
r2
copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
cmp r0,
r2
ble copy_loop
stack_setup:
ldr r0,
_TEXT_BASE
sub r0, r0,
#CFG_MALLOC_LEN
sub r0, r0,
#CFG_GBL_DATA_SIZE
#ifdef
CONFIG_USE_IRQ
sub r0, r0,
#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0,
#12
clear_bss:
ldr r0,
_bss_start
ldr r1,
_bss_end
mov r2,
#0x00000000
clbss_l:str r2,
[r0]
add r0, r0, #4
cmp r0, r1
bne clbss_l
ldr pc,
_start_armboot
_start_armboot: .word
start_armboot
//start_armboot函数在lib_arm/board.c中实现
cpu_init_crit:
…… //初始化CACHE,关闭MMU等操作指令
mov ip, lr
bl
memsetup
//memsetup子程序在board/smdk2410/memsetup.S中实现
mov lr, ip
mov pc, lr
2.lib_arm/board.c
start_armboot是U-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。
void
start_armboot (void)
{
DECLARE_GLOBAL_DATA_PTR;
ulong size;
init_fnc_t **init_fnc_ptr;
char *s;
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN -
sizeof(gd_t));
__asm__ __volatile__("": : :"memory");
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd -
sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
monitor_flash_len = _bss_start - _armboot_start;
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr)
{
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
size = flash_init ();
display_flash_config (size);
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
env_relocate ();
gd->bd->bi_ip_addr = getenv_IPaddr
("ipaddr");
……
devices_init ();
jumptable_init ();
console_init_r ();
enable_interrupts ();
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
for (;;) {
main_loop ();
}
}
3.init_sequence[]
init_sequence[]数组保存着基本的初始化函数指针。这些函数名称和实现的程序文件在下列注释中。
init_fnc_t
*init_sequence[] = {
cpu_init,
board_init,
interrupt_init,
env_init,
init_baudrate,
serial_init,
console_init_f,
display_banner,
dram_init,
display_dram_config,
NULL,
};