6.3.3 U-Boot啓動過程
http://www.dearbook.com.cn/WebResource.aspx?id=14904儘管有了調試跟蹤手段,甚至也可以通過串口打印信息了,但是不一定能夠判斷出錯原因。如果能夠充分理解代碼的啓動流程,那麼對準確地解決和分析問題很有幫助。
開發板上電後,執行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 //中斷向量
…
/* the actual reset code */
reset: //復位啓動子程序
/* 設置CPU爲SVC32模式 */
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
/* 關閉看門狗 */
/* 這些初始化代碼在系統重起的時候執行,運行時熱復位從RAM中啓動不執行 */
#ifdef CONFIG_INIT_CRITICAL
bl cpu_init_crit
#endif
relocate: /* 把U-Boot重新定位到RAM */
adr r0, _start /* r0是代碼的當前位置 */
ldr r1, _TEXT_BASE /* 測試判斷是從Flash啓動,還是RAM */
cmp r0, r1 /* 比較r0和r1,調試的時候不要執行重定位 */
beq stack_setup /* 如果r0等於r1,跳過重定位代碼 */
/* 準備重新定位代碼 */
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 得到armboot的大小 */
add r2, r0, r2 /* r2 得到要複製代碼的末尾地址 */
copy_loop: /* 重新定位代碼 */
ldmia r0!, {r3-r10} /*從源地址[r0]複製 */
stmia r1!, {r3-r10} /* 複製到目的地址[r1] */
cmp r0, r2 /* 複製數據塊直到源數據末尾地址[r2] */
ble copy_loop
/* 初始化堆棧等 */
stack_setup:
ldr r0, _TEXT_BASE /* 上面是128 KiB重定位的u-boot */
sub r0, r0, #CFG_MALLOC_LEN /* 向下是內存分配空間 */
sub r0, r0, #CFG_GBL_DATA_SIZE /* 然後是bdinfo結構體地址空間 */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* 爲abort-stack預留3個字 */
clear_bss:
ldr r0, _bss_start /* 找到bss段起始地址 */
ldr r1, _bss_end /* bss段末尾地址 */
mov r2, #0x00000000 /* 清零 */
clbss_l:str r2, [r0] /* bss段地址空間清零循環... */
add r0, r0, #4
cmp r0, r1
bne clbss_l
/* 跳轉到start_armboot函數入口,_start_armboot字保存函數入口指針 */
ldr pc, _start_armboot
_start_armboot: .word start_armboot //start_armboot函數在lib_arm/board.c中實現
/* 關鍵的初始化子程序 */
cpu_init_crit:
…… //初始化CACHE,關閉MMU等操作指令
/* 初始化RAM時鐘。
* 因爲內存時鐘是依賴開發板硬件的,所以在board的相應目錄下可以找到memsetup.S文件。
*/
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;
/* Pointer is writable since we allocated a register for it */
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
/* compiler optimization barrier needed for GCC >= 3.4 */
__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;
/* 順序執行init_sequence數組中的初始化函數 */
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/*配置可用的Flash */
size = flash_init ();
display_flash_config (size);
/* _armboot_start 在u-boot.lds鏈接腳本中定義 */
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
/* 配置環境變量,重新定位 */
env_relocate ();
/* 從環境變量中獲取IP地址 */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/* 以太網接口MAC 地址 */
……
devices_init (); /* 獲取列表中的設備 */
jumptable_init ();
console_init_r (); /* 完整地初始化控制檯設備 */
enable_interrupts (); /* 使能例外處理 */
/* 通過環境變量初始化 */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
/* main_loop()總是試圖自動啓動,循環不斷執行 */
for (;;) {
main_loop (); /* 主循環函數處理執行用戶命令 -- common/main.c */
}
/* NOTREACHED - no way out of command loop except booting */
}
3.init_sequence[]
init_sequence[]數組保存着基本的初始化函數指針。這些函數名稱和實現的程序文件在下列註釋中。
init_fnc_t *init_sequence[] = {
cpu_init, /* 基本的處理器相關配置 -- cpu/arm920t/cpu.c */
board_init, /* 基本的板級相關配置 -- board/smdk2410/smdk2410.c */
interrupt_init, /* 初始化例外處理 -- cpu/arm920t/s3c24x0/interrupt.c */
env_init, /* 初始化環境變量 -- common/cmd_flash.c */
init_baudrate, /* 初始化波特率設置 -- lib_arm/board.c */
serial_init, /* 串口通訊設置 -- cpu/arm920t/s3c24x0/serial.c */
console_init_f, /* 控制檯初始化階段1 -- common/console.c */
display_banner, /* 打印u-boot信息 -- lib_arm/board.c */
dram_init, /* 配置可用的RAM -- board/smdk2410/smdk2410.c */
display_dram_config, /* 顯示RAM的配置大小 -- lib_arm/board.c */
NULL,
};