1. 前言
Linux kernel在ARM架構(gòu)中引入device tree(全稱是flattened device tree,后續(xù)將會(huì)以FDT代稱)的時(shí)候[1],其實(shí)懷揣了一個(gè)Unify Kernel的夢(mèng)想----同一個(gè)Image,可以支持多個(gè)不同的平臺(tái)。隨著新的ARM64架構(gòu)將FDT列為必選項(xiàng),并將和體系結(jié)構(gòu)有關(guān)的代碼剝離之后,這個(gè)夢(mèng)想已經(jīng)接近實(shí)現(xiàn):
在編譯linux kernel的時(shí)候,不必特意的指定具體的架構(gòu)和SOC,只需要告訴kernel本次編譯需要支持哪些板級(jí)的platform即可,最終將會(huì)生成一個(gè)Kernel image,以及多個(gè)和具體的板子(哪個(gè)架構(gòu)、哪個(gè)SOC、哪個(gè)版型)有關(guān)的FDT image(dtb文件)。
bootloader在啟動(dòng)的時(shí)候,根據(jù)硬件環(huán)境,加載不同的dtb文件,即可使linux kernel運(yùn)行在不同的硬件平臺(tái)上,從而達(dá)到unify kernel的目標(biāo)。
本文將基于嵌入式產(chǎn)品中普遍使用的u-boot,以其新的uImage格式(FIT image,F(xiàn)lattened uImage Tree)為例,介紹達(dá)到此目標(biāo)的步驟,以及背后的思考和意義。
2. Legacy uImage
從u-boot的角度看,它要boot一個(gè)二進(jìn)制文件(例如kernel Image),需要了解該文件的一些信息,例如:
該文件的類型,如kernel image、dtb文件、ramdisk image等等?
該文件需要放在memory的哪個(gè)位置(加載地址)?
該文件需要從memory哪個(gè)位置開(kāi)始執(zhí)行(執(zhí)行地址)?
該文件是否有壓縮?
該文件是否有一些完整性校驗(yàn)的信息(如CRC)?
等等
結(jié)合“X-010-UBOOT-使用booti命令啟動(dòng)kernel(Bubblegum-96平臺(tái))”中有關(guān)booti的例子,上面信息被隱含在我們的命令行中了,例如:
通過(guò)DFU工具,將指定的image文件下載到指定的memory地址,間接的指定了二進(jìn)制文件加載地址;
booti命令本身,說(shuō)明加載的文件類型是ARM64平臺(tái)的Image文件;
通過(guò)booti的參數(shù),可以指定Kernel Image、ramdisk、DTB文件的執(zhí)行位置;
等等。
不過(guò),這種做法缺點(diǎn)很明顯(總結(jié)來(lái)說(shuō),就是太啰嗦了):
需要往memory中搬不同的二進(jìn)制文件(Kernel、DTB、ramdisk等);
boot指令(booti等)有比較復(fù)雜的參數(shù);
無(wú)法靈活地處理二進(jìn)制文件的校驗(yàn)、解壓縮等操作;
等等。
為了解決上述缺點(diǎn),u-boot自定義了一種Image格式----uImage。最初的時(shí)候,uImage的格式比較簡(jiǎn)單,就是為二進(jìn)制文件加上一個(gè)header(具體可參考“include/image.h”中的定義),標(biāo)示該文件的特性。然后在boot該類型的Image時(shí),從header中讀取所需的信息,按照指示,進(jìn)行相應(yīng)的動(dòng)作即可。這種原始的Image格式,稱作Legacy uImage,其特征可總結(jié)為:
1)使用mkimage工具(位于u-boot source code的tools/mkimage中)生成。
2)支持OS Kernel Images、RAMDisk Images等多種類型的Image。
3)支持gzip、bzip2等壓縮算法。
4)支持CRC32 checksums。
5)等等。
最后,之所以稱作Legacy,說(shuō)明又有新花樣了,這種舊的方式,我們就不再過(guò)多關(guān)注了,擁抱新事物去吧。
3. FIT uImage
3.1 簡(jiǎn)介
device tree在ARM架構(gòu)中普及之后,u-boot也馬上跟進(jìn)、大力支持,畢竟,美好的Unify kernel的理想,需要bootloader的成全。為了支持基于device tree的unify kernel,u-boot需要一種新的Image格式,這種格式需要具備如下能力:
1)Image中需要包含多個(gè)dtb文件。
2)可以方便的選擇使用哪個(gè)dtb文件boot kernel。
綜合上面的需求,u-boot推出了全新的image格式----FIT uImage,其中FIT是flattened image tree的簡(jiǎn)稱。是不是覺(jué)得FIT和FDT(flattened device tree)有點(diǎn)像?沒(méi)錯(cuò),它利用了Device Tree Source files(DTS)的語(yǔ)法,生成的image文件也和dtb文件類似(稱作itb),下面我們會(huì)詳細(xì)描述。
3.2 思路
為了簡(jiǎn)單,我們可以直接把FIT uImage類比為device tree的dtb文件,其生成和使用過(guò)程為[2]:
image source file??????? mkimage + dtc?????????????????????????? transfer to target?
?????????? +??????????????? -----------------------------> image file -----------------------------------> bootm?
image data file(s)
其中image source file(.its)和device tree source file(.dts)類似,負(fù)責(zé)描述要生成的image file的信息(上面第2章描述的信息)。mkimage和dtc工具,可以將.its文件以及對(duì)應(yīng)的image data file,打包成一個(gè)image file。我們將這個(gè)文件下載到么memory中,使用bootm命令就可以執(zhí)行了。
3.3 image source file的語(yǔ)法
image source file的語(yǔ)法和device tree source file完全一樣(可參考[3][4][5]中的例子),只不過(guò)自定義了一些特有的節(jié)點(diǎn),包括images、configurations等。說(shuō)明如下:
1)images節(jié)點(diǎn)
指定所要包含的二進(jìn)制文件,可以指定多種類型的多個(gè)文件,例如multi.its[5]中的包含了3個(gè)kernel image、2個(gè)ramdisk image、2個(gè)fdt image。每個(gè)文件都是images下的一個(gè)子node,例如:
kernel@2 {?
??? description = "2.6.23-denx";?
??? data = /incbin/("./2.6.23-denx.bin.gz");?
??? type = "kernel";?
??? arch = "ppc";?
??? os = "linux";?
??? compression = "gzip";?
??? load = <00000000>;?
??? entry = <00000000>;?
??? hash@1 {?
??????? algo = "sha1";?
??? };?
};
可以包括如下的關(guān)鍵字:
description,描述,可以隨便寫;
data,二進(jìn)制文件的路徑,格式為----/incbin/("path/to/data/file.bin");
type,二進(jìn)制文件的類型,"kernel", "ramdisk", "flat_dt"等,具體可參考中[6]的介紹;
arch,平臺(tái)類型,“arm”, “i386”等,具體可參考中[6]的介紹;
os,操作系統(tǒng)類型,linux、vxworks等,具體可參考中[6]的介紹;
compression,二進(jìn)制文件的壓縮格式,u-boot會(huì)按照?qǐng)?zhí)行的格式解壓;
load,二進(jìn)制文件的加載位置,u-boot會(huì)把它c(diǎn)opy對(duì)應(yīng)的地址上;
entry,二進(jìn)制文件入口地址,一般kernel Image需要提供,u-boot會(huì)跳轉(zhuǎn)到該地址上執(zhí)行;
hash,使用的數(shù)據(jù)校驗(yàn)算法。
2)configurations
可以將不同類型的二進(jìn)制文件,根據(jù)不同的場(chǎng)景,組合起來(lái),形成一個(gè)個(gè)的配置項(xiàng),u-boot在boot的時(shí)候,以配置項(xiàng)為單位加載、執(zhí)行,這樣就可以根據(jù)不同的場(chǎng)景,方便的選擇不同的配置,實(shí)現(xiàn)unify kernel目標(biāo)。還以multi.its[5]為例,
configurations {?
??? default = "config@1";?
???? config@1 {?
???????? description = "tqm5200 vanilla-2.6.23 configuration";?
???????? kernel = "kernel@1";?
???????? ramdisk = "ramdisk@1";?
??????? fdt = "fdt@1";?
???? };?
???? config@2 {?
???????? description = "tqm5200s denx-2.6.23 configuration";?
???????? kernel = "kernel@2";?
???????? ramdisk = "ramdisk@1";?
???????? fdt = "fdt@2";?
??? };?
?????config@3?{?
???????? description = "tqm5200s denx-2.4.25 configuration";?
??????? kernel = "kernel@3";?
???????? ramdisk = "ramdisk@2";?
???? };?
};
它包含了3種配置,每種配置使用了不同的kernel、ramdisk和fdt,默認(rèn)配置項(xiàng)由“default”指定,當(dāng)然也可以在運(yùn)行時(shí)指定。
3.4 Image的編譯和使用
FIT uImage的編譯過(guò)程很簡(jiǎn)單,根據(jù)實(shí)際情況,編寫image source file之后(假設(shè)名稱為kernel_fdt.its),在命令行使用mkimage工具編譯即可:
$ mkimage -f kernel_fdt.its?kernel_fdt.itb
其中-f指定需要編譯的source文件,并在后面指定需要生成的image文件(一般以.itb為后綴,例如kernel_fdt.itb)。
Image文件生成后,也可以使用mkimage命令查看它的信息:
$ mkimage -l kernel.itb
最后,我們可以使用dfu工具將生成的.idb文件,下載的memory的某個(gè)地址(沒(méi)有特殊要求,例如0x100000),然后使用bootm命令即可啟動(dòng),步驟包括:
1)使用iminfo命令,查看memory中存在的images和configurations。
2)使用bootm命令,執(zhí)行默認(rèn)配置,或者指定配置。
使用默認(rèn)配置啟動(dòng)的話,可以直接使用bootm:
bootm 0x100000
選擇其它配置的話,可以指定配置名:
bootm 0x100000#config@2
以上可參考“doc/uImage.FIT/howto.txt[2]”,具體細(xì)節(jié)我們會(huì)在后續(xù)的文章中結(jié)合實(shí)例說(shuō)明。
4. 總結(jié)
本文簡(jiǎn)單的介紹了u-boot為了實(shí)現(xiàn)Unify kernel所做的努力,但有一個(gè)問(wèn)題,大家可以思考一下:bootloader的unify怎么保證呢?
SOC廠家提供(固化、提供二進(jìn)制文件等)?
格式統(tǒng)一,UEFI?
后面有時(shí)間的話,可以追著這個(gè)疑問(wèn)研究研究。
?
評(píng)論