1. Bootloader簡介
系統上電後,需要一段程式來進行初始化:關閉看門狗,改變系統時鐘,初始化存儲控制器,将更多的代碼複制到記憶體中等。它就是bootloader。
bootloader的實作非常依賴具體硬體,在嵌入式系統中,硬體配置千差萬别,即使是相同的CPU,它的外設(比如flash)也可能不同,是以不可能有一個bootloader支援所有的CPU,所有的電路闆。即使是支援CPU架構比較多的UBoot,也不是一拿來就可以使用(除非裡面的配置剛好和你的闆子相同)。需要進行一些配置。
CPU上電後,會從某個位址開始執行,比如MIPS結構的CPU會從0xBFC00000取第一條指令,而ARM結構的CPU則會從0x00000000開始,嵌入式開發闆中,需要把存儲器件的ROM或Flash等映射到這個位址,Bootloader就存放在這個位址的開始處,一上電就開始執行。(手機中的RAM和ROM分别對應電腦的記憶體和硬碟)
2. 啟動流程。
u-boot系統啟動流程 大多數bootloader都分為stage1和stage2兩部分,u-boot也不例外。
依賴于CPU體系結構的代碼(如裝置初始化代碼等)通常都放在stage1且可以用彙編語言來實作,而stage2則通常用C語言來實作,這樣可以實作複雜的功能,而且有更好的可讀性和移植性。
1.Stage1 start.S代碼結構 u-boot的stage1代碼通常放在start.S檔案中,他用彙編語言寫成,其主要代碼部分如下
(1) 定義入口。: 該工作通過修改連接配接器腳本來完成。
(2)設定異常向量(Exception Vector)。
(3)設定CPU的速度、時鐘頻率及終端控制寄存器。
(4)初始化記憶體控制器。
(5)将ROM中的程式複制到RAM中。
(6) 關中斷,關看門狗
(7)初始化堆棧,清bss段,為第二階段準備。
(8)轉到RAM中執行,該工作可使用指令ldr pc來完成。
2、Stage2
C語言代碼部分 lib_arm/board.c中的start arm boot是C語言開始的函數也是整個啟動代碼中C語言的主函數,同時還是整個u-boot(armboot)的主函數,該函數隻要完成如下操作:
(1)調用一系列的初始化函數。
(2)初始化儲存設備
(3)初始化簡單硬體如序列槽,lcd等
(4)初始化相關網絡裝置,填寫IP、MAC位址等。
(5)進去指令循環(即整個boot的工作循環),接受使用者從序列槽輸入的指令,然後進行相應的工作。
3、U-Boot的啟動順序
主要順序如下圖所示
圖為 U-Boot順序
下面就根據代碼進行解釋:
.globl _start //u-boot啟動入口
_start: b reset //複位向量并且跳轉到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 //中斷向量
b sleep_setting //跳轉到sleep_setting
并通過下段代碼拷貝到記憶體裡
relocate: //把uboot重新定位到RAM
adr r0, _start // r0 是代碼的目前位置
ldr r2, _armboot_start //r2 是armboot的開始位址
ldr r3, _armboot_end //r3 是armboot的結束位址
sub r2, r3, r2 // r2得到armboot的大小
ldr r1, _TEXT_BASE // r1 得到目标位址
add r2, r0, r2 // r2 得到源結束位址
copy_loop: //重新定位代碼
ldmia r0!, {r3-r10} //從源位址[r0]中複制
stmia r1!, {r3-r10} //複制到目标位址[r1]
cmp r0, r2 //複制資料塊直到源資料末尾位址[r2]
ble copy_loop
系統上電或reset後,cpu的PC一般都指向0x0位址,在0x0位址上的指令是
reset: //複位啟動子程式
mrs r0,cpsr //将CPSR狀态寄存器讀取,儲存到R0中
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
//将R0寫入狀态寄存器中
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
ldr r2, =0x7ff
ldr r0, =INTSUBMSK
str r2, [r0]
ldr r0, =LOCKTIME
ldr r1, =0xffffff
str r1, [r0]
clear_bss:
ldr r0, _bss_start //找到bss的起始位址
add r0, r0, #4 //從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
/ * cpu初始化關鍵寄存器
* 設定重要寄存器
* 設定記憶體時鐘
* /
cpu_init_crit:
mov r0, #0
mcr p15, 0, r0, c7, c7, 0
mcr p15, 0, r0, c8, c7, 0
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
mov ip, lr
#ifndef CONFIG_S3C2440A_JTAG_BOOT
bl memsetup //調用memsetup子程式(在board/smdk2442memsetup.S)
#endif
mov lr, ip
mov pc, lr //子程式傳回
memsetup:
mov r1, #MEM_CTL_BASE
adrl r2, mem_cfg_val
add r3, r1, #52
1: ldr r4, [r2], #4
str r4, [r1], #4
cmp r1, r3
bne 1b
mov pc, lr //子程式傳回
ldr r0, _armboot_end //armboot_end重定位
add r0, r0, #CONFIG_STACKSIZE //向下配置堆棧空間
sub sp, r0, #12 //為abort-stack預留個3字
ldr pc, _start_armboot //跳轉到start_armboot函數入口,start_armboot
字儲存函數入口指針
_start_armboot: .word start_armboot //start_armboot函數在lib_arm/board.c中實作
從此進入第二階段C語言代碼部分
.align 5
undefined_instruction: //未定義指令
get_bad_stack
bad_save_user_regs
bl do_undefined_instruction
.align 5
software_interrupt: //軟體中斷
get_bad_stack
bad_save_user_regs
bl do_software_interrupt
.align 5
prefetch_abort: //預取異常中止
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort
.align 5
data_abort: //資料異常中止
get_bad_stack
bad_save_user_regs
bl do_data_abort
.align 5
not_used: //未利用
get_bad_stack
bad_save_user_regs
bl do_not_used
.align 5
irq: //中斷請求
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
.align 5
fiq: //快速中斷請求
get_fiq_stack
irq_save_user_regs
bl do_fiq
irq_restore_user_regs
sleep_setting: //休眠設定
@ prepare the SDRAM self-refresh mode
ldr r0, =0x48000024 @ REFRESH Register
ldr r1, [r0]
orr r1, r1,#(1bd = &bd_data;
memset (gd->bd, 0, sizeof (bd_t));
monitor_flash_len = _armboot_end_data - _armboot_start;
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr){
if ((*init_fnc_ptr)() != 0)
{
hang ();
}
}
#if 0
size = flash_init (); //初始化flash
display_flash_config (size); //顯示flash的大小
#endif
#ifdef CONFIG_VFD
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
addr = (_armboot_real_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
size = vfd_setmem (addr);
gd->fb_base = addr;
addr += size;
addr = (addr + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
mem_malloc_init (addr);
#else
mem_malloc_init (_armboot_real_end);
#endif
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
puts ("NAND:");
nand_init();
#endif
#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif
env_relocate ();
#ifdef CONFIG_VFD
drv_vfd_init();
#endif
bd_data.bi_ip_addr = getenv_IPaddr ("ipaddr");
{
int i;
ulong reg;
char *s, *e;
uchar tmp[64];
i = getenv_r ("ethaddr", tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;
for (reg = 0; reg bd->bi_enetaddr);
#endif
#ifdef CONFIG_DRIVER_LAN91C96
if (getenv ("ethaddr")) {
smc_set_mac_addr(gd->bd->bi_enetaddr);
}
#endif
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
#if (CONFIG_COMMANDS & CFG_CMD_NET)
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
}
#endif
#ifdef BOARD_POST_INIT
board_post_init ();
#endif
for (;;) {
main_loop ();
}
UBOOT 學習心得(UBOOT流程分析):
http://www.360doc.com/content/14/0630/16/17775826_391005628.shtml
高通平台BootLoader的流程
https://blog.csdn.net/makeyourprogress/article/details/73920431