解 霏,蔣大明
(北京交通大學 電子信息工程學院,北京 100044)
嵌入式Linux系統通常由引導裝載程序 (包括固化在固件中的 boot代碼和 BootLoader),Linux內核,文件系統,應用程序這4部分構成。引導裝載程序是系統加電后執行的第一段軟件代碼,在嵌入式系統中整個系統的加載啟動任務就完全由 BootLoader來完成[1]。BootLoader嚴重依賴硬件,需要根據不同的硬件配置進行移植。U-boot是德國DENX小組開發的多種嵌入式CPU的BootLoader程序,支持Linux VxWorks QNX等多種嵌入式系統。筆者通過對U-Boot啟動過程的分析并對其修改,使其支持自動識別啟動,并在基于S3C2440的硬件平臺驗證通過,同時正確引導Linux內核。
硬件平臺選用的是基于ARM9體系結構,ARM920T的CPU,SOC選用的是三星S3C2440A,同時支持Nor FLASH與Nand FLASH啟動。主要硬件資源如表1所示。
U-Boot全稱是Universal Bootloader,是遵循GPL條款的開放源碼項目。DENX軟件工程中心的Wolfgang Denk最早基于8xxROM的源碼創建了PCC-Boot工程,并不斷添加處理器的支持。后來Sysgo Gmbh把PPCBoot移植到ARM平臺上,創建了ARMBoot工程,然后以PPCBoot工程和ARMBoot工程為基礎,創建了U-Boot工程[2]。

表1 開發板硬件資源Tab.1 Hardware resource of platform
U-Boot的啟動分為階段 1(stage1)和階段 2(stage2),階段1的代碼通常在start.S中,由匯編語言寫成。階段2的代碼通常用C語言來實現,可以實現復雜功能,階段2的代碼通常在lib_arm/board.c中。啟動流程如圖1所示。第1階段完成的工作包括,基本硬件初始化,屏蔽所有中斷源,關閉看門狗,設置CPU速度和時鐘頻率,RAM初始化等。設置堆棧指針,為階段2的C語言做好準備,拷貝階段2代碼到RAM,跳轉到階段2的C入口點。第2階段完成的工作包括,初始化本階段用到的硬件,檢測系統內存映射,加載內核映像與根文件系統映像,設置內核啟動參數,啟動內核[3]。

圖1 U-Boot啟動流程Fig.1 Flow chart of boot U-Boot
基于S3C2440的開發板支持Nor FLASH和Nand FLASH這兩種啟動方式,不論從那種方式啟動,程序都是從0x00000000地址處開始執行的,不同的地方是地址映射不一樣。要使開發板上電后能自動識別啟動設備,需要根據2種FLASH啟動時的不同點加以區別。
當采用Nor FLASH這種啟動時,Nor FLASH被映射到nGCS0(0x00000000)中,S3C2440可以直接從 0x00000000運行。
當采用Nand FLASH這種啟動時,S3C2440的Nand FLASH控制器會自動把Nand FLASH中前4K數據被拷貝到S3C2440內部大小為4 k的 SRAM(Steppingstone)中,同時內部的SRAM被映射到nGCS0(0x00000000)和0x40000000~0x40001000地址空間。S3C2440從0x00000000開始執行[4]。
S3C2440的啟動方式可以由引腳OM1與OM0決定,這2個引腳的邏輯電平是實現U-Boot自動識別啟動的關鍵。OM功能如表2所示。

表2OM功能Tab.2 Function of OM
當OM[1:0]=00時,S3C2440從 Nand FLASH啟動,其他情況則S3C2440從Nor FLASH啟動,所以可以通過檢測OM[1:0]的電平來判定S3C2440的啟動方式。寄存器rBWSCON的[2:1]位是由OM[1:0]電平狀態決定的,因此可以通過檢測寄存rBWSCON[2:1]的狀態來識別啟動方式。
筆者將AUTO_DETECT函數添加到start.S文件中,這個函數可以實現啟動位置的自動識別,識別為Nor FLASH啟動后,函數會自動跳轉到NOR_BOOT函數中,識別為Nand FLASH啟動后,函數則會跳轉到NAND_BOOT函數中,實現語句如下:

_TEXT_BASE存放的是編譯連接時U-Boot在SRAM中運行的基地址,由config.mk定義。函數首先檢測U-Boot是否在內存中運行,然后檢測寄存器rBWSCON[2:1]的值是否為0,為0時跳轉到NAND_BOOT函數,不為0時跳轉到NOR_BOOT函數。
調用NOR_BOOT時,由于Nor FLASH的結構特點,其支持片內運行可以直接從Nor FLASH中復制U-Boot到SRAM中,所以啟動函數較為簡單。實現語句如下:

_armboot_start為代碼段的起始地址,_bss_start為代碼段的末地址,兩者間的差值即為U-boot的大小,存放寄存器R2中,寄存器R0中存放的是當前代碼的地址。調用一個拷貝循環將U-Boot拷貝到RAM中。
調用NAND_BOOT時,Nand FLASH的讀寫需要用專門的驅動程序來控制,而驅動程序又比較復雜,采用C語言實現NAND_BOOT的核心功能要比匯編語言容易些[7]。NAND_BOOT函數中復制U-Boot到RAM這段核心代碼用C語言實現,其他部分匯編語言實現。鑒于篇幅有限本文只重點說明下U-Boot復制到RAM的實現過程。部分實現語句如下:


NAND_BOOT中調用的C語言函數為int nand_read_ll(unsigned char*buf, unsigned long start_addr, int size), 這個函數在nand_read.c這個文件中。nand_read_ll函數的參數分別存放在R0,R1,R2這3個寄存器中,R0中存放目標地址,R1中存放源地址,R2中存放復制長度[5]。函數的執行過程如圖2所示。

圖2 nand_read_ll函數執行流程Fig.2 Execution flow chart of function nand_read_ll
U-Boot最終要完成的任務是引導并成功啟動內核,內核啟動前需要正確設置內核的啟動參數,U-Boot與內核的交互式單向的,U-Boot需要將各類參數傳遞給內核。筆者引導的Linux內核為2.6.32.2,通過對Linux中Nand FLASH分區表分析,可以得出如下的啟動參數:

采用NFS方式掛載根文件系統。
UvBoot默認支持uImage格式的linux內核引導,而Linux2.6.32.2默認編譯生成zImage格式的linux內核[6],利用mkimage這個 U-Boot自帶的工具處理下即可,uImage比zImage多一個大小為0x40的頭信息,里面包含CPU體系結構,操作系統,加載到內存的位置,在內存中入口點的位置及映像名等信息。
將編譯生成的u-boot.bin通過JTAG下載到FLASH的0地址處,通過tftp將uImage下載到Nand FLASH中,重啟開發板,若從串口打印出如下信息則說明U-Boot成功引導了linux內核。

圖3 串口打印信息Fig.3 Printed information of UART
BootLoader一直是嵌入式開發的熱門研究方向,U-Boot是一款十分優秀的開源BootLoader,對U-Boot的研究具有很大的實用價值。本文對于基于S3C2440硬件平臺的自動識別啟動原理進行了詳細的分析,并提出了實現方案,同時對linux內核的啟動參數做了簡要分析。編譯生成的u-boot.bin燒寫到Nor FLASH或者Nand FLASH均能穩定運行,這對進一步嵌入式系統開 發帶來了很大的便利。
[1]文全剛.ARM嵌入式技術原理與應用[M].北京:北京航空航天大學出版社,2011.
[2]查婧.ARM9嵌入式系統設計及U-boot移植[D].西安:中科院西安光學精密機械研究所,2009.
[3]韋東山.嵌入式Linux應用開發完全手冊[M].北京:人民郵電出版社,2008.
[4]Samsung Electronics.S3C2440A 32-BIT CMOS MICROCONTROLLER USR`MANUAL[S].2004:195-197.
[5]楊水清.ARM嵌入式Linux系統開發技術詳解[M].北京:電子工業出版社,2008.
[6]高文輝,師奕兵,張偉.基于S3C2440的U-Boot雙啟動實現[J].測控技術,2012,31(2):87-91.
GAO Wen-hui,SHI Yi-bing,ZHANG Wei.Implementation of double boot in U-Boot based on S3C2440[J].Measurement&Control Technology,2012,31(2):87-91.
[7]吳玉香,周建香,郭建勛.U-Boot在S3C2410上的移植于功能擴展[J].計算機科學與工程,2010,31(4):729-732.
WU Yu-xiang,ZHOU Jian-xiang,GUO Jian-xun.Porting and expansionofU-BootbasedonS3C2410[J].ComputerEngineering and Design,2010,31(4):729-732.