1. 程式人生 > >全志A10/A20 Bootloader載入過程分析

全志A10/A20 Bootloader載入過程分析

【轉】A10/A20 Bootloader載入過程分析

轉自:http://blog.csdn.net/allen6268198/article/details/12905425

A10/A20 Bootloader載入過程分析

注:由於全志A10和A20在載入Bootloader過程方面基本一致,下面僅以A20敘述,但同時也適用於A10。另外在不需要區分Cubieboard1和Cubieboard2的情況下,統稱為Cubieboard;另現在市面上一般所說的SD卡即為Micro SD Card,也就是TF卡,為區別於一般傳統的SD卡,本文一般使用TF卡描述,但同於平時所說的SD卡。

A20的啟動過程大概可分為5步:Boot ROM,SPL,Uboot,Kernel,RootFileSystem。本文關注的是映象的載入和啟動過程,分析Boot ROM→SPL→Uboot→Kernel的啟動流程。

系統上電後,ARM處理器在復位時從地址0×000000開始執行指令,把板上ROM或Flash對映到這一地址。A20將啟動裝置選擇程式固化在CPU內部的一個32KB ROM中,預設的啟動時序為SD Card0,NAND FLASH,SD Card2,SPI NOR FLASH。另外通過外部的一個啟動選擇引腳可以使其跳轉到USB啟動模式。通常情況下,啟動選擇引腳狀態連線50K內部上拉電阻。在上電後,執行儲存在Boot ROM中的啟動程式碼,將自動檢測啟動選擇引腳狀態。只有當該引腳狀態為低電平時才選擇USB啟動模式。

在選擇啟動裝置後將載入並執行bootloader程式,CPU通過拷貝或對映bootloader程式到記憶體,然後執行bootloader的第一條指令。通過閱讀官方的uboot燒寫方法,發現A20通過uboot引導系統之前先載入了uboot SPL。什麼是SPL?通過查閱uboot的官網資料得知,SPL是一個迷你版的uboot,全拼為Second Program Loader。適用於SOC的內部SRAM<64K的情況,用它來載入完整的uboot程式到SDRAM,並通過完整uboot載入核心來啟動系統。其中SRAM一般指CPU內部的L1/L2或外部的L2快取記憶體,這裡即為Boot ROM,而SDRAM一般指記憶體。

SPL程式流程如下:

初始化ARM處理器
初始化串列埠控制檯
配置時鐘和最基礎的分頻
初始化SDRAM
配置引腳多路複用功能
啟動裝置初始化(即上面選擇的啟動裝置)
載入完整的uboot程式並轉交控制權
啟動裝置選擇程式的流程圖:

搞清楚了上面的概念,可以知道Cubieboard出廠已經燒寫了NandFlash中的程式,即在啟動時使用的是NandFlash。現在根據全志A20上述步驟,我們嘗試用SD Card0(即Cubieboard上卡槽中的TF卡)來啟動系統。

環境準備

本文所使用的主機環境為kubuntu 12.10,然而一般情況下,下面涉及到的命令對基於Debian的(X)ubuntu系列都應該適用。

為不引起混淆,我們作如下約定:

工作目錄為 $WORK_DIR
命令均以root使用者執行
筆者的設定如下:

WORK_DIR=/home/itviewer/src
下載必須的工具軟體

apt-get install build-essential libncurses5-dev u-boot-tools qemu-user-static debootstrap git binfmt-support libusb-1.0-0-dev pkg-config
apt-get install gcc-arm-linux-gnueabihf
下載原始碼

從 github 下載 SPL,U-BOOT,Linux 核心原始碼。注意 linux-sunxi 超過 3.8G ,耗時最長。如果您曾經下載過這些程式碼,記得分別用 git pull 更新後再進行後續操作,因為程式碼倉庫每天都有變化。

cd $WORK_DIR
git clone git://github.com/linux-sunxi/u-boot-sunxi.git
git clone git://github.com/cubieboard2/linux-sunxi
git clone git://github.com/linux-sunxi/sunxi-tools.git
git clone git://github.com/linux-sunxi/sunxi-boards.git
編譯uboot

cd $WORK_DIR/u-boot-sunxi
make distclean CROSS_COMPILE=arm-linux-gnueabihf-
make Cubieboard2 CROSS_COMPILE=arm-linux-gnueabihf-
得到 u-boot-sunxi-with-spl.bin(同時生成的還有其它幾個檔案,這裡我們只用該檔案,注意上面Cubieboard2,最新程式碼需要首字母大寫)

編譯kernel

cd $WORK_DIR/linux-sunxi
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- cubieboard2_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 uImage modules
得到核心檔案 arch/arm/boot/uImage(還有其它核心模組,這裡暫時沒有用到)

生成 boot.scr和script.bin

在將uboot和kernel燒寫到TF卡之前,我們需要先編譯生成兩個啟動引數檔案:boot.scr和script.bin。

生成 boot.scr

boot.scr是什麼?

根據資料描述(https://github.com/linux-sunxi/u-boot-sunxi/wiki#bootscr-support),u-boot在啟動的時候會在第一個分割槽(FAT/extX格式)尋找/boot.scr或者/boot/boot.scr檔案,boot.scr中可以包含用於載入script.bin,kernel,initrd(可選)以及設定核心啟動引數的uboot命令。

boot.scr如何生成?

在$WORK_DIR目錄新建 boot.cmd 檔案,新增以下內容:

setenv bootargs console=ttyS0,115200 noinitrd disp.screen0_output_mode=EDID:1280x1024p60 init=/init root=/dev/mmcblk0p2 rootfstype=ext4 rootwait panic=10 ${extra}
fatload mmc 0 0x43000000 boot/script.bin
fatload mmc 0 0x48000000 boot/uImage
bootm 0x48000000
詳細解釋:

1.上述第一行設定uboot的bootargs啟動引數,格式為 引數=值,不同引數使用空格分開,其中

console=ttyS0,115200 含義為使用特定的串列埠ttyS0,波特率為 115200
noinitrd 含義為不使用ramdisk(記憶體磁碟)
init=/init 含義為核心啟起來後,進入系統中執行的第一個指令碼
root=/dev/mmcblk0p2 含義為指定rootfs的位置為TF卡第二個分割槽
rootfstype=ext4 含義為根檔案系統型別
rootwait 含義為等待裝置/dev/mmcblk0p2就緒後才嘗試掛載rootfs
panic=10 傳遞核心引數,當遇到panic(核心嚴重錯誤)時等待10秒後重啟
screen0_output_mode 設定合適的螢幕顯示解析度
更多的引數可以通過檢視Linux核心原始碼目錄下Documentation/kernel-parameters.txt檔案瞭解

2.第二行和第三行為將script.bin和核心uImage載入到指定記憶體地址。fatload是U-Boot中裝載linux kernel 到記憶體的指令。

基本用法:fatload <interface> <dev[:part]> <addr> <filename> <bytes>

interface:所用到介面,如:MMC、USB
dev [:part]: 檔案存放的裝置 如:ide 0:1
addr: 裝載到記憶體的開始地址。
filename: 裝載的檔名稱。
bytes: copy的位元組數.
3.第四行bootm 用於將核心映像載入到指定的地址

儲存檔案後,執行以下命令生成boot.scr:

mkimage -C none -A arm -T script -d boot.cmd boot.scr
生成 script.bin

script.bin是什麼?

script.bin是被全志SOC核心驅動或LiveSuit使用的針對特定目標板的二進位制配置檔案,包含如何設定基於A10/A20目標版的各種外設,埠,I/O針腳資訊。

其對應的可讀文字檔案格式為FEX,可以利用 Sunxi-tools在二進位制和文字檔案之間進行轉換。更多關於FEX配置的資訊可以參考http://linux-sunxi.org/Fex_Guide

script.bin如何生成?

首先需要編譯sunxi-tools:

cd $WORK_DIR/sunxi-tools
make
得到fex2bin、bin2fex等檔案,其中fex2bin能把 *.fex 檔案生成 *.bin檔案。反之bin2fex可以將得到的*.bin檔案生成可讀的*.fex檔案。

然後編譯生成script.bin:

cd $WORK_DIR/sunxi-boards/sys_config/a20
$WORK_DIR/sunxi-tools/fex2bin cubieboard2.fex script.bin
燒寫uboot和kernel到TF卡

上面羅嗦了這麼多,其實就是為了將uboot和kernel燒寫到TF卡上並能夠啟動,OK,讓我們先從分割槽開始:

A20 晶片上電啟動的時候,會讀取SD卡最前面的 1M 內容,從而得到 bootloader,所以我們需要把 u-boot 寫到SD卡的前1M區間。

其中詳細的SD卡布局如下:

起始 大小 用途
0 8KB 存放分割槽表等內容
8 24KB SPL loader
32 512KB u-boot
544 128KB environment
672 352KB 保留
1024 - 用於剩餘分割槽
接下來,我們開始使用fdisk進行分割槽(由於sfdisk對部分TF不相容,故除非你真的知道怎麼用sfdisk,否則不要使用):

將TF卡插到電腦上並確認裝置名,為不至於混淆,我們使用sdX代替,您需要根據自己的情況修改,如sdb:

card=/dev/sdX
dd if=/dev/zero of=${card} bs=1M count=1 # 把SD卡前1M的區域填充為0,預留給 u-boot
sfdisk -R ${card} # 重新讀取${card}
fdisk ${card} #使用fdisk進行分割槽
具體分割槽步驟如下:

建立第一個分割槽

[email protected]
:~/src/u-boot-sunxi# fdisk ${card}
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0x911332e8.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.

Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)

Command (m for help): n #鍵入n然後回車
Partition type:
p primary (0 primary, 0 extended, 4 free)
e extended
Select (default p): #直接回車
Using default response p
Partition number (1-4, default 1): #直接回車
Using default value 1
First sector (2048-15278079, default 2048): #直接回車
Using default value 2048
Last sector, +sectors or +size{K,M,G} (2048-15278079, default 15278079): +64M #鍵入+64M後回車,即分割槽大小為64M
建立第二個分割槽

Command (m for help): n #鍵入n然後回車
Partition type:
p primary (1 primary, 0 extended, 3 free)
e extended
Select (default p): #直接回車
Using default response p
Partition number (1-4, default 2): #直接回車
Using default value 2
First sector (133120-15278079, default 133120): #直接回車
Using default value 133120
Last sector, +sectors or +size{K,M,G} (133120-15278079, default 15278079): #直接回車,即第二個分割槽使用全部剩餘空間
Using default value 15278079
接下來指定分割槽型別:

Command (m for help): t #鍵入t然後回車
Partition number (1-4): 1 #鍵入1然後回車,即指定第一個分割槽
Hex code (type L to list codes): c #鍵入c然後回車,即指定第一個分割槽為vfat
Changed system type of partition 1 to c (W95 FAT32 (LBA))

Command (m for help): w #鍵入w然後回車,儲存分割槽表
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.
格式化分割槽:

mkfs.vfat ${card}1
mkfs.ext4 ${card}2 #需要稍等片刻
然後寫入bootloader:

cd $WORK_DIR/u-boot-sunxi
dd if=u-boot-sunxi-with-spl.bin of=$card bs=1024 seek=8
最後安裝核心 uImage,設定啟動引數:

mount ${card}1 /mnt
mkdir /mnt/boot
cp $WORK_DIR/linux-sunxi/arch/arm/boot/uImage /mnt/boot
cp $WORK_DIR/sunxi-boards/sys_config/a20/script.bin /mnt/boot
cp $WORK_DIR/boot.scr /mnt/
sync && umount /mnt
至此,啟動到linux核心的工作已經完成,接下來我們就可以觀看linux核心啟動過程、進行核心除錯了。

列印串列埠除錯資訊

通過檢視串列埠輸出資訊,可以方便我們判斷問題所在,進而找到解決問題的方法。

在linux和windows下有多種串列埠除錯工具可供使用,這裡僅給出兩篇參考文章:

http://linux-sunxi.org/Cubieboard/TTL
http://cn.cubiebook.org/index.php?title=Cubieboard/串列埠除錯
這裡附上筆者的串列埠輸出資訊:http://mer.jolladev.net/data/media/cutecom.txt

資訊結尾由於無法掛載根檔案系統、啟動init而等待10秒後重啟。

總結

本文參考了眾多網路內容,在後面列出了主要文章地址,在此一併感謝。

之所以寫這篇內容,是因為從本人入手cubieboard2以來,通過苦尋資料,在各種痛苦與錯誤的嘗試中不斷走上了”正道“,深知對於一個沒有嵌入式開發基礎或開發經驗的普通玩家而言,想把各種環節搞清楚是一件很難的事情,故通過大量引用、參考網路內容加上自己的摸索,總結出該文,以期望對新的玩家能夠有所幫助;就嵌入式linux而言,整個 加電——啟動bootloader——啟動核心——載入rootfs流程對於新手會感到非常的模糊,而不知如何下手。本篇內容儘可能詳細的描述了利用cubieboard從加電到啟動linux核心的整個操作過程,為進一步學習如何構建一個可執行的linux系統打下了基礎。後面,將會在此基礎上繼續介紹如何進一步掛載跟檔案系統,啟動到shell甚至GUI圖形介面,從而構建一個完整、可用的linux系統。

參考

http://linux-sunxi.org/Bootable_SD_card
http://linux-sunxi.org/Boot
https://github.com/linux-sunxi/u-boot-sunxi/wiki
http://cn.cubieboard.org/forum.php?mod=viewthread&tid=1108&extra=page%3D1
http://blog.csdn.net/yichunjie/article/details/8661176
http://blog.csdn.net/abc47bca/article/details/6306005