Linux内核DTB文件启动的几种方式

 
版权:  凌云物网智科实验室< www.iot-yun.com >
声明:  本文档由凌云物网智科实验室郭工编著!
作者:  郭文学< QQ: 281143292   [email protected]
版本:  v1.0.0
 
1. Device Tree简介
 
  Linus Torvalds在2011年3月17日的ARM Linux邮件列表宣称“this whole ARM thing is a fucking pain in the ass”,引发ARM Linux社区的地震,随后ARM社区进行了一系列的重大修正。在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data。 社区必须改变这种局面,于是PowerPC等其他体系架构下已经使用的Flattened Device Tree(FDT)进入ARM社区的视野。Device Tree是一种描述硬件的数据结构,它起源于OpenFirmware(OF)。在Linux2.6中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx,采用Device Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。
 
Device Tree由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。所谓属性,其实就是成对出现的name和value。在Device Tree中,可描述的信息包括(原先这些信息大多被hard code到kernel中):
 
  • CPU的数量和类别
  • 内存基地址和大小
  • 总线和桥
  • 外设连接
  • 中断控制器和中断使用情况
  • GPIO控制器和GPIO使用情况
 
它基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备。这些设备用到的内存、IRQ等资源,也被传递给了kernel,kernel会将这些资源绑定给展开的相应的设备。
 
 
2. Device Tree编译
 
Device Tree文件的格式为dts,包含的头文件格式为dtsi,dts文件是一种人可以看懂的编码格式。但是uboot和linux不能直接识别,他们只能识别二进制文件,所以需要把dts文件编译成dtb文件。dtb文件是一种可以被kernel和uboot识别的二进制文件。把dts编译成dtb文件的工具是dtc。Linux源码目录下scripts/dtc目录包含dtc工具的源码。在Linux的scripts/dtc目录下除了提供dtc工具外,也可以自己安装dtc工具,linux下执行:sudo apt-get install device-tree-compiler安装dtc工具。其中还提供了一个fdtdump的工具,可以反编译dtb文件。dts和dtb文件的转换如图1所示。
dtc工具的使用方法是:dtc –I dts –O dtb –o xxx.dtb xxx.dts,即可生成dts文件对应的dtb文件了。
 
 
 
3.  早期Linux内核启动
 
   早期的Linux内核(Linux-3.0以前)里的设备信息(platform_device)和驱动信息(platform_driver)都是通过C代码硬写入到Linux内核里去了,这些源文件都在arch/arm/mach-xxx或plat-xxx下:
 
 
例如我们移植Linux内核代码到FL2440开发板时,就会在设备文件 arch/arm/mach-s3c2440/mach-smdk2440.c中作大量修改的,该文件就描述了开发板上所有的设备信息。
 
 
我们在编译Linux内核源码之后会生成zImage文件,该文件并不能直接被u-boot启动。之后需要使用u-boot里的mkimage工具生成uImage。
 
 
在将zImage转换成uImage文件后,我们在u-boot下就可以直接使用tftp 下载并通过bootm 命令启动Linux内核了。
 
U-Boot> tftp 30008000 linuxrom-s3c2440.bin && bootm 30008000
 
   在前些年我们玩ARM Linux时大多是使用的这种方法。但自从Linus大神发飙之后, ARM社区几乎“一夜”之间将 arch/arm/mach-xxx 或 arch/arm/plat-xxx的代码全部废除,并不再支持。这也就是使用像S3C2440这样的开发板,最高Linux内核版本只能到Linux-3.0的原因。而最新的内核中所有硬件信息都必须通过arch/arm/boot/dts中的DTS(Device Tree Source)文件来描述。这样如果S3C2440想要升级到更高版本的Linux话,就必须自己重写S3C2440的DTS文件,当然很少有人愿意为一个停产的CPU做这些无用功的。
 
 
4.  设备树启动
 
   Linux-3.x之后的内核统一启用Device Tree机制之后,所有的设备硬件信息描述都会放到 arch/arm/boot/dts/ 路径下的 xxx.dts文件中描述。这些dts(Device Tree Source)文件并不是C代码,而是具有相应语法格式的源文件。在编译内核时,我们可以使用 make dtbs 命令编译生成相应开发板的dtb(Device Tree Blob)文件。因为这些源文件并不是C程序,所以不是用gcc来编译,而是由其相应的编译工具dtc(Device Tree Compiler)来编译。
 
 
  如下面我对Atmel SAMA5D44开发板移植Linux内核的编译过程和结果:
 
 
很显然,这里Linux内核uImage文件中只包含了Linux内核驱动相关的信息,而所有的设备硬件信息都在编译生成的at91-sama5d4_xplained.dtb设备树文件中。这也就意味着u-boot在启动时只有uImage是不够的,而是两个文件都需要。对于这种情况,u-boot在启动时需要这两个文件,同时bootm命令里还要指定它们加载到内存中的地址。如下所示:
 
 
 
5  设备树和uImage合并
 
  参考上面的例子我们可以看到,在这里使用dtb文件会有一个很大的好处,即通过dtb文件将设备的硬件信息和Linux内核分离开了。这样也就意味着我们只需要编译一个Linux内核,然后加载不同的dtb文件,就可以为不同的硬件开发板服务了。譬如在上面的例子中,我使用同一个内核uImage,如果我想在Atmel的SAMA5D4 Xplained开发板上运行就只需要加载dtb文件at91-sama5d4_xplained.dtb即可; 而如果我们想启动开发板SAMA5D3 Xplained的话,只需要将DTB文件更新为at91-sama5d3_xplained.dtb即可,而不需更新uImage。这为今后的产品升级换代提供了很大的便利。
 
  但嵌入式是一个软硬件高度定制的产品,我们一般很少使用这种特性。因为在生产时Linux系统内核要提供两个文件(uImage和dtb)并下载烧录,显得有点繁琐,这时我们更多地是希望将dtb和uImage打包到一个image中烧录启动。这时候可以分别通过Linux内核和u-boot来实现:
 
5.1  Linux内核append DTB
 
   之所以Linux内核会提供这种方式是因为很多厂家都有自己的bootloader,但是这些bootloader并不都一定支持设备树,为了实现支持设备树启动,就引入了这种启动方式,即将编译出的zImage和编译出的设备树镜像文件拼成一个新的镜像,在内核的自解压代码中会识别到,不会出现自解压时导致设备树被覆盖。2016年在本人深圳消安做的一个LoRa物联网网关产品使用的Atmel的处理器AT91SAM9X35+Linux-4.1内核,在该内核代码中就是通过内核里支持的功能来合并uImage和dtb文件的。具体的实现方式是:
 
首先在内核 make menuconfig的“ Boot options  --->”选项里要选择:
 
 
在编译Linux内核生成uImage和dtb文件之后,使用cat命令将他们合并,然后再使用mkimage命令生成u-boot启动相关的uImage文件:
 
guowenxue@ubuntu-master: ~/linux-at91-linux4sam_5.3$  cat arch/arm/boot/dts/at91sam9x35ek.dtb >> arch/arm/boot/zImage
guowenxue@ubuntu-master: ~/linux-at91-linux4sam_5.3$  mkimage -A arm -O linux -n AT91SAM9X35EK -C NONE -a 0x20008000 -e 0x20008000 -d arch/arm/boot/zImage linuxrom-sam9x35ek.bin
guowenxue@ubuntu-master: ~/linux-at91-linux4sam_5.3$  chmod a+x linuxrom-sam9x35ek.bin
            
这样,在u-boot里直接下载生成的uImage文件启动即可。
 
U-Boot> tftp 22000000 linuxrom-sam9x35ek.bin && bootm 22000000
          
 
5.2  u-boot FIT image合并
 
  最近接的马来西亚CoherentPlus的一个NFC支付读卡器项目,选用Atmel的Cortex A5处理器SAMA5D44,所使用的是Linux-4.9和U-Boot 2014.07。同样尝试上面SAM9X35的套路打包uImage和dtb文件并启动Linux内核时失败,U-boot启动时提示如下错误。毕竟现在已经是9102年了,在这里没有太大兴趣研究这种老的打包方式,而转向u-boot的全兴工作方式FIT Image。
 
 
  我们知道,Linux kernel在ARM架构中引入device tree(全称是Flattened Device Tree,后续将会以FDT代称)的时候,其实怀揣了一个Unify Kernel的梦想----同一个Image,可以支持多个不同的平台。随着新的ARM64架构将FDT列为必选项,并将和体系结构有关的代码剥离之后,这个梦想已经接近实现。Device Tree在ARM架构中普及之后,u-boot也马上跟进、大力支持,毕竟,美好的Unify kernel的理想,需要bootloader的成全。为了支持基于device tree的unify kernel,u-boot需要一种新的Image格式,这种格式需要具备如下能力:
  1. Image中需要包含多个dtb文件;
  2. 可以方便的选择使用哪个dtb文件boot kernel;
 
  是不是这样就感觉跟Linux内核一样Niubility了?没错!要的就是这种感觉。综合上面的需求,u-boot推出了全新的image格式----FIT uImage,其中FIT是flattened image tree的简称。它利用了Device Tree Source files(DTS)的语法,生成的image文件也和dtb文件类似(称作itb),下面是我们项目中的示例代码。:
 
guowenxue@ubuntu-master:~/sama5d4-sdk/linux-bsp/linux-at91$ cat linuxrom-sama5d4.its
 
/* U-Boot uImage source file for "sama5d4_xplained" */
/dts-v1/;
/ {
    description = "U-Boot uImage source file for SAMA5D4 Xplained";
    #address-cells = <1>;

    images {

        kernel@sama5d4 {
            description = "Linux kernel for SAMA5D4 Xplained";
            data = /incbin/("arch/arm/boot/zImage");
            type = "kernel";
            arch = "arm";
            os = "linux";
            compression = "none";
            load = <0x20008000>;
            entry = <0x20008000>;
        };

        fdt@sama5d4 {
            description = "Flattened Device Tree blob for SAMA5D4 Xplained";
            data = /incbin/("arch/arm/boot/dts/at91-sama5d4_xplained.dtb");
            type = "flat_dt";
            arch = "arm";
            compression = "none";
        };
    };

    configurations {
        default = "conf@sama5d4";
        conf@sama5d4 {
            description = "Boot Linux kernel with FDT blob";
            kernel = "kernel@sama5d4";
            fdt = "fdt@sama5d4";
        };
    };
};
 上面的代码是不是很眼熟?没错,就是跟Linux内核树里的DTS文件语法一样,里面的一些参数就是mkimage制作uImage时的一些参数。在编译生成Linux内核zImage和dtb文件之后,我们只需要使用mkimage命令就可以生成相应的itb文件了。当然,上面的文件遵循dts语法,那他的编译就需要dtc编译器,默认ubuntu并没有安装该命令,所以在使用之前还得安装相应的命令,好在ubuntu下都提供了,如果没有可以在u-boot或linux内核下去找:
 
guowenxue@ubuntu-master:~/sama5d4-sdk/linux-bsp/linux-at91$ sudo apt-get install u-boot-tools device-tree-compiler
guowenxue@ubuntu-master:~/sama5d4-sdk/linux-bsp/linux-at91$ mkimage -f linuxrom-sama5d4.its linuxrom-sama5d4.itb
 
FIT description: U-Boot uImage source file for SAMA5D4 Xplained
Created:         Fri Aug 23 21:43:12 2019

Image 0 (kernel@sama5d4)
  Description:  Linux kernel for SAMA5D4 Xplained
  Created:      Fri Aug 23 21:43:12 2019
  Type:         Kernel Image
  Compression:  uncompressed
  Data Size:    4879744 Bytes = 4765.38 kB = 4.65 MB
  Architecture: ARM
  OS:           Linux
  Load Address: 0x20008000
  Entry Point:  0x20008000

Image 1 (fdt@sama5d4)
  Description:  Flattened Device Tree blob for SAMA5D4 Xplained
  Created:      Fri Aug 23 21:43:12 2019
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    32670 Bytes = 31.90 kB = 0.03 MB
  Architecture: ARM

Default Configuration: 'conf@sama5d4'
Configuration 0 (conf@sama5d4)
  Description:  Boot Linux kernel with FDT blob
  Kernel:       kernel@sama5d4
  FDT:          fdt@sama5d4
 
既然是全新的东西,u-boot默认并不一定支持。如果要U-boot支持FIT Image启动的话,我们还得在u-boot的配置文件中添加它的支持,即加上 CONFIG_FIT 宏定义即可:
 
guowenxue@ubuntu-master:~/sama5d4-sdk/linux-bsp/u-boot-at91$ vim include/configs/sama5d4_xplained.h
 
/* add by guowenxue, 2019.08.22 */
#define CONFIG_FIT              1
#define CONFIG_ENV_OVERWRITE    1
#define CONFIG_ETHADDR          42:96:ab:be:a7:5e
#define CONFIG_IPADDR           192.168.2.199
#define CONFIG_SERVERIP         192.168.2.2
 
编译升级u-boot之后,我们则可以直接启动该itb文件了。启动过程如下所示:
 
 
 
 
6  PS:
 
   本文档主要是讲解Linux内核设备树的使用,并不涉及到Device Tree Source的语法和原理,如果有需要的请自行百度、参考Linux内核里的设备树文件学习。
 
 
 

猜你喜欢

转载自www.cnblogs.com/iot-yun/p/11403498.html
今日推荐