你好!欢迎来到深圳市品慧电子有限公司!
语言
当前位置:首页 >> 技术中心 >> 传感技术 >> secure boot (一)FIT Image

secure boot (一)FIT Image


前言

secure boot 和FIT Image是前段时间接触到的,其实早就该总结下了,奈何懒癌犯了,拖了好久才写出来。

之前也有人问我,工作后最大的感受是什么?我的回答是:“快速学习”。

就嵌入式来讲,大多数应届生在校期间可能都没做过完整的项目,仅凭在校期间学习的内容很难胜任公司的要求。

就底层驱动来讲,虽然我之前也学习过韦东山老师的上s3c2440的课程,但是到了公司才发现,这些内容其实都已经过时了。

但并不是说这些内容都没有必要去学习了。在学习的过程中,认为最重要的是培养我们的自学能力。

很多初学者在刚开始学习时,可能就败在了搭建环境上。搭建环境时遇到问题不知道怎么办?

我们日常开发中遇到的90%的问题,在网上都有人遇到过,也有相应的解决办法。学会利用bing,google,stackoverflow等搜索工具是一项很重要的技能。

如果遇到了网上没有的问题怎么办?软件问题要先搞清楚原理,再去看代码逻辑。硬件问题看官方手册。像Linux kernel,ARM等都提供了完善的手册,大部分问题在手册中都有相应说明。

好了,扯远了。下面回归正题。

本文主要介绍了FIT Image起源,制作方法,its的语法结构,bootm 启动FIT Image的方式。

本文这篇文章是对后面介绍的secure boot做铺垫。ARMv8 secure boot一种实现的方式就是利用了FIT Image的特性。

zImage,uImage, Legacy uImage 和 FIT uImage

内核经过编译后,会生成一个elf的可执行程序,叫vmlinux,这个就是原始的未经任何处理加工的原版内核elf文件。不过,最终烧写在嵌入式设备上的并不是这个文件。而是经过objcopy工具加工后的专门用于烧录的镜像格式Image。

原则上Image就可以直接被烧录到Flash上进行启动执行,但linux的内核开发者觉得Image还是太大了,因此对Image进行了压缩,并且在Image压缩后的文件的前端附加了一部分解压缩代码,构成了一个压缩格式的镜像文件就叫zImage

解压的时候,通过zImage镜像头部的解压缩代码进行自解压,然后执行解压出来的内核镜像。

Uboot要正确启动Linux内核,就需要知道内核的一些信息,比如镜像的类型(kernel image,dtb,ramdisk image),镜像在内存的位置,镜像的链接地址,镜像文件是否有压缩等等。

Uboot为了拿到这些信息,发明了一种内核格式叫uImage,也叫Legacy uImage。uImage是由zImage加工得到的,uboot中有一个工具mkimage,该工具会给zImage加一个64字节的header,将启动内核所需的信息存储在header中。uboot启动后,从header中读取所需的信息,按照指示,进行相应的动作即可。

header格式可以参考:include/image.h。mkimage源码在tools/mkimage

FIT image的来源

有了Legacy uImage后,为什么又搞出来一个FIT uImage呢?

在Linus Torvalds 看来,内核中arch/arm/mach-xxx充斥着大量的垃圾代码。因为内核并不关心板级细节,比如板上的platform设备、resource、i2c_board_info、spi_board_info等等。大家有兴趣可以看下s3c2410的板级目录,代码量在数万行。

因此,ARM社区引入了Device Tree,使用Device Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。

为了更好的支持单个固件的通用性,Uboot也需要对这种uImage固件进行支持。FIT uImage中加入多个dtb文件 和ramdisk文件,当然如果需要的话,同样可以支持多个kernel文件。

内核中的FDT全程为flattened device tree,FIT全称叫flattened image tree。FIT利用了Device Tree Source files(DTS)的语法,生成的Image文件也和dtb文件类似(称作itb)。

这样的目的就是能够使同一个uImage能够在Uboot中选择特定的kernel/dtb和ramdisk进行启动了,达成一个uImage可以通用多个板型的目的。

制作FIT Image

制作FIT Image需要用到两个工具,mkimage和的dtc。dtc要导入到环境变量$PATH中,mkimage会调用dtc。

mkimage的输入为 image source file,它定义了启动过程中image的各种属性,扩展名为.its。its只是描述了Image的属性,实际的Image data 是在uImage中,具体路径由its指定。

如下是kernel 的its文件,后面会介绍各项内容的含义。

/*
?*?Simple?U-Boot?uImage source file?containing?a?single?kernel
?*/

/dts-v1/;

/?{
?description?= "Simple?image?with?single?Linux?kernel"; #address-cells?=; images?{
??kernel@1?{
???description?= "Vanilla?Linux?kernel";
???data?=?/incbin/("./vmlinux.bin.gz"); #?Image?data?具体路径 type = "kernel";
???arch?= "ppc";
???os?= "linux";
???compression?= "gzip";
???load?=;
???entry?=; hash@1?{
????algo?= "crc32";
???}; hash@2?{
????algo?= "sha1";
???};
??};
?};

?configurations?{
??default?= "config@1";
??config@1?{
???description?= "Boot?Linux?kernel";
???kernel?= "kernel@1";
??};
?};
};

mkimage的输出是一个后缀为.itb的二进制文件,包含了所有需要的数据(kernel,dtb,ramdisk)。itb文件制作好之后,就可以直接加载到嵌入式设备上,通过bootm命令启动。

总结下制作FIT Image的4个必要文件:

  • mkimage,
  • dtc
  • its(image source file (*.its))
  • image data file(s)。

its语法结构

uImage Tree 的根节点结构

/?o?image-tree
????|-?description?= "image?description" |-?timestamp?=|- #address-cells?= |
????o?images
????|?|
????|?o?image@1?{...}
????|?o?image@2?{...}
????|?...
????|
????o?configurations
??????|-?default?= "conf@1" |
??????o?conf@1?{...}
??????o?conf@2?{...}
??????...
  • description:描述uImage的文本。

  • timestamp:修改Image镜像的时间,由mkimage工具自动生成。在security boot中,timestamp不同也会被认为是不同的Image。

  • images:子镜像,如kernel Image,ramdisk Image。

  • configurations:配置项节点,可以将不同类型的二进制文件,根据不同的场景,组合起来,形成一个个的配置项。u-boot在boot的时候,以配置项为单位加载、执行,这样就可以根据不同的场景,方便的选择不同的配置。

'/images' node

该节点中描述了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?=|-?entry?=|
???o hash@1?{...}
???o hash@2?{...}
???...
  • description:子镜像的文本描述,可以随便写。

  • type:子镜像的类型,比如standalone,kernel,ramdisk,firmware等等。

  • data:包含该节点二进制文件的路径。

  • compression:压缩方式,比如none,gzip,bzip2。

  • os:操作系统的名称,如solaris,uboot,qnx等。

  • arch:平台架构,如arm,mips,i386等。

  • entry:二进制文件入口地址,即链接地址。

  • load:二进制文件的加载位置。

  • hash@1:镜像使用的校验算法,如sha256,crc32等。

Hash nodes

o hash@1
??|-?algo?= "hash?or?checksum?algorithm?name" |-?value?=?[hash or?checksum?value]
  • algo:算法名称,如crc32,md5,sha256等。

  • value:算法校验值,即algo计算后的数值。

'/configurations' node

o?configurations
??|-?default?= "default?configuration?sub-node?unit?name" |
??o?config@1?{...}
??o?config@2?{...}
??...
  • default:默认的子节点的配置

  • 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:额外的可加载的二进制文件的列表,U-Boot将在给定的起始地址加载每个二进制文件。

举例

如下是一个有多种kernels, ramdisks and FDT blobs镜像多套配置的its文件。它包含了3种配置,每种配置使用了不同的kernel、ramdisk和fdt,默认配置项由“default”指定,当然也可以在运行时指定。

/*
?*?U-Boot?uImage source file?with?multiple?kernels,?ramdisks?and?FDT?blobs
?*/

/dts-v1/;

/?{
?description?= "Various?kernels,?ramdisks?and?FDT?blobs"; #address-cells?=; images?{
??kernel@1?{
???description?= "vanilla-2.6.23";
???data?=?/incbin/("./vmlinux.bin.gz"); type = "kernel";
???arch?= "ppc";
???os?= "linux";
???compression?= "gzip";
???load?=;
???entry?=; 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?=;
???entry?=; 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?=;
???entry?=; 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?=;
???entry?=; 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?=;
???entry?=; 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?=; 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的编译和启动

在服务器上,可以使用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?=; images?{
??kernel@1?{
???description?= "Vanilla?Linux?kernel";
???data?=?/incbin/("./vmlinux.bin.gz"); type = "kernel";
???arch?= "ppc";
???os?= "linux";
???compression?= "gzip";
???load?=;
???entry?=; 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

在当前目录下就可以找到kernel_fdt.itb,itb文件就可以加载到设备上启动。

>?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启动不同的配置

对于FIT Image,bootm有多种启动方式。

1.?bootm2.?bootm?[]:3.?bootm?[]#[# 4.?bootm?[]:[]:5.?bootm?[]:[]:[]:6.?bootm?[]:[]:7.?bootm?[]:-?????[]:8.?bootm?[]:-

对于有多种镜像,多套配置的itb,都是以configurations 中default 指定的配置启动。

bootm?200000

也可以手动指定使用那套配置

bootm?200000#cfg@1 

也可以手动搭配不同的镜像节点启动

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的时候不指定地址,则会使用CONFIG_SYS_LOAD_ADDR配置的地址。

总结

本文对FIT Image作了简单的介绍,更详细的内容可以参考官方文档。后面有时间会动手制作一个FIT Image在板子上跑下。

FIT Image可以兼容于多种板子,而无需重新进行编译烧写。对于有多个kernel节点或者fdt节点等等,兼容性更强。同时,可以有多种configurations,来对kernel、fdt、ramdisk来进行组合。

用户评论

发评论送积分,参与就有奖励!

发表评论

评论内容:发表评论不能请不要超过250字;发表评论请自觉遵守互联网相关政策法规。

深圳市品慧电子有限公司