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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

u-boot 详细介绍 .

發布時間:2023/12/10 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 u-boot 详细介绍 . 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Bootloader 對于計算機系統來說,從開機上電到操作系統啟動需要一個引導過程嵌入式Linux系統同樣離不開引導程序,這個引導程序就叫作Bootloader6.1.1? Bootloader介紹 Bootloader是在操作系統運行之前執行的一段小程序。通過這段小程序,我們可以初始化硬件設備、建立內存空間的映射表,從而建立適當的系統軟硬件環境,為最終調用操作系統內核做好準備。 對于嵌入式系統,Bootloader是基于特定硬件平臺來實現的。因此,幾乎不可能為所有的嵌入式系統建立一個通用的Bootloader,不同的處理器架構都有不同的Bootloader。Bootloader不但依賴于CPU的體系結構,而且依賴于嵌入式系統板級設備的配置。對于2塊不同的嵌入式板而言,即使它們使用同一種處理器,要想讓運行在一塊板子上的Bootloader程序也能運行在另一塊板子上,一般也都需要修改Bootloader的源程序。 反過來,大部分Bootloader仍然具有很多共性,某些Bootloader也能夠支持多種體系結構的嵌入式系統。例如,U-Boot就同時支持PowerPC、ARM、MIPS和X86等體系結構,支持的板子有上百種。通常,它們都能夠自動從存儲介質上啟動,都能夠引導操作系統啟動,并且大部分都可以支持串口和以太網接口。 本章將對各種Bootloader總結分類,分析它們的共同特點。以U-Boot為例,詳細討論Bootloader的設計與實現。

6.1.2? Bootloader的啟動 Linux系統是通過Bootloader引導啟動的。一上電,就要執行Bootloader來初始化系統。可以通過第4章的Linux啟動過程框圖回顧一下。 系統加電或復位后,所有CPU都會從某個地址開始執行,這是由處理器設計決定的。比如,X86的復位向量在高地址端,ARM處理器在復位時從地址0x00000000取第一條指令。嵌入式系統的開發板都要把板上ROM或Flash映射到這個地址因此,必須把Bootloader程序存儲在相應的Flash位置系統加電后,CPU將首先執行它。 主機和目標機之間一般有串口可以連接,Bootloader軟件通常會通過串口來輸入輸出。例如:輸出出錯或者執行結果信息到串口終端,從串口終端讀取用戶控制命令等。 Bootloader啟動過程通常是多階段的,這樣既能提供復雜的功能,又有很好的可移植性。例如:從Flash啟動的Bootloader多數是兩階段的啟動過程。從后面U-Boot的內容可以詳細分析這個特性。 大多數Bootloader都包含2種不同的操作模式:本地加載模式遠程下載模式。這2種操作模式的區別僅對于開發人員才有意義,也就是不同啟動方式的使用。從最終用戶的角度看,Bootloader的作用就是用來加載操作系統,而并不存在所謂的本地加載模式與遠程下載模式的區別。 因為Bootloader的主要功能是引導操作系統啟動,所以我們詳細討論一下各種啟動方式的特點。 1.網絡啟動方式 這種方式開發板不需要配置較大的存儲介質,跟無盤工作站有點類似。但是使用這種啟動方式之前,需要把Bootloader安裝到板上的EPROM或者Flash中Bootloader通過以太網接口遠程下載Linux內核映像或者文件系統。第4章介紹的交叉開發環境就是以網絡啟動方式建立的。這種方式對于嵌入式系統開發來說非常重要。 使用這種方式也有前提條件,就是目標板有串口、以太網接口或者其他連接方式。串口一般可以作為控制臺,同時可以用來下載內核影像和RAMDISK文件系統。串口通信傳輸速率過低,不適合用來掛接NFS文件系統。所以以太網接口成為通用的互連設備,一般的開發板都可以配置10M以太網接口。 對于PDA等手持設備來說,以太網的RJ-45接口顯得大了些,而USB接口,特別是USB的迷你接口,尺寸非常小。對于開發的嵌入式系統,可以把USB接口虛擬成以太網接口來通訊。這種方式在開發主機和開發板兩端都需要驅動程序。 另外,還要在服務器上配置啟動相關網絡服務。Bootloader下載文件一般都使用TFTP網絡協議,還可以通過DHCP的方式動態配置IP地址。 DHCP/BOOTP服務為Bootloader分配IP地址,配置網絡參數,然后才能夠支持網絡傳輸功能。如果Bootloader可以直接設置網絡參數,就可以不使用DHCP。 TFTP服務為Bootloader客戶端提供文件下載功能,把內核映像和其他文件放在/tftpboot目錄下。這樣Bootloader可以通過簡單的TFTP協議遠程下載內核映像到內存。如圖6.1所示。 0 _fcksavedurl="> 圖6.1? 網絡啟動示意圖 大部分引導程序都能夠支持網絡啟動方式。例如:BIOS的PXE(Preboot Execution Environment)功能就是網絡啟動方式;U-Boot也支持網絡啟動功能。 2.磁盤啟動方式 傳統的Linux系統運行在臺式機或者服務器上,這些計算機一般都使用BIOS引導,并且使用磁盤作為存儲介質。如果進入BIOS設置菜單,可以探測處理器、內存、硬盤等設備,可以設置BIOS從軟盤、光盤或者某塊硬盤啟動。也就是說,BIOS并不直接引導操作系統。那么在硬盤的主引導區,還需要一個Bootloader。這個Bootloader可以從磁盤文件系統中把操作系統引導起來。 Linux傳統上是通過LILO(LInux LOader)引導的,后來又出現了GNU的軟件GRUB(GRand Unified Bootloader)。這2種Bootloader廣泛應用在X86的Linux系統上。你的開發主機可能就使用了其中一種,熟悉它們有助于配置多種系統引導功能。 LILO軟件工程是由Werner Almesberger創建,專門為引導Linux開發的。現在LILO的維護者是John Coffman,最新版本下載站點:http://lilo.go.dyndns.org。LILO有詳細的文檔,例如LILO套件中附帶使用手冊和參考手冊。此外,還可以在LDP的“LILO mini-HOWTO”中找到LILO的使用指南。 GRUB是GNU計劃的主要bootloader。GRUB最初是由Erich Boleyn為GNU Mach操作系統撰寫的引導程序。后來有Gordon Matzigkeit和Okuji Yoshinori接替Erich的工作,繼續維護和開發GRUB。GRUB的網站http://www.gnu.org/software/grub/上有對套件使用的說明文件,叫作《GRUB manual》。GRUB能夠使用TFTP和BOOTP或者DHCP通過網絡啟動,這種功能對于系統開發過程很有用。 除了傳統的Linux系統上的引導程序以外,還有其他一些引導程序,也可以支持磁盤引導啟動。例如:LoadLin可以從DOS下啟動Linux;還有ROLO、LinuxBIOS,U-Boot也支持這種功能。 3.Flash啟動方式 大多數嵌入式系統上都使用Flash存儲介質。Flash有很多類型,包括NOR Flash、NAND Flash和其他半導體盤。其中,NOR Flash(也就是線性Flash)使用最為普遍。 NOR Flash可以支持隨機訪問,所以代碼是可以直接在Flash上執行的。Bootloader一般是存儲在Flash芯片上的。另外,Linux內核映像和RAMDISK也可以存儲在Flash上。通常需要把Flash分區使用,每個區的大小應該是Flash擦除塊大小的整數倍。圖6.2是Bootloader和內核映像以及文件系統的分區表。 0 _fcksavedurl=" border=0> 圖6.2? Flash存儲示意圖 Bootloader一般放在Flash的底端或者頂端,這要根據處理器的復位向量設置。要使Bootloader的入口位于處理器上電執行第一條指令的位置。 接下來分配參數區,這里可以作為Bootloader的參數保存區域。 再下來內核映像區。Bootloader引導Linux內核,就是要從這個地方把內核映像解壓到RAM中去,然后跳轉到內核映像入口執行。 然后是文件系統區。如果使用Ramdisk文件系統,則需要Bootloader把它解壓到RAM中。如果使用JFFS2文件系統,將直接掛接為根文件系統。這兩種文件系統將在第12章詳細講解。 最后還可以分出一些數據區,這要根據實際需要和Flash大小來考慮了。 這些分區是開發者定義的,Bootloader一般直接讀寫對應的偏移地址。到了Linux內核空間,可以配置成MTD設備來訪問Flash分區。但是,有的Bootloader也支持分區的功能,例如:Redboot可以創建Flash分區表,并且內核MTD驅動可以解析出redboot的分區表。 除了NOR Flash,還有NAND Flash、Compact Flash、DiskOnChip等。這些Flash具有芯片價格低,存儲容量大的特點。但是這些芯片一般通過專用控制器的I/O方式來訪問,不能隨機訪問,因此引導方式跟NOR Flash也不同。在這些芯片上,需要配置專用的引導程序。通常,這種引導程序起始的一段代碼就把整個引導程序復制到RAM中運行,從而實現自舉啟動,這跟從磁盤上啟動有些相似。
6.1.3? Bootloader的種類 嵌入式系統世界已經有各種各樣的Bootloader,種類劃分也有多種方式。除了按照處理器體系結構不同劃分以外,還有功能復雜程度的不同。 首先區分一下“Bootloader”和“Monitor”的概念。嚴格來說,“Bootloader”只是引導設備并且執行主程序的固件;而“Monitor”還提供了更多的命令行接口,可以進行調試、讀寫內存、燒寫Flash、配置環境變量等。“Monitor”在嵌入式系統開發過程中可以提供很好的調試功能,開發完成以后,就完全設置成了一個“Bootloader”。所以,習慣上大家把它們統稱為Bootloader。 表6.1列出了Linux的開放源碼引導程序及其支持的體系結構。表中給出了X86 ARM PowerPC體系結構的常用引導程序,并且注明了每一種引導程序是不是“Monitor”。 表6.1?????????????????????????????????????????????????? 開放源碼的Linux 引導程序
Bootloader Monitor 描??? 述 x86 ARM PowerPC
LILO Linux磁盤引導程序
GRUB GNU的LILO替代程序
Loadlin 從DOS引導Linux
ROLO 從ROM引導Linux而不需要BIOS
Etherboot 通過以太網卡啟動Linux系統的固件
LinuxBIOS 完全替代BUIS的Linux引導程序
BLOB LART等硬件平臺的引導程序
U-boot 通用引導程序
RedBoot 基于eCos的引導程序
對于每種體系結構,都有一系列開放源碼Bootloader可以選用。 (1)X86 X86的工作站和服務器上一般使用LILO和GRUB。LILO是Linux發行版主流的Bootloader。不過Redhat Linux發行版已經使用了GRUB,GRUB比LILO有更有好的顯示界面,使用配置也更加靈活方便。 在某些X86嵌入式單板機或者特殊設備上,會采用其他Bootloader,例如:ROLO。這些Bootloader可以取代BIOS的功能,能夠從FLASH中直接引導Linux啟動。現在ROLO支持的開發板已經并入U-Boot,所以U-Boot也可以支持X86平臺。 (2)ARM ARM處理器的芯片商很多,所以每種芯片的開發板都有自己的Bootloader。結果ARM bootloader也變得多種多樣。最早有為ARM720處理器的開發板的固件,又有了armbootStrongARM平臺的blob,還有S3C2410處理器開發板上的vivi等。現在armboot已經并入了U-Boot,所以U-Boot也支持ARM/XSCALE平臺。U-Boot已經成為ARM平臺事實上的標準Bootloader (3)PowerPC PowerPC平臺的處理器有標準的Bootloader,就是ppcboot。PPCBOOT在合并armboot等之后,創建了U-Boot,成為各種體系結構開發板的通用引導程序。U-Boot仍然是PowerPC平臺的主要Bootloader。 (4)MIPS MIPS公司開發的YAMON是標準的Bootloader,也有許多MIPS芯片商為自己的開發板寫了Bootloader。現在,U-Boot也已經支持MIPS平臺。 (5)SH SH平臺的標準Bootloader是sh-boot。Redboot在這種平臺上也很好用。 (6)M68K M68K平臺沒有標準的Bootloader。Redboot能夠支持m68k系列的系統。 值得說明的是Redboot,它幾乎能夠支持所有的體系結構,包括MIPS、SH、M68K等體系結構。Redboot是以eCos為基礎,采用GPL許可的開源軟件工程。現在由core eCos的開發人員維護,源碼下載網站是http://www.ecoscentric.com/snapshots。Redboot的文檔也相當完善,有詳細的使用手冊《RedBoot User’s Guide》。

6.2.1 ?U-Boot工程簡介 最早,DENX軟件工程中心的Wolfgang Denk基于8xxrom的源碼創建了PPCBOOT工程,并且不斷添加處理器的支持。后來,Sysgo Gmbh把ppcboot移植到ARM平臺上,創建了ARMboot工程。然后以ppcboot工程和armboot工程為基礎,創建了U-Boot工程。 現在U-Boot已經能夠支持PowerPC、ARM、X86、MIPS體系結構的上百種開發板,已經成為功能最多、靈活性最強并且開發最積極的開放源碼Bootloader。目前仍然由DENX的Wolfgang Denk維護。 U-Boot的源碼包可以從sourceforge網站下載,還可以訂閱該網站活躍的U-Boot Users郵件論壇,這個郵件論壇對于U-Boot的開發和使用都很有幫助。 U-Boot軟件包下載網站:http://sourceforge.net/project/u-boot。 U-Boot郵件列表網站:http://lists.sourceforge.net/lists/listinfo/u-boot-users/。 DENX相關的網站:http://www.denx.de/re/DPLG.html。

6.2.2 ?U-Boot源碼結構 從網站上下載得到U-Boot源碼包,例如:U-Boot-1.1.2.tar.bz2 解壓就可以得到全部U-Boot源程序。在頂層目錄下有18個子目錄,分別存放和管理不同的源程序。這些目錄中所要存放的文件有其規則,可以分為3類。 ·?第1類目錄與處理器體系結構或者開發板硬件直接相關; ·?第2類目錄是一些通用的函數或者驅動程序; ·?第3類目錄是U-Boot的應用程序、工具或者文檔。 表6.2列出了U-Boot頂層目錄下各級目錄存放原則。 表6.2????????????????????????????????????????????????? U-Boot的源碼頂層目錄說明
目??? 錄 特??? 性 解 釋 說 明
board 平臺依賴 存放電路板相關的目錄文件,例如:RPXlite(mpc8xx)、smdk2410(arm920t)、sc520_cdp(x86) 等目錄
cpu 平臺依賴 存放CPU相關的目錄文件,例如:mpc8xx、ppc4xx、arm720t、arm920t、 xscale、i386等目錄
lib_ppc 平臺依賴 存放對PowerPC體系結構通用的文件,主要用于實現PowerPC平臺通用的函數
目??? 錄 特??? 性 解 釋 說 明
lib_arm 平臺依賴 存放對ARM體系結構通用的文件,主要用于實現ARM平臺通用的函數
lib_i386 平臺依賴 存放對X86體系結構通用的文件,主要用于實現X86平臺通用的函數
include 通用 頭文件和開發板配置文件,所有開發板的配置文件都在configs目錄下
common 通用 通用的多功能函數實現
lib_generic 通用 通用庫函數的實現
Net 通用 存放網絡的程序
Fs 通用 存放文件系統的程序
Post 通用 存放上電自檢程序
drivers 通用 通用的設備驅動程序,主要有以太網接口的驅動
Disk 通用 硬盤接口程序
Rtc 通用 RTC的驅動程序
Dtt 通用 數字溫度測量器或者傳感器的驅動
examples 應用例程 一些獨立運行的應用程序的例子,例如helloworld
tools 工具 存放制作S-Record 或者 U-Boot格式的映像等工具,例如mkimage
Doc 文檔 開發使用文檔
U-Boot的源代碼包含對幾十種處理器、數百種開發板的支持。可是對于特定的開發板,配置編譯過程只需要其中部分程序。這里具體以S3C2410 arm920t處理器為例,具體分析S3C2410處理器和開發板所依賴的程序,以及U-Boot的通用函數和工具。

6.2.3 ?U-Boot的編譯 U-Boot的源碼是通過GCC和Makefile組織編譯的。頂層目錄下的Makefile首先可以設置開發板的定義,然后遞歸地調用各級子目錄下的Makefile,最后把編譯過的程序鏈接成U-Boot映像。 1.頂層目錄下的Makefile 它負責U-Boot整體配置編譯。按照配置的順序閱讀其中關鍵的幾行。 每一種開發板在Makefile都需要有板子配置的定義。例如smdk2410開發板的定義如下。 smdk2410_config :?? unconfig @./mkconfig $(@:_config=) arm arm920t smdk2410 NULL s3c24x0 執行配置U-Boot的命令make smdk2410_config,通過./mkconfig腳本生成include/config.
mk的配置文件。文件內容正是根據Makefile對開發板的配置生成的。 ARCH?? = arm CPU??? = arm920t BOARD? = smdk2410 SOC??? = s3c24x0 上面的include/config.mk文件定義了ARCH、CPU、BOARD、SOC這些變量。這樣硬件平臺依賴的目錄文件可以根據這些定義來確定。SMDK2410平臺相關目錄如下。 board/smdk2410/ cpu/arm920t/ cpu/arm920t/s3c24x0/ lib_arm/ include/asm-arm/ include/configs/smdk2410.h 再回到頂層目錄的Makefile文件開始的部分,其中下列幾行包含了這些變量的定義。 # load ARCH, BOARD, and CPU configuration include include/config.mk export? ???? ARCH CPU BOARD VENDOR SOC Makefile的編譯選項和規則在頂層目錄的config.mk文件中定義。各種體系結構通用的規則直接在這個文件中定義。通過ARCH、CPU、BOARD、SOC等變量為不同硬件平臺定義不同選項。不同體系結構的規則分別包含在ppc_config.mk、arm_config.mk、mips_config.mk等文件中。 頂層目錄的Makefile中還要定義交叉編譯器,以及編譯U-Boot所依賴的目標文件。 ifeq ($(ARCH),arm) CROSS_COMPILE = arm-linux- ???????? //交叉編譯器的前綴 #endif export? CROSS_COMPILE … # U-Boot objects....order is important (i.e. start must be first) OBJS? = cpu/$(CPU)/start.o????????????????? //處理器相關的目標文件 … LIBS? = lib_generic/libgeneric.a ?????????? //定義依賴的目錄,每個目錄下先把目標文件連接成*.a文件。 LIBS += board/$(BOARDDIR)/lib$(BOARD).a LIBS += cpu/$(CPU)/lib$(CPU).a ifdef SOC LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a endif LIBS += lib_$(ARCH)/lib$(ARCH).a … 然后還有U-Boot映像編譯的依賴關系。 ALL = u-boot.srec u-boot.bin System.map all:??????? $(ALL) u-boot.srec:??? u-boot $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@ u-boot.bin: u-boot $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@ …… u-boot:???? ??? depend $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT) UNDEF_SYM='$(OBJDUMP) -x $(LIBS) / |sed? -n -e 's/.*/(__u_boot_cmd_.*/)/-u/1/p'|sort|uniq`;/ $(LD) $(LDFLAGS) $$UNDEF_SYM $(OBJS) / --start-group $(LIBS) $(PLATFORM_LIBS) --end-group / -Map u-boot.map -o u-boot Makefile缺省的編譯目標為all,包括u-boot.srec、u-boot.bin、System.map。u-boot.srecu-boot.bin又依賴于U-BootU-Boot就是通過ld命令按照u-boot.map地址表把目標文件組裝成u-boot。 其他Makefile內容就不再詳細分析了,上述代碼分析應該可以為閱讀代碼提供了一個線索。 2.開發板配置頭文件 除了編譯過程Makefile以外,還要在程序中為開發板定義配置選項或者參數。這個頭文件是include/configs/<board_name>.h。<board_name>用相應的BOARD定義代替。 這個頭文件中主要定義了兩類變量。 一類是選項,前綴是CONFIG_,用來選擇處理器、設備接口、命令、屬性等。例如: #define?? CONFIG_ARM920T? ?????? 1 #define?? CONFIG_DRIVER_CS8900? 1 另一類是參數,前綴是CFG_,用來定義總線頻率、串口波特率、Flash地址等參數。例如: #define ??? CFG_FLASH_BASE????? 0x00000000 #define CFG_PROMPT? ??? ??? "=>" 3.編譯結果 根據對Makefile的分析,編譯分為2步。第1步配置,例如:make smdk2410_config;第2步編譯,執行make就可以了。 編譯完成后,可以得到U-Boot各種格式的映像文件和符號表,如表6.3所示。 表6.3????????????????????????????????????????????????? U-Boot編譯生成的映像文件
文 件 名 稱 說??? 明 文 件 名 稱 說??? 明
System.map U-Boot映像的符號表 u-boot.bin U-Boot映像原始的二進制格式
u-boot U-Boot映像的ELF格式 u-boot.srec U-Boot映像的S-Record格式
U-Boot的3種映像格式都可以燒寫到Flash中,但需要看加載器能否識別這些格式。一般u-boot.bin最為常用,直接按照二進制格式下載,并且按照絕對地址燒寫到Flash中就可以了。U-Boot和u-boot.srec格式映像都自帶定位信息。 4.U-Boot工具 在tools目錄下還有些U-Boot的工具。這些工具有的也經常用到。表6.4說明了幾種工具的用途。 表6.4????????????????????????????????????????????????????????????? U-Boot的工具
工 具 名 稱 說??? 明 工 具 名 稱 說 ???明
bmp_logo 制作標記的位圖結構體 img2srec 轉換SREC格式映像
envcrc 校驗u-boot內部嵌入的環境變量 mkimage 轉換U-Boot格式映像
gen_eth_addr 生成以太網接口MAC地址 updater U-Boot自動更新升級工具
這些工具都有源代碼,可以參考改寫其他工具。其中mkimage是很常用的一個工具,Linux內核映像和ramdisk文件系統映像都可以轉換成U-Boot的格式。

6.2.4? U-Boot的移植 U-Boot能夠支持多種體系結構的處理器,支持的開發板也越來越多。因為Bootloader是完全依賴硬件平臺的,所以在新電路板上需要移植U-Boot程序。 開始移植U-Boot之前,先要熟悉硬件電路板和處理器。確認U-Boot是否已經支持新開發板的處理器和I/O設備。假如U-Boot已經支持一塊非常相似的電路板,那么移植的過程將非常簡單。 移植U-Boot工作就是添加開發板硬件相關的文件、配置選項,然后配置編譯。 開始移植之前,需要先分析一下U-Boot已經支持的開發板,比較出硬件配置最接近的開發板。選擇的原則是,首先處理器相同,其次處理器體系結構相同,然后是以太網接口等外圍接口。還要驗證一下這個參考開發板的U-Boot,至少能夠配置編譯通過。 以S3C2410處理器的開發板為例,U-Boot-1.1.2版本已經支持SMDK2410開發板。我們可以基于SMDK2410移植,那么先把SMDK2410編譯通過。 我們以S3C2410開發板fs2410為例說明。移植的過程參考SMDK2410開發板,SMDK2410在U-Boot-1.1.2中已經支持。 移植U-Boot的基本步驟如下。 (1)在頂層Makefile中為開發板添加新的配置選項,使用已有的配置項目為例。 smdk2410_config?? :?????? unconfig @./mkconfig $(@:_config=) arm arm920t smdk2410 NULL s3c24x0 參考上面2行,添加下面2行。 fs2410_config?? :?????? unconfig @./mkconfig $(@:_config=) arm arm920t fs2410 NULL s3c24x0 (2)創建一個新目錄存放開發板相關的代碼,并且添加文件。 board/fs2410/config.mk board/fs2410/flash.c board/fs2410/fs2410.c board/fs2410/Makefile board/fs2410/memsetup.S board/fs2410/u-boot.lds (3)為開發板添加新的配置文件 可以先復制參考開發板的配置文件,再修改。例如: $cp include/configs/smdk2410.h? include/configs/fs2410.h 如果是為一顆新的CPU移植,還要創建一個新的目錄存放CPU相關的代碼。 (4)配置開發板 $ make fs2410_config (5)編譯U-Boot 執行make命令,編譯成功可以得到U-Boot映像。有些錯誤是跟配置選項是有關系的,通常打開某些功能選項會帶來一些錯誤,一開始可以盡量跟參考板配置相同。 (6)添加驅動或者功能選項 在能夠編譯通過的基礎上,還要實現U-Boot的以太網接口、Flash擦寫等功能。 對于FS2410開發板的以太網驅動和smdk2410完全相同,所以可以直接使用。CS8900驅動程序文件如下。 drivers/cs8900.c drivers/cs8900.h 對于Flash的選擇就麻煩多了,Flash芯片價格或者采購方面的因素都有影響。多數開發板大小、型號不都相同。所以還需要移植Flash的驅動。每種開發板目錄下一般都有flash.c這個文件,需要根據具體的Flash類型修改。例如: board/fs2410/flash.c (7)調試U-Boot源代碼,直到U-Boot在開發板上能夠正常啟動。 調試的過程可能是很艱難的,需要借助工具,并且有些問題可能困擾很長時間。

6.2.5?添加U-Boot命令 U-Boot的命令為用戶提供了交互功能,并且已經實現了幾十個常用的命令。如果開發板需要很特殊的操作,可以添加新的U-Boot命令。 U-Boot的每一個命令都是通過U_Boot_CMD宏定義的。這個宏在include/command.h頭文件中定義,每一個命令定義一個cmd_tbl_t結構體。 #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) / cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help} 這樣每一個U-Boot命令有一個結構體來描述。結構體包含的成員變量:命令名稱、最大參數個數、重復數、命令執行函數、用法、幫助。 從控制臺輸入的命令是由common/command.c中的程序解釋執行的。find_cmd()負責匹配輸入的命令,從列表中找出對應的命令結構體。 基于U-Boot命令的基本框架,來分析一下簡單的icache操作命令,就可以知道添加新命令的方法。 (1)定義CACHE命令。在include/cmd_confdefs.h中定義了所有U-Boot命令的標志位。 #define CFG_CMD_CACHE?????? 0x00000010ULL?? /* icache, dcache?????? */ 如果有更多的命令,也要在這里添加定義。 (2)實現CACHE命令的操作函數。下面是common/cmd_cache.c文件中icache命令部分的代碼。 #if (CONFIG_COMMANDS & CFG_CMD_CACHE) static int on_off (const char *s) {?? ??? //這個函數解析參數,判斷是打開cache,還是關閉cache if (strcmp(s, "on") == 0) {? //參數為“on” return (1); } else if (strcmp(s, "off") == 0) {? //參數為“off” return (0); } return (-1); } int do_icache ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {?? ? //對指令cache的操作函數 switch (argc) { case 2:?????????????? /* 參數個數為1,則執行打開或者關閉指令cache操作 */ switch (on_off(argv[1])) { case 0: ??? icache_disable();??????? //打開指令cache break; case 1: ??? icache_enable ();??????? //關閉指令cache break; } /* FALL TROUGH */ case 1:?????????? /* 參數個數為0,則獲取指令cache狀態*/? printf ("Instruction Cache is %s/n", icache_status() ? "ON" : "OFF"); return 0; default:? //其他缺省情況下,打印命令使用說明 printf ("Usage:/n%s/n", cmdtp->usage); return 1; } return 0; } …… U_Boot_CMD( //通過宏定義命令 icache,?? 2,?? 1,???? do_icache,? //命令為icache,命令執行函數為do_icache() "icache? - enable or disable instruction cache/n",?? //幫助信息 "[on, off]/n" "??? - enable or disable instruction cache/n" ); …… #endif U-Boot的命令都是通過結構體__U_Boot_cmd_##name來描述的。根據U_Boot_CMD在include/command.h中的兩行定義可以明白。 #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) / cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help} 還有,不要忘了在common/Makefile中添加編譯的目標文件。 (3)打開CONFIG_COMMANDS選項的命令標志位。這個程序文件開頭有#if語句需要預處理是否包含這個命令函數。CONFIG_COMMANDS選項在開發板的配置文件中定義。例如:SMDK2410平臺在include/configs/smdk2410.h中有如下定義。 /*********************************************************** * Command definition ***********************************************************/ #define CONFIG_COMMANDS / (CONFIG_CMD_DFL? | / CFG_CMD_CACHE ?? ?| / CFG_CMD_REGINFO? ? | / CFG_CMD_DATE ???? | / CFG_CMD_ELF) 按照這3步,就可以添加新的U-Boot命令。

6.3? U-Boot的調試 新移植的U-Boot不能正常工作,這時就需要調試了。調試U-Boot離不開工具,只有理解U-Boot啟動過程,才能正確地調試U-Boot源碼。

6.3.1?硬件調試器 硬件電路板制作完成以后,這時上面還沒有任何程序,就叫作裸板。首要的工作是把程序或者固件加載到裸板上,這就要通過硬件工具來完成。習慣上,這種硬件工具叫作仿真器。 仿真器可以通過處理器的JTAG等接口控制板子,直接把程序下載到目標板內存,或者進行Flash編程。如果板上的Flash是可以拔插的,就可以通過專用的Flash燒寫器來完成。在第4章介紹過目標板跟主機之間的連接,其中JTAG等接口就是專門用來連接仿真器的。 仿真器還有一個重要的功能就是在線調試程序,這對于調試Bootloader和硬件測試程序很有用。 從最簡單的JTAG電纜,到ICE仿真器,再到可以調試Linux內核的仿真器。 復雜的仿真器可以支持與計算機間的以太網或者USB接口通信。 對于U-Boot的調試,可以采用BDI2000。BDI2000完全可以反匯編地跟蹤Flash中的程序,也可以進行源碼級的調試。 使用BDI2000調試U-boot的方法如下。 (1)配置BDI2000和目標板初始化程序,連接目標板。 (2)添加U-Boot的調試編譯選項,重新編譯。 U-Boot的程序代碼是位置相關的,調試的時候盡量在內存中調試,可以修改連接定位地址TEXT_BASE。TEXT_BASE在board/<board_name>/config.mk中定義。 另外,如果有復位向量也需要先從鏈接腳本中去掉。鏈接腳本是board/<board_name>/
u-boot.lds。 添加調試選項,在config.mk文件中查找,DBGFLAGS,加上-g選項。然后重新編譯U-Boot (3)下載U-Boot到目標板內存。 通過BDI2000的下載命令LOAD,把程序加載到目標板內存中。然后跳轉到U-Boot入口。 (4)啟動GDB調試。 啟動GDB調試,這里是交叉調試的GDB。GDB與BDI2000建立鏈接,然后就可以設置斷點執行了。 $ arm-linux-gdb u-boot (gdb)target remote 192.168.1.100:2001 (gdb)stepi (gdb)b start_armboot (gdb)c

6.3.2?軟件跟蹤 假如U-Boot沒有任何串口打印信息,手頭又沒有硬件調試工具,那樣怎么知道U-Boot執行到什么地方了呢?可以通過開發板上的LED指示燈判斷。 開發板上最好設計安裝八段數碼管等LED,可以用來顯示數字或者數字位。 U-Boot可以定義函數show_boot_progress (int status),用來指示當前啟動進度。在include/common.h頭文件中聲明這個函數。 #ifdef CONFIG_SHOW_BOOT_PROGRESS void??? show_boot_progress (int status); #endif CONFIG_SHOW_BOOT_PROGRESS是需要定義的。這個在板子配置的頭文件中定義。CSB226開發板對這項功能有完整實現,可以參考。在頭文件include/configs/csb226.h中,有下列一行。 #define CONFIG_SHOW_BOOT_PROGRESS?????? 1 函數show_boot_progress (int status)的實現跟開發板關系密切,所以一般在board目錄下的文件中實現。看一下CSB226在board/csb226/csb226.c中的實現函數。 /** 設置CSB226板的0、1、2三個指示燈的開關狀態 * csb226_set_led: - switch LEDs on or off * @param led:?? LED to switch (0,1,2) * @param state: switch on (1) or off (0) */ void csb226_set_led(int led, int state) { switch(led) { case 0: if (state==1) { GPCR0 |= CSB226_USER_LED0; } else if (state==0) { GPSR0 |= CSB226_USER_LED0; } break; case 1: if (state==1) { GPCR0 |= CSB226_USER_LED1; } else if (state==0) { GPSR0 |= CSB226_USER_LED1; } break; case 2: if (state==1) { GPCR0 |= CSB226_USER_LED2; } else if (state==0) { GPSR0 |= CSB226_USER_LED2; } break; } return; } /** 顯示啟動進度函數,在比較重要的階段,設置三個燈為亮的狀態(1, 5, 15)*/ void show_boot_progress (int status) { switch(status) { case? 1: csb226_set_led(0,1); break; case? 5: csb226_set_led(1,1); break; case 15: csb226_set_led(2,1); break; } return; } 這樣,在U-Boot啟動過程中就可以通過show_boot_progresss指示執行進度。比如hang()函數是系統出錯時調用的函數,這里需要根據特定的開發板給定顯示的參數值。 void hang (void) { puts ("### ERROR ### Please RESET the board ###/n"); #ifdef CONFIG_SHOW_BOOT_PROGRESS show_boot_progress(-30); #endif for (;;); 6.3.3? U-Boot啟動過程 盡管有了調試跟蹤手段,甚至也可以通過串口打印信息了,但是不一定能夠判斷出錯原因。如果能夠充分理解代碼的啟動流程,那么對準確地解決和分析問題很有幫助。 開發板上電后,執行U-Boot的第一條指令,然后順序執行U-Boot啟動函數。函數調用順序如圖6.3所示。 看一下board/smsk2410/u-boot.lds這個鏈接腳本,可以知道目標程序的各部分鏈接順序。第一個要鏈接的是cpu/arm920t/start.o,那么U-Boot的入口指令一定位于這個程序中。下面詳細分析一下程序跳轉和函數的調用關系以及函數實現。 1.cpu/arm920t/start.S 這個匯編程序是U-Boot的入口程序,開頭就是復位向量的代碼。 0 _fcksavedurl=" border=0> 圖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, }; 6.3.4? U-Boot與內核的關系 U-Boot作為Bootloader,具備多種引導內核啟動的方式。常用的go和bootm命令可以直接引導內核映像啟動。U-Boot與內核的關系主要是內核啟動過程中參數的傳遞。 1.go命令的實現 /* common/cmd_boot.c? */ int do_go (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { ulong addr, rc; int???? rcode = 0; if (argc < 2) { printf ("Usage:/n%s/n", cmdtp->usage); return 1; } addr = simple_strtoul(argv[1], NULL, 16); printf ("## Starting application at 0x%08lX .../n", addr); /* * pass address parameter as argv[0] (aka command name), * and all remaining args */ rc = ((ulong (*)(int, char *[]))addr) (--argc, &argv[1]); if (rc != 0) rcode = 1; printf ("## Application terminated, rc = 0x%lX/n", rc); return rcode; } go命令調用do_go()函數,跳轉到某個地址執行的。如果在這個地址準備好了自引導的內核映像,就可以啟動了。盡管go命令可以帶變參,實際使用時一般不用來傳遞參數。 2.bootm命令的實現 /* common/cmd_bootm.c */ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { ulong iflag; ulong addr; ulong data, len, checksum; ulong? *len_ptr; uint? unc_len = 0x400000; int?? i, verify; char? *name, *s; int?? (*appl)(int, char *[]); image_header_t *hdr = &header; s = getenv ("verify"); verify = (s && (*s == 'n')) ? 0 : 1; if (argc < 2) { addr = load_addr; } else { addr = simple_strtoul(argv[1], NULL, 16); } SHOW_BOOT_PROGRESS (1); printf ("## Booting image at %08lx .../n", addr); /* Copy header so we can blank CRC field for re-calculation */ memmove (&header, (char *)addr, sizeof(image_header_t)); if (ntohl(hdr->ih_magic) != IH_MAGIC) { puts ("Bad Magic Number/n"); SHOW_BOOT_PROGRESS (-1); return 1; } SHOW_BOOT_PROGRESS (2); data = (ulong)&header; len? = sizeof(image_header_t); checksum = ntohl(hdr->ih_hcrc); hdr->ih_hcrc = 0; if(crc32 (0, (char *)data, len) != checksum) { puts ("Bad Header Checksum/n"); SHOW_BOOT_PROGRESS (-2); return 1; } SHOW_BOOT_PROGRESS (3); /* for multi-file images we need the data part, too */ print_image_hdr ((image_header_t *)addr); data = addr + sizeof(image_header_t); len? = ntohl(hdr->ih_size); if(verify) { puts ("?? Verifying Checksum ... "); if(crc32 (0, (char *)data, len) != ntohl(hdr->ih_dcrc)) { printf ("Bad Data CRC/n"); SHOW_BOOT_PROGRESS (-3); return 1; } puts ("OK/n"); } SHOW_BOOT_PROGRESS (4); len_ptr = (ulong *)data; …… switch (hdr->ih_os) { default:????????????? ? /* handled by (original) Linux case */ case IH_OS_LINUX: do_bootm_linux? (cmdtp, flag, argc, argv, ???addr, len_ptr, verify); break; …… } bootm命令調用do_bootm函數。這個函數專門用來引導各種操作系統映像,可以支持引導Linux、vxWorks、QNX等操作系統。引導Linux的時候,調用do_bootm_linux()函數。 3.do_bootm_linux函數的實現 /* lib_arm/armlinux.c */ void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[], ulong addr, ulong *len_ptr, int verify) { DECLARE_GLOBAL_DATA_PTR; ulong len = 0, checksum; ulong initrd_start, initrd_end; ulong data; void (*theKernel)(int zero, int arch, uint params); image_header_t *hdr = &header; bd_t *bd = gd->bd; #ifdef CONFIG_CMDLINE_TAG char *commandline = getenv ("bootargs"); #endif theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); /* Check if there is an initrd image */ if(argc >= 3) { SHOW_BOOT_PROGRESS (9); addr = simple_strtoul (argv[2], NULL, 16); printf ("## Loading Ramdisk Image at %08lx .../n", addr); /* Copy header so we can blank CRC field for re-calculation */ memcpy (&header, (char *) addr, sizeof (image_header_t)); if (ntohl (hdr->ih_magic) != IH_MAGIC) { printf ("Bad Magic Number/n"); SHOW_BOOT_PROGRESS (-10); do_reset (cmdtp, flag, argc, argv); } data = (ulong) & header; len = sizeof (image_header_t); checksum = ntohl (hdr->ih_hcrc); hdr->ih_hcrc = 0; if(crc32 (0, (char *) data, len) != checksum) { printf ("Bad Header Checksum/n"); SHOW_BOOT_PROGRESS (-11); do_reset (cmdtp, flag, argc, argv); } SHOW_BOOT_PROGRESS (10); print_image_hdr (hdr); data = addr + sizeof (image_header_t); len = ntohl (hdr->ih_size); if(verify) { ulong csum = 0; printf ("?? Verifying Checksum ... "); csum = crc32 (0, (char *) data, len); if (csum != ntohl (hdr->ih_dcrc)) { printf ("Bad Data CRC/n"); SHOW_BOOT_PROGRESS (-12); do_reset (cmdtp, flag, argc, argv); } printf ("OK/n"); } SHOW_BOOT_PROGRESS (11); if ((hdr->ih_os != IH_OS_LINUX) || (hdr->ih_arch != IH_CPU_ARM) || (hdr->ih_type != IH_TYPE_RAMDISK)) { printf ("No Linux ARM Ramdisk Image/n"); SHOW_BOOT_PROGRESS (-13); do_reset (cmdtp, flag, argc, argv); } /* Now check if we have a multifile image */ } else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) { ulong tail = ntohl (len_ptr[0]) % 4; int i; SHOW_BOOT_PROGRESS (13); /* skip kernel length and terminator */ data = (ulong) (&len_ptr[2]); /* skip any additional image length fields */ for (i = 1; len_ptr[i]; ++i) data += 4; /* add kernel length, and align */ data += ntohl (len_ptr[0]); if (tail) { data += 4 - tail; } len = ntohl (len_ptr[1]); } else { /* no initrd image */ SHOW_BOOT_PROGRESS (14); len = data = 0; } if (data) { initrd_start = data; initrd_end = initrd_start + len; } else { initrd_start = 0; initrd_end = 0; } SHOW_BOOT_PROGRESS (15); debug ("## Transferring control to Linux (at address %08lx) .../n", (ulong) theKernel); #if defined (CONFIG_SETUP_MEMORY_TAGS) || / defined (CONFIG_CMDLINE_TAG) || / defined (CONFIG_INITRD_TAG) || / defined (CONFIG_SERIAL_TAG) || / defined (CONFIG_REVISION_TAG) || / defined (CONFIG_LCD) || / defined (CONFIG_VFD) setup_start_tag (bd); #ifdef CONFIG_SERIAL_TAG setup_serial_tag (&params); #endif #ifdef CONFIG_REVISION_TAG setup_revision_tag (&params); #endif #ifdef CONFIG_SETUP_MEMORY_TAGS setup_memory_tags (bd); #endif #ifdef CONFIG_CMDLINE_TAG setup_commandline_tag (bd, commandline); #endif #ifdef CONFIG_INITRD_TAG if (initrd_start && initrd_end) setup_initrd_tag (bd, initrd_start, initrd_end); #endif setup_end_tag (bd); #endif /* we assume that the kernel is in place */ printf ("/nStarting kernel .../n/n"); cleanup_before_linux (); theKernel (0, bd->bi_arch_number, bd->bi_boot_params); } do_bootm_linux()函數是專門引導Linux映像的函數,它還可以處理ramdisk文件系統的映像。這里引導的內核映像和ramdisk映像,必須是U-Boot格式的。U-Boot格式的映像可以通過mkimage工具來轉換,其中包含了U-Boot可以識別的符號。

6.4? 使用U-Boot U-Boot是“Monitor”。除了Bootloader的系統引導功能,它還有用戶命令接口,提供了一些復雜的調試、讀寫內存、燒寫Flash、配置環境變量等功能。掌握U-Boot的使用,將極大地方便嵌入式系統的開發。

6.4.1?燒寫U-Boot到Flash 新開發的電路板沒有任何程序可以執行,也就不能啟動,需要先將U-Boot燒寫到Flash中。 如果主板上的EPROM或者Flash能夠取下來,就可以通過編程器燒寫。例如:計算機BIOS就存儲在一塊256KB的Flash上,通過插座與主板連接。 但是多數嵌入式單板使用貼片的Flash,不能取下來燒寫。這種情況可以通過處理器的調試接口,直接對板上的Flash編程。 處理器調試接口是為處理器芯片設計的標準調試接口,包含BDM、JTAG和EJTAG 3種接口標準。JTAG接口在第4章已經介紹過;BDM(Background Debug Mode)主要應用在PowerPC8xx系列處理器上;EJTAG主要應用在MIPS處理器上。這3種硬件接口標準定義有所不同,但是功能基本相同,下面都統稱為JTAG接口。 JTAG接口需要專用的硬件工具來連接。無論從功能、性能角度,還是從價格角度,這些工具都有很大差異。關于這些工具的選擇,將在第6.4.1節詳細介紹。 最簡單方式就是通過JTAG電纜,轉接到計算機并口連接。這需要在主機端開發燒寫程序,還需要有并口設備驅動程序。開發板上電或者復位的時候,燒寫程序探測到處理器并且開始通信,然后把Bootloader下載并燒寫到Flash中。這種方式速率很慢,可是價格非常便宜。一般來說,平均每秒鐘可以燒寫100~200個字節。 燒寫完成后,復位實驗板,串口終端應該顯示U-Boot的啟動信息。 6.4.2? U-Boot的常用命令 U-Boot上電啟動后,敲任意鍵可以退出自動啟動狀態,進入命令行。 U-Boot 1.1.2 (Apr 26 2005 - 12:27:13) U-Boot code: 11080000 -> 1109614C? BSS: -> 1109A91C RAM Configuration: Bank #0: 10000000 32 MB Micron StrataFlash MT28F128J3 device initialized Flash: 32 MB In:??? serial Out:?? serial Err:?? serial Hit any key to stop autoboot:? 0 U-Boot> 在命令行提示符下,可以輸入U-Boot的命令并執行。U-Boot可以支持幾十個常用命令,通過這些命令,可以對開發板進行調試,可以引導Linux內核,還可以擦寫Flash完成系統部署等功能。掌握這些命令的使用,才能夠順利地進行嵌入式系統的開發。 輸入help命令,可以得到當前U-Boot的所有命令列表。每一條命令后面是簡單的命令說明。 => help ??????? - alias for 'help' autoscr - run script from memory base??? - print or set address offset bdinfo? - print Board Info structure boot??? - boot default, i.e., run 'bootcmd' bootd?? - boot default, i.e., run 'bootcmd' bootm?? - boot application image from memory bootp?? - boot image via network using BootP/TFTP protocol cmp???? - memory compare coninfo? - print console devices and information cp????? - memory copy crc32?? - checksum calculation dhcp??? - invoke DHCP client to obtain IP/boot params echo??? - echo args to console erase?? - erase FLASH memory flinfo? - print FLASH memory information go????? - start application at address 'addr' help??? - print online help iminfo? - print header information for application image imls??? - list all images found in flash itest?? ?- return true/false on integer compare loadb?? - load binary file over serial line (kermit mode) loads?? - load S-Record file over serial line loop?? - infinite loop on address range md??? - memory display mm??? - memory modify (auto-incrementing) mtest?? - simple RAM test mw????? - memory write (fill) nfs???? - boot image via network using NFS protocol nm????? - memory modify (constant address) printenv - print environment variables protect - enable or disable FLASH write protection rarpboot - boot image via network using RARP/TFTP protocol reset?? - Perform RESET of the CPU run???? - run commands in an environment variable saveenv - save environment variables to persistent storage setenv? - set environment variables sleep?? - delay execution for some time tftpboot - boot image via network using TFTP protocol version - print monitor version => U-Boot還提供了更加詳細的命令幫助,通過help命令還可以查看每個命令的參數說明。由于開發過程的需要,有必要先把U-Boot命令的用法弄清楚。接下來,根據每一條命令的幫助信息,解釋一下這些命令的功能和參數。 => help bootm bootm [addr [arg ...]] - boot application image stored in memory passing arguments 'arg ...'; when booting a Linux kernel, 'arg' can be the address of an initrd image bootm命令可以引導啟動存儲在內存中的程序映像。這些內存包括RAM和可以永久保存的Flash。 第1個參數addr是程序映像的地址,這個程序映像必須轉換成U-Boot的格式。 第2個參數對于引導Linux內核有用,通常作為U-Boot格式的RAMDISK映像存儲地址;也可以是傳遞給Linux內核的參數(缺省情況下傳遞bootargs環境變量給內核)。 => help bootp bootp [loadAddress] [bootfilename] bootp命令通過bootp請求,要求DHCP服務器分配IP地址,然后通過TFTP協議下載指定的文件到內存。 第1個參數是下載文件存放的內存地址。 第2個參數是要下載的文件名稱,這個文件應該在開發主機上準備好。 => help cmp cmp [.b, .w, .l] addr1 addr2 count - compare memory cmp命令可以比較2塊內存中的內容。.b以字節為單位;.w以字為單位;.l以長字為單位。注意:cmp.b中間不能保留空格,需要連續敲入命令。 第1個參數addr1是第一塊內存的起始地址。 第2個參數addr2是第二塊內存的起始地址。 第3個參數count是要比較的數目,單位按照字節、字或者長字。 => help cp cp [.b, .w, .l] source target count - copy memory cp命令可以在內存中復制數據塊,包括對Flash的讀寫操作。 第1個參數source是要復制的數據塊起始地址。 第2個參數target是數據塊要復制到的地址。這個地址如果在Flash中,那么會直接調用寫Flash的函數操作。所以U-Boot寫Flash就使用這個命令,當然需要先把對應Flash區域擦干凈。 第3個參數count是要復制的數目,根據cp.b cp.w cp.l分別以字節、字、長字為單位。 => help crc32 crc32 address count [addr] - compute CRC32 checksum [save at addr]?? crc32命令可以計算存儲數據的校驗和。 第1個參數address是需要校驗的數據起始地址。 第2個參數count是要校驗的數據字節數。 第3個參數addr用來指定保存結果的地址。 => help echo echo [args..] - echo args to console; /c suppresses newline echo命令回顯參數。 => help erase erase start end - erase FLASH from addr 'start' to addr 'end' erase N:SF[-SL] - erase sectors SF-SL in FLASH bank # N erase bank N - erase FLASH bank # N erase all - erase all FLASH banks erase命令可以擦Flash。 參數必須指定Flash擦除的范圍。 按照起始地址和結束地址,start必須是擦除塊的起始地址;end必須是擦除末尾塊的結束地址。這種方式最常用。舉例說明:擦除0x20000 – 0x3ffff區域命令為erase 20000 3ffff。 按照組和扇區,N表示Flash的組號,SF表示擦除起始扇區號,SL表示擦除結束扇區號。另外,還可以擦除整個組,擦除組號為N的整個Flash組。擦除全部Flash只要給出一個all的參數即可。 => help flinfo flinfo - print information for all FLASH memory banks flinfo N - print information for FLASH memory bank # N flinfo命令打印全部Flash組的信息,也可以只打印其中某個組。一般嵌入式系統的Flash只有一個組。 => help go go addr [arg ...] - start application at address 'addr' ???passing 'arg' as arguments go命令可以執行應用程序。 第1個參數是要執行程序的入口地址。 第2個可選參數是傳遞給程序的參數,可以不用。 => help iminfo iminfo addr [addr ...] - print header information for application image starting at address 'addr' in memory; this includes verification of the image contents (magic number, header and payload checksums) iminfo可以打印程序映像的開頭信息,包含了映像內容的校驗(序列號、頭和校驗和)。 第1個參數指定映像的起始地址。 可選的參數是指定更多的映像地址。 => help loadb loadb [ off ] [ baud ] - load binary file over serial line with offset 'off' and baudrate 'baud' loadb命令可以通過串口線下載二進制格式文件。 => help loads loads [ off ] - load S-Record file over serial line with offset 'off' loads命令可以通過串口線下載S-Record格式文件。 => help mw mw [.b, .w, .l] address value [count] - write memory mw命令可以按照字節、字、長字寫內存,.b .w .l的用法與cp命令相同。 第1個參數address是要寫的內存地址。 第2個參數value是要寫的值。 第3個可選參數count是要寫單位值的數目。 => help nfs nfs [loadAddress] [host ip addr:bootfilename] nfs命令可以使用NFS網絡協議通過網絡啟動映像。 => help nm nm [.b, .w, .l] address - memory modify, read and keep address nm命令可以修改內存,可以按照字節、字、長字操作。 參數address是要讀出并且修改的內存地址。 => help printenv printenv - print values of all environment variables printenv name ... - print value of environment variable 'name' printenv命令打印環境變量。 可以打印全部環境變量,也可以只打印參數中列出的環境變量。 => help protect protect on? start end - protect Flash from addr 'start' to addr 'end' protect on? N:SF[-SL] - protect sectors SF-SL in Flash bank # N protect on? bank N - protect Flash bank # N protect on? all - protect all Flash banks protect off start end ???- make Flash from addr 'start' to addr 'end' writable protect off N:SF[-SL] ???? - make sectors SF-SL writable in Flash bank # N protect off bank N ???? - make Flash bank # N writable protect off all ???? - make all Flash banks writable protect命令是對Flash寫保護的操作,可以使能和解除寫保護。 第1個參數on代表使能寫保護;off代表解除寫保護。 第2、3參數是指定Flash寫保護操作范圍,跟擦除的方式相同。 => help rarpboot rarpboot [loadAddress] [bootfilename] rarboot命令可以使用TFTP協議通過網絡啟動映像。也就是把指定的文件下載到指定地址,然后執行。 第1個參數是映像文件下載到的內存地址。 第2個參數是要下載執行的映像文件。 => help run run var [...] - run the commands in the environment variable(s) 'var' run命令可以執行環境變量中的命令,后面參數可以跟幾個環境變量名。 => help setenv setenv name value ... - set environment variable 'name' to 'value ...' setenv name - delete environment variable 'name' setenv命令可以設置環境變量。 第1個參數是環境變量的名稱。 第2個參數是要設置的值,如果沒有第2個參數,表示刪除這個環境變量。 => help sleep sleep N - delay execution for N seconds (N is _decimal_ !!!) sleep命令可以延遲N秒鐘執行,N為十進制數。 => help tftpboot tftpboot [loadAddress] [bootfilename] tftpboot命令可以使用TFTP協議通過網絡下載文件。按照二進制文件格式下載。另外使用這個命令,必須配置好相關的環境變量。例如serverip和ipaddr。 第1個參數loadAddress是下載到的內存地址。 第2個參數是要下載的文件名稱,必須放在TFTP服務器相應的目錄下。 這些U-Boot命令為嵌入式系統提供了豐富的開發和調試功能。在Linux內核啟動和調試過程中,都可以用到U-Boot的命令。但是一般情況下,不需要使用全部命令。比如已經支持以太網接口,可以通過tftpboot命令來下載文件,那么還有必要使用串口下載的loadb嗎?反過來,如果開發板需要特殊的調試功能,也可以添加新的命令。 在建立交叉開發環境和調試Linux內核等章節時,在ARM平臺上移植了U-Boot,并且提供了具體U-Boot的操作步驟。

6.4.3? U-Boot的環境變量 有點類似ShellU-Boot也使用環境變量。可以通過printenv命令查看環境變量的設置。 U-Boot> printenv bootdelay=3 baudrate=115200 netmask=255.255.0.0 ethaddr=12:34:56:78:90:ab bootfile=uImage bootargs=console=ttyS0,115200 root=/dev/ram rw initrd=0x30800000,8M bootcmd=tftp 0x30008000 zImage;go 0x30008000 serverip=192.168.1.1 ipaddr=192.168.1.100 stdin=serial stdout=serial stderr=serial Environment size: 337/131068 bytes U-Boot> 表6.5是常用環境變量的含義解釋。通過printenv命令可以打印出這些變量的值。 表6.5????????????????????????????????????????????????? U-Boot環境變量的解釋說明
環 境 變 量 解 釋 說 明
bootdelay 定義執行自動啟動的等候秒數
baudrate 定義串口控制臺的波特率
netmask 定義以太網接口的掩碼
ethaddr 定義以太網接口的MAC地址
bootfile 定義缺省的下載文件
bootargs 定義傳遞給Linux內核的命令行參數
bootcmd 定義自動啟動時執行的幾條命令
serverip 定義tftp服務器端的IP地址
ipaddr 定義本地的IP地址
stdin 定義標準輸入設備,一般是串口
stdout 定義標準輸出設備,一般是串口
stderr 定義標準出錯信息輸出設備,一般是串口
U-Boot的環境變量都可以有缺省值,也可以修改并且保存在參數區。U-Boot的參數區一般有EEPROM和Flash兩種設備。 環境變量的設置命令為setenv,在6.2.2節有命令的解釋。 舉例說明環境變量的使用。 =>setenv serverip? 192.168.1.1 =>setenv ipaddr? 192.168.1.100 =>setenv rootpath? "/usr/local/arm/3.3.2/rootfs" =>setenv bootargs ?"root=/dev/nfs rw nfsroot=/$(serverip):/$(rootpath) ip=
/$(ipaddr) " =>setenv kernel_addr 30000000 =>setenv nfscmd ?"tftp /$(kernel_addr) uImage; bootm /$(kernel_addr) " =>run nfscmd 上面定義的環境變量有serverip ipaddr rootpath bootargs kernel_addr。環境變量bootargs中還使用了環境變量,bootargs定義命令行參數,通過bootm命令傳遞給內核。環境變量nfscmd中也使用了環境變量,功能是把uImage下載到指定的地址并且引導起來。可以通過run命令執行nfscmd腳本。
?

http://blog.csdn.net/menuconfig/article/details/2270502

總結

以上是生活随笔為你收集整理的u-boot 详细介绍 .的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。