隨著整個微電子行業日新月異的發展,ARM處理器已經帶給了人們越來越多的驚喜。目前它憑借著自身高性能,低成本,低功耗等特點,已經廣泛應用于消費電子、數字家電、及工業生產等領域中。ARM公司推出的新一代cortex系列產品為開發人員提供了更多不同層面的選擇。cortex-A8為cortex系列中的“A”系,屬于ARMV7架構,主要面向搭載操作系統的,高性能的應用領域。Bootloader是系統上電后執行的第一段代碼,其功能類似于電腦的BIOS。簡單來說,Bootloader主要的工作有兩點:其一是初始化底層硬件資源,為操作系統啟動提供必要的環境;其二是從存儲設備中讀取操作系統鏡像并啟動。但為了方便后期開發,我們往往要為Bootloader添加其他的功能。如支持串口打印調試信息,支持nfs網絡下載,支持根文件系統燒寫等。本文以u-boot-2013.01為源碼包,設計和實現了一個基于s5pv210平臺的,功能齊全且高效穩定的Bootloader。 1 硬件平臺 1.1 s5pv210簡介 s5pv210是三星公司推出的一款基于cortex-A8架構的,高性能的應用處理器。該處理器支持ARM V7指令集,具有32位內部總線結構,主頻最高可達1GHz。另外該處理器還支持掛接LPDDR1、LPDDR2和DDR2類型的RAM,Flash方面可選擇Nand Flash,Nor Flash等。不僅如此,該處理器還提供了包括串口、LCD、IIC、SPI、USB、HDMI等豐富的外部接口資源。目前s5pv210以其高效的性能和卓越的圖形處理能力已經廣泛的應用于智能手機和平板電腦之中。 1.2 s5pv210的啟動方式 s5pv210支持多種啟動方式,下圖為s5pv210啟動的流程圖: 圖1 s5pv210的啟動流程 s5pv210的啟動過程由BL0,BL1和BL2(BL為Bootloader的簡稱)三部分代碼實現,其中BL0在出廠時已經被固化到64KB的iROM中。s5pv210上電后首先執行BL0,該段代碼主要負責一些簡單的初始化工作,如關看門狗,初始化ICache等。然后BL0會根據硬件設置判斷為何種方式啟動,并將BL1從啟動設備(OneNand,Nand Flash,USB,UART,SD card等)拷貝到Internal SRAM的BL1區中。最后BL0會比較一個校驗值,如果相等則跳轉到BL1中繼續執行,否則轉入其他啟動方式。該校驗值存在BL1的頭部中,其大小為16字節。 開發人員一般會單獨編寫一個工具對BL1編譯出的二進制文件添加頭部。其校驗值(checksum)的計算方法如下: ... a = Buf + SPL_HEADER_SIZE; for(i = 0, checksum = 0; i 2 軟件平臺 2.1 U-Boot介紹 U-Boot(全稱Universal Boot Loader)是Bootloader的一種,其他常見的Bootloader還有RedBoot,vivi等。與之相比,U-Boot具有代碼開源,可靠性和擴展性較高等優點。另外,對于類似于Nand Flash、Nor Flash、網卡、LCD等硬件資源的,U-Boot已經抽象出與硬件平臺無關的代碼作為設備驅動源碼提供給開發者。對于與硬件平臺相關的代碼,U-Boot將其定義成宏,并保留到配置文件中。開發者往往只需要修改這些宏的值就能成功使用這些硬件資源,這讓我們的移植工作變得十分簡單。 2.2 U-Boot中幾個比較重要的源文件 U-Boot的源碼包包含有上千個源文件,但經過分析之后,可以找出如下幾個比較重要的源文件(括號中為該源文件所在源碼包的位置)。 (1)start.S(arch\arm\cpu\armv7\start.S) 通過分析U-Boot的鏈接文件可以發現,start.S是U-Boot上電后被執行的第一個源文件。該源文件包括定義了異常向量入口,相關的全局變量,禁用L2緩存,關閉MMU等,之后跳轉到lowlevel_init()函數中繼續執行。 (2)lowlevel_init.S(board\samsung\smdkv210\lowlevel_init.S) 該源文件同樣用匯編代碼編寫,其中只定義了一個函數lowlevel_init()。該函數實現對平臺硬件資源的一系列初始化過程,包括關看門狗,初始化系統時鐘,內存和串口。 (3)mem_setup.S(board\samsung\smdkv210) 該源文件包含對內存進行初始化的匯編源碼。 (4)board.c(arch\arm\lib\board.c) 該源文件是用C編寫的,主要實現了U-Boot第二階段啟動過程。包括初始化環境變量,串口控制臺,Flash和打印調試信息等,最后調用main_loop()函數。 (5)smdkv210.h(include\configs\Smdkv210.h) s5pv210平臺的配置文件,該源文件定義了一些與CPU或者外設相關的參數,這些參數都是用宏來定義的。 2.3 U-Boot啟動的一般流程 2.3.1 第一階段初始化 U-Boot的啟動過程分為兩個階段,第一個階段主要由匯編代碼實現,負責對CPU及底層硬件資源的初始化。第二階段用C實現,負責使能Flash,網卡和引導操作系統等。其第一階段流程如下圖所示: 圖2 U-Boot第一階段啟動流程 U-Boot上電后首先會設置cpu為管理模式,禁用L1緩存,關閉MMU和清除caches。之后會調用底層初始化函數lowlevel_init()。該函數實現如下: .globl lowlevel_init lowlevel_init: push {lr} #if defined(CONFIG_SPL_BUILD) /* 初始化時鐘 */ bl system_clock_init /* 初始化內存 */ bl mem_ctrl_asm_init /* 初始化串口 */ bl uart_asm_init #endif pop {pc} 上述代碼中system_clock_init(), mem_ctrl_asm_init(),uart_asm_init()這三個函數需要開發者結合具體硬件環境進行修改和實現。 初始化完成之后,U-Boot會調用一個拷貝函數將BL2拷貝到內存地址為0x3FF00000處,然后跳轉到該位置執行BL2。在U-Boot中,BL1和BL2是基于相同的一些源文件編譯生成的。開發者在編寫代碼時需要使用預編譯宏CONFIG_SPL_BUILD來實現BL1和BL2不同的功能。其拷貝函數實現如下: void copy_code_2_sdram_and_run(void) { unsigned long ch; void (*u_boot)(void); ch = *(volatile unsigned int *)(0xD0037488); /* 根據該地址的值判斷傳輸通道 */ /* copy_bl2()函數不需要開發者去實現,s5pv210在出廠時已經固化在了0xD0037F98地址處 */ copy_sd_mmc_to_mem copy_bl2 = (copy_sd_mmc_to_mem) (*(unsigned int *) (0xD0037F98)); unsigned int ret; if (ch == 0xEB000000) { /* CONFIG_SYS_TEXT_BASE = 0x3FF00000 */ ret = copy_bl2(0, 49, 1024,(unsigned int *)CONFIG_SYS_TEXT_BASE, 0); } else if (ch == 0xEB200000) { ret = copy_bl2(2, 49, 1024,(unsigned int *)CONFIG_SYS_TEXT_BASE, 0); } else { return; } u_boot = (void *)CONFIG_SYS_TEXT_BASE; (*u_boot)(); /* 跳轉到該地址執行 */ } 值得注意的是以上代碼中,copy_bl2()函數不需要開發者去實現,s5pv210在出廠時已經將該函數固化在了0xD0037F98地址處。其函數原型如下: u32 (*copy_sd_mmc_to_mem)(u32 channel, u32 start_block, u16 block_size, u32 *trg, u32 init); /* 參數介紹: channel:通道數2或0,該值通過讀取0xD0037488地址上的值判斷。 start_block:從第幾個扇區開始拷貝,一個扇區為512byte。 block_size:拷貝多少個扇區,這里拷貝512K trg:目的地址:0x3FF00000, 即離內存頂部1M的位置 init:是否需要初始化sd卡,寫0即可。 */ 2.3.2 第二階段初始化 U-Boot進入第二階段后會首先聲明一個gd_t結構體類型的指針指向內存地址(0x40000000 - GD_SIZE)處。0x40000000為內存結束地址,GD_SIZE為結構體gd_t的大小。這樣相當于在內存最頂端分配了一段空間用于存放一個臨時結構體gd_t。該結構體在global_data.h中被定義,U-Boot用它來存儲所有的全局變量。之后U-Boot會調用board_init_f()和board_init_r()兩個函數進一步對底板進行初始化。 (1)board_init_f() 進入board_init_f()之后,U-Boot首先設置之前分配的臨時結構體,然后開始劃分內存空間,其內存分配示意圖如下: 圖3 U-Boot內存分配狀態 從內存分配狀態圖中我們可以看到,gd指針指向的臨時結構體存放在內存的最頂部。BL2代碼存放在內存地址0x3ff00000處,即距離內存頂部1M空間的位置。接下來依次分配malloc空間,bd_t結構體空間和gd_t結構體空間,并且重新設置棧。最后將臨時結構體拷貝到id指針所指向的位置。board_init_f()實現過程大致如下: unsigned int board_init_f(ulong bootflag) { memset((void *)gd, 0, sizeof(gd_t)); ... 設置gd結構體; ... addr = CONFIG_SYS_TEXT_BASE; /* CONFIG_SYS_TEXT_BASE = 0x3ff00000000 */ addr_sp = addr - TOTAL_MALLOC_LEN; addr_sp -= sizeof (bd_t); bd = (bd_t *) addr_sp; gd->bd = bd; addr_sp -= sizeof (gd_t); id = (gd_t *) addr_sp; ... memcpy(id, (void *)gd, sizeof(gd_t)); base_sp = addr_sp; return (unsigned int)id; } (2)board_init_r() board_init_r()負責對其他硬件資源進行初始化。如網卡、Flash、MMC、中斷等。最后調用main_loop(),等待用戶輸入命令。 3 設計實現 3.1 支持Nand Flash讀寫 Nand Flash是嵌入式系統中重要的存儲設備,其儲存對象包括Bootloader本身,操作系統內核,環境變量,根文件系統等,所以使能Nand Flash讀寫是U-Boot移植過程中必須完成的一個步驟。U-Boot中Nand Flash初始化函數調用關系為: board_init_r()->nand_init()->nand_init_chip()->board_nand_init()。 board_nand_init()完成兩件事:(1)對s5pv210關于Nand Flash控制器的相關寄存器進行設置。(2)對nand_chip結構體進行設置。我們需要設置的成員項有IO_ADDR_R,IO_ADDR_W,這兩個成員都指向地址0xB0E00010,即 Nand Flash控制器的數據寄存器的地址。另外還需要實現以下三個成員函數: ① void (*select_chip)(struct mtd_info *mtd, int chip); 該函數實現Nand Flash設備選中或取消選中。 ② void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); 該函數實現對Nand Flash發送命令或者地址。 ③ int (*dev_ready)(struct mtd_info *mtd); 該函數實現檢測Nand Flash設備狀態。 最后將成員ecc.mode設置為NAND_ECC_SOFT,即ECC軟件校驗。 配置文件中相應的宏定義如下所示: #define CONFIG_NAND_S5PV210 /* 告訴Makefile編譯Nand模塊 */ #define CONFIG_SYS_MAX_NAND_DEVICE 1 /* 指定設備數量 */ #define CONFIG_SYS_NAND_BASE 0xB0E00000 /* Nand Flash 控制器的基址 */ 3.2 支持網卡 支持nfs或tftp網絡下載會極大的方便我們從Linux服務器上下載文件或鏡像到硬件平臺上。所以使能網卡在U-Boot移植過程中也顯得非常重要。以網卡dm9000為例,U-Boot已經抽象出一套完整的關于dm9000的驅動代碼(其源碼路徑為drivers\net\dm9000x.c)。用戶只需要根據具體的硬件電路配置相應的宏即可。U-Boot中dm9000網卡初始化函數的調用關系為: board_init_r()->eth_initialize()->board_eth_init()->dm9000_initialize()。 配置文件中相應的宏定義如下所示: #define CONFIG_DRIVER_DM9000 /* DM9000網卡模塊加入編譯 */ #define CONFIG_DM9000_BASE (0x88001000) /* 基地址 */ #define DM9000_IO (CONFIG_DM9000_BASE) /* IO口地址 */ #define DM9000_DATA (CONFIG_DM9000_BASE + 0x300C) /* 數據口地址 */ 3.3.支持環境變量的保存和修改 為了方便用戶配置,U-Boot將一部分變量,如串口波特率、ip地址、內核參數、啟動命令等存在Flash或SD卡上,這部分數據稱為環境變量。每次上電啟動時,U-Boot會檢查Flash或SD卡上是否存放有環境變量。如果有則將其讀取出來并使用,如果沒有就使用默認的環境變量。默認的環境變量定義在env_default.h中。用戶也可以隨時修改或保存環境變量到Flash或SD卡中。 對于環境變量的移植也非常簡單。以Nand Flash為例,開發人員在smdkv210.h源文件中只需要添加如下的宏定義即可: #define CONFIG_ENV_IS_IN_NAND /* 告訴Makefile環境變量保存在Nand Flash中 */ #define CONFIG_ENV_OFFSET 0x80000 /* 環境變量保存的Nand Flash中的偏移地址 */ #define CONFIG_ENV_SIZE 0x20000 /* 環境變量的大小 */ #define CONFIG_ENV_OVERWRITE /* 規定環境變量和覆蓋 */ 4. 測試結果 4.1. 測試U-Boot啟動 測試平臺的軟硬件環境: ◆ 硬件平臺為友善之臂的smart210開發板; ◆ Linux服務器版本為ubuntu9.0; ◆ U-Boot版本為2013-01; ◆ GCC交叉編譯工具鏈版本為4.3.2。 首先將修改后的源碼包放到裝有交叉編譯工具鏈的Linux服務器上,輸入命令make即可以編譯生成兩個二進制文件。一個是smdkv210-spl.bin,即BL1代碼,存放在spl目錄下。另一個是u-boot.bin,即BL2代碼,存放在頂層目錄下。然后插入SD卡,執行write2sd.sh腳本就可以把BL1和BL2分別燒寫到SD卡的第1扇區和第49扇區。write2sd.sh腳本實現如下: #!/bin/sh sudo dd iflag=dsync oflag=dsync if=spl/smdkv210-spl.bin of=/dev/sdc seek=1 sudo dd iflag=dsync oflag=dsync if=u-boot.bin of=/dev/sdc seek=49 燒寫完成之后將SD卡插入到smart210開發板的SD卡槽中,連接好串口和網線,上電啟動。之后可以通過串口工具(本次測試使用的是SecureCRT)看到輸出信息如圖四所示,表示該U-Boot已經成功運行在開發板上。 圖四 測試U-Boot啟動 4.2 測試Nand Flash和網卡。 在等待命令狀態下輸入“nfs 20000000 192.168.1.123:/work/nfs_root/uImage”,該命令表示從ip為192.168.1.123的Linux服務器上,通過nfs下載該服務器上“/work/nfs_root”目錄下的uImage,并存放在內存地址為0x20000000處。uImage為適配于s5pv210開發板的Linux內核鏡像。等待一段時間后看到“Bytes transferred = 2127008 (2074a0 hex)”字樣,表示下載成功。 下載成功之后輸入命令“nand erase.part kernel”,該命令表示擦除Nand Flash上的kernel分區。接著輸入命令“nand write 20000000 kernel”,該命令表示拷貝內存地址為0x20000000的內容,燒寫到Nand Flash的kernel分區中。內存地址0x20000000存放的內容正是之前我們從Linux服務器上下載下來的uImage。kernel保存在環境變量mtdparts中,kernel規定了該分區的起始地址和大小。執行完這兩條命令后看到“4194304 bytes written: OK”字樣,表示系統內核已經成功燒寫到Nand Flash中,證明了Nand Flash和網卡均可以正常使用。其實驗結果如圖五所示: 圖五 測試Nand Flash和網卡 4.3 測試引導內核啟動 燒寫完內核之后執行boot命令就可以啟動該內核。boot命令實際上是執行環境變量bootcmd中的一條語句,其內容為“nand read 20000000 kernel; bootm 20000000”,該語句表示從Nand Flash的kernel分區中讀出內核并存放在內存地址為0x20000000處,然后跳轉在該地址執行內核鏡像。啟動過程中可以看到串口打印出如圖六所示的信息,證明了該U-Boot已經成功支持引導Linux操作系統。 圖六 測試引導內核啟動 結語 本文從硬件,軟件兩個方面分析了s5pv210的特性及啟動方式,然后通過u-boot-2013.01源碼包詳細闡述了U-Boot啟動過程中的兩個階段,最后結合smart210開發板成功設計了一個基于SD卡啟動的,多功能的Bootloader。測試結果表明該Bootloader支持Nand Flash 讀寫,nfs網絡下載,環境變量保存和修改等功能,為后期開發帶來了極大的便利。 |