LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

FIT(1):基于FIT的镜像创建和解析/启动

关键词:FIT、mkimage、dtc、its、itb、bootm、initrd、sha256等等、

Kernel中引入的Device Trace概念,将配置信息放入dtb中。达到一个kernel,结合不同dtb适配多个平台。

FIT是Flattened Image Tree的意思,即将多个镜像通过dts语法编译生成一个镜像文件。

uboot支持编写its文件,通过mkimage和dtc创建kernel、ramdisk、dtb等等文件的打包镜像。

这里的dts文件通常命名为.its,输出的镜像文件通常命名.itb。

1 FIT相关工具

安装mkimage工具:

sudo apt install  u-boot-tools

还需要安装dtc:

sudo apt-get install device-tree-compiler

mkimage调用dtc创建itb文件。

2 FIT镜像创建流程

创建FIT镜像的命令:

mkimage -f multi.its fit.itb

查看FIT镜像头:

mkimage -l fit.itb

its(image tree source)是创建FIT镜像的配置脚本,itb(flattened image tree blob)是FIT镜像。

下面介绍如何创建its文件。

2.1 its示例

下面结合实例介绍its编写规则,完整的编写规则参考《Flattened Image Tree (FIT) Format》:

/dts-v1/;

/ {
    description = "Various kernels, ramdisks and FDT blobs";--对本镜像的描述。
    #address-cells = <1>;--下面load/entry属性的格式,1表示只有一个地址。
  timestamp = <12399321>--镜像修改时间。

images {--镜像节点,必须包含一个子节点。 kernel
-1 {--子节点名称。 description = "Linux ARM kernel";--子节点描述。 data = /incbin/("arch/arm/boot/zImage");--子节点二进制数据。 type = "kernel";--子节点类型为IH_TYPE_KERNEL,可以是"standalone", "kernel", "kernel_noload", "ramdisk", "firmware", "script","filesystem", "flat_dt"等。
            arch = "arm";--架构名称,比如arm等。
            os = "linux";--OS名称。
            compression = "none";--对data使用的压缩算法。
            load = <0x830000e8>;--本数据加载到的地址,注意地址要正确。
            entry = <0x830000e8>;--镜像启动入口地址。Linux的load地址也是entry地址。
            hash-1 {--使用md5进行哈希。哈希值存放于value属性中。
                algo = "crc32";--支持的hash算法包括:crc32、md5、sha1、sha256。
            };
            hash-2 {--使用sha256进行哈希。
                algo = "sha256";
            };
        };

        ramdisk {
            description = "Linux ramdisk";
            data = /incbin/("rootfs.cpio.gz");
            type = "ramdisk";--数据类型为ramdisk,对应类型为IH_TYPE_RAMDISK。
            arch = "arm";
            os = "linux";
            compression = "none";--注意这里的数据虽然被gzip压缩了,但是不需要uboot处理。所以uboot认为的数据压缩类型为none。
            hash-1 {
                algo = "crc32";
            };
        };

        kernel-fdt {
            description = "NAND FDT blob";
            data = /incbin/("nand.dtb");
            type = "flat_dt";
            arch = "arm";
            compression = "none";
       load = <0x86F00000>; hash
-1 { algo = "crc32"; }; }; ramdisk-fdt { description = "RAMDISK FDT blob"; data = /incbin/("ramdisk.dtb"); type = "flat_dt";--子镜像类型为flat_dt,对应类型为IH_TYPE_FLATDT。 arch = "arm"; compression = "none"; load = <0x86F00000>;--dtb加载到指定地址。 hash-1 { algo = "sha1"; }; }; }; configurations {--提供对上述镜像的组合,有默认配置和多种配置可选。 default = "kernel";--默认配置,bootm未指定特殊配置时使用。 kernel {--配置名称。加载zImage和nand.dtb,rootfs从NAND中获取。 description = "Boot Linux kernel with FDT blob";--配置说明。 kernel = "kernel"; fdt = "kernel-fdt"; }; ramdisk {--加载zImage和dtb,文件系统通过rootfs.cpio.gz解压作为ramdisk使用。 description = "Boot Linux kernel with FDT blob and ramdisk"; kernel = "kernel";--指向kernel类型镜像。 ramdisk = "ramdisk";--指向ramdisk类型镜像。 fdt = "ramdiskfdt";--指向flat_dt类型数据。 }; }; };

另外如果数据位于外部,通过data-offset和data-size指定。

3 FIT镜像解析流程

3.1 bootm命令使用

 通过bootm命令可以解析FIT镜像并启动:

bootm--后面没有地址则使用CONFIG_SYS_LOAD_ADDR地址作为启动地址。
bootm <addr>--使用default配置启动。
bootm <addr>:<image name>--单独加载FIT中<image name>镜像。
bootm <addr>#<config name>--使用指定<config name>启动。

bootm还支持将整个流程拆解为如下步骤:

        start [addr [arg ...]]
        loados  - load OS image
        ramdisk - relocate initrd, set env initrd_start/initrd_end
        fdt     - relocate flat device tree
        cmdline - OS specific command line processing/setup
        bdt     - OS specific bd_t processing
        prep    - OS specific prep before relocation or go
        go      - start OS

3.2 bootm流程解析

下面解析bootm关于FIT的处理流程:

do_bootm
  ->do_bootm_states--根据states调用一系列函数进行处理。
    ->bootm_start
    ->bootm_find_os--加载OS镜像。
      ->boot_get_kernel
        ->fit_image_load--从指定或默认configuration中加载kernel镜像。
      ->genimg_get_format--对于IMAGE_FORMAT_FIT类型镜像,从FIT中获取kernel镜像。
        ->fit_image_get_type--images.os.type。
        ->fit_image_get_comp--images.os.comp。
        ->fit_image_get_os--images.os.os。
        ->fit_image_get_arch--images.os.arch。
        ->fit_image_get_load--images.os.load。
        ->fit_image_get_entry--images.ep。
    ->bootm_find_other--加载OS相关镜像,比如ramdisk和dtb等。
      ->bootm_find_images
        ->boot_get_ramdisk--获取images.rd_start和images.rd_end。
          ->fit_image_load--从FIT镜像中获取ramdisk文件。
        ->boot_get_fdt--获取images.ft_addr和images.ft_len。
          
->boot_get_fdt_fit--从FIT中获取dtb文件。
            ->fit_image_load--根据fdt镜像名获取dtb文件。

    ->bootm_load_os
      ->image_decomp--解压缩kernel镜像。至此kernel镜像已经完成加载。
    ->boot_ramdisk_high--根据initrd_high的值否则分配地址移动initrd内容。自此ramdisk内容就绪。
    ->boot_relocate_fdt--根据fdt_high的值否则分区地址移动fdt内容。至此fdt内容就绪。
    ->bootm_os_get_boot_func--根据os类型从boot_os[]获取启动函数,linux对应do_bootm_linux()。
    ->boot_selected_os
      ->do_bootm_linux
        ->boot_prep_linux
          ->image_setup_linux
            ->boot_fdt_add_mem_rsv_regions
            ->boot_relocate_fdt
            ->image_setup_libfdt--根据需要更新dtb文件内容。
              ->fdt_root
              ->fdt_chosen
              ->arch_fixup_fdt
              ->fdt_initrd--创建或更新linux,initrd-start和linux,initrd-end节点。
        ->boot_jump_linux
          ->kernel_entry(0, machid, r2)--kernel_entry即为kernel镜像的entry属性,machid是从环境变量machid获取,r2为dtb地址。

随后进入Linux kernel启动,dtb从r2获取,initrd已在dtb中指定。kernel启动解析dtb,然后解压initrd内容形成ramfs。

3.3 Linux initrd解析

内核从start_kernel开始执行C代码,对initrd的处理如下:

start_kernel
  ->setup_arch
    ->setup_arch
      ->setup_machine_fdt--参数__atags_pointer即为从r2获取的dtb地址。
        ->early_init_dt_scan_nodes
          ->early_init_dt_scan_chosen
            ->early_init_dt_check_for_initrd--从dtb中获取linux,initrd-start和linux,initrd-end的值。
              ->__early_init_dt_declare_initrd--将initrd的起始和结束地址赋给initrd_start和initrd_end。
  ->arch_call_rest_init
    ->rest_init
      ->kernel_init
        ->kernel_init_freeable
          ->do_basic_setup
            ->do_initcalls
              ->do_initcall_level
                ->populate_rootfs
                  ->unpack_to_rootfs(__initramfs_start, __initramfs_size);--优先解压内部ramfs。如果initrd_start为0或者强制使用initramfs,则使用initramfs。
                  ->unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start)--其次解压从dtb传入的外部initrd。

4 小结

综上整个FIT方案包括如下部分:

  • 编写its文件。
  • 通过mkimage+dtc生成FIT镜像itb文件。
  • uboot启动时通过bootm解析FIT镜像。
  • 跳转到Linux执行镜像;如果是存在ramdisk,则作为initrd在Linux启动过程中解析创建ramfs。

FIT还有一个功能是对镜像内容进行校验,后续会继续分析基于FIT的校验和验签流程。

posted on 2023-12-23 23:59  ArnoldLu  阅读(327)  评论(0编辑  收藏  举报

导航