我認為學習和應用u-boot對於剛接觸的人來說最大的困難在於其功能比較全,文件(.S .c .h makefile文件)非常多,結構也比較復雜,難以理清各方面的關系。熟悉了一段時間之后,發現還是有很多的規律可以總結的。
一、首先要理解bootloader的作用,以及U-boot具體可以實現什么樣的一些功能。
二、弄清U-boot的啟動流程,這時候需要跟着整個程序走一遍,先不管實現這個流程的代碼文件與文件結構。
三、熟悉整個U-boot的文件結構,重點是理解那些“移植到不同開發板上需要修改的”文件,以及它們之間的關系。重點這些文件是怎樣與實際的硬件(開發板)對應和統一起來的。
四、進一步細化第二步和第三步,這是一個來回穿插的考查,考查在具體的實現整個程序流程和功能中,這些文件的結構與層次關系是怎樣實施的。
五、動手針對一個開發板進行移植實驗,可以參考別人的移植記錄,但是通過自己的動手實驗,會更加熟悉U-boot,並提高解決實際問題(排錯)的能力。
U-boot下載地址: ftp://ftp.denx.de/pub/u-boot/
一
說到底,bootloader是一段裸機程序,是直接與硬件打交道的。① 它在系統上電后開始執行(PC系統中在bootloader之前有個BIOS固件,嵌入式系統中一般沒有),其最終目的是“初始化硬件設備,准備好軟件環境,最后調用操作系統內核”。② 為了方便進行底層開發(比如調試內核等),bootloader增加了很多功能,主要有UART、網絡、USB、讀寫Flash(Nor or Nand)、解壓縮、LCD支持等等。這樣的bootloader具有比較強大的功能,除了不能進行多任務調度,已經類似一個小型操作系統了,被稱為“Monitor”。U-boot就是這樣一種bootloader,全功能體積高達數百Kbytes,如果不需要這些開發調試功能,發布產品時它能配置到很小的體積。
那我們也知道其實OS(如linux)的Kernel其實也是一段裸機程序,也是直接掌管硬件的,不過這個程序極其復雜,許許多多的計算機專家和黑客為其耗費心血,如果三四百K的U-boot已經比較復雜,那么一個典型功能的linux Kernel編譯后竟然能高達萬Kbytes,光從代碼量來看,linux的復雜度已經是U-boot的百倍。而其實遠不止。
雖然kernel很complex,但終究不過是一段裸機代碼,Bootloader進行所謂的“kernel引導”,其過程不過是從bootloader里的一句跳轉代碼,跳轉到kernel代碼處(執行kernel中的第一個函數),所謂傳遞參數也不過是bootloader和kernel約定一個內存地點存放。在這個過程中,bootloader和kernel雖然都處於同一個內存里,但是它們除了“引導”與“傳遞有限的參數”這樣的關系,並無其它關系,完全是兩個獨立的程序。之所以在kernel之前用一個bootloader來引導(為什么開機不能直接執行kernel,所有事情都交給kernel做?),其思想類似於一個板級支持包:kernel假定執行的時候已經具備了一個基本的硬件運行條件,這個環境的初始化(最底層的一些硬件初始化、硬件信息設定)需要bootloader來完成,也許這樣kernel的設計才能保持一定的一致性。
U-boot為了能支持很多不同的體系結構、SoC、電路板,設計了一種代碼文件的組織結構(包括makefile文件),這種設計很大程度上借鑒了linux kernel,很多硬件驅動代碼也直接從linux kernel中搬遷過去,因為linux kernel也是一種移植性非常好的程序。“在通用代碼與系統特定代碼之間,不惜代價,保持一條明確的分界線”,這是可移植程序設計的不變箴言。
二
U-boot的啟動流程分析,上一篇轉載的博文敘述的很全面,也比較有條理。
---->《U-boot研究與移植心得》 http://www.cnblogs.com/andrew-wang/archive/2012/12/06/2806238.html
總的主線就是: 初始化硬件--->加載內核到內存--->設置好各項參數--->跳轉到內核。其他命令都是開發調試時使用的。
三
U-boot的代碼文件組織架構是比較重要的一項,首先是要知道U-boot的代碼結構經歷過什么樣的“歷史變遷”。博主的這篇《U-boot版本與移植概況》有些這方面的知識(http://www.cnblogs.com/andrew-wang/archive/2012/11/04/2753971.html)。無論怎樣變化,代碼的組織架構的目標都是要使U-boot的結構更清晰、移植性更好,而其實每次的變化都是微小的進步。以版本2010.09的U-boot為例:
├── api 存放uboot提供的API函數 ├── arch 與體系結構相關的代碼,uboot的重頭戲 +++ ├── board 根據不同開發板定制的代碼,代碼也不少 +++ ├── common 通用的代碼,涵蓋各個方面,已命令行處理為主 ├── disk 磁盤分區相關代碼 ├── doc 文檔,一堆README開頭的文件 ├── drivers 驅動,很豐富,每種類型的設備驅動占用一個子目錄 +++ ├── examples 示例程序 ├── fs 文件系統,支持嵌入式開發板常見的文件系統 ├── include 頭文件,已通用的頭文件為主 +++ ├── lib 通用庫文件 ├── nand_spl NAND存儲器相關代碼 ├── net 網絡相關代碼,小型的協議棧 ├── onenand_ipl ├── post 加電自檢程序 └── tools 輔助程序,用於編譯和檢查uboot目標文件
可以將這些代碼分為4類
1、平台相關(arch)、或開發板相關(board)(系統特定代碼) 2、通用的函數(include common) 3、通用驅動(disk drivers fs nand_spl onenand_ipl net post) 4、工具、實例、文檔(api doc examples tools)
除了引導kernel的主線之外,U-boot的支持的各種硬件操作驅動實現,有一定的層次性:為了實現“通用的函數”,首先要調用“通用驅動”,這些通用驅動如果有引用“平台相關”的宏或者外部函數,就需要調用到這些系統特定代碼。其層次是" 2 <--> 3 <--> 1 " 。
一般來說,移植U-boot主要就是修改“1”項中的代碼,以及在include/configs/XXX.h中打開需要的功能以及定義一些常量,具體的實現就是在arch和board子目錄,來看一下這兩個子目錄的組織:
|--arch\
|--config.mk ===>配置腳本 |--arm\ |--cpu\ 子目錄對應一種處理器的不同產品型號或者系列; |--arm920t\ |--a320\ |... |--s3c24x0\ |--interrupts.c |--speed.c |--timer.c |--usb.c |--usb_ohci.c |--usb_ohci.h |--Makefile |--cpu.c |--interrupts.c |--start.S <==整個bootloader入口點 |--u-boot.lds <==鏈接腳本 |--Makefile |--include\ 子目錄是處理器用到的頭文件; |--asm\ |--arch-a320\ |... |--arch_s3c24x0\ |--memory.h |--s3c2400.h |--s3c2410.h |--s3c24x0_cpu.h |--s3c24x0.h |... |--proc-armv\(公用) |--atomic.h |--bitops.h |--byteorder.h |--cache.h |--... (等共24個.h公用頭文件) |--lib\ 目錄對應用到處理器公用的代碼; |--board.c |--config.mk
|--... (等共16個文件) |--board\ |--ppmc7xx\ |... |--samsung\ |--goni\ |--smdk2400\ |--smdk2410\ |--smdk2410.c |--flash.c |--lowlevel_init.S |--config.mk |--Makefile |--nand_read.c |--nand_read_save.c |...
我們知道ARM是ARM公司設計的一種體系結構,這個公司專門設計IP core出售給半導體廠商加上一些外圍部件生產SoC,這些IP Core有很多代的產品,如v4、v5、v6、v7,還根據有沒有thumb指令集(t)、DSP加強(e)、java硬件加速(j)等進一步細分。而每一代IP core都有不少半導體廠家生產SoC,如qualcomm、Samsung、TI等,因此ARM平台的組織最為復雜。
打開arch,其中所有的子目錄代表一種體系結構11種;選取其中的ARM類,里面包含cpu、include、lib三個子目錄,其中lib是這個體系結構的公用代碼;include下面只有一個子目錄asm,其中包含的是不同的SoC對應的頭文件及一些公用頭文件;cpu中是ARM體系結構中不同代際的IP core,每種IP core中都有一些子目錄代表不同廠商的SoC以及公用代碼。(其他體系結構的platform沒這么復雜)。
board則是基於某種SoC設計的電路板,2010.09的版本支持有近290種board。一般選擇相似的board進行U-boot的移植。當然board里面的代碼都是跟特定的board有關的。因此,要分arch、IP-core、SoC、Board 4個層次來理解U-boot在ARM系統上的移植性,而其他體系結構有可能只有2-3層。以基於S3C2440開發板為例,這種層次關系像下圖:
要完全理解U-boot的代碼,需要熟悉平台硬件及其匯編、熟悉C語言、熟悉編譯原理。由於U-boot的編譯是基於linux和Gcc的,因此還需要對linux的技術文化有一定的理解,比如其makefile體系架構,前文的博主推薦看《從庖丁解牛說uboot如何編譯》《詳細分析make uboot 最后的編譯鏈接的具體執行過程》這兩篇(看官去Google~)。博主的《U-boot架構分析與移植重點》其中對編譯鏈接過程有個主線的介紹(主要參考了韋東山的書)---> http://www.cnblogs.com/andrew-wang/archive/2012/11/04/2753972.html 。U-boot文件雖多,結構雖復雜,但搞清楚了主線,就比較好理解了。
四、
具體的移植過程,從下載原版U-boot、linux kernel、busybox、FSmaker、gcc,到最后用它們組件一個完整的嵌入式系統,參考下面的博文,它們主要轉載自yanghao和趙春江兩位的博客(都標明了source)(另外也推薦mobilefzb的博客,其中關於TQ2440的移植博文也很好 http://nervfzb.blog.163.com/ ),我也都在FL2440開發板上實驗過,原文可能有問題的地方做了補充注釋,有些地方稍作修改以便理解,有些是原創的:
FL2440開發板的U-boot-2010.09版本移植(一)建立特定開發板的U-boot框架
FL2440開發板的U-boot-2010.09版本移植(二)片上系統SoC初始化移植
FL2440開發板的U-boot-2010.09版本移植(三) 如何利用JLINK燒寫U-boot到NAND Flash中
FL2440開發板的U-boot-2010.09版本移植(四)Nor Flash啟動支持
FL2440開發板的U-boot-2010.09版本移植(五)支持DM9000網卡和板級相關LED等配置
FL2440開發板的U-boot-2010.09版本移植(六)uboot架構中NAND Flash驅動修改
FL2440開發板的U-boot-2010.09版本移植(七)NAND Flash啟動支持
FL2440開發板的U-boot-2010.09版本移植(八)LCD的支持
FL2440開發板的U-boot-2010.09版本移植 ____ U-boot中常用參數設定及常用宏(u-boot環境變量、USB、內核引導)
FL2440開發板的U-boot-2010.09版本移植(九)NAND Flash啟動支持的一種新型方法,利用U-Boot自帶nand_spl/nand_boot.c
FL2440開發板的U-boot-2010.09版本移植(十)支持燒寫YAFFS2文件系統
FL2440開發板的U-boot-2010.09版本移植(十一)U-boot引導內核設置、編譯linux內核、編譯文件系統、加載...
以下原創:
J-link & AXD仿真調試ARM開發板 “halt不住” 的問題的解決
最后,希望這篇博文能幫助到U-boot的初學者。^_^
By Andrew 2012.12.10