boot.img是内核的镜像,不过该镜像并不只有内核的可执行文件,还有其他的信息,比如设备树等。但是有些嵌入式系统它的内核段分区也其他名称的,这里就不讨论定制的内核镜像格式。 内核镜像它生成后,最终会被uboot使用,所以我们从标准的uboot开始着手去探讨内核镜像如何的生成与制作。 这里的讲解以RK3588, kernel-6.6为参考进行分析。
U-Boot制作kernel镜像的工具
我们可以在官网下载最新的uboot源码:
接着解压,我们可以在目录u-boot-2024.10/tools中看到文件:
这里有mkimage工具源码。 所以说,内核的镜像格式,或者组织结构,是由uboot决定的。
FIT格式
我们可以在UBOOT DOC-Package中找到如下信息: 
mkimage简单分析
略(未来有空完善)
示例1-RK3588的内核镜像
RK3588的内核镜像(boot.img)支持两种方式:
Android模式FIT模式
Android模式
在你使用./build.sh kernel(实际上是make ARCH=arm64 kernel_source)时,在arch/arm64/Makefile中有如下代码:
%.img: rockchip/%.dtb kernel.img $(LOGO) $(LOGO_KERNEL)
$(Q)$(srctree)/scripts/mkimg --dtb $*.dtbRK3588代码中有一个制作Android 启动内核的工具:scripts/mkimg:
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd.
......
OUT=out
ITB=${BOOT_IMG}
ITS=${OUT}/boot.its
MKIMAGE=${MKIMAGE-"mkimage"}
MKIMAGE_ARG="-E -p 0x800"
make_boot_img()
{
RAMDISK_IMG_PATH=${objtree}/ramdisk.img
echo "RAMDISK_IMG_PATH=${RAMDISK_IMG_PATH}, PWD=$(pwd)"
[ -f ${RAMDISK_IMG_PATH} ] && RAMDISK_IMG=ramdisk.img && RAMDISK_ARG="--ramdisk ${RAMDISK_IMG_PATH}"
${srctree}/scripts/mkbootimg \
${KERNEL_IMAGE_ARG} \
${RAMDISK_ARG} \
--second resource.img \
-o boot.img && \
echo " Image: boot.img (with Image ${RAMDISK_IMG} resource.img) is ready";
${srctree}/scripts/mkbootimg \
${KERNEL_ZIMAGE_ARG} \
${RAMDISK_ARG} \
--second resource.img \
-o zboot.img && \
echo " Image: zboot.img (with ${ZIMAGE} ${RAMDISK_IMG} resource.img) is ready"
}
......
scripts/resource_tool ${DTB_PATH} ${LOGO} ${LOGO_KERNEL} >/dev/null
echo " Image: resource.img (with ${DTB} ${LOGO} ${LOGO_KERNEL}) is ready"
echo "BOOT_IMG=${BOOT_IMG}"
echo "BOOT_ITS=${BOOT_ITS}"
if [ -f "${BOOT_IMG}" ]; then
if file -L -p -b ${BOOT_IMG} | grep -q 'Device Tree Blob' ; then
repack_itb;
elif [ -x ${srctree}/scripts/repack-bootimg ]; then
repack_boot_img;
fi
elif [ -f "${BOOT_ITS}" ]; then
make_fit_boot_img;
elif [ -x ${srctree}/scripts/mkbootimg ]; then
echo "running make_boot_img"
make_boot_img;
fi这里有两个指令:
${srctree}/scripts/mkbootimg \
${KERNEL_IMAGE_ARG} \
${RAMDISK_ARG} \
--second resource.img \
-o boot.img
${srctree}/scripts/mkbootimg \
${KERNEL_ZIMAGE_ARG} \
${RAMDISK_ARG} \
--second resource.img \
-o zboot.imgmkbootimg是一个Android的构建工具,我们可以在Android mkbootimg.py源码这里获取。接着我们来看看该工具生成的boot.img是怎么样的结构。 我们在bootimg.h中可以看到其结构注释如下:
/* When a boot header is of version 0, the structure of boot image is as
* follows:
*
* +-----------------+
* | boot header | 1 page
* +-----------------+
* | kernel | n pages
* +-----------------+
* | ramdisk | m pages
* +-----------------+
* | second stage | o pages
* +-----------------+
*
* n = (kernel_size + page_size - 1) / page_size
* m = (ramdisk_size + page_size - 1) / page_size
* o = (second_size + page_size - 1) / page_size
*
* 0. all entities are page_size aligned in flash
* 1. kernel and ramdisk are required (size != 0)
* 2. second is optional (second_size == 0 -> no second)
* 3. load each element (kernel, ramdisk, second) at
* the specified physical address (kernel_addr, etc)
* 4. prepare tags at tag_addr. kernel_args[] is
* appended to the kernel commandline in the tags.
* 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
* 6. if second_size != 0: jump to second_addr
* else: jump to kernel_addr
*/为了验证是否正确,我们可以进行如下操作,先在build.sh(你的编译脚本可能不是这个)中阻止mk-fitimage.sh覆盖boot.img,这里在mkimage打包后,立马停止打包。这样我们就可以得到一个Android格式的boot.img。
build_kernel()
{
......
echo "============Start building kernel============"
echo "TARGET_KERNEL_ARCH =$RK_KERNEL_ARCH"
echo "TARGET_KERNEL_CONFIG =$RK_KERNEL_DEFCONFIG"
echo "TARGET_KERNEL_DTS =$RK_KERNEL_DTS"
echo "TARGET_KERNEL_CONFIG_FRAGMENT =$RK_KERNEL_DEFCONFIG_FRAGMENT"
echo "=========================================="
......
$KMAKE $RK_KERNEL_DTS.img -j1
exit 0 #增加这句
ITS="$CHIP_DIR/$RK_KERNEL_FIT_ITS"
if [ -f "$ITS" ]; then
$COMMON_DIR/mk-fitimage.sh kernel/$RK_BOOT_IMG \
"$ITS" $RK_KERNEL_IMG
fi
}我们将内核目录下生成的boot.img进行解压,在此前,我们需要安装工具unpackbootimg:
git clone https://github.com/osm0sis/mkbootimg
cd mkbootimg
make
sudo make install解压:
mkdir bootimg_out
unpackbootimg -i ./boot.img -o ./bootimg_out
上面得知,magic为ANDROID!,这个是boot header,他有不同的版本,我们可以参考Android boot镜像Header来分析,这里我实际的应该是v1或者v2。同时我们使用Hex Editor查看该boot.img: 
kernel size(小端模式)与arch/arm64/boot/Image的大小比较,基本一致。 接着我们偏移1个page_size,查看内核Image的内容:
内核可执行文件Image被放置到1 page_size位置,和bootimg.h中指示的一样。如果我们直接烧录该boot.img,启动时会有如下log:
这个证明Android镜像可以成功被加载。
FIT模式
我们在build.sh中找到如下代码(和上面的build.sh有些差异,但基本意思不变):
cd kernel
make ARCH=$RK_ARCH $RK_KERNEL_DEFCONFIG $RK_KERNEL_DEFCONFIG_FRAGMENT
make ARCH=$RK_ARCH $RK_KERNEL_DTS.img -j$RK_JOBS
if [ -f "$TOP_DIR/device/rockchip/$RK_TARGET_PRODUCT/$RK_KERNEL_FIT_ITS" ]; then
$COMMON_DIR/mk-fitimage.sh $TOP_DIR/kernel/$RK_BOOT_IMG \
$TOP_DIR/device/rockchip/$RK_TARGET_PRODUCT/$RK_KERNEL_FIT_ITS \
$TOP_DIR/kernel/ramdisk.img
fi这里会检查$TOP_DIR/device/rockchip/$RK_TARGET_PRODUCT/$RK_KERNEL_FIT_ITS是否存在,而预设的变量RK_KERNEL_FIT_ITS,RK_BOOT_IMG等会放在软链接指示的地方: 

所以上面的命令是判断是否在RK3588_SOURCE/device/rockchip/.target_product下是否有boot.its文件,如果有则使用FIT:
这里的boot.its只是提供一个模板,实际上会在mk-fitimages.sh中使用sed进行替换,比如某次替换结果如下:
/*
* Copyright (C) 2021 Rockchip Electronics Co., Ltd
*
* SPDX-License-Identifier: GPL-2.0
*/
/dts-v1/;
/ {
description = "U-Boot FIT source file for arm";
images {
fdt {
data = /incbin/("/home/xxx/RK3588/kernel-6.6.29-git/arch/arm64/boot/dts/rockchip/rk3588-evb7-lp4-v10-linux.dtb");
type = "flat_dt";
arch = "arm64";
compression = "none";
load = <0xffffff00>;
hash {
algo = "sha256";
};
};
kernel {
data = /incbin/("/home/xxx/RK3588/kernel-6.6.29-git/arch/arm64/boot/Image");
type = "kernel";
arch = "arm64";
os = "linux";
compression = "none";
entry = <0xffffff01>;
load = <0xffffff01>;
hash {
algo = "sha256";
};
};
resource {
data = /incbin/("/home/xxx/RK3588/kernel-6.6.29-git/resource.img");
type = "multi";
arch = "arm64";
compression = "none";
hash {
algo = "sha256";
};
};
};
configurations {
default = "conf";
conf {
rollback-index = <0x00>;
fdt = "fdt";
kernel = "kernel";
multi = "resource";
signature {
algo = "sha256,rsa2048";
padding = "pss";
key-name-hint = "dev";
sign-images = "fdt", "kernel", "multi";
};
};
};
};打包成功后有如下打印:
我们烧录后,启动uboot时: 
示例2-A40i的加载方式
参考链接
Android boot镜像头Android boot分区mkimage用法Android源码Android mkbootimg.py源码安卓boot镜像解析-博客园安卓boot类镜像解析-CSDNhttp://nerveware.org/device-and-image-trees/1.htmlhttps://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841676/U-Boot+Flattened+Device+Treehttp://www.wowotech.net/u-boot/fit_image_overview.htmlhttps://docs.u-boot.org/en/stable/usage/fit/index.html