hi3516 uboot uimage


原文链接: hi3516 uboot uimage

1.内核镜像介绍

  通常一个可启动的内核镜像 (bootable kernel image) 是经过算法压缩的,2.6.30 之后采用 LZMA 或者 BZIP2,vmlinuz 最后的 z 表示内核是压缩的,这也意味着内核中会有一段解压程序。

  内核中包含了各种内核镜像的格式,如 vmlinux、zImage、bzImage、uImage 等,首先介绍内核中常见内核文件。

a. vmlinux是最原始,静态链接的,可执行的,不能bootable的,未压缩的内核镜像。vm代表Virtual Memory。Linux支持虚拟内存,因此得名vm。它是通过源码经过编译汇编, 链接而成的 ELF 文件。

b. zImage是 vmlinux 经过 gzip 压缩后的文件,适用于小内核。
make ARCH=arm CROSS_COMPILE=arm-linux- zImage

c. bzImage 是 vmlinux 经过 gzip 压缩后的文件,适用于大内核,”bz” 表示 “big zImage”。

d. uImage 是 uboot 专用的镜像文件,它是在 zImage 之前加上一个长度为 0x40 的头信息,包括了该镜像文件的类型、加载位置、生成时间、大小等信息。
mkimage -A arm -O linux -C none -T kernel -a 0x20008000 -e 0x20008040 -n Linux_Image -d zImage uImage
————————————————
版权声明:本文为CSDN博主「Hacker_Albert」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41028621/article/details/90039920

  1. vmlinux 编译出来最原始的内核文件,未经压缩。

  2. zImage 由 vmlinux 经过 gzip 压缩而得。
      
      zImage 是由压缩后的 vmlinux 和解压缩程序组成。

  3. bzImage bz表示“big zImage”,不是用bzip2压缩的。

  zImage 和 bzImage 的区别在于,zImage 解压缩内核到低端内存(第一个640KB),bzImage解压缩内核到高端内存(1MB以上)。如果内核比较小,那么采用 zImage 或 bzImage 都行,如果比较大应该用 bzImage。

uImage U-Boot专用的映像文件。

  uImage 是在 zImage 之前加上一个长度为 0x40 的 tag (64个字节,说明这个image文件的类型、加载位置、生成时间、大小等信息)。其实就是一个自动跟手动的区别,有了 uImage 头部的描述,uboot 就知道对应 image 的信息,如果没有头部则需要自己手动设置这些参数。换句话说,如果直接从 uImage 的 0x40 位置开始执行,zImage 和 uImage 没有任何区别。
  生成 uImage 需要用到 mkimage 工具,它位于相应版本的 uboot 源码根目录下的 tools 目录下,将 mkimage 复制到系统环境变量 bin 目录下。然后在内核目录下运行“make uImage”,我们发现 uImage 的大小比 zImage 多64个字节。
  

vmlinuz 是 bzImage/zImage 文件的拷贝或指向 bzImage/zImage 的链接。

initrd 是“initial ramdisk”的简写。一般被用于临时引导硬件到实际内核 vmlinuz 能够接管并继续引导的状态。

  一般情况下,在生成 vmlinux 后,再对内核映像进行压缩成为 zImage 或 uImage,压缩的后的内核映像位于 arch/$(ARCH)/boot 目录下面。
  嵌入式开发,一般下载到 flash 中的是 zImage 或 uImage,zImage 可由 uboot 命令 bootzImage 引导,uImage 则可由 bootm 引导。
  假设内存映射的基址是 0x3000 0000 ,我们通过设置 bootcmd 为“ tftp 0x30008000 uImage; bootm 0x300080000”来下载并启动内核映像,那么 0x30008000 又是如何来的呢?这32KB(0x8000)的空间有什么用?

在内核文件 arch/arm/mach-s5pv210/Makefile.boot 中,有如下描述:
zreladdr-y += 0x30008000#内核自解压缩后的释放地址
params_phys-y := 0x30000100#内核启动参数的存放地址,
————————————————
版权声明:本文为CSDN博主「阿基米东」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lu_embedded/article/details/51426182

一、uImage

编译kernel之后,会生成Image或者压缩过的zImage。但是这两种镜像的格式并没有办法提供给uboot的足够的信息来进行load、jump或者验证操作等等。因此,uboot提供了mkimage工具,来将kernel制作为uboot可以识别的格式,将生成的文件称之为uImage。

uboot支持两种类型的uImage。
Legacy-uImage
在kernel镜像的基础上,加上64Byte的信息提供给uboot使用。

FIT-uImage
以类似FDT的方式,将kernel、fdt、ramdisk等等镜像打包到一个image file中,并且加上一些需要的信息(属性)。uboot只要获得了这个image file,就可以得到kernel、fdt、ramdisk等等镜像的具体信息和内容。

Legacy-uImage实现较为简单,并且长度较小。但是实际上使用较为麻烦,需要在启动kernel的命令中额外添加fdt、ramdisk的加载信息。
而FIT-uImage实现较为复杂,但是使用起来较为简单,兼容性较好,(可以兼容多种配置)。但是需要的额外信息也较长。

二、Legacy-uImage

1、使能需要打开的宏
CONFIG_IMAGE_FORMAT_LEGACY=y
1
注意,这个宏在自动生成的autoconf.mk中会自动配置,不需要额外配置。

2、如何制作 & 使用
(1)工具mkimage
编译完uboot之后会在uboot的tools目录下生成mkimage可执行文件
(2)命令

mkimage -A arm -O linux -C none -T kernel -a 0x20008000 -e 0x20008040 -n Linux_Image -d zImage uImage

各个参数意义如下
Usage: mkimage -l image

      -l ==> list image header information
   mkimage [-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image
      -A ==> set architecture to 'arch'  // 体系
      -O ==> set operating system to 'os' // 操作系统
      -T ==> set image type to 'type' // 镜像类型
      -C ==> set compression type 'comp' // 压缩类型
      -a ==> set load address to 'addr' (hex) // 加载地址
      -e ==> set entry point to 'ep' (hex) // 入口地址
      -n ==> set image name to 'name' // 镜像名称,注意不能超过32B
      -d ==> use image data from 'datafile' // 输入文件
      -x ==> set XIP (execute in place) 

(3)使用
将生成的Legacy-uImage下载到参数中指定的load address中,使用bootm ‘实际load address’命令跳转到kernel中。
但是注意,如果使用Legacy-uImage后面还需要跟上文件系统的ram地址以及dtb的ram地址,否则可能会导致bootm失败。
格式如下:

bootm Legacy-uImage加载地址 ramdisk加载地址 dtb加载地址

3、和zImage的区别
-rw-rw-rw- 1 hlos hlos 1560120 Dec 5 14:46 uImage
-rwxrwxrwx 1 hlos hlos 1560056 Nov 30 15:42 zImage*

可以看出uImage比zImage多了64Byte,通过对比可以发现这64Byte的数据是加载zImage的头部的。
直接查看这64Byte的数据,通过od命令查看如下:

hlos@node4:boot$ od -tx1 -tc -Ax -N64 uImage
000000 27 05 19 56 5a f3 f7 8e 58 45 0d 3d 00 17 cd f8

     ' 005 031   V   Z 363 367 216   X   E  \r   =  \0 027 315 370

000010 20 00 80 00 20 00 80 40 e2 4b 43 b6 05 02 02 00

        \0 200  \0      \0 200   @ 342   K   C 266 005 002 002  \0

000020 4c 69 6e 75 78 5f 49 6d 61 67 65 00 00 00 00 00

     L   i   n   u   x   _   I   m   a   g   e  \0  \0  \0  \0  \0

000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

    \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0

Legacy-uImage是在zImage的基础上加上64Byte的头部。
下面通过Legacy-uImage的头部数据结构来解析这串数据。

4、格式头说明
uboot用image_header来表示Legacy-uImage的头部

typedef struct image_header {
    __be32      ih_magic;   /* Image Header Magic Number    */   // 幻数头,用来校验是否是一个Legacy-uImage
    __be32      ih_hcrc;    /* Image Header CRC Checksum    */ // 头部的CRC校验值
    __be32      ih_time;    /* Image Creation Timestamp */ // 镜像创建的时间戳
    __be32      ih_size;    /* Image Data Size      */ // 镜像数据长度
    __be32      ih_load;    /* Data  Load  Address      */ // 加载地址
    __be32      ih_ep;      /* Entry Point Address      */ // 入口地址
    __be32      ih_dcrc;    /* Image Data CRC Checksum  */ // 镜像的CRC校验
    uint8_t     ih_os;      /* Operating System     */ // 操作系统类型
    uint8_t     ih_arch;    /* CPU architecture     */ // 体系 
    uint8_t     ih_type;    /* Image Type           */ // 镜像类型
    uint8_t     ih_comp;    /* Compression Type     */ // 压缩类型
    uint8_t     ih_name[IH_NMLEN];  /* Image Name       */ // 镜像名
} image_header_t;
#define IH_NMLEN        32  /* Image Name Length        */

可以发现这个头部就是64Byte。根据上述3来说明一下这个数据结构

ih_magic
27 05 19 56
头部幻数,用来判断这个是否是Legacy-uImage的头部。为0x27051956则表示是一个Legacy-uImage。
ih_size
00 17 cd f8
数据镜像长度。这里是0x17cdf8,也就是1560056,和我们前面测得的zImage的长度一致。
ih_load
20 00 80 00
加载地址,也就是0x20008000, 和我们执行mkimage使用的参数一致。
ih_ep
20 00 80 40
入口地址,也就是0x20008040, 和我们执行mkimage使用的参数一致。
三、FIT-uImage
0、原理说明
flattened image tree,类似于FDT(flattened device tree)的一种实现机制。其通过一定语法和格式将一些需要使用到的镜像(例如kernel、dtb以及文件系统)组合到一起生成一个image文件。其主要使用四个组件。

its文件
image source file,类似于dts文件,负责描述要声称的image的的信息。需要自行进行构造。
itb文件
最终得到的image文件,类似于dtb文件,也就是uboot可以直接对其进行识别和解析的FIT-uImage。
mkimage
mkimage则负责dtc的角色,用于通过解析its文件、获取对应的镜像,最终生成一个uboot可以直接进行识别和解析的itb文件。
image data file
实际使用到的镜像文件。
mkimage将its文件以及对应的image data file,打包成一个itb文件,也就是uboot可以识别的image file(FIT-uImage)。我们将这个文件下载到么memory中,使用bootm命令就可以执行了。

1、使能需要打开的宏
CONFIG_FIT=y

2、如何制作 & 使用
(1)its文件制作
因为mkimage是根据its文件中的描述来打包镜像生成itb文件(FIT-uImage),所以首先需要制作一个its文件,在its文件中描述需要被打包的镜像,主要是kernel镜像,dtb文件,ramdisk镜像。
简单的例子如下:

/*
 * U-Boot uImage source file for "X project"
 */

/dts-v1/;

/ {
    description = "U-Boot uImage source file for X project";
    #address-cells = <1>;

    images {
        kernel@tiny210 {
            description = "Unify(TODO) Linux kernel for project-x";
            data = /incbin/("/home/hlos/code/xys/temp/project-x/build/out/linux/arch/arm/boot/zImage");
            type = "kernel";
            arch = "arm";
            os = "linux";
            compression = "none";
            load = <0x20008000>;
            entry = <0x20008000>;
        };
        fdt@tiny210 {
            description = "Flattened Device Tree blob for project-x";
            data = /incbin/("/home/hlos/code/xys/temp/project-x/build/out/linux/arch/arm/boot/dts/s5pv210-tiny210.dtb");
            type = "flat_dt";
            arch = "arm";
            compression = "none";
        };
        ramdisk@tiny210 {
            description = "Ramdisk for project-x";
            data = /incbin/("/home/hlos/code/xys/temp/project-x/build/out/rootfs/initramfs.gz");
            type = "ramdisk";
            arch = "arm";
            os = "linux";
            compression = "gzip";
        };
    };

    configurations {
        default = "conf@tiny210";
        conf@tiny210 {
            description = "Boot Linux kernel with FDT blob";
            kernel = "kernel@tiny210";
            fdt = "fdt@tiny210";
            ramdisk = "ramdisk@tiny210";
        };
    };
};

语法可以参考蜗窝科技的u-boot FIT image介绍
注意,可以有多个kernel节点或者fdt节点等等,兼容性更强。同时,可以有多种configurations,来对kernel、fdt、ramdisk来进行组合,使FIT-uImage可以兼容于多种板子,而无需重新进行编译烧写。

(2)生成FIT-uImage
生成的命令相对Legacy-uImage较为简单,因为信息都在its文件里面描述了

mkimage -f ITS文件 要生成的ITB文件,如下:
${UBOOT_OUT_DIR}/tools/mkimage -f ${UIMAGE_ITS_FILE} ${UIMAGE_ITB_FILE}
1
2
最终在project X项目上生成了xprj_uImage.itb,这就是我们需要的FIT-uImage文件。

(3)使用
将生成的FIT-uImage下载到参数中指定的load address中,使用bootm ‘实际load address’命令跳转到kernel中。
uboot会自动解析出FIT-uImage中,kernel、ramdisk、dtb的信息,使用起来相当方便。

3、简单说明
FIT-uImage的格式类似于DTB。uboot会去解析出FIT-uImage中的configurations节点,根据节点选择出需要的kernel、dtb、rootfs。因此,在节点的基础上,添加很多节点信息,提供uboot使用。
————————————————
版权声明:本文为CSDN博主「ooonebook」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ooonebook/article/details/53495002

1. 前言

Linux kernel在ARM架构中引入device tree(全称是flattened device tree,后续将会以FDT代称)的时候[1],其实怀揣了一个Unify Kernel的梦想----同一个Image,可以支持多个不同的平台。随着新的ARM64架构将FDT列为必选项,并将和体系结构有关的代码剥离之后,这个梦想已经接近实现:

在编译linux kernel的时候,不必特意的指定具体的架构和SOC,只需要告诉kernel本次编译需要支持哪些板级的platform即可,最终将会生成一个Kernel image,以及多个和具体的板子(哪个架构、哪个SOC、哪个版型)有关的FDT image(dtb文件)。
bootloader在启动的时候,根据硬件环境,加载不同的dtb文件,即可使linux kernel运行在不同的硬件平台上,从而达到unify kernel的目标。

本文将基于嵌入式产品中普遍使用的u-boot,以其新的uImage格式(FIT image,Flattened uImage Tree)为例,介绍达到此目标的步骤,以及背后的思考和意义。

2. Legacy uImage

从u-boot的角度看,它要 boot 一个二进制文件(例如kernel Image),需要了解该文件的一些信息,例如:

该文件的类型,如kernel image、dtb文件、ramdisk image等等?
该文件需要放在memory的哪个位置(加载地址)?
该文件需要从memory哪个位置开始执行(执行地址)?
该文件是否有压缩?
该文件是否有一些完整性校验的信息(如CRC)?
等等

结合“X-010-UBOOT-使用booti命令启动kernel(Bubblegum-96平台)”中有关booti的例子,上面信息被隐含在我们的命令行中了,例如:

通过DFU工具,将指定的image文件下载到指定的memory地址,间接的指定了二进制文件加载地址;
booti命令本身,说明加载的文件类型是ARM64平台的Image文件;
通过booti的参数,可以指定Kernel Image、ramdisk、DTB文件的执行位置;
等等。

不过,这种做法缺点很明显(总结来说,就是太啰嗦了):

需要往memory中搬不同的二进制文件(Kernel、DTB、ramdisk等);
boot指令(booti等)有比较复杂的参数;
无法灵活地处理二进制文件的校验、解压缩等操作;
等等。

为了解决上述缺点,u-boot自定义了一种Image格式----uImage。最初的时候,uImage的格式比较简单,就是为二进制文件加上一个header(具体可参考“include/image.h”中的定义),标示该文件的特性。然后在boot该类型的Image时,从header中读取所需的信息,按照指示,进行相应的动作即可。这种原始的Image格式,称作Legacy uImage,其特征可总结为:

1)使用mkimage工具(位于u-boot source code的tools/mkimage中)生成。
2)支持OS Kernel Images、RAMDisk Images等多种类型的Image。
3)支持gzip、bzip2等压缩算法。
4)支持CRC32 checksums。
5)等等。

最后,之所以称作Legacy,说明又有新花样了,这种旧的方式,我们就不再过多关注了,拥抱新事物去吧。

3. FIT uImage

3.1 简介

device tree在ARM架构中普及之后,u-boot也马上跟进、大力支持,毕竟,美好的Unify kernel的理想,需要bootloader的成全。为了支持基于device tree的unify kernel,u-boot需要一种新的Image格式,这种格式需要具备如下能力:

1)Image中需要包含多个dtb文件。
2)可以方便的选择使用哪个dtb文件boot kernel。

综合上面的需求,u-boot推出了全新的image格式----FIT uImage,其中FIT是flattened image tree的简称。是不是觉得FIT和FDT(flattened device tree)有点像?没错,它利用了Device Tree Source files(DTS)的语法,生成的image文件也和dtb文件类似(称作itb),下面我们会详细描述。

3.2 思路

为了简单,我们可以直接把FIT uImage类比为device tree的dtb文件,其生成和使用过程为[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)类似,负责描述要生成的image file的信息(上面第2章描述的信息)。mkimage和dtc工具,可以将.its文件以及对应的image data file,打包成一个image file。我们将这个文件下载到么memory中,使用bootm命令就可以执行了。

3.3 image source file的语法

image source file的语法和device tree source file完全一样(可参考[3][4][5]中的例子),只不过自定义了一些特有的节点,包括images、configurations等。说明如下:

1)images节点

指定所要包含的二进制文件,可以指定多种类型的多个文件,例如multi.its[5]中的包含了3个kernel image、2个ramdisk image、2个fdt image。每个文件都是images下的一个子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";
};
};

可以包括如下的关键字:

description,描述,可以随便写;
data,二进制文件的路径,格式为----/incbin/("path/to/data/file.bin");
type,二进制文件的类型,"kernel", "ramdisk", "flat_dt"等,具体可参考中[6]的介绍;
arch,平台类型,“arm”, “i386”等,具体可参考中[6]的介绍;
os,操作系统类型,linux、vxworks等,具体可参考中[6]的介绍;
compression,二进制文件的压缩格式,u-boot会按照执行的格式解压;
load,二进制文件的加载位置,u-boot会把它copy对应的地址上;
entry,二进制文件入口地址,一般kernel Image需要提供,u-boot会跳转到该地址上执行;
hash,使用的数据校验算法。

2)configurations

可以将不同类型的二进制文件,根据不同的场景,组合起来,形成一个个的配置项,u-boot在boot的时候,以配置项为单位加载、执行,这样就可以根据不同的场景,方便的选择不同的配置,实现unify kernel目标。还以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,默认配置项由“default”指定,当然也可以在运行时指定。

3.4 Image的编译和使用

FIT uImage的编译过程很简单,根据实际情况,编写image source file之后(假设名称为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的某个地址(没有特殊要求,例如0x100000),然后使用bootm命令即可启动,步骤包括:

1)使用iminfo命令,查看memory中存在的images和configurations。

2)使用bootm命令,执行默认配置,或者指定配置。

使用默认配置启动的话,可以直接使用bootm:

bootm 0x100000

选择其它配置的话,可以指定配置名:

bootm 0x100000#config@2

以上可参考“doc/uImage.FIT/howto.txt[2]”,具体细节我们会在后续的文章中结合实例说明。

4. 总结

本文简单的介绍了u-boot为了实现Unify kernel所做的努力,但有一个问题,大家可以思考一下:bootloader的unify怎么保证呢?

SOC厂家提供(固化、提供二进制文件等)?
格式统一,UEFI?

后面有时间的话,可以追着这个疑问研究研究。

5. 参考文档

[1] Device Tree(一):背景介绍

[2] doc/uImage.FIT/howto.txt

[3] doc/uImage.FIT/kernel.its

[4] doc/uImage.FIT/kernel_fdt.its

[5] doc/uImage.FIT/multi.its

[6] doc/uImage.FIT/source_file_format.txt

`