前言
secure boot 和FIT Image是前段時(shí)間接觸到的,其實(shí)早就該總結(jié)下了,奈何懶癌犯了,,拖了好久才寫出來,。
之前也有人問我,工作后最大的感受是什么,?我的回答是:“快速學(xué)習(xí)”。
就嵌入式來講,大多數(shù)應(yīng)屆生在校期間可能都沒做過完整的項(xiàng)目,,僅憑在校期間學(xué)習(xí)的內(nèi)容很難勝任公司的要求。
就底層驅(qū)動(dòng)來講,,雖然我之前也學(xué)習(xí)過韋東山老師的上s3c2440的課程,,但是到了公司才發(fā)現(xiàn),這些內(nèi)容其實(shí)都已經(jīng)過時(shí)了,。
但并不是說這些內(nèi)容都沒有必要去學(xué)習(xí)了,。在學(xué)習(xí)的過程中,認(rèn)為最重要的是培養(yǎng)我們的自學(xué)能力,。
很多初學(xué)者在剛開始學(xué)習(xí)時(shí),,可能就敗在了搭建環(huán)境上。搭建環(huán)境時(shí)遇到問題不知道怎么辦,?
我們?nèi)粘i_發(fā)中遇到的90%的問題,,在網(wǎng)上都有人遇到過,也有相應(yīng)的解決辦法,。學(xué)會(huì)利用bing,,google,stackoverflow等搜索工具是一項(xiàng)很重要的技能,。
如果遇到了網(wǎng)上沒有的問題怎么辦?軟件問題要先搞清楚原理,,再去看代碼邏輯。硬件問題看官方手冊,。像Linux kernel,,ARM等都提供了完善的手冊,大部分問題在手冊中都有相應(yīng)說明,。
好了,,扯遠(yuǎn)了。下面回歸正題,。
本文主要介紹了FIT Image起源,,制作方法,its的語法結(jié)構(gòu),,bootm 啟動(dòng)FIT Image的方式,。
本文這篇文章是對后面介紹的secure boot做鋪墊。ARMv8 secure boot一種實(shí)現(xiàn)的方式就是利用了FIT Image的特性,。
zImage,,uImage, Legacy uImage 和 FIT uImage
內(nèi)核經(jīng)過編譯后,,會(huì)生成一個(gè)elf的可執(zhí)行程序,,叫vmlinux,這個(gè)就是原始的未經(jīng)任何處理加工的原版內(nèi)核elf文件,。不過,,最終燒寫在嵌入式設(shè)備上的并不是這個(gè)文件。而是經(jīng)過objcopy工具加工后的專門用于燒錄的鏡像格式Image,。
原則上Image就可以直接被燒錄到Flash上進(jìn)行啟動(dòng)執(zhí)行,,但linux的內(nèi)核開發(fā)者覺得Image還是太大了,因此對Image進(jìn)行了壓縮,,并且在Image壓縮后的文件的前端附加了一部分解壓縮代碼,,構(gòu)成了一個(gè)壓縮格式的鏡像文件就叫zImage。
解壓的時(shí)候,,通過zImage鏡像頭部的解壓縮代碼進(jìn)行自解壓,,然后執(zhí)行解壓出來的內(nèi)核鏡像。
Uboot要正確啟動(dòng)Linux內(nèi)核,,就需要知道內(nèi)核的一些信息,,比如鏡像的類型(kernel image,dtb,,ramdisk image),,鏡像在內(nèi)存的位置,,鏡像的鏈接地址,鏡像文件是否有壓縮等等,。
Uboot為了拿到這些信息,,發(fā)明了一種內(nèi)核格式叫uImage,也叫Legacy uImage,。uImage是由zImage加工得到的,,uboot中有一個(gè)工具mkimage,該工具會(huì)給zImage加一個(gè)64字節(jié)的header,,將啟動(dòng)內(nèi)核所需的信息存儲(chǔ)在header中,。uboot啟動(dòng)后,從header中讀取所需的信息,,按照指示,,進(jìn)行相應(yīng)的動(dòng)作即可。
header格式可以參考:include/image.h,。mkimage源碼在tools/mkimage
FIT image的來源
有了Legacy uImage后,為什么又搞出來一個(gè)FIT uImage呢,?
在Linus Torvalds 看來,,內(nèi)核中arch/arm/mach-xxx充斥著大量的垃圾代碼。因?yàn)閮?nèi)核并不關(guān)心板級細(xì)節(jié),,比如板上的platform設(shè)備,、resource、i2c_board_info,、spi_board_info等等,。大家有興趣可以看下s3c2410的板級目錄,代碼量在數(shù)萬行,。
因此,,ARM社區(qū)引入了Device Tree,使用Device Tree后,,許多硬件的細(xì)節(jié)可以直接透過它傳遞給Linux,,而不再需要在kernel中進(jìn)行大量的冗余編碼。
為了更好的支持單個(gè)固件的通用性,,Uboot也需要對這種uImage固件進(jìn)行支持,。FIT uImage中加入多個(gè)dtb文件 和ramdisk文件,當(dāng)然如果需要的話,,同樣可以支持多個(gè)kernel文件,。
內(nèi)核中的FDT全程為flattened device tree,F(xiàn)IT全稱叫flattened image tree,。FIT利用了Device Tree Source files(DTS)的語法,,生成的Image文件也和dtb文件類似(稱作itb),。
這樣的目的就是能夠使同一個(gè)uImage能夠在Uboot中選擇特定的kernel/dtb和ramdisk進(jìn)行啟動(dòng)了,達(dá)成一個(gè)uImage可以通用多個(gè)板型的目的,。
制作FIT Image
制作FIT Image需要用到兩個(gè)工具,,mkimage和的dtc。dtc要導(dǎo)入到環(huán)境變量$PATH中,,mkimage會(huì)調(diào)用dtc,。
mkimage的輸入為 image source file,它定義了啟動(dòng)過程中image的各種屬性,擴(kuò)展名為.its,。its只是描述了Image的屬性,,實(shí)際的Image data 是在uImage中,具體路徑由its指定,。
如下是kernel 的its文件,后面會(huì)介紹各項(xiàng)內(nèi)容的含義,。
/*
* Simple U-Boot uImage source file containing a single kernel
*/
/dts-v1/;
/ {
description = "Simple image with single Linux kernel";
#address-cells = <1>;
images {
kernel@1 {
description = "Vanilla Linux kernel";
data = /incbin/("./vmlinux.bin.gz"); # Image data 具體路徑
type = "kernel";
arch = "ppc";
os = "linux";
compression = "gzip";
load = <00000000>;
entry = <00000000>;
hash@1 {
algo = "crc32";
};
hash@2 {
algo = "sha1";
};
};
};
configurations {
default = "config@1";
config@1 {
description = "Boot Linux kernel";
kernel = "kernel@1";
};
};
};
mkimage的輸出是一個(gè)后綴為.itb的二進(jìn)制文件,包含了所有需要的數(shù)據(jù)(kernel,,dtb,,ramdisk)。itb文件制作好之后,,就可以直接加載到嵌入式設(shè)備上,,通過bootm命令啟動(dòng)。
總結(jié)下制作FIT Image的4個(gè)必要文件:
mkimage,,
dtc
its(image source file (*.its))
image data file(s),。
its語法結(jié)構(gòu)
uImage Tree 的根節(jié)點(diǎn)結(jié)構(gòu)
/ o image-tree
|- description = "image description"
|- timestamp = <12399321>
|- #address-cells = <1>
|
o images
| |
| o image@1 {...}
| o image@2 {...}
| ...
|
o configurations
|- default = "conf@1"
|
o conf@1 {...}
o conf@2 {...}
...
description:描述uImage的文本。
timestamp:修改Image鏡像的時(shí)間,,由mkimage工具自動(dòng)生成,。在security boot中,timestamp不同也會(huì)被認(rèn)為是不同的Image,。
images:子鏡像,,如kernel Image,ramdisk Image,。
configurations:配置項(xiàng)節(jié)點(diǎn),,可以將不同類型的二進(jìn)制文件,根據(jù)不同的場景,,組合起來,,形成一個(gè)個(gè)的配置項(xiàng)。u-boot在boot的時(shí)候,,以配置項(xiàng)為單位加載,、執(zhí)行,這樣就可以根據(jù)不同的場景,,方便的選擇不同的配置,。
'/images' node
該節(jié)點(diǎn)中描述了Image鏡像必要的信息.
o image@1
|- description = "component sub-image description"
|- data = /incbin/("path/to/data/file.bin")
|- type = "sub-image type name"
|- arch = "ARCH name"
|- os = "OS name"
|- compression = "compression name"
|- load = <00000000>
|- entry = <00000000>
|
o hash@1 {...}
o hash@2 {...}
...
description:子鏡像的文本描述,,可以隨便寫。
type:子鏡像的類型,,比如standalone,,kernel,ramdisk,,firmware等等,。
data:包含該節(jié)點(diǎn)二進(jìn)制文件的路徑。
compression:壓縮方式,,比如none,,gzip,bzip2,。
os:操作系統(tǒng)的名稱,,如solaris,uboot,,qnx等,。
arch:平臺(tái)架構(gòu),如arm,,mips,,i386等。
entry:二進(jìn)制文件入口地址,,即鏈接地址。
load:二進(jìn)制文件的加載位置,。
hash@1:鏡像使用的校驗(yàn)算法,,如sha256,crc32等,。
Hash nodes
o hash@1
|- algo = "hash or checksum algorithm name"
|- value = [hash or checksum value]
algo:算法名稱,,如crc32,md5,,sha256等,。
value:算法校驗(yàn)值,即algo計(jì)算后的數(shù)值,。
'/configurations' node
o configurations
|- default = "default configuration sub-node unit name"
|
o config@1 {...}
o config@2 {...}
...
default:默認(rèn)的子節(jié)點(diǎn)的配置
config@1: 該配置具體使用那些kernel Image,,ramdisk Image等。
Configuration nodes
o config@1
|- description = "configuration description"
|- kernel = "kernel sub-node unit name"
|- ramdisk = "ramdisk sub-node unit name"
|- fdt = "fdt sub-node unit-name" [, "fdt overlay sub-node unit-name", ...]
|- fpga = "fpga sub-node unit-name"
|- loadables = "loadables sub-node unit-name"
description:該配置的名稱,。
kernel:鏡像類型為kernel的單元的名稱,。
ramdisk:鏡像類型為ramdisk的單元的名稱。
fdt:鏡像類型為fdt的單元的名稱,。
loadables:額外的可加載的二進(jìn)制文件的列表,,U-Boot將在給定的起始地址加載每個(gè)二進(jìn)制文件,。
舉例
如下是一個(gè)有多種kernels, ramdisks and FDT blobs鏡像多套配置的its文件。它包含了3種配置,,每種配置使用了不同的kernel,、ramdisk和fdt,默認(rèn)配置項(xiàng)由“default”指定,,當(dāng)然也可以在運(yùn)行時(shí)指定,。
/*
* U-Boot uImage source file with multiple kernels, ramdisks and FDT blobs
*/
/dts-v1/;
/ {
description = "Various kernels, ramdisks and FDT blobs";
#address-cells = <1>;
images {
kernel@1 {
description = "vanilla-2.6.23";
data = /incbin/("./vmlinux.bin.gz");
type = "kernel";
arch = "ppc";
os = "linux";
compression = "gzip";
load = <00000000>;
entry = <00000000>;
hash@1 {
algo = "md5";
};
hash@2 {
algo = "sha1";
};
};
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";
};
};
kernel@3 {
description = "2.4.25-denx";
data = /incbin/("./2.4.25-denx.bin.gz");
type = "kernel";
arch = "ppc";
os = "linux";
compression = "gzip";
load = <00000000>;
entry = <00000000>;
hash@1 {
algo = "md5";
};
};
ramdisk@1 {
description = "eldk-4.2-ramdisk";
data = /incbin/("./eldk-4.2-ramdisk");
type = "ramdisk";
arch = "ppc";
os = "linux";
compression = "gzip";
load = <00000000>;
entry = <00000000>;
hash@1 {
algo = "sha1";
};
};
ramdisk@2 {
description = "eldk-3.1-ramdisk";
data = /incbin/("./eldk-3.1-ramdisk");
type = "ramdisk";
arch = "ppc";
os = "linux";
compression = "gzip";
load = <00000000>;
entry = <00000000>;
hash@1 {
algo = "crc32";
};
};
fdt@1 {
description = "tqm5200-fdt";
data = /incbin/("./tqm5200.dtb");
type = "flat_dt";
arch = "ppc";
compression = "none";
hash@1 {
algo = "crc32";
};
};
fdt@2 {
description = "tqm5200s-fdt";
data = /incbin/("./tqm5200s.dtb");
type = "flat_dt";
arch = "ppc";
compression = "none";
load = <00700000>;
hash@1 {
algo = "sha1";
};
};
};
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";
};
};
};
FIT Image的編譯和啟動(dòng)
在服務(wù)器上,可以使用mkimage工具制作 FIT Image,。
如下是kernel_fdt.its,,下面將使用該文件制作itb。
/*
* Simple U-Boot uImage source file containing a single kernel and FDT blob
*/
/dts-v1/;
/ {
description = "Simple image with single Linux kernel and FDT blob";
#address-cells = <1>;
images {
kernel@1 {
description = "Vanilla Linux kernel";
data = /incbin/("./vmlinux.bin.gz");
type = "kernel";
arch = "ppc";
os = "linux";
compression = "gzip";
load = <00000000>;
entry = <00000000>;
hash@1 {
algo = "crc32";
};
hash@2 {
algo = "sha1";
};
};
fdt@1 {
description = "Flattened Device Tree blob";
data = /incbin/("./target.dtb");
type = "flat_dt";
arch = "ppc";
compression = "none";
hash@1 {
algo = "crc32";
};
hash@2 {
algo = "sha1";
};
};
};
configurations {
default = "conf@1";
conf@1 {
description = "Boot Linux kernel with FDT blob";
kernel = "kernel@1";
fdt = "fdt@1";
};
};
};
$ mkimage -f kernel_fdt.its kernel_fdt.itb
DTC: dts->dtb on file "kernel_fdt.its"
$
$ mkimage -l kernel_fdt.itb
FIT description: Simple image with single Linux kernel and FDT blob
Created: Tue Mar 11 16:29:22 2008
Image 0 (kernel@1)
Description: Vanilla Linux kernel
Type: Kernel Image
Compression: gzip compressed
Data Size: 1092037 Bytes = 1066.44 kB = 1.04 MB
Architecture: PowerPC
OS: Linux
Load Address: 0x00000000
Entry Point: 0x00000000
Hash algo: crc32
Hash value: 2c0cc807
Hash algo: sha1
Hash value: 264b59935470e42c418744f83935d44cdf59a3bb
Image 1 (fdt@1)
Description: Flattened Device Tree blob
Type: Flat Device Tree
Compression: uncompressed
Data Size: 16384 Bytes = 16.00 kB = 0.02 MB
Architecture: PowerPC
Hash algo: crc32
Hash value: 0d655d71
Hash algo: sha1
Hash value: 25ab4e15cd4b8a5144610394560d9c318ce52def
Default Configuration: 'conf@1'
Configuration 0 (conf@1)
Description: Boot Linux kernel with FDT blob
Kernel: kernel@1
FDT: fdt@1
在當(dāng)前目錄下就可以找到kernel_fdt.itb,,itb文件就可以加載到設(shè)備上啟動(dòng),。
> tftp 900000 /path/to/tftp/location/kernel_fdt.itb
Using FEC device
TFTP from server 192.168.1.1; our IP address is 192.168.160.5
Filename '/path/to/tftp/location/kernel_fdt.itb'.
Load address: 0x900000
Loading: #################################################################
###########
done
Bytes transferred = 1109776 (10ef10 hex)
=> iminfo
## Checking Image at 00900000 ...
FIT image found
FIT description: Simple image with single Linux kernel and FDT blob
Created: 2008-03-11 15:29:22 UTC
Image 0 (kernel@1)
Description: Vanilla Linux kernel
Type: Kernel Image
Compression: gzip compressed
Data Start: 0x009000ec
Data Size: 1092037 Bytes = 1 MB
Architecture: PowerPC
OS: Linux
Load Address: 0x00000000
Entry Point: 0x00000000
Hash algo: crc32
Hash value: 2c0cc807
Hash algo: sha1
Hash value: 264b59935470e42c418744f83935d44cdf59a3bb
Image 1 (fdt@1)
Description: Flattened Device Tree blob
Type: Flat Device Tree
Compression: uncompressed
Data Start: 0x00a0abdc
Data Size: 16384 Bytes = 16 kB
Architecture: PowerPC
Hash algo: crc32
Hash value: 0d655d71
Hash algo: sha1
Hash value: 25ab4e15cd4b8a5144610394560d9c318ce52def
Default Configuration: 'conf@1'
Configuration 0 (conf@1)
Description: Boot Linux kernel with FDT blob
Kernel: kernel@1
FDT: fdt@1
=> bootm
## Booting kernel from FIT Image at 00900000 ...
Using 'conf@1' configuration
Trying 'kernel@1' kernel subimage
Description: Vanilla Linux kernel
Type: Kernel Image
Compression: gzip compressed
Data Start: 0x009000ec
Data Size: 1092037 Bytes = 1 MB
Architecture: PowerPC
OS: Linux
Load Address: 0x00000000
Entry Point: 0x00000000
Hash algo: crc32
Hash value: 2c0cc807
Hash algo: sha1
Hash value: 264b59935470e42c418744f83935d44cdf59a3bb
Verifying Hash Integrity ... crc32+ sha1+ OK
Uncompressing Kernel Image ... OK
## Flattened Device Tree from FIT Image at 00900000
Using 'conf@1' configuration
Trying 'fdt@1' FDT blob subimage
Description: Flattened Device Tree blob
Type: Flat Device Tree
Compression: uncompressed
Data Start: 0x00a0abdc
Data Size: 16384 Bytes = 16 kB
Architecture: PowerPC
Hash algo: crc32
Hash value: 0d655d71
Hash algo: sha1
Hash value: 25ab4e15cd4b8a5144610394560d9c318ce52def
Verifying Hash Integrity ... crc32+ sha1+ OK
Booting using the fdt blob at 0xa0abdc
Loading Device Tree to 007fc000, end 007fffff ... OK
[ 0.000000] Using lite5200 machine description
[ 0.000000] Linux version 2.6.24-rc6-gaebecdfc (m8@hekate) (gcc version 4.0.0 (DENX ELDK 4.1 4.0.0)) #1 Sat Jan 12 15:38:48 CET 2008
bootm啟動(dòng)不同的配置
對于FIT Image,bootm有多種啟動(dòng)方式,。
1. bootm <addr1>
2. bootm [<addr1>]:<subimg1>
3. bootm [<addr1>]#<conf>[#<extra-conf[#...]]
4. bootm [<addr1>]:<subimg1> [<addr2>]:<subimg2>
5. bootm [<addr1>]:<subimg1> [<addr2>]:<subimg2> [<addr3>]:<subimg3>
6. bootm [<addr1>]:<subimg1> [<addr2>]:<subimg2> <addr3>
7. bootm [<addr1>]:<subimg1> - [<addr3>]:<subimg3>
8. bootm [<addr1>]:<subimg1> - <addr3>
對于有多種鏡像,,多套配置的itb,都是以configurations 中default 指定的配置啟動(dòng),。
bootm 200000
也可以手動(dòng)指定使用那套配置
bootm 200000#cfg@1
也可以手動(dòng)搭配不同的鏡像節(jié)點(diǎn)啟動(dòng)
bootm 200000:kernel@1 800000:ramdisk@2
bootm 200000:kernel@1 800000:ramdisk@1 800000:fdt@1
bootm 200000:kernel@2 200000:ramdisk@2 600000
bootm 200000:kernel@2 - 200000:fdt@1
如果bootm的時(shí)候不指定地址,,則會(huì)使用CONFIG_SYS_LOAD_ADDR配置的地址。
總結(jié)
本文對FIT Image作了簡單的介紹,,更詳細(xì)的內(nèi)容可以參考官方文檔,。后面有時(shí)間會(huì)動(dòng)手制作一個(gè)FIT Image在板子上跑下。
FIT Image可以兼容于多種板子,,而無需重新進(jìn)行編譯燒寫,。對于有多個(gè)kernel節(jié)點(diǎn)或者fdt節(jié)點(diǎn)等等,兼容性更強(qiáng),。同時(shí),,可以有多種configurations,來對kernel,、fdt,、ramdisk來進(jìn)行組合。
更多信息可以來這里獲取==>>電子技術(shù)應(yīng)用-AET<<
電子技術(shù)應(yīng)用專欄作家 嵌入式與Linux那些事
原文鏈接:https://mp.weixin.qq.com/s/MtfGhhP_6PSa_mTeLn_XFQ