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、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备。这些设备用到的内存、IRQ等资源,也被传递给了kernel,kernel会将这些资源绑定给展开的相应的设备。
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文件了。
早期的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做这些无用功的。
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命令里还要指定它们加载到内存中的地址。如下所示:
参考上面的例子我们可以看到,在这里使用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格式,这种格式需要具备如下能力:
是不是这样就感觉跟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";
};
};
};
<< · Back Index ·>>