日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【Bootloader】探究bootloader,分析u-boot源码

發(fā)布時(shí)間:2025/4/14 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Bootloader】探究bootloader,分析u-boot源码 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Preface

? ?之前也發(fā)表過(guò)關(guān)于《Bootloader啟動(dòng)過(guò)程分析》的文章,但是內(nèi)容表達(dá)得比較抽象,大多是文字?jǐn)⑹?#xff0c;所以這里從系統(tǒng)和代碼的角度來(lái)深入分析bootloader的啟動(dòng)過(guò)程。

? ?工具:Source Insight

? ?目標(biāo):U-Boot-1.1.6

? ?僅留此分析過(guò)程,日后再作補(bǔ)充(純手打也不容易啊,嘿嘿)。

?


U-Boot工程結(jié)構(gòu)

? ?學(xué)習(xí)一個(gè)軟件,尤其是開源軟件,首先應(yīng)該從分析軟件的工程結(jié)構(gòu)開始。一個(gè)好的軟件有良好的工程結(jié)構(gòu),對(duì)于讀者學(xué)習(xí)和理解軟件的架構(gòu)以及工作流程都有很好的幫助。

? ?U-Boot的源代碼布局和Linux類似,使用了按照模塊劃分的結(jié)構(gòu),并且充分考慮了體系結(jié)構(gòu)和跨平臺(tái)問題。

U-Boot源代碼目錄結(jié)構(gòu)

子目錄名

作用

board開發(fā)板相關(guān)的定義和結(jié)構(gòu)
common包含U-Boot用到的各種處理函數(shù)
cpu各種不同類型的處理器相關(guān)代碼
docU-Boot文檔
drivers常用外部設(shè)備驅(qū)動(dòng)程序
examples

存放U-Boot開發(fā)代碼樣例

fs

文件系統(tǒng)有關(guān)的代碼,包括cramfs、ext2、fat等常見文件系統(tǒng)

include

U-Boot用到的頭文件

lib_arm

ARM體系結(jié)構(gòu)有關(guān)的數(shù)據(jù)定義和操作

lib_generic

U-Boot通用的操作函數(shù)

net

常用的網(wǎng)絡(luò)協(xié)議,包括bootp、rarp、arp、tftp等

post

上電自檢相關(guān)代碼

rtc

實(shí)時(shí)鐘有關(guān)操作

tools

U-Boot有關(guān)的數(shù)據(jù)代碼

?


U-Boot總體工作流程

? ?與大多數(shù)Bootloader類似,U-Boot的啟動(dòng)分成stage1和stage2兩個(gè)階段。

? ?stage1使用匯編語(yǔ)言編寫,通常與CPU體系緊密相關(guān),如處理器初始化和設(shè)備初始化代碼等,該階段在start.S文件中實(shí)現(xiàn)。

? ?上圖是U-Boot中Stage1工作流程。Stage1的代碼都是與平臺(tái)相關(guān)的,使用匯編語(yǔ)言編寫占用空間小而且執(zhí)行速度快。

? ?Stage1負(fù)責(zé)建立Stage1階段使用堆棧和代碼段,然后復(fù)制Stage2階段的代碼到內(nèi)存。

? ?Stage2階段一般包括:初始化Flash器件、swim 系統(tǒng)內(nèi)存映射、初始化網(wǎng)絡(luò)設(shè)備、進(jìn)入命令循環(huán),接收用戶從串口發(fā)送的命令然后進(jìn)行相應(yīng)的處理。

? ?Stage2使用C語(yǔ)言編寫,用于加載操作系統(tǒng)內(nèi)核,該階段主要是board.c中是start_armboot()函數(shù)實(shí)現(xiàn)。下圖為U-Boot的Stage1和Stage2在Flash和RAM中的分配。

? ?從上圖中可以看出,U-Boot在加載到內(nèi)存后,使用了操作系統(tǒng)空余的內(nèi)存空間。

?


U-Boot啟動(dòng)流程分析

?

?

? ?從圖中可以看出U-Boot的啟動(dòng)代碼分布在start.S、low_level_init.S、board.c和main.c文件中

? ?Start.S是U-Boot整個(gè)程序的入口,該文件使用匯編語(yǔ)言編寫,不同體系結(jié)構(gòu)的啟動(dòng)代碼不同

? ?low_level_init.S是特定開發(fā)板的設(shè)置代碼;

? ?board.c包含開發(fā)板底層設(shè)備驅(qū)動(dòng);

? ?main.c是一個(gè)與平臺(tái)無(wú)關(guān)的代碼,U-Boot應(yīng)用程序的入口在此文件中。

?


①_start標(biāo)號(hào)

? ?在U-Boot工程中,每種處理器目錄下都有一個(gè)start.S文件,該文件中有一個(gè)_start標(biāo)號(hào),是整個(gè)U-Boot代碼的入口點(diǎn)。

/**************************************************************************** Jump vector table as in table 3.1 in [1]***************************************************************************/ .globl _start _start: b reset //復(fù)位向量:無(wú)條件跳轉(zhuǎn)到reset標(biāo)號(hào)ldr pc, _undefined_instruction //未定義指令向量ldr pc, _software_interrupt //軟件中斷向量ldr pc, _prefetch_abort //預(yù)取指令異常向量ldr pc, _data_abort //數(shù)據(jù)操作異常向量ldr pc, _not_used //未使用ldr pc, _irq //慢速中斷向量ldr pc, _fiq //快速中斷向量 _undefined_instruction: .word undefined_instruction //定義中斷向量表入口地址 _software_interrupt: .word software_interrupt _prefetch_abort: .word prefetch_abort _data_abort: .word data_abort _not_used: .word not_used _irq: .word irq _fiq: .word fiq.balignl 16,0xdeadbeef /**************************************************************************** Startup Code (reset vector)** do important init only if we don't start from memory!* relocate armboot to ram* setup stack* jump to second stage***************************************************************************/ _TEXT_BASE:.word TEXT_BASE //定義整個(gè)錟-Boot鏡像文件在內(nèi)存加載的地址 .globl _armboot_start _armboot_start:.word _start /** These are defined in the board-specific linker script.*/ .globl _bss_start _bss_start:.word __bss_start //定義代碼段起始 .globl _bss_end _bss_end:.word _end //定義代碼段結(jié)束地址 #ifdef CONFIG_USE_IRQ /* IRQ stack memory (calculated at run-time) */ .globl IRQ_STACK_START //定義IRQ的堆棧地址 IRQ_STACK_START:.word 0x0badc0de /* IRQ stack memory (calculated at run-time) */ .globl FIQ_STACK_START //定義FIQ的堆棧地址 FIQ_STACK_START:.word 0x0badc0de #endif

?

? ?_start標(biāo)號(hào)下面的代碼主要是一些偽指令,設(shè)置全局變量,供啟動(dòng)程序把U-Boot映像從Flash存儲(chǔ)器復(fù)制到內(nèi)存中。

? ?其中比較重要的變量是TEXT_BASE,該變量是通過(guò)連接腳本得到的。TEXT_BASE變量需要根據(jù)開發(fā)板的情況自己修改,具體地址需要根據(jù)硬件設(shè)計(jì)確定。

? ?_start標(biāo)號(hào)一開始定義了ARM處理器7個(gè)中斷向量的向量表,對(duì)應(yīng)ARM處理器的7種模式。

由于上電一開始處理器會(huì)從0地址執(zhí)行指令,因此第一個(gè)指令直接跳轉(zhuǎn)到reset標(biāo)號(hào)。

? ?reset執(zhí)行機(jī)器初始化的一些操作,此處的跳轉(zhuǎn)指令,無(wú)論是冷啟動(dòng)還是熱啟動(dòng)開發(fā)板都會(huì)執(zhí)行reset標(biāo)號(hào)的代碼。

? ?reset也屬于一種異常模式,并且該模式的代碼不需要返回。

?


②reset標(biāo)號(hào)

? ?reset標(biāo)號(hào)的代碼在處理器啟動(dòng)的時(shí)候最先被執(zhí)行。

/** the actual reset code*/ reset:/** set the cpu to SVC32 mode*/mrs r0,cpsr //保存CPSR寄存器的值到r0寄存器bic r0,r0,#0x1f //清除中斷orr r0,r0,#0xd3msr cpsr,r0 //設(shè)置CPSR為超級(jí)保護(hù)模式 /* turn off the watchdog */ //關(guān)閉看門狗 #if defined(CONFIG_S3C2400) # define pWTCON 0x15300000 //看門狗地址 # define INTMSK 0x14400008 /* Interupt-Controller base addresses */ //中斷控制器基址 # define CLKDIVN 0x14800014 /* clock divisor register */ #elif defined(CONFIG_S3C2410) # define pWTCON 0x53000000 # define INTMSK 0x4A000008 /* Interupt-Controller base addresses */ # define INTSUBMSK 0x4A00001C # define CLKDIVN 0x4C000014 /* clock divisor register */ #endif #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)ldr r0, =pWTCON //取出當(dāng)前看門狗控制寄存器的地址到r0mov r1, #0x0 //設(shè)置r1寄存器的值為0str r1, [r0] //寫入看門狗控制寄存器/** mask all IRQs by setting all bits in the INTMR - default*/mov r1, #0xffffffff //設(shè)置r1ldr r0, =INTMSK //取出中斷屏蔽寄存器地址到r0str r1, [r0] //r1的值寫入中斷屏蔽寄存器 # if defined(CONFIG_S3C2410)ldr r1, =0x3ffldr r0, =INTSUBMSKstr r1, [r0] # endif/* FCLK:HCLK:PCLK = 1:2:4 *//* default FCLK is 120 MHz ! */ldr r0, =CLKDIVN //取出時(shí)鐘寄存器地址到r0mov r1, #3 //設(shè)置r1的值str r1, [r0] //寫入時(shí)鐘配置 #endif /* CONFIG_S3C2400 || CONFIG_S3C2410 *//** we do sys-critical inits only at reboot,* not when booting from ram!*/ #ifndef CONFIG_SKIP_LOWLEVEL_INITbl cpu_init_crit //跳轉(zhuǎn)到開發(fā)板相關(guān)初始化代碼 #endif

?

? ?注意,最后根據(jù)CONFIG_SKIP_LOWLEVEL_INIT宏的值是否跳到cpu_init_crit標(biāo)號(hào)執(zhí)行。

? ?請(qǐng)注意這里使用的是bl指令,在執(zhí)行完cpu_init_crit標(biāo)號(hào)的代碼后會(huì)返回。

?

?


③cpu_init_crit標(biāo)號(hào)

? ?cpu_init_crit標(biāo)號(hào)處的代碼初始化ARM處理器關(guān)鍵的寄存器。

/**************************************************************************** CPU_init_critical registers** setup important registers* setup memory timing***************************************************************************/ #ifndef CONFIG_SKIP_LOWLEVEL_INIT cpu_init_crit:/** flush v4 I/D caches*/mov r0, #0mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ //刷新cachemcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ //刷新TLB/** disable MMU stuff and caches //關(guān)閉MMU*/mrc p15, 0, r0, c1, c0, 0bic 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) Alignorr r0, r0, #0x00001000 @ set bit 12 (I) I-Cachemcr p15, 0, r0, c1, c0, 0/** before relocating, we have to setup RAM timing* because memory timing is board-dependend, you will* find a lowlevel_init.S in your board directory.*/mov ip, lrbl lowlevel_init //跳轉(zhuǎn)到lowlevel_initmov lr, ipmov pc, lr #endif /* CONFIG_SKIP_LOWLEVEL_INIT */

?

? ?注意刷新cache和TLB。

? ?cache是一種高速緩存存儲(chǔ)器,用于保存CPU頻繁使用的數(shù)據(jù),在使用Cache技術(shù)的處理器上,當(dāng)一條指令要訪問內(nèi)存的數(shù)據(jù)時(shí),首先查詢cache緩存中是否有數(shù)據(jù)以及數(shù)據(jù)是否過(guò)期,如果數(shù)據(jù)未過(guò)期則從cache讀出數(shù)據(jù),處理器會(huì)定期回寫cache中的數(shù)據(jù)到內(nèi)存。根據(jù)程序的局部性原理,使用cache后可以大大加快處理器訪問內(nèi)存數(shù)據(jù)的速度。

? ?TLB的作秀是在處理器訪問內(nèi)存數(shù)據(jù)的時(shí)候做地址轉(zhuǎn)換。TLB的全稱是Translation Lookaside Buffer,可以翻譯做旁路緩沖。TLB中存放了一些頁(yè)表文件,文件中記錄了虛擬地址和物理地址的映射關(guān)系。當(dāng)應(yīng)用程序訪問一個(gè)虛擬地址的時(shí)候,會(huì)從TLB中查詢出對(duì)就的物理地址,然后訪問物理地址。TLB通常是一個(gè)分層結(jié)構(gòu),使用與cache類似的原理。處理器使用一定的算法把最常用的頁(yè)表放在最先訪問的層次。

? ?MMU是內(nèi)存管理單元(Memory Management Unit)的縮寫,在現(xiàn)代計(jì)算機(jī)體系結(jié)構(gòu)上,MMU被廣泛應(yīng)用。使用MMU技術(shù)可以向應(yīng)用程序提供一個(gè)巨大的虛擬地址空間。在U-Boot初始化的時(shí)候,程序看到的地址都是物理地址,無(wú)須使用MMU。

?


④lowlevel_init標(biāo)號(hào)

? ?lowlevel_init標(biāo)號(hào),執(zhí)行與開發(fā)板相關(guān)的初始化配置。

.globl lowlevel_init lowlevel_init:/* memory control configuration *//* make r0 relative the current location so that it *//* reads SMRDATA out of FLASH rather than memory ! */ldr r0, =SMRDATA //讀取SMRDATA變量地址ldr r1, _TEXT_BASE //讀取_TEXT_BASE變量地址sub r0, r0, r1ldr r1, =BWSCON /* Bus Width Status Controller */ //讀取總線寬度寄存器add r2, r0, #13*4 //得到SMRDATA占用的大小 0:ldr r3, [r0], #4 //加載SMRDATA到內(nèi)存str r3, [r1], #4cmp r2, r0bne 0b/* everything is fine now */mov pc, lr.ltorg /* the literal pools origin */ SMRDATA: //定義SMRDATA的值.word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28)).word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)).word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC)).word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)).word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)).word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)).word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)).word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)).word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)).word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT).word 0x32.word 0x30.word 0x30

?

? ?程序中需要計(jì)算SMRDATA需要加載的內(nèi)存地址和大小。

? ?首先讀取SMRDATA的變量地址,之后計(jì)算存放的內(nèi)存地址并且記錄在r0寄存器,然后根據(jù)總線寬度計(jì)算需要加載的SMRDATA大小,并且把加載結(jié)束的地址存放在r2寄存器。

? ?最后復(fù)制SMRDATA到內(nèi)存。SMRDATA是開發(fā)板上內(nèi)存映射的配置。

?


⑤relocate標(biāo)號(hào)

? ?relocate部分的代碼負(fù)責(zé)把U-Boot Stage2的代碼從Flash存儲(chǔ)器加載到內(nèi)存。

#ifndef CONFIG_SKIP_RELOCATE_UBOOT relocate: /* relocate U-Boot to RAM */adr r0, _start /* r0 <- current position of code *///獲取當(dāng)前代碼存放地址ldr r1, _TEXT_BASE /* test if we run from flash or RAM *///獲取內(nèi)存存放代碼地址cmp r0, r1 /* don't reloc during debug *///檢查是否需要加載beq stack_setupldr r2, _armboot_start //獲取stage2代碼存放地址ldr r3, _bss_start //獲取內(nèi)存代碼段起始地址sub r2, r3, r2 /* r2 <- size of armboot */ //計(jì)算stage2代碼長(zhǎng)度add r2, r0, r2 /* r2 <- source end address */ //計(jì)算stage2代碼結(jié)束地址 copy_loop:ldmia r0!, {r3-r10} /* copy from source address [r0] *///從Flash復(fù)制代碼到內(nèi)存stmia r1!, {r3-r10} /* copy to target address [r1] */cmp r0, r2 /* until source end addreee [r2] */ble copy_loop #endif /* CONFIG_SKIP_RELOCATE_UBOOT *//* Set up the stack */ stack_setup: //在內(nèi)存中建立堆棧ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */sub r0, r0, #CFG_MALLOC_LEN /* malloc area */ //分配內(nèi)存區(qū)域sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */ #ifdef CONFIG_USE_IRQsub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endifsub sp, r0, #12 /* leave 3 words for abort-stack */ clear_bss: //初始化內(nèi)存bss段內(nèi)容為0ldr r0, _bss_start /* find start of bss segment *///查找bss段起始地址ldr r1, _bss_end /* stop here *///查找bss段結(jié)束地址mov r2, #0x00000000 /* clear */ clbss_l:str r2, [r0] /* clear loop... */add r0, r0, #4cmp r0, r1ble clbss_l #if 0/* try doing this stuff after the relocation */ldr r0, =pWTCONmov r1, #0x0str r1, [r0]/** mask all IRQs by setting all bits in the INTMR - default*/mov r1, #0xffffffffldr r0, =INTMRstr r1, [r0]/* FCLK:HCLK:PCLK = 1:2:4 *//* default FCLK is 120 MHz ! */ldr r0, =CLKDIVNmov r1, #3str r1, [r0]/* END stuff after relocation */ #endifldr pc, _start_armboot //設(shè)置程序指針為start_armboot()函數(shù)地址 _start_armboot: .word start_armboot

?

? ?程序首先檢查當(dāng)前是否在內(nèi)存中執(zhí)行代碼,根據(jù)結(jié)果決定是否需要從Flash存儲(chǔ)器加載代碼。

程序通過(guò)獲取_start和_TEXT_BASE所在的地址比較,如果地址相同說(shuō)明程序已經(jīng)在內(nèi)存中,無(wú)須加載。

? ?然后計(jì)算要加載的stage2代碼起始地址和長(zhǎng)度,然后在循環(huán)復(fù)制Flash的數(shù)據(jù)到內(nèi)存,每次可以復(fù)制8個(gè)字長(zhǎng)的數(shù)據(jù)。stage2程序復(fù)制完成后,程序設(shè)置系統(tǒng)堆棧,最后清空內(nèi)存bss段內(nèi)容。

? ?relocate程序最后在設(shè)置程序指針寄存器為start_armboot()函數(shù)地址,程序跳轉(zhuǎn)到stage2部分執(zhí)行,注意最后的定義,_start_armboot全局變量的值是C語(yǔ)言函數(shù)start_armboot()函數(shù)的地址,使用這種方式可以在匯編中調(diào)用C語(yǔ)言編寫的函數(shù)。

? ?另外,有一種NOR類型Flash存儲(chǔ)器,可以像使用內(nèi)存一樣直接執(zhí)行程序,NOR Flash被映射到地址0開始的內(nèi)存空間。

? ?注意,程序中第12行的_armboot_start即標(biāo)號(hào)⑥_armboot_start

?


⑦start_armboot()函數(shù)

? ?start_armboot()函數(shù)主要初始化ARM系統(tǒng)的硬件和環(huán)境變量,包括Flash存儲(chǔ)器、FrameBuffer、網(wǎng)卡等,最后進(jìn)入U(xiǎn)-Boot應(yīng)用程序主循環(huán)。

void start_armboot (void) {init_fnc_t **init_fnc_ptr;char *s; #ifndef CFG_NO_FLASHulong size; #endif #if defined(CONFIG_VFD) || defined(CONFIG_LCD)unsigned long addr; #endif/* 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;for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {if ((*init_fnc_ptr)() != 0) {hang ();}} #ifndef CFG_NO_FLASH/* configure available FLASH banks */size = flash_init (); //初始化Flash存儲(chǔ)器配置display_flash_config (size); //顯示Flash存儲(chǔ)器配置 #endif /* CFG_NO_FLASH */ #ifdef CONFIG_VFD # ifndef PAGE_SIZE # define PAGE_SIZE 4096 # endif/** reserve memory for VFD display (always full pages)*//* bss_end is defined in the board-specific linker script */addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); //計(jì)算FrameBuffer內(nèi)存地址size = vfd_setmem (addr); //計(jì)算FrameBuffer占用內(nèi)存大小gd->fb_base = addr; //設(shè)置FrameBuffer內(nèi)存起始地址 #endif /* CONFIG_VFD */ #ifdef CONFIG_LCD # ifndef PAGE_SIZE # define PAGE_SIZE 4096 # endif/** reserve memory for LCD display (always full pages)*//* bss_end is defined in the board-specific linker script */addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); //計(jì)算rameBuffer內(nèi)存地址size = lcd_setmem (addr); //計(jì)算FrameBuffer占用內(nèi)存大小gd->fb_base = addr; //設(shè)置FrameBuffer內(nèi)存起始地址 #endif /* CONFIG_LCD *//* armboot_start is defined in the board-specific linker script */mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); #if (CONFIG_COMMANDS & CFG_CMD_NAND)puts ("NAND: ");nand_init(); /* go init the NAND */ //初始化NAND Flash存儲(chǔ)器 #endif #ifdef CONFIG_HAS_DATAFLASHAT91F_DataflashInit(); //初始化Hash表dataflash_print_info(); #endif/* initialize environment */env_relocate (); //重新設(shè)置環(huán)境變量 #ifdef CONFIG_VFD/* must do this after the framebuffer is allocated */drv_vfd_init(); //初始化虛擬顯示設(shè)置 #endif /* CONFIG_VFD *//* IP Address */gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); //設(shè)置網(wǎng)卡的IP地址/* MAC Address */{int i;ulong reg;char *s, *e;char tmp[64];i = getenv_r ("ethaddr", tmp, sizeof (tmp)); //從網(wǎng)卡寄存器讀取MAC地址s = (i > 0) ? tmp : NULL;for (reg = 0; reg < 6; ++reg) {gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;if (s)s = (*e) ? e + 1 : e;} #ifdef CONFIG_HAS_ETH1i = getenv_r ("eth1addr", tmp, sizeof (tmp)); //讀取Hash值s = (i > 0) ? tmp : NULL;for (reg = 0; reg < 6; ++reg) {gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;if (s)s = (*e) ? e + 1 : e;} #endif}devices_init (); /* get the devices list going. */ //初始化開發(fā)板上的設(shè)備 #ifdef CONFIG_CMC_PU2load_sernum_ethaddr (); #endif /* CONFIG_CMC_PU2 */jumptable_init (); //初始化跳轉(zhuǎn)表console_init_r (); /* fully init console as a device */ //初始化控制臺(tái) #if defined(CONFIG_MISC_INIT_R)/* miscellaneous platform dependent initialisations */misc_init_r (); //初始化其他設(shè)備 #endif/* enable exceptions */enable_interrupts (); //打開中斷/* Perform network card initialisation if necessary */ #ifdef CONFIG_DRIVER_CS8900cs8900_get_enetaddr (gd->bd->bi_enetaddr); //獲取CS8900網(wǎng)卡MAC地址 #endif #if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)if (getenv ("ethaddr")) {smc_set_mac_addr(gd->bd->bi_enetaddr); //設(shè)置SMC網(wǎng)卡MAC地址} #endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 *//* Initialize from environment */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)); //保存FrameBuffer} #endif /* CFG_CMD_NET */ #ifdef BOARD_LATE_INITboard_late_init (); //開發(fā)板相關(guān)設(shè)備初始化 #endif #if (CONFIG_COMMANDS & CFG_CMD_NET) #if defined(CONFIG_NET_MULTI)puts ("Net: "); #endifeth_initialize(gd->bd); #endif/* main_loop() can return to retry autoboot, if so just run it again. */for (;;) {main_loop (); //進(jìn)入主循環(huán)}/* NOTREACHED - no way out of command loop except booting */ } void hang (void) {puts ("### ERROR ### Please RESET the board ###\n");for (;;); }

?

? ?start_armboot()函數(shù)代碼里有許多的宏相關(guān),這個(gè)根據(jù)開發(fā)板的情況進(jìn)行配置。函數(shù)里面的board_late_init()函數(shù),該函數(shù)是開發(fā)板提供的,供不同的開發(fā)板做一些特有的初始化工作。

? ?在start_armboot()函數(shù)中,使用宏開關(guān)括起來(lái)的代碼是在各種開發(fā)板是最常用的功能,如CS8900網(wǎng)卡配置。整個(gè)函數(shù)配置完畢后,進(jìn)入一個(gè)for死循環(huán),調(diào)用main_loop()函數(shù)。這里需要注意,在main_loop()函數(shù)中也有一個(gè)for死循環(huán)。

? ?start_armboot()函數(shù)使用死循環(huán)調(diào)用main_loop()函數(shù),作用是防止main_loop()函數(shù)開始的初始化代碼如果調(diào)用失敗后重新執(zhí)行初始化操作,保證程序能進(jìn)入到U-Boot的命令行。

?

?


⑧main_loop()函數(shù)

? ?main_loop()函數(shù)做的都是與具體平臺(tái)無(wú)關(guān)的工作,主要包括初始化啟動(dòng)次數(shù)限制機(jī)制、設(shè)置軟件版本號(hào)、打印啟動(dòng)信息、解析命令等。

? ??設(shè)置啟動(dòng)次數(shù)有關(guān)參數(shù)。在進(jìn)入main_loop()函數(shù)后,首先是根據(jù)配置加載已經(jīng)保留的啟動(dòng)次數(shù),并且根據(jù)配置判斷是否超過(guò)啟動(dòng)次數(shù)。

void main_loop (void) { #ifndef CFG_HUSH_PARSERstatic char lastcommand[CFG_CBSIZE] = { 0, };int len;int rc = 1;int flag; #endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)char *s;int bootdelay; #endif #ifdef CONFIG_PREBOOTchar *p; #endif #ifdef CONFIG_BOOTCOUNT_LIMITunsigned long bootcount = 0;unsigned long bootlimit = 0;char *bcs;char bcs_set[16]; #endif /* CONFIG_BOOTCOUNT_LIMIT */ #if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)ulong bmp = 0; /* default bitmap */extern int trab_vfd (ulong bitmap); #ifdef CONFIG_MODEM_SUPPORTif (do_mdm_init)bmp = 1; /* alternate bitmap */ #endiftrab_vfd (bmp); #endif /* CONFIG_VFD && VFD_TEST_LOGO */ #ifdef CONFIG_BOOTCOUNT_LIMITbootcount = bootcount_load(); //加載保存的啟動(dòng)次數(shù)bootcount++; //啟動(dòng)次數(shù)加1bootcount_store (bootcount); //更新啟動(dòng)次數(shù)sprintf (bcs_set, "%lu", bootcount); //打印啟動(dòng)次數(shù)setenv ("bootcount", bcs_set);bcs = getenv ("bootlimit");bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; //轉(zhuǎn)換啟動(dòng)次數(shù)字符串為UINT類型 #endif /* CONFIG_BOOTCOUNT_LIMIT */

?

? ?函數(shù)啟動(dòng)次數(shù)限制功能,啟動(dòng)次數(shù)限制可以被用戶設(shè)置一個(gè)啟動(dòng)次數(shù),然后保存在Flash存儲(chǔ)器的特定位置,當(dāng)?shù)竭_(dá)啟動(dòng)次數(shù)后,U-Boot無(wú)法啟動(dòng),該功能適合一些商業(yè)產(chǎn)品,通過(guò)配置不同的License限制用戶重新啟動(dòng)系統(tǒng)。

? ??接下來(lái)是Modem功能。如果系統(tǒng)中有Modem,打開該功能可以接受其他用戶通過(guò)電話網(wǎng)絡(luò)的撥號(hào)請(qǐng)求。Modem功能通常供一些遠(yuǎn)程控制的系統(tǒng)使用

#ifdef CONFIG_MODEM_SUPPORTdebug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init);if (do_mdm_init) { //判斷是否需要初始化Modemchar *str = strdup(getenv("mdm_cmd")); //獲取Modem參數(shù)setenv ("preboot", str); /* set or delete definition */if (str != NULL)free (str);mdm_init(); /* wait for modem connection */ //初始化Modem} #endif /* CONFIG_MODEM_SUPPORT */

?

? ??然后設(shè)置U-Boot版本號(hào),初始化命令自動(dòng)完成功能等。

#ifdef CONFIG_VERSION_VARIABLE{extern char version_string[];setenv ("ver", version_string); /* set version variable */ //設(shè)置版本號(hào)} #endif /* CONFIG_VERSION_VARIABLE */ #ifdef CFG_HUSH_PARSERu_boot_hush_start (); //初始化Hash功能 #endif #ifdef CONFIG_AUTO_COMPLETEinstall_auto_complete(); //初始化命令自動(dòng)完成功能 #endif #ifdef CONFIG_PREBOOTif ((p = getenv ("preboot")) != NULL) { # ifdef CONFIG_AUTOBOOT_KEYEDint prev = disable_ctrlc(1); /* disable Control C checking *///關(guān)閉Crtl+C組合鍵 # endif # ifndef CFG_HUSH_PARSERrun_command (p, 0); //運(yùn)行Boot參數(shù) # elseparse_string_outer(p, FLAG_PARSE_SEMICOLON |FLAG_EXIT_FROM_LOOP); # endif # ifdef CONFIG_AUTOBOOT_KEYEDdisable_ctrlc(prev); /* restore Control C checking *///恢復(fù)Ctrl+C組合鍵 # endif} #endif /* CONFIG_PREBOOT */

?

? ?程序開始是動(dòng)態(tài)版本號(hào)功能支持代碼,version_string變量是在其他文件定義的一個(gè)字符串變量,當(dāng)用戶改變U-Boot版本的時(shí)候會(huì)更新該變量。打開動(dòng)態(tài)版本支持功能后,U-Boot在啟動(dòng)的時(shí)候會(huì)顯示最新的版本號(hào)。

? ?install_auto_comlpete()函數(shù)設(shè)置命令行自動(dòng)完成功能,該功能與linux的shell類似,當(dāng)用戶輸入一個(gè)部分命令后,可以通過(guò)按下鍵盤上的Tab鍵補(bǔ)全命令的剩余部分,main_loop()函數(shù)不同的功能使用宏開關(guān)控制不僅能提高代碼模塊化,理主要的是針對(duì)嵌入式系統(tǒng)Flash存儲(chǔ)器大小設(shè)計(jì)的。在嵌入式系統(tǒng)上,不同的系統(tǒng)Flash存儲(chǔ)空間不同。對(duì)于一些Flash空間比較緊張的設(shè)備來(lái)說(shuō),通過(guò)宏開關(guān)關(guān)閉一些不是特別必要的功能如命令行自動(dòng)完成,可以減小U-Boot編譯后的文件大小。

? ??在進(jìn)入主循環(huán)之前,如果配置了啟動(dòng)延遲功能,需要等待用戶從串口或者網(wǎng)絡(luò)接口輸入。如果用戶按下任意鍵打斷,啟動(dòng)流程,會(huì)向終端打印出一個(gè)啟動(dòng)菜單。

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)s = getenv ("bootdelay");bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; //啟動(dòng)延遲debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); # ifdef CONFIG_BOOT_RETRY_TIMEinit_cmd_timeout (); //初始化命令行超時(shí)機(jī)制 # endif /* CONFIG_BOOT_RETRY_TIME */ #ifdef CONFIG_BOOTCOUNT_LIMITif (bootlimit && (bootcount > bootlimit)) { //檢查是否超出啟動(dòng)次數(shù)限制printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",(unsigned)bootlimit);s = getenv ("altbootcmd");}else #endif /* CONFIG_BOOTCOUNT_LIMIT */s = getenv ("bootcmd"); //獲取啟動(dòng)命令參數(shù)debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");if (bootdelay >= 0 && s && !abortboot (bootdelay)) { //檢查是否支持啟動(dòng)延遲功能 # ifdef CONFIG_AUTOBOOT_KEYEDint prev = disable_ctrlc(1); /* disable Control C checking *///關(guān)閉Ctrl+C組合鍵 # endif # ifndef CFG_HUSH_PARSERrun_command (s, 0); //運(yùn)行啟動(dòng)命令行 # elseparse_string_outer(s, FLAG_PARSE_SEMICOLON |FLAG_EXIT_FROM_LOOP); # endif # ifdef CONFIG_AUTOBOOT_KEYEDdisable_ctrlc(prev); /* restore Control C checking *///打開Ctrl+C組合鍵 # endif} # ifdef CONFIG_MENUKEYif (menukey == CONFIG_MENUKEY) { //檢查是否支持菜單鍵s = getenv("menucmd");if (s) { # ifndef CFG_HUSH_PARSERrun_command (s, 0); # elseparse_string_outer(s, FLAG_PARSE_SEMICOLON |FLAG_EXIT_FROM_LOOP); # endif}} #endif /* CONFIG_MENUKEY */ #endif /* CONFIG_BOOTDELAY */ #ifdef CONFIG_AMIGAONEG3SE{extern void video_banner(void);video_banner(); //打印啟動(dòng)圖標(biāo)} #endif

?

? ??在各功能設(shè)置完畢后,程序進(jìn)入一個(gè)for死循環(huán),該循環(huán)不斷使用readline()函數(shù)從控制臺(tái)(一般是串口)讀取用戶的輸入,然后解析,有關(guān)如何解析命令則可以參考U-Boot代碼中run_command()函數(shù)的定義。

/** Main Loop for Monitor Command Processing*/ #ifdef CFG_HUSH_PARSERparse_file_outer();/* This point is never reached */for (;;); #elsefor (;;) { //進(jìn)入命令行超時(shí) #ifdef CONFIG_BOOT_RETRY_TIMEif (rc >= 0) {/* Saw enough of a valid command to* restart the timeout.*/reset_cmd_timeout(); //設(shè)置命令行超時(shí)} #endiflen = readline (CFG_PROMPT); //讀取命令flag = 0; /* assume no special flags for now */if (len > 0)strcpy (lastcommand, console_buffer);else if (len == 0)flag |= CMD_FLAG_REPEAT; #ifdef CONFIG_BOOT_RETRY_TIMEelse if (len == -2) {/* -2 means timed out, retry autoboot*/puts ("\nTimed out waiting for command\n"); # ifdef CONFIG_RESET_TO_RETRY/* Reinit board to run initialization code again */do_reset (NULL, 0, 0, NULL); # elsereturn; /* retry autoboot */ # endif} #endifif (len == -1)puts ("<INTERRUPT>\n");elserc = run_command (lastcommand, flag); //運(yùn)行命令if (rc <= 0) {/* invalid command or not repeatable, forget it */lastcommand[0] = 0;}} #endif /*CFG_HUSH_PARSER*/ }

?


結(jié)束語(yǔ)

? ?整個(gè)U-Boot的啟動(dòng)流程代碼,最關(guān)鍵的就是這些了,其中主要語(yǔ)句都作了相應(yīng)注釋,另外我把自己注釋后的四個(gè)源文件上傳到附件,以備查看。

? ?如果有人覺得哪里注釋沒對(duì),歡迎留言探討。

?

?

本文出自 “成鵬致遠(yuǎn)” 博客,請(qǐng)務(wù)必保留此出處http://infohacker.blog.51cto.com/6751239/1202976

轉(zhuǎn)載于:https://www.cnblogs.com/lcw/p/3159399.html

總結(jié)

以上是生活随笔為你收集整理的【Bootloader】探究bootloader,分析u-boot源码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。