國嵌視訊學習第九天——UBOOT基礎
BootLoader
軟體層次
一個嵌入式系統從軟體角度來看分為三個層次:
1. 引導載入程式
包括固化在韌體(fireware)中的boot程式(可選)(比如CMOS中的BIOS),和BootLoader(比如grub)兩大部分
2.Linux核心
特定嵌入式平臺的定製核心
3.檔案系統
包括了系統命令和應用程式
PC機的引導載入程式由BIOS(其本質是一段韌體程式)和GRUB和 LILO一起組成。BIOS在完成硬體檢測和資源分配後,將硬碟中的載入程式讀到系統記憶體中然後將控制權交給載入程式。載入程式的主要任務是將核心從硬碟上讀到記憶體中,然後跳轉到核心的入口點去執行,即啟動作業系統
定義
在嵌入式系統中,通常沒有想BIOS那樣的韌體程式,因此整個系統的載入啟動任務就完全由BootLoader來完成。比如在一個基於ARM7TDMI core的嵌入式系統中,系統在上電或復位時都從地址0x00000000開始執行。而在這個地址處安排的通常就是系統的BootLoader程式
簡單地說,BootLoader就是在作業系統執行之前執行的一段小程式。通過這段小程式,可以初始化硬體裝置,從而將系統的軟硬體環境帶到一個合適的狀態,以便為最終呼叫作業系統做好準備。
安裝
系統加電或復位後,所有的CPU通常都從CPU製造商預先安排地址開始執行。比如,S3C6410在復位後從地址0x00000000起開始執行。而嵌入式系統則將固態儲存裝置(比如:FLASH)安排在這個地址上,而bootloader程式又安排在固態儲存器的最前端,這樣就能保證在系統加電後,CPU首先執行BootLoader程式
流程
BootLoader的啟動過程可分為單階段(Single-Stage)和多階段(Muti-Stage)兩種,通常多階段的BootLoader具有更復雜的功能,更好的可移植性。從固態儲存裝置上啟動的BootLoader大多采用兩階段,即啟動過程可以分為stage1和stage2:stage1完成初始化硬體,為stage2準備記憶體空間,並將stage2複製到記憶體中,設定堆疊,然後跳轉到stage2.
BootLoader的stage1通常包括以下步驟:
----硬體裝置初始化
----為載入BootLoader的stage2準備RAM空間
----拷貝BootLoader的stage2到RAM空間中
----設定好堆疊(目的是為stage2中的C程式的執行搭建環境)
----跳轉到stage2的C入口點
該階段的程式主要是由彙編程式碼寫成
BootLoader的stage2通常包括以下步驟:
----初始化本階段要使用的硬體裝置(板載硬體,比如串列埠、網口)
----將核心映像和根檔案系統映像從flash上讀到RAM中
----呼叫核心
該階段的程式主要是由C程式碼寫成
工作模式
大多數BootLoader都包含兩種不同的工作模式:“啟動模式”和“下載模式”,這種區別僅對於開發人員才有意義,從終端使用者的角度來看,BootLoader的作用就是用來載入作業系統,而不存在所謂的啟動模式與下載模式。
啟動模式:處理器一旦上電後,不需要干預便能自動地啟動bootloader,並通過bootloader啟動核心
下載模式:CPU上電後,目標機上的BootLoader將通過串列埠或網路等通訊手段從主機(Host)下載檔案,然後控制啟動流程(常用於開發時)。
所以,使用者常見的是啟動模式
交叉工具鏈
工具鏈:gcc、gdb、ld等工具的集合便是工具鏈(sets)
交叉:軟體產生於宿主機,運行於目標機的開發模式
安裝
進行嵌入式開發前,首先需安裝交叉工具鏈,步驟如下:
1.解壓工具鏈到某一目錄下,例:
tar xvzf arm-linux-gcc-4.5.1-v6-vfp.tgz –C/ (-C表示解壓到後面指定的目錄。如果沒有-C則是預設解壓到當前目錄。解壓後可以進入/4.5.1/bin/,可見很多工具)
2.修改/etc/profile,新增
export PATH=$PATH: /4.5.1/bin(如果不修改/etc/profile,那麼每次執行工具如arm-linux-gcc則必須使用全路徑:/4.5.1/bin/arm-linux-gcc)
3.讓第二步對環境變數的修改生效
執行source /etc/profile
上述三步驟之後,可以在任何目錄下執行arm-linux-gcc
工具使用
----編譯器:arm-linux-gcc
arm-linux-gcc hello.c –o hello
和gcc的用法完全一樣,不過gcc編譯的執行在X86上,而arm-linux-gcc編譯的執行在arm上
----反彙編工具:arm-linux-objdump
arm-linux-objdum –D –S hello
將二進位制檔案程式設計彙編程式碼
arm-linux-objdum–D –S hello > log 匯出到log檔案中
----ELF檔案檢視工具:arm-linux-readelf
arm-linux-readelf –a hello
檢視的資訊中,常用的是:Data項的大小端模式
Machine項
arm-linux-readelf –d hello 檢視hello使用的動態庫
UBoot
用於多種嵌入式CPU(MIPS、X86、ARM、XScale等)的bootloader程式,UBoot不僅支援嵌入式Linux系統的引導,還支援VxWorks,QNX等多種嵌入式作業系統。
U-BOOT啟動流程
開發板上電後,執行U-BOOT的第一條指令,然後順序執行U-BOOT啟動函式。看一下board/smdk2410/u-boot.lds這個連結指令碼,可以知道目標程式的各部分連結順序,第一個喲連結的是cpu/arm920t/start.o,那麼U-BOOT的入口指令一定位於這個程式中
下載
從下面地址可以下載到uboot的原始碼:
目錄結構
進入到UBOOT目錄,可以得到如下的目錄結構:
|--board
|--common
|--cpu
|--disk
|--doc
|--drivers
|--dtt
|--examples
|--fs
|--include
----board:和開發板有關的檔案。每一個開發板都以一個子目錄出現在當前目錄中,比如:smdk2410,子目錄中存放於開發板相關的檔案
----common:實現Uboot支援的命令
----cpu:與特定CPU架構相關的程式碼,每一款Uboot下支援的CPU在該目錄下對應一個子目錄,比如有子目錄arm920t等
----disk:對磁碟的支援
----doc:文件目錄,uboot有非常完善的文件,推薦閱讀
----drivers:uboot支援的裝置驅動程式都放在該目錄,比如各種網絡卡、支援CFI的Flash、串列埠、USB等
.....
----tools:uboot的工具,如:mkimage、crc等等
編譯
Uboot的Makefile從功能上可以分成兩個部分:
1.執行每種board相關的配置
2.編譯生成uboot.bin檔案
uboot.bin的生成也分為兩步,以mini2440開發板為例來說明,如下(申明:該演示所使用的u-boot為經過移植後的u-boot,未經移植的u-boot並不支援該6410開發板,移植過程將在嵌入式linux系統移植專題班講解):
可以在/4.5.1/根目錄下檢視Makefile,來知道uboot支援哪些開發板(比如在這裡可以檢視smdk6410):
1. 選擇要使用的board:
$make mini2440_config
2. 編譯生成u-boot.bin:
#makeCROSS_COMPILE=arm-linux-
編譯好後有個uboot.bin檔案。之後將該檔案燒寫到開發板中去
UBoot命令
常用命令
儘管UBOOT提供了豐富的命令集,但不同的單板所支援的命令並不一定一樣(可配置),help命令可用於檢視當前單板所支援的命令。
不同的board支援的命令不一定一樣(可配置)
環境變數相關
print(Printenv) 檢視環境變數
——用法:
printenv:
-print values of all environment variables
printenv name ...
-print value of environment variable `name`
setenv:新增、修改、刪除環境變數(存在於記憶體中,只在本次單板開機中有效)
——用法:
setenv name value...
-set environment variable `name` to `value...`
setenv name
-delete environment varible `name`
saveenv儲存環境變數(存在於flash中)。將當前定義的所有變數及其值存入flash中
檔案下載
tftp:通過網路下載檔案(開發板必須有ip地址ipaddr和serverip)可以通過ping serverip看網路是否連通
注意:使用tftp,需要先配置好網路
如:
Uboot>setenv ethaddr 12:34:45:78:9A:BC
Uboot>setenv ipaddr 192.168.1.1
Uboot>setenv serverip 192.168.1.254(tftp伺服器的地址)
例:
Uboot>tftp c0800000 uImage
把serber(ip=環境變數中設定的serverip)中服務目錄下的uImage通過TFTP讀入到0xc0800000處
loadb:通過串列埠下載(速度慢,少用)
記憶體操作
md顯示記憶體區的內容。
md採用十六進位制和ASCII碼兩種形式來顯示儲存單元的內容。這條命令還可以採用長度識別符號 .l ,.w和.b;
md[.b , .w , .l]address
如:
md.w 100000
00100000:(顯示的內容)
00100010:(顯示的內容)
mm:修改記憶體,地址自動遞增
mm [.b , .w ,.l] address
mm提供了一種互動修改儲存器內容的方法。它會顯示地址和當前值,然後提示使用者輸入。如果你輸入了一個合法的十六進位制數,這個新的值將會被寫入該地址。然後提示下一個地址。如果你沒有輸入任何值,只是按了一下回車,那麼該地址的內容保持不變。如果想結束輸入,則輸入空格,然後回車
如:mm 100000
00100000:(顯示的內容)
00100004:(顯示的內容)
FLASH操作
flinfo:檢視flash扇區資訊
用法:Uboot>flinfo
protect :flash防寫
開啟或關閉扇區防寫
——用法:
protect off all
關閉所有扇區的防寫
protect on all
開啟所有扇區的防寫
protect off start end
關閉從start到end扇區的防寫(start為要關閉的第1個扇區的起始地址,end為要關閉的最後一個扇區的結束地址)
protect on start end
開啟從start 到end 扇區的防寫
erase:擦除flash扇區
——用法:
erase start end
擦除從start 到end的扇區,start為要擦除的第1個扇區的起始地址,end為要擦除的最後一個扇區的結束地址(在使用cp命令向NOR型flash寫入資料之前必須先使用erase命令擦除flash,因為nor flash按位元組寫入時,無法寫入1,所以必須通過擦除來寫入1)
如:erase 30000 1effff
cp:資料拷貝
——用法:
cp[.b , .w , .l]saddress daddress len(預設是.l,即32位的拷貝)
cp提供了一種記憶體與記憶體,記憶體與flash之間資料拷貝的方法
例:
cp.b 31000000 50000 d0000
將記憶體地址0x31000000處的資料(長度為0xd0000)拷貝到地址0x50000處(flash中)
cp.b 32000000 120000 c0000
將記憶體地址0x32000000處的資料(長度為0xc0000)拷貝到地址0x120000處(flash中)
注意:必須先用erase命令擦除flash,才能用cp命令執行成功
執行程式
go:執行記憶體中的二進位制程式碼,一個簡單的跳轉到指定地址
——go addr [arg...]
-start application at address `addr` , passing `arg` as arguments
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要求所有可執行的二進位制程式碼前部有固定格式的檔案頭,那麼如何做這個檔案頭呢?——uboot下tools中有個mkimage工具可以
注意:bootm可以沒有addr地址,這時它自動執行個預設的地址
開發板資訊
bdinfo——顯示開發板資訊
bdinfo命令(簡寫為bdi)將在終端顯示諸如記憶體地址和大小、時鐘頻率、MAC地址等資訊。這些資訊在傳遞給linux核心一些引數時可能會用到
自動啟動
1.設定自動啟動——通過設定環境變數:
比如:Mini2440=>setenv bootcmd tftp 31000000 uImage \; bootm 31000000
Mini2440=>saveenv
——執行了兩個操作並儲存