树莓派引导FIT格式的压缩kernel

Posted by 婷 on December 8, 2024 本文总阅读量

简介

设置树莓派引导FIT镜像格式的linux,顺带验证FIT打包压缩的arm64linux镜像

uboot配置

uboot需要打开CONFIG_FIT配置,然后剩下的其他的FIT配置会帮你自动勾上,就不操心了

kernel镜像打包

arm64 kernel默认只支持gzip压缩算法,产物有ImageImage.gz

image-20241208155127957

要打包FIT的镜像,我们需要由一个its文件来描述

如果我们打包的是没有压缩的镜像,则its文件如下

/dts-v1/;

/ {
    description = "Raspi4b FIT Image";
    #address-cells = <1>;

    images {
        kernel {
            description = "Kernel";
            data = /incbin/("Image");
            type = "kernel";
            arch = "arm64";
            os = "linux";
            compression = "none";
            load = <0x1000000>;
            entry = <0x1000000>;
        };
        fdt@0 {
            description = "BCM2711-rpi-4b DTB";
            data = /incbin/("bcm2711-rpi-4-b.dtb");
            type = "flat_dt";
            arch = "arm64";
            compression = "none";
            load = <0x5000000>;
            entry = <0x5000000>;
        };
    };

    configurations {
        default = "config@0";
        config@0 {
            description = "kernel no compression";
            kernel = "kernel";
            fdt = "fdt@0";
        };

    };
};

如果是打包的gzip压缩过的镜像,则its文件如下

/dts-v1/;

/ {
    description = "Raspi4b gzip FIT Image";
    #address-cells = <1>;

    images {
        kernel {
            description = "Kernel";
            data = /incbin/("Image.gz");
            type = "kernel";
            arch = "arm64";
            os = "linux";
            compression = "gzip";
            load = <0x1000000>;
            entry = <0x1000000>;
        };
        fdt@0 {
            description = "BCM2711-rpi-4b DTB";
            data = /incbin/("bcm2711-rpi-4-b.dtb");
            type = "flat_dt";
            arch = "arm64";
            compression = "none";
            load = <0x5000000>;
            entry = <0x5000000>;
        };
    };

    configurations {
        default = "config@0";
        config@0 {
            description = "kernel gzip";
            kernel = "kernel";
            fdt = "fdt@0";
        };

    };
};

压缩的重点在于此处

image-20241208155027790

有了its文件后,通过mkimage工具可以生成相关的FIT格式的镜像

mkimage -f kernel-gz.its linux-gz.itb
mkimage -f kernel.its linux.itb

启动

启动gzip压缩的FIT镜像,uboot设置如下,对于FIT镜像格式,bootm addr如此便可,代码中会解析到是FIT镜像的格式,自行提取跟解压

U-Boot> setenv itbgz_bootcmd 'tftp 0x4000000 linux-gz.itb ; bootm 0x4000000 '
U-Boot> saveenv
Saving Environment to FAT... OK
U-Boot> run itbgz_bootcmd

image-20241208154225759

如果引导没有压缩的内核,则设置如下

U-Boot> setenv itb_bootcmd 'tftp 0xffff48 linux.itb ; bootm 0xffff48'
U-Boot> saveenv
Saving Environment to FAT... OK
U-Boot> run itb_bootcmd

image-20241208163347419

多镜像打包

其实有的芯片引导流程有spl -> atf -> uboot这个过程,spl也可以引导FIT格式的uboot+atf镜像,如下面的its文件所示

/dts-v1/;
/ {
    description = "Configuration to load ATF before U-Boot";
    
    images {
        uboot@1 {
            description = "U-Boot (64-bit)";
            data = /incbin/("./u-boot-nodtb.bin");
            type = "standalone";
            arch = "arm64";
            os = "U-Boot";
            compression = "none";
            load = <0x48000000>;
            entry = <0x48000000>;
        };
        atf@1 {
            description = "ARM Trusted Firmware";
            data = /incbin/("bl31.bin");
            type = "firmware";
            arch = "arm64";
            os = "arm-trusted-firmware";
            compression = "none";
            load = <0x1000000>;
            entry = <0x1000000>;
        };
        fdt {
            description = "U-Boot dtb";
            data = /incbin/("./u-boot.dtb");
            type = "flat_dt";
            arch = "arm64";
            compression = "none";
        };
    };
    configurations {
        default = "config@1";
        config@1 {
            description = "atf uboot fdt image";
            firmware = "atf@1";
            loadables = "uboot@1";
            fdt = "fdt";
        };
    };
};

如何确定load的地址

在调试的时候碰到如下问题

ERROR: new format image overwritten - must RESET the board to recover

image-20241208163035621

最后将bootm.c中相关的地址打印出来,如上图的打印,发现当我们将FIT下载到0x1000000地址的时候,会发现实际的FIT的头占了0xb8的大小,然后灵光一闪,将load的内存地址改为0x1000000 - 0xb8后即可。

image-20241208195838364

从图上的出错日志可以看出这里的startendloadload_end是对应不上的,所以出错了?那其实是不是只要保证这个load的地方能保留好FIT的头即可了?

image-20241208200955729

但是这里留着超过的了,却不行

image-20241208201238246

最后证明确实就是跟FIT的开头占了多少有关系。。。。。而且这个开头多大,还跟你its文件的字符有关系呢

image-20241208201857291

image-20241208201927364

那为啥压缩的就可以不遵循这个bootm addr ,addr = load_addr - FIT_header呢?我们压缩后的镜像是大概14M多,而设备树地址是在0x5000000,刚好距离我们的load地址0x4000000的大小为16M。我们从0x40000bc开始解压,解压后的数据就放到0x1000000,然后解压后的数据末尾为0x3238a00,刚好没有踩地址0x4000000。所以这个0x4000000这个地址上下都非常的极限。

反正对于压缩的镜像,bootm addr中的addr,保证要两点

  • addr放压缩镜像的内存,不踩FDT
  • linux解压后的地址,比如图中的load end = 0x3238a00,不要踩到addr

image-20241208202120825

按照这个约束,其实bootm 0x3800000也是可以的

image-20241208210128272

那能不能就是压缩的镜像跟不压缩的镜像那样,也开头留好FIT的头部大小呢?那不行呀,image_buf对于压缩的镜像来说就是src地址,怎么还能原地解压不是?

image-20241208203257222

image-20241208203207747

另外,目前树莓派支持镜像解压能申请到的的内存大小为0x4000000,这个是由CONFIG_SYS_BOOTM_LEN这个宏来控制的,对于非解压的镜像,不用考虑这个CONFIG

image-20241208163127114

参考链接