OpenWrt编译时镜像的生成过程

 

运行环境相关说明

1
2
3
4
5
6
7
8
# 这里以Rockchip架构的设备nlnet_xiguapi-v3和OpenWrt 24.10.2系统源码为例来讲述

# 未来会成为OpenWrt的BOOT分区,该目录下包含启动脚本 boot.scr 和FIT固件 kernel.img
build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/tmp/immortalwrt-24.10.2-rockchip-armv8-nlnet_xiguapi-v3-ext4-sysupgrade.img.gz.boot/

# 未来会成为OpenWrt中ROOT分区,该目录打包成root.ext4文件后会与上面的boot目录做成 *-sysupgrade.img 镜像
build_dir/target-aarch64_generic_musl/root-rockchip/

内核及设备树处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# $(KERNEL_BUILD_DIR) = build_dir/target-aarch64_generic_musl/linux-rockchip_armv8

# 内核文件 $(KERNEL_BUILD_DIR)/vmlinux 是由原始内核文件 $(KERNEL_BUILD_DIR)/linux-6.6.93/vmlinux 处理而来
/home/leux/immortalwrt/staging_dir/toolchain-aarch64_generic_gcc-13.3.0_musl/bin/aarch64-openwrt-linux-musl-objcopy \
-O binary -R .reginfo -R .notes -R .note -R .comment -R .mdebug -R .note.gnu.build-id \
-S $(KERNEL_BUILD_DIR)/linux-6.6.93/vmlinux $(KERNEL_BUILD_DIR)/vmlinux

# 拷贝后将内核从 $(KERNEL_BUILD_DIR)/vmlinux 23.6MB 压缩到 $(KERNEL_BUILD_DIR)/nlnet_xiguapi-v3-kernel.bin 5.85MB
cp $(KERNEL_BUILD_DIR)/vmlinux $(KERNEL_BUILD_DIR)/nlnet_xiguapi-v3-kernel.bin
/home/leux/immortalwrt/staging_dir/host/bin/lzma e $(KERNEL_BUILD_DIR)/vmlinux -lc1 -lp2 -pb2 $(KERNEL_BUILD_DIR)/nlnet_xiguapi-v3-kernel.bin

# 生成FIT源文件 $(KERNEL_BUILD_DIR)/nlnet_xiguapi-v3-kernel.bin.its 用来合并镜像,文件中定义了内核与设备树的路径
/home/leux/immortalwrt/scripts/mkits.sh -D nlnet_xiguapi-v3 \
-o $(KERNEL_BUILD_DIR)/nlnet_xiguapi-v3-kernel.bin.its -k $(KERNEL_BUILD_DIR)/nlnet_xiguapi-v3-kernel.bin \
-C lzma -d $(KERNEL_BUILD_DIR)/linux-6.6.93/arch/arm64/boot/dts/rockchip/rk3568-xiguapi-v3.dtb \
-a 0x03200000 -e 0x03200000 -c "config-1" -A arm64 -v 6.6.93

# 然后再把压缩后的内核与设备树合并生成新的 $(KERNEL_BUILD_DIR)/nlnet_xiguapi-v3-kernel.bin.new
/home/leux/immortalwrt/staging_dir/host/bin/mkimage -f $(KERNEL_BUILD_DIR)/nlnet_xiguapi-v3-kernel.bin.its $(KERNEL_BUILD_DIR)/nlnet_xiguapi-v3-kernel.bin.new

# 将合并好的内核与设备树拷贝成 kernel.img 文件复制到指定目录
cp -fpR $(KERNEL_BUILD_DIR)/nlnet_xiguapi-v3-kernel.bin.new \
$(KERNEL_BUILD_DIR)/tmp/immortalwrt-rockchip-armv8-nlnet_xiguapi-v3-ext4-sysupgrade.img.gz.boot/kernel.img

# 创建U-Boot启动系统所用的脚本到指定目录
/home/leux/immortalwrt/staging_dir/host/bin/mkimage -A arm -O linux -T script -C lzma -a 0 -e 0 -d default.bootscript \
$(KERNEL_BUILD_DIR)/tmp/immortalwrt-rockchip-armv8-nlnet_xiguapi-v3-ext4-sysupgrade.img.gz.boot/boot.scr

生成最终系统镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# $(KERNEL_BUILD_DIR) = build_dir/target-aarch64_generic_musl/linux-rockchip_armv8
# 创建启动脚本和镜像是在 target/linux/rockchip/image/Makefile 内的 define Build/boot-script 和 define Build/pine64-img 中定义

# 将要作为OpenWrt根目录的 root-rockchip 目录打包成 root.ext4 文件,命令参数 -L 卷标 -l 大小160M -b 块大小 -m 保留区块百分比 -T 时间戳 生成文件名 目录
/home/leux/immortalwrt/staging_dir/host/bin/make_ext4fs -L rootfs -l 167772160 -b 4096 -m 0 -T 1750865788 $(KERNEL_BUILD_DIR)/root.ext4 \
/home/leux/immortalwrt/build_dir/target-aarch64_generic_musl/root-rockchip/

# 创建分区大小为16M boot和160M root分区的镜像,注意 *-sysupgrade.img.gz 其实是个img镜像
PADDING=1 /home/leux/immortalwrt/scripts/gen_image_generic.sh \
$(KERNEL_BUILD_DIR)/tmp/immortalwrt-rockchip-armv8-nlnet_xiguapi-v3-ext4-sysupgrade.img.gz \
16 $(KERNEL_BUILD_DIR)/tmp/immortalwrt-rockchip-armv8-nlnet_xiguapi-v3-ext4-sysupgrade.img.gz.boot \
160 /home/leux/immortalwrt/build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/root.ext4 32768

# 写入U-Boot到上面创建的镜像,跳过 *.img.gz 文件的前32KB (512*64=32768) 后再写入,这个区域保存着分区表相关信息
dd if=/home/leux/immortalwrt/staging_dir/target-aarch64_generic_musl/image/xgp-rk3568-u-boot-rockchip.bin \
of="$(KERNEL_BUILD_DIR)/tmp/immortalwrt-rockchip-armv8-nlnet_xiguapi-v3-ext4-sysupgrade.img.gz" seek=64 conv=notrunc

# 将上面已写入U-Boot的镜像 *.img.gz 文件压缩成新文件 *.img.gz.new
/home/leux/immortalwrt/staging_dir/host/bin/gzip -f -9n -c $(KERNEL_BUILD_DIR)/tmp/immortalwrt-rockchip-armv8-nlnet_xiguapi-v3-ext4-sysupgrade.img.gz \
> $(KERNEL_BUILD_DIR)/tmp/immortalwrt-rockchip-armv8-nlnet_xiguapi-v3-ext4-sysupgrade.img.gz.new

# 对压缩文件写入相关元数据信息使其可在OpenWrt的WebUI中直接刷入
echo '{ "metadata_version": "1.1", "compat_version": "1.0", "supported_devices":["nlnet,xiguapi-v3"], \
"version": { "dist": "ImmortalWrt", "version": "24.10.2", "revision": "r33247-467867283bb9", "target": "rockchip/armv8", "board": "nlnet_xiguapi-v3" } }' \
| fwtool -I - $(KERNEL_BUILD_DIR)/tmp/immortalwrt-rockchip-armv8-nlnet_xiguapi-v3-squashfs-sysupgrade.img.gz

# 复制出最终生成的系统镜像文件
cp $(KERNEL_BUILD_DIR)/tmp/immortalwrt-rockchip-armv8-nlnet_xiguapi-v3-ext4-sysupgrade.img.gz \
/home/leux/immortalwrt/bin/targets/rockchip/armv8/immortalwrt-rockchip-armv8-nlnet_xiguapi-v3-ext4-sysupgrade.img.gz

替换设备树适配法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 将官方系统镜像内的 kernel.img 文件中内核和设备树分离,用新的设备树替换并与分离的内核再次生成 kernel.img 后替换掉原官方系统镜像中的,来达到适配新设备的目的
# 注意设备树需用与官方镜像同一版本源码编译而成,否则也许能正常启动但因设备树有差异不能正常使用(24.10.3-sige3镜像与24.10.2的dtb测试卡死)
# 例如用 immortalwrt-24.10.2 源码编译生成的 rk3568-xiguapi-v3.dtb 文件最好也用 immortalwrt-24.10.2 官方镜像替换

# mkimage # 用于打包linux内核,增加头部信息,制作uImage,基于its创建itb启动文件(即FIT image)
# dumpimage # 用于解析和提取 ITB 文件,常用于从打包的镜像文件中提取独立的文件内容
sudo apt install u-boot-tools # 先安装所需工具包,上面两个命令都在该软件包中

# 以如下官方镜像文件为例,首先提取出镜像中第一分区内的itb文件 kernel.img 供下一步使用,我是直接用7-zip打开压缩包复制出来的
https://mirrors.ustc.edu.cn/immortalwrt/releases/24.10.2/targets/rockchip/armv8/immortalwrt-24.10.2-rockchip-armv8-lyt_t68m-ext4-sysupgrade.img.gz

# 列出itb文件中描述内核与设备树等的相关信息,根据结果得知OpenWrt的含有Image 0 (kernel-1)和Image 1 (fdt-1)镜像
dumpimage -l kernel.img

# 提取其中的 Image 0 (kernel-1)和Image 1 (fdt-1),参数 -p 指定要提取的镜像索引,-o 指定输出文件
dumpimage -T flat_dt -p 0 -o rockchip-armv8-kernel kernel.img
dumpimage -T flat_dt -p 1 -o rk3588-armsom-sige3.dtb kernel.img

# 下载执行脚本来生成用来合并镜像的FIT源文件 nlnet_xiguapi-v3-kernel.bin.its 该文件中定义了要合并的内核与设备树的路径及其他参数
wget https://github.com/immortalwrt/immortalwrt/raw/master/scripts/mkits.sh && chmod +x ./mkits.sh

./mkits.sh -D nlnet_xiguapi-v3 -o nlnet_xiguapi-v3-kernel.bin.its -k rockchip-armv8-kernel \
-C lzma -d $(KERNEL_BUILD_DIR)/linux-6.6.93/arch/arm64/boot/dts/rockchip/rk3568-xiguapi-v3.dtb \
-a 0x03200000 -e 0x03200000 -c "config-1" -A arm64 -v 6.6.93

# 使用上面的 *.its 文件将提取出的内核与新的设备树合并
mkimage -f nlnet_xiguapi-v3-kernel.bin.its new-kernel.img

# 用新生成的 new-kernel.img 替换官方镜像文件中的 kernel.img 即可
sudo su
losetup -f -P immortalwrt-24.10.2-rockchip-armv8-lyt_t68m-ext4-sysupgrade.img
mount -t ext4 /dev/loop0p1 /mnt/rootfs/
rm /mnt/rootfs/kernel.img
cp new-kernel.img /mnt/rootfs/kernel.img
umount /dev/loop0p1
losetup -D

其他知识或者技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
# 如下几个文件是相同的
build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/linux-6.6.93/vmlinux
build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/vmlinux.debug

build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/Image
build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/linux-6.6.89/arch/arm64/boot/Image

build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/linux-6.6.89/arch/arm64/boot/dts/rockchip/rk3568-xiguapi-v3.dtb
build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/image-rk3568-xiguapi-v3.dtb

build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/nlnet_xiguapi-v3-kernel.bin
build_dir/target-aarch64_generic_musl/linux-rockchip_armv8/tmp/immortalwrt-rockchip-armv8-nlnet_xiguapi-v3-ext4-sysupgrade.img.gz.boot/kernel.img