簡介===>
1.U-Boot系統加載器
U-Boot是一個規模龐大的開源Bootloader軟件,最初是由denx(www.denx.de)發起。U-Boot的前身是PPCBoot,目前是SourceForge(www.sourceforge.net)的一個項目。
最初的U-Boot僅支持PowerPC架構的系統,稱做PPCBoot。從0.3.2官方版本之后開始逐步支 持多種架構的處理器,目前可以支持 PowerPC(MPC5xx、MPC8xx、MPC82xx、MPC7xx、MPC74xx)、ARM(ARM7、ARM9、StrongARM、 Xscale)、MIPS(4kc、5kc)、X86等處理器,支持的嵌入式操作系統有Linux、Vx-Works、NetBSD、QNX、 RTEMS、ARTOS、LynxOS等,是PowerPC、ARM9、Xscale、X86等系統通用的Boot方案。
U-Boot支持的處理器和操作系統很多,但是它對PowerPC系列處理器和Linux操作系統支持最好。U-Boot支持的功能也較多,對于嵌 入式開發常用的查看、修改內存,從網絡下載操作系統鏡像等功能都提供了很好的支持。U-Boot的項目更新較快,支持的目標板眾多,是學習底層開發很好的 示例。
2.ViVi系統加載器
ViVi是韓國的mizi公司專門針對ARM9處理器設計的一款Bootloader。它的特點是操作簡便,同時提供了完備的命令體系,目前在三星系列的ARM9處理器上ViVi也比較流行。
與U-Boot相比,由于ViVi支持的處理器單一,ViVi的代碼也要小很多。同時,ViVi的軟件架構和配置方法采用和Linux內核類似的風格,對于有過配置編譯Linux內核經驗的讀者,ViVi更容易上手。
與其他的Bootloader一樣,ViVi有兩種工作模式:啟動加載模式和下載模式。使用啟動加載模式,在目標板上電后,ViVi會從預先配置好 的Flash分區讀取Linux或者其他系統的鏡像并且啟動系統;使用下載模式,ViVi向用戶提供了一個命令行接口,通過該接口用戶可以使用ViVi提 供的命令。ViVi主要提供了5個命令如下:
Load:把二進制文件載入Flash或RAM。
Part:操作MTD分區信息。顯示、增加、刪除、復位、保存MTD分區。
Param:設置參數。
Boot:啟動系統。
Flash:管理Flash,如刪除Flash的數據。
與Linux內核的組織類似,ViVi的源代碼主要包括arch、init、lib、drivers和include等幾個目錄,共200多個代碼文件。各目錄的具體功能請參考ViVi相關的信息。
=====================================================================》》》
=====================================================================》》》
=====================================================================》》》
?
基本目錄分類:
common目錄?是與體系結構無關的文件,包括實現各種命令的C語言源代碼文件。
cpu目錄??????????存 放與CPU相關的文件,每種CPU需要的代碼文件存放在以CPU名稱命名的子目錄下,arm920t存放了arm920t為內核的 CPU相關的文件。在每個特定的子目錄下都包括cpu.c、interrupt.c和start.S這3個文件,這3個文件是CPU初始化以及配置中斷的 代碼。U-Boot自帶了很多CPU相關的代碼,用戶可以在現有CPU支持的基礎上修改自己所需要的配置。
通用設備的驅動程序存放在drivers目錄下。U-Boot自帶了許多設備的驅動,包括顯示芯片、網絡接口控制器、USB控制器、I2C器件等,對于大多數用戶而言已經夠用,用戶也可以按照自己的需求增加或者修改設備驅動。
fs????????????????? ??存放支持的文件系統代碼,U-Boot目前支持cramfs、ext2、fat、jffs、reiserfs、yaffs等多種常見的文件系統。
net目錄??????? ?是與網絡協議有關的代碼,比如BOOTP協議、TFTP協議、RARP協議等。
post????????????? ?存放與硬件自檢有關的代碼。
rtc目錄??????????存放與硬件實時時鐘相關的代碼。
tools目錄??????存放U-Boot編譯過程中用到的一些工具代碼。? // 例如:mkimage
==========================================
?
hao:
start_armboot => bootm.c
向量表在_start開始階段已經匯編搞定。
其實主要就是個寄存器和內存的基本處理。
===》》》
列出了U-Boot在ARM處理器啟動過程中的幾個關鍵點,
從圖中看出U-Boot的啟動代碼分布在start.S、low_level_init.S、 board.c和main.c文件中。
start.S????????????????????是U-Boot整個程序的入口,該文件使用匯編語言編寫,不同體系結構的啟動代碼是不同 的;
low_level_init.S????是特定開發板的設置代碼;
board.c??????????????????包含開發板底層設備驅動;
main.c是一個與平臺無關的代碼,U-Boot應用程序的入口在此文件中。
取出CPSR寄存器的值,CPSR寄存器保存當前系統狀態,
使用比特清除命令清空了CPSR寄存器的中斷控制位,表示清除 中斷。
設置了CPSR寄存器的處理器模式位為管理模式,然后在第117行寫入 CPSR的值強制切換處理器為超級保護模式。
定義看門狗控制器有關的變量,
根據平臺設置看門狗定時器。
設置時鐘分頻寄存器的值。
需要根據CONFIG_SKIP_LOWLEVEL_INIT宏的值是否跳轉到cpu_init_crit標號執行
===========cpu_init_crit==========
????????????????????????????????????????????????????????????????cpu_init_crit標號處的代碼初始化ARM處理器關鍵的寄存器
228?/* ?229??***************************************************************** ?230??* ?231??*?CPU_init_critical?registers ?232??* ?233??*?setup?important?registers ?234??*?setup?memory?timing ?235??* ?236??****************************************************************** ?237??*/ ?238? ?239? ?240?#ifndef?CONFIG_SKIP_LOWLEVEL_INIT ?241?cpu_init_crit: ?242???/* ?243????*?flush?v4?I/D?caches ?244????*/ ?245???mov?r0,?#0 ?246???mcr?p15,?0,?r0,?c7,?c7,?0?/*?flush?v3/v4?cache?*/?????//?1.刷新cache ?247???mcr?p15,?0,?r0,?c8,?c7,?0?/*?flush?v4?TLB?*/??????????//?2.刷新TLB ?248? ?249???/* ?250????*?disable?MMU?stuff?and?caches???????????????????????//?3.關閉MMU??251????*/ ?252???mrc?p15,?0,?r0,?c1,?c0,?0 ?253???bic?r0,?r0,?#0x00002300?@?clear?bits?13,?9:8?(--V-?--RS) ?254???bic?r0,?r0,?#0x00000087?@?clear?bits?7,?2:0?(B---?-CAM) ?255???orr?r0,?r0,?#0x00000002?@?set?bit?2?(A)?Align ?256???orr?r0,?r0,?#0x00001000?@?set?bit?12?(I)?I-Cache ?257???mcr?p15,?0,?r0,?c1,?c0,?0 ?258? ?259???/* ?260????*?before?relocating,?we?have?to?setup?RAM?timing ?261????*?because?memory?timing?is?board-dependend,?you?will ?262????*?find?a?lowlevel_init.S?in?your?board?directory. ?263????*/ ?264???mov?ip,?lr ?265???bl??lowlevel_init???//?跳轉到lowlevel_init ?266???mov?lr,?ip ?267???mov?pc,?lr ?268?#endif?/*?CONFIG_SKIP_LOWLEVEL_INIT?*/?
?
?
==>2.
TLB的作用是在處理器訪問內存數據的時候做地址轉換。TLB的全稱是Translation Lookaside Buffer,可以翻譯做旁路緩沖。
TLB中存放了一些頁表文件,文件中記錄了虛擬地址和物理地址的映射關系。當應用程序訪問一個虛擬地址的時候,會從 TLB中查詢出對應的物理地址,然后訪問物理地址。TLB通常是一個分層結構,使用與Cache類似的原理。處理器使用一定的算法把最常用的頁表放在最先 訪問的層次。
==>3.
程序第252~257行關閉MMU。MMU是內存管理單元(Memory Management Unit)的縮寫。在現代計算機體系結構上,MMU被廣泛應用。使用MMU技術可以向應用程序提供一個巨大的虛擬地址空間。在U-Boot初始化的時候, 程序看到的地址都是物理地址,無須使用MMU。
?
?
=========================lowlevel_init=========================??
位于board/smdk2410/lowlevel_init.S文件 ???????????????????????????????????????? 開發板相關的初始化配置 133?lowlevel_init: ?134???/*?memory?control?configuration?*/ ?135???/*?make?r0?relative?the?current?location?so?that?it?*/ ?136???/*?reads?SMRDATA?out?of?FLASH?rather?than?memory?!?*/ ?137???ldr???? ?r0, ? =SMRDATA??????????????????????//?讀取SMRDATA變量地址 ?138???ldr ?r1,? _TEXT_BASE????????????????????????//?讀取_TEXT_BASE變量地址 ?139???sub????r0, ?r0, r1?????????????????????????????????// 得出相對偏移140???ldr ???? r1, ?=BWSCON????????????????????????// 主要是了解BANK的位寬,16位141???add????r2,?????r0,? #13*4??? ????????????????? //?得到SMRDATA占用的大小,結尾處的偏移
142?0: ?143???ldr?????r3,?[r0],?#4????????????????? ?????????????//?加載SMRDATA到內存 ?,相當于一個while循環144???str?????r3,?[r1],?#4 ?145???cmp?????r2,?r0 ?146???bne?????0b??????????????????????????????????????? // 循環,相當于while循環147? ?148???/*?everything?is?fine?now?*/ ?149???mov?pc,?lr ?152?/*?the?literal?pools?origin?*/ ?153? ?154?SMRDATA:????????????//?定義SMRDATA值 ?155?????.word?(0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON? ?????????<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28)) ?156?????.word?((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_?Tcoh<<6)+ ?????????(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) ?157?????.word?((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+? ?????????(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC)) ?158?????.word?((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+? ?????????(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)) ?159?????.word?((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+? ?????????(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)) ?160?????.word?((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+? ?????????(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)) ?161?????.word?((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+? ?????????(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)) ?162?????.word?((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) ?163?????.word?((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) ?164?????.word?((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+? ?????????(Tchr<<16)+REFCNT) ?165?????.word?0x32 ?166?????.word?0x30 ?167?????.word?0x30? 程序第137~141行計算SMRDATA需要加載的內存地址和大小。首先在137行讀取SMRDATA的變量地址,之后計算存放的內存地址并且記錄在r0寄存器,然后根據總線寬度計算需要加載的SMRDATA大小,并且把加載結束地址存放在r2寄存器。
程序第142~146行復制SMRDATA到內存。SMRDATA是開發板上內存映射的配置
正式開始了第二階段:
relocate部分的代碼負責把U-Boot Stage2的代碼從Flash存儲器加載到內存,代碼如下:
163?#ifndef?CONFIG_SKIP_RELOCATE_UBOOT ?164?relocate:???????/*?relocate?U-Boot?to?RAM?????*/ ?165???adr? r0,? _start???? /*?r0?<-?current?position?of?code???*/?? ????????????????????????????//?獲取當前代碼存放地址 ?00000000166???ldr ?r1, _TEXT_BASE????/*?test?if?we?run?from?flash?or?RAM?*/???????????????????????????????//?獲取內存存放代碼地址 ?33f80000167???cmp?????r0,? r1????????/*?don't?reloc?during?debug?????????*/ ??????????????????????????????//地址相同說明程序已經在內存中?則不需要加載??168???beq?????stack_setup??169???//開始加載
170???ldr? r2,?_armboot_start????//?獲取stage2代碼存放地址??171???ldr r3,? _bss_start??????????//?獲取內存代碼段起始地址 ?172???sub ?r2, ?r3, r2?? ??????? /*?r2?<-?size?of?armboot??*/?? ??????????????????????????????????// 不包括向量表,U-BOOT的整個大小?
173???add ?r2, ?r0, ?r2??? /*?r2?<-?source?end?address?????????*/?? ????????????????????????33f80000 size???????? //?計算stage2代碼結束地址?174? ?175?copy_loop: ?176???ldmia ?r0!, ?{r3-r10}?? /*?copy?from?source?address?[r0]????*/ ???????????????????????????????//?從Flash復制代碼到內存 ?177???stmia r1!, ?{r3-r10}??? /*?copy?to???target?address?[r1]????*/ ?178???cmp? r0, ?r2???? ?????? /*?until?source?end?addreee?[r2]????*/ ?179???ble ?copy_loop ?180?#endif? ?/*?CONFIG_SKIP_RELOCATE_UBOOT?*/??181? ?182?? /*?Set?up?the?stack?*/?? ???????//?在內存中建立堆棧 ?183?stack_setup: ?184???ldr?r0,?_TEXT_BASE???????????????/*?upper?128?KiB:?relocated?uboot???*/ ?185???sub?r0,?r0,?#CFG_MALLOC_LEN ? ?? /*?malloc?area????*/??//?分配內存區域 ?186???sub?r0,?r0,?#CFG_GBL_DATA_SIZE ? /*?bdinfo??*/ ?187?#ifdef?CONFIG_USE_IRQ ?188???sub?r0,?r0,?#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) ?189?#endif ?190???sub?sp,?r0,?#12??? ? /*?leave?3?words?for?abort-stack????*/ ?191? ?192?clear_bss:?????????????????//?初始化內存bss段內容為0 ?193???ldr?r0,?_bss_start???? ? /*?find?start?of?bss?segment????????*/ ????????????????????????????????//?查找bss段起始地址 ?194???ldr? r1,? _bss_end??? ?? /*?stop?here??????*/??//?查找bss段結束地址 ?195???mov???r2,? #0x00000000???/*?clear???*/?//?清空bss段內容 ?196? ?197? clbss_l: str?r2,?[r0]???? /*?clear?loop...????????????????????*/ ?198???add ?r0, ?r0, ?#4 ?199???cmp ?r0, ?r1 ?200???ble? clbss_l ? 223???ldr? pc,? _start_armboot?? ??//?設置程序指針為start_armboot()函數地址 ?224? ?225?_start_armboot:.word??start_armboot //這里是個C的函數名字,也就是入口地址
代碼解釋:
????程序首先在165~168行檢查當前是否在內存中執行代碼,根據結果決定是否需要從Flash存儲器加載代碼。程序通過獲取_start和_TEXT_BASE所在的地址比較,如果地址相同說明程序已經在內存中,無須加載。
??? 程序第170~173行計算要加載的Stage2代碼起始地址和長度,然后在第176~179行循環復制Flash的數據到內存,每次可以復制8個字長的數據。
??? Stage2程序復制完畢后,程序第184~190行設置系統堆棧,最后在第193~200行清空內存bss段的內容。
??? relocate程序最后在223行設置程序指針寄存器為start_armboot()函數地址,程序跳轉到Stage2部分執行。請注意第 225行的定義,_start_armboot全局變量的值是C語言函數start_armboot()函數的地址,使用這種方式可以在匯編中調用C語言?編寫的函數。???
這里就是之前啟動的C的函數:start_armboot()
<lib_arm/board.c>
start_armboot()函數主要初始化ARM系統的硬件和環境變量,包括Flash存儲器、FrameBuffer、網卡等,最后進入U-Boot應用程序主循環。
=========================================
236?void?start_armboot?(void) ?237?{ ?238????init_fnc_t?**init_fnc_ptr; ?239??? char?*s; ?240?#ifndef?CFG_NO_FLASH ?241?? ulong?size; ?242?#endif ?243?#if?defined(CONFIG_VFD)?||?defined(CONFIG_LCD) ?244?? ?unsigned?long?addr; ?245?#endif ?246? ?247???/*?Pointer?is?writable?since?we?allocated?a?register?for?it?*/??248???gd?=?(gd_t*)(_armboot_start?-?CFG_MALLOC_LEN?-?sizeof(gd_t)); ?//全局的系統初始化參數249???/*?compiler?optimization?barrier?needed?for?GCC?>=?3.4?*/??250???__asm__?__volatile__("":?:?:"memory"); ?251? ?252???memset( (void*)gd, ?0,? sizeof?(gd_t) ); ?253???gd->bd?=?(bd_t*)( (char*)gd ?- ?sizeof(bd_t) ); ?254???memset?( gd->bd,? 0, ?sizeof?(bd_t) ); ??//初始化了板子的參數255? ?256???monitor_flash_len?=?_bss_start?-?_armboot_start; ?257? ?258???for?(init_fnc_ptr?=?init_sequence; ?*init_fnc_ptr; ?++init_fnc_ptr)?{ ?259???? ?if?((*init_fnc_ptr)()?!=?0)?{ ?260??????? hang?(); ?261?????} ?262???} ?263? ?264?#ifndef?CFG_NO_FLASH ?265???/*?configure?available?FLASH?banks?*/ ?266?? ?size?=?flash_init?();????????? //?初始化?Flash存儲器?配置 ?267??? display_flash_config?(size);?? //?顯示?Flash存儲器?配置 ?268?#endif?/*?CFG_NO_FLASH?*/ ?269? ?270?#ifdef?CONFIG_VFD??271?#?ifndef?PAGE_SIZE ?272?#???define?PAGE_SIZE?4096 ?273?#?endif ?274???/* ?275????*?reserve?memory?for?VFD?display?(always?full?pages) ?276????*/ ?277???/*?bss_end?is?defined?in?the?board-specific?linker?script?*/ ?278???addr?=?(_bss_end?+?(PAGE_SIZE?-?1))?&?~(PAGE_SIZE?-?1); ?????????????????????????????????????????????//?計算FrameBuffer?內存地址??279???size?=?vfd_setmem?(addr);?????????????//?設置FrameBuffer?占用內存大小?280???gd->fb_base?=?addr;???????????????? ?//?設置FrameBuffer?內存起始地址??281?#endif?/*?CONFIG_VFD?*/??282? ?283?#ifdef?CONFIG_LCD??284?#?ifndef?PAGE_SIZE ?285?#???define?PAGE_SIZE?4096 ?286?#?endif ?287???/* ?288????*?reserve?memory?for?LCD?display?(always?full?pages) ?289????*/ ?290???/*?bss_end?is?defined?in?the?board-specific?linker?script?*/ ?291???addr?=?(_bss_end?+?(PAGE_SIZE?-?1))?&?~(PAGE_SIZE?-?1); ?????????????????????????????????????????????//?計算FrameBuffer內存地址 ?292???size?=?lcd_setmem?(addr);?????????????//?設置FrameBuffer大小 ?293???gd->fb_base?=?addr;?????????????????? //?設置FrameBuffer內存起始地址 ?294?#endif?/*?CONFIG_LCD?*/??295? ?296???/*?armboot_start?is?defined?in?the?board-specific?linker?script?*/ ?297???mem_malloc_init?(_armboot_start?-?CFG_MALLOC_LEN); ?298? ?299?#if?(CONFIG_COMMANDS?&?CFG_CMD_NAND) ?300?? ?puts?("NAND:??"); ?301?? nand_init();????/*?go?init?the?NAND?*/??? ?//?初始化NAND?Flash存儲器 ?302?#endif ?303? ?304?#ifdef?CONFIG_HAS_DATAFLASH ?305???AT91F_DataflashInit();???????????????? //?初始化Hash表??306???dataflash_print_info(); ?307?#endif ?308? ?309???/*?initialize?environment?*/ ?310???env_relocate?();?????????????????????????????????? //?重新設置環境變量??311????? ?312?#ifdef?CONFIG_VFD ?313???/*?must?do?this?after?the?framebuffer?is?allocated?*/ ?314???drv_vfd_init();?????????????????????????????????? ?//?初始化虛擬顯示設備 ?315?#endif?/*?CONFIG_VFD?*/ ?316? ?317???/*?IP?Address?*/ ?318???gd->bd->bi_ip_addr?=?getenv_IPaddr?("ipaddr");???? //?設置網卡的IP地址 ?319? ?320???/*?MAC?Address?*/ ?321???{ ?322?????int?i; ?323?????ulong?reg; ?324?????char?*s,?*e; ?325?????char?tmp[64]; ?326? ?327?????i?=?getenv_r?("ethaddr",?tmp,?sizeof?(tmp));??? ? //?從網卡寄存器讀取 ?MAC地址 ?328?????s?=?(i?>?0)???tmp?:?NULL; ?329? ?330?????for?(reg?=?0;?reg?<?6;?++reg)?{ ?331???????gd->bd->bi_enetaddr[reg]?=?s???simple_strtoul?(s,?&e,?16)?:?0; ?332???????if?(s) ?333?????????s?=?(*e)???e?+?1?:?e; ?334?????} ?335? ?336?#ifdef?CONFIG_HAS_ETH1 ?337?????i?=?getenv_r?("eth1addr",?tmp,?sizeof?(tmp));?? ?//?讀取Hash值 ?338?????s?=?(i?>?0)???tmp?:?NULL; ?339? ?340?????for?(reg?=?0;?reg?<?6;?++reg)?{ ?341???????gd->bd->bi_enet1addr[reg]?=?s???simple_strtoul?(s,?&e,?16)?:?0; ?342???????if?(s) ?343?????????s?=?(*e)???e?+?1?:?e; ?344?????} ?345?#endif ?346???} ?347? ?348???devices_init?();?? /*?get?the?devices?list?going.?*/ ???????????????????????????????????????????//?初始化開發板上的設備??349? ?350?#ifdef?CONFIG_CMC_PU2 ?351???load_sernum_ethaddr?(); ?352?#endif?/*?CONFIG_CMC_PU2?*/ ?353? ?354???jumptable_init?();???????????????? //?初始化跳轉表??355? ?356???console_init_r?();?? /*?fully?init?console?as?a?device?*/ ??????????????????????????? //?初始化控制臺??357? ?358?#if?defined(CONFIG_MISC_INIT_R) ?359???/*?miscellaneous?platform?dependent?initialisations?*/ ?360???misc_init_r?();??????????????????? //?初始化其他設備??361?#endif ?362? ?363???/*?enable?exceptions?*/ ?364???enable_interrupts?();????????????? //?打開中斷??365? ?366???/*?Perform?network?card?initialisation?if?necessary?*/ ?367?#ifdef?CONFIG_DRIVER_CS8900??368???cs8900_get_enetaddr?(gd->bd->bi_enetaddr);????//?獲取CS8900網卡MAC地址 ?369?#endif??370? ?371?#if?defined(CONFIG_DRIVER_SMC91111)?||?defined?(CONFIG_DRIVER_? ?LAN91C96) ?372???if?(getenv?("ethaddr"))?{ ?373?????smc_set_mac_addr(gd->bd->bi_enetaddr);??????//?設置SMC網卡MAC地址 ?374???} ?375?#endif?/*?CONFIG_DRIVER_SMC91111?||?CONFIG_DRIVER_LAN91C96?*/ ?376? ?377???/*?Initialize?from?environment?*/ ?378???if?((s?=?getenv?("loadaddr"))?!=?NULL)?{ ?379?????load_addr?=?simple_strtoul?(s,?NULL,?16); ?380???} ?381?#if?(CONFIG_COMMANDS?&?CFG_CMD_NET) ?382???if?((s?=?getenv?("bootfile"))?!=?NULL)?{ ?383?????copy_filename?(BootFile,?s,?sizeof?(BootFile)); ?//?保存FrameBuffer ?384???} ?385?#endif??/*?CFG_CMD_NET?*/ ?386? ?387?#ifdef?BOARD_LATE_INIT ?388???board_late_init?();?????????????????????????? //?開發板相關設備初始化 ?389?#endif ?390?#if?(CONFIG_COMMANDS?&?CFG_CMD_NET) ?391?#if?defined(CONFIG_NET_MULTI) ?392???puts?("Net:???"); ?393?#endif ?394???eth_initialize(gd->bd); ?395?#endif ?396???/*?main_loop()?can?return?to?retry?autoboot,?if?so?just?run?it?again.?*/ ?397???for?(;;)?{ ? 398?????main_loop?();???????????????????????????????//?進入主循環 ?399???} ?400? ?401???/*?NOTREACHED?-?no?way?out?of?command?loop?except?booting?*/ ?402?}? ??? start_armboot()函數代碼里有許多的宏開關,供用戶根據自己開發板的情況進行配置。在start_armboot()函數第388行調用board_late_init()函數,該函數是開發板提供的,供不同的開發板做一些特有的初始化工作。
??? 在start_armboot()函數中,使用宏開關括起來的代碼是在各種開發板上最常用的功能,如 CS8900網卡配置。整個函數配置完畢后,進 入一個for死循環,調用main_loop()函數。請讀者注意,在main_loop()函數中也有一個for死循環。 start_armboot()函數使用死循環調用main_loop()函數,作用是防止main_loop()函數開始的初始化代碼如果調用失敗后重 新執行初始化操作,保證程序能進入到U-Boot的命令行。
??? main_loop()函數做的都是與具體平臺無關的工作,主要包括初始化啟動次數限制機制、設置軟件版本號、打印啟動信息、解析命令等。
(1)設置啟動次數有關參數。在進入main_loop()函數后,首先是根據配置加載已經保留的啟動次數,并且根據配置判斷是否超過啟動次數。代碼如下:
295?void?main_loop?(void) ?296?{ ?297?#ifndef?CFG_HUSH_PARSER ?298???static?char?lastcommand[CFG_CBSIZE]?=?{?0,?}; ?299???int?len; ?300???int?rc?=?1; ?301???int?flag; ?302?#endif ?303? ?304?#if?defined(CONFIG_BOOTDELAY)?&&?(CONFIG_BOOTDELAY?>=?0) ?305???char?*s; ?306???int?bootdelay; ?307?#endif ?308?#ifdef?CONFIG_PREBOOT ?309???char?*p; ?310?#endif ?311?#ifdef?CONFIG_BOOTCOUNT_LIMIT ?312???unsigned?long?bootcount?=?0; ?313???unsigned?long?bootlimit?=?0; ?314???char?*bcs; ?315???char?bcs_set[16]; ?316?#endif?/*?CONFIG_BOOTCOUNT_LIMIT?*/ ?317? ?318?#if?defined(CONFIG_VFD)?&&?defined(VFD_TEST_LOGO) ?319???ulong?bmp?=?0;????/*?default?bitmap?*/ ?320???extern?int?trab_vfd?(ulong?bitmap); ?321? ?322?#ifdef?CONFIG_MODEM_SUPPORT ?323???if?(do_mdm_init) ?324?????bmp?=?1;??/*?alternate?bitmap?*/ ?325?#endif ?326???trab_vfd?(bmp); ?327?#endif??/*?CONFIG_VFD?&&?VFD_TEST_LOGO?*/ ?328? ?329?#ifdef?CONFIG_BOOTCOUNT_LIMIT ?330???bootcount?=?bootcount_load();?????????//?加載保存的啟動次數 ?331???bootcount++;????????????????????????? //?啟動次數加1 ?332???bootcount_store?(bootcount);??????????//?更新啟動次數 ?333???sprintf?(bcs_set,?"%lu",?bootcount);??//?打印啟動次數 ?334???setenv?("bootcount",?bcs_set); ?335???bcs?=?getenv?("bootlimit"); ?336???bootlimit?=?bcs???simple_strtoul?(bcs,?NULL,?10)?:?0; ???????????????????????????????????????????? //?轉換啟動次數字符串為UINT類型 ?337?#endif?/*?CONFIG_BOOTCOUNT_LIMIT?*/? 第329~337行是啟動次數限制功能,啟動次數限制可以被用戶設置一個啟動次數,然后保存在Flash存儲器的特定位置,當到達啟動次數后,U-Boot無法啟動。該功能適合一些商業產品,通過配置不同的License限制用戶重新啟動系統。
(2)程序第339~348行是Modem功能。如果系統中有Modem,打開該功能可以接受其他用戶通過電話網絡的撥號請求。Modem功能通常供一些遠程控制的系統使用,代碼如下:
339?#ifdef?CONFIG_MODEM_SUPPORT ?340???debug?("DEBUG:?main_loop:???do_mdm_init=%d\n",?do_mdm_init); ?341???if?(do_mdm_init)?{????????????????????????????//?判斷是否需要初始化Modem ?342?????char?*str?=?strdup(getenv("mdm_cmd"));??????//?獲取Modem參數 ?343?????setenv?("preboot",?str);??/*?set?or?delete?definition?*/??344?????if?(str?!=?NULL) ?345???????free?(str); ?346?????mdm_init(); ??/*?wait?for?modem?connection?*/?//?初始化Modem ?347???} ?348?#endif???/*?CONFIG_MODEM_SUPPORT?*/
(3)接下來設置U-Boot的版本號,初始化命令自動完成功能等。代碼如下:
350?#ifdef?CONFIG_VERSION_VARIABLE ?351???{ ?352?????extern?char?version_string[]; ?353? ?354?????setenv?("ver",?version_string);???/*?set?version?variable?*/? ??????????????????????????????????????????????//?設置版本號??355???} ?356?#endif?/*?CONFIG_VERSION_VARIABLE?*/ ?357? ?358?#ifdef?CFG_HUSH_PARSER ?359???u_boot_hush_start?();?????????????????//?初始化Hash功能 ?360?#endif ?361? ?362?#ifdef?CONFIG_AUTO_COMPLETE??363???install_auto_complete();??????????????//?初始化命令自動完成功能??364?#endif??365? ?366?#ifdef?CONFIG_PREBOOT ?367???if?((p?=?getenv?("preboot"))?!=?NULL)?{ ?368?#?ifdef?CONFIG_AUTOBOOT_KEYED ?369?????int?prev?=?disable_ctrlc(1);???/*?disable?Control?C?checking?*/ ????????????????????????????????????????????//?關閉Crtl+C組合鍵?370?#?endif ?371? ?372?#?ifndef?CFG_HUSH_PARSER ?373?????run_command?(p,?0);??//?運行Boot參數 ?374?#?else ?375?????parse_string_outer(p,?FLAG_PARSE_SEMICOLON?| ?376?????????????FLAG_EXIT_FROM_LOOP); ?377?#?endif ?378? ?379?#?ifdef?CONFIG_AUTOBOOT_KEYED ?380?????disable_ctrlc(prev);??/*?restore?Control?C?checking?*/ ????????????????????????????????????????????//?恢復Ctrl+C組合鍵 ?381?#?endif ?382???} ?383?#endif?/*?CONFIG_PREBOOT?*/? 程序第350~356行是動態版本號功能支持代碼,version_string變量是在其他文件定義的一個字符串變量,當用戶改變U-Boot版本的時候會更新該變量。打開動態版本支持功能后,U-Boot在啟動的時候會顯示最新的版本號。
程序第363行設置命令行自動完成功能,該功能與Linux的shell類似,當用戶輸入一部分命令后,可以通過按下鍵盤上的Tab鍵補全命令的剩 余部分。
main_loop()函數不同的功能使用宏開關控制不僅能提高代碼模塊化,更主要的是針對嵌入式系統Flash存儲器大小設計的。在嵌入式系統 上,不同的系統Flash存儲空間不同。對于一些Flash空間比較緊張的設備來說,通過宏開關關閉一些不是特別必要的功能如命令行自動完成,可以減小 U-Boot編譯后的文件大小。
?
(4)在進入主循環之前,如果配置了啟動延遲功能,需要等待用戶從串口或者網絡接口輸入。如果用戶按下任意鍵打斷,啟動流程,會向終端打印出一個啟動菜單。代碼如下:
385?#if?defined(CONFIG_BOOTDELAY)?&&?(CONFIG_BOOTDELAY?>=?0) ?386???s?=?getenv?("bootdelay"); ?387???bootdelay?=?s???(int)simple_strtol(s,?NULL,?10)?:?CONFIG_BOOTDELAY; ??????????????????????????????????????????????????????????//?啟動延遲 ?388? ?389???debug?("###?main_loop?entered:?bootdelay=%d\n\n",?bootdelay); ?390? ?391?#?ifdef?CONFIG_BOOT_RETRY_TIME ?392???init_cmd_timeout?();????? ? //?初始化命令行超時機制 ?393?#?endif ?/*?CONFIG_BOOT_RETRY_TIME?*/ ?394? ?395?#ifdef?CONFIG_BOOTCOUNT_LIMIT??????? //一般不會檢查這破玩意。396???if?(bootlimit?&&?(bootcount?>?bootlimit))?{ ?//?檢查是否超出啟動次數限制 ?397?????printf?("Warning:?Bootlimit?(%u)?exceeded.?Using?altbootcmd.\n", ?398?????????????(unsigned)bootlimit); ?399?????s?=?getenv?("altbootcmd"); ?400???} ?401???else ?402?#endif ?/*?CONFIG_BOOTCOUNT_LIMIT?*/ ?403?????s?=?getenv?("bootcmd"); ?//?獲取啟動命令參數 ?404? ?405???debug?("###?main_loop:?bootcmd=\"%s\"\n",?s???s?:?"<UNDEFINED>"); ?406? ?407???if?(bootdelay?>=?0?&&?s?&&?!abortboot?(bootdelay))?{?? ???????????????????????????????????????????????????? ?//檢查是否支持啟動延遲功能 ?408?#?ifdef?CONFIG_AUTOBOOT_KEYED ?409?????int?prev?=?disable_ctrlc(1);??/*?disable?Control?C?checking?*/?? ??????????????????????????????????????????????????? ? //?關閉Ctrl+C組合鍵 ?410?#?endif ?411? ?412?#?ifndef?CFG_HUSH_PARSER ?413?????run_command?(s,?0);????? //?運行啟動命令行 ?414?#?else ?415?????parse_string_outer(s,?FLAG_PARSE_SEMICOLON?| ?416?????????????FLAG_EXIT_FROM_LOOP); ?417?#?endif ?418? ?419?#?ifdef?CONFIG_AUTOBOOT_KEYED ?420?????disable_ctrlc(prev);?? /*?restore?Control?C?checking?*/ ????????????????????????????????????????????????????? //?打開Ctrl+C組合鍵 ?421?#?endif ?422???} ?423? ?424?#?ifdef?CONFIG_MENUKEY ?425???if?(menukey?==?CONFIG_MENUKEY)?{? ?//?檢查是否支持菜單鍵 ?426???????s?=?getenv("menucmd"); ?427???????if?(s)?{ ?428?#?ifndef?CFG_HUSH_PARSER ?429?????run_command?(s,?0); ?430?#?else ?431?????parse_string_outer(s,?FLAG_PARSE_SEMICOLON?| ?432?????????????FLAG_EXIT_FROM_LOOP); ?433?#?endif ?434???????} ?435???} ?436?#endif?/*?CONFIG_MENUKEY?*/ ?437?#endif??/*?CONFIG_BOOTDELAY?*/ ?438? ?439?#ifdef?CONFIG_AMIGAONEG3SE ?440???{ ?441???????extern?void?video_banner(void); ?442???????video_banner();???????????????//?打印啟動圖標??443???} ?444?#endif?
(5)在各功能設置完畢后,程序第454行進入一個for死循環,該循環不斷使用readline()函數(第463行)從控制臺(一般是串口)讀 取用戶的輸入,然后解析。有關如何解析命令請參考U-Boot代碼中run_command()函數的定義,
446???/* ?447????*?Main?Loop?for?Monitor?Command?Processing??448????*/?449?#ifdef?CFG_HUSH_PARSER ?450???parse_file_outer(); ?451???/*?This?point?is?never?reached?*/ ?452???for?(;;); ?453?#else ?454???for?(;;)?{????????????????????????//?進入命令行循環?455?#ifdef?CONFIG_BOOT_RETRY_TIME??456?????if?(rc?>=?0)?{ ?457???????/*?Saw?enough?of?a?valid?command?to ?458????????*?restart?the?timeout. ?459????????*/ ?460???????reset_cmd_timeout();??????????//?設置命令行超時 ?461?????}??462?#endif ?463?????len?=?readline?(CFG_PROMPT);????//?讀取命令 ?464? ?465?????flag?=?0;?/*?assume?no?special?flags?for?now?*/ ?466?????if?(len?>?0) ?467???????strcpy?(lastcommand,?console_buffer);?468?????else?if?(len?==?0) ?469???????flag?|=?CMD_FLAG_REPEAT; ?470?#ifdef?CONFIG_BOOT_RETRY_TIME ?471?????else?if?(len?==?-2)?{ ?472???????/*?-2?means?timed?out,?retry?autoboot ?473????????*/ ?474???????puts?("\nTimed?out?waiting?for?command\n"); ?475?#?ifdef?CONFIG_RESET_TO_RETRY ?476???????/*?Reinit?board?to?run?initialization?code?again?*/ ?477???????do_reset?(NULL,?0,?0,?NULL); ?478?#?else ?479???????return;???/*?retry?autoboot?*/ ?480?#?endif?481?????} ?482?#endif ?483? ?484?????if?(len?==?-1) ?485???????puts?("<INTERRUPT>\n"); ?486?????else ?487???????rc?=?run_command?(lastcommand,?flag);?????//?運行命令?488? ?489?????if?(rc?<=?0)?{ ?490???????/*?invalid?command?or?not?repeatable,?forget?it?*/ ?491???????lastcommand[0]?=?0; ?492?????} ?493???}???// dead loop494?#endif?/*CFG_HUSH_PARSER*/ ?495?}?
U-BOOT的功能設計 基本就在這里。
總結
以上是生活随笔為你收集整理的u-boot 源码分析讲解的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。