版本:v1.0.0
1.DeviceTree简介
LinusTorvalds在2011年3月17日的ARMLinux电邮列表声称“thiswholeARMthingisafuckingpainintheass”,引起ARMLinux社区的余震,此后ARM社区进行了一系列的重整修正。在过去的ARMLinux中,arch/arm/plat-xxx和arch/arm/mach-xxx中参杂着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这种板级细节对于内核来讲,不过是垃圾,如板上的platform设备、resource、i2c_board_info、spi_board_info以及各类硬件的platform_data。社区必须改变这些局面,于是PowerPC等其他体系构架下早已使用的FlattenedDeviceTree(FDT)步入ARM社区的视野。DeviceTree是一种描述硬件的数据结构,它起源于OpenFirmware(OF)。在Linux2.6中,ARM构架的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx,采用DeviceTree后,许多硬件的细节可以直接透过它传递给Linux,而不再须要在kernel中进行大量的冗余编码。
DeviceTree由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。所谓属性,虽然就是成对出现的name和value。在DeviceTree中,可描述的信息包括(以前那些信息大多被hardcode到kernel中):
它基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,之后内核可以辨识这棵树,并按照它展开出Linux内核中的platform_device、i2c_client、spi_device等设备。这种设备用到的显存、IRQ等资源,也被传递给了kernel,kernel会将这种资源绑定给展开的相应的设备。
2.DeviceTree编译
DeviceTree文件的格式为dts,包含的头文件格式为dtsi,dts文件是一种人可以读懂的编码格式。并且uboot和linux不能直接辨识,她们只能辨识二补码文件,所以须要把dts文件编译成dtb文件。dtb文件是一种可以被kernel和uboot辨识的二补码文件。把dts编译成dtb文件的工具是dtc。Linux源码目录下scripts/dtc目录包含dtc工具的源码。在Linux的scripts/dtc目录下不仅提供dtc工具外linux移植时需要编译设备树文件吗,也可以自己安装dtc工具,linux下执行:sudoapt-getinstalldevice-tree-compiler安装dtc工具。其中还提供了一个fdtdump的工具,可以反编译dtb文件。dts和dtb文件的转换如图1所示。
dtc工具的使用方式是:dtc–Idts–Odtb–oxxx.dtbxxx.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
在前些年我们玩ARMLinux时大多是使用的这些技巧。但自从Linus高手发怒过后,ARM社区几乎“一夜”之间将arch/arm/mach-xxx或arch/arm/plat-xxx的代码全部废除,并不再支持。这也就是使用像S3C2440这样的开发板,最高Linux内核版本只能到Linux-3.0的诱因。而最新的内核中所有硬件信息都必须通过arch/arm/boot/dts中的DTS(DeviceTreeSource)文件来描述。这样假如S3C2440想要升级到更高版本的Linux话,就必须自己重画S3C2440的DTS文件,其实极少有人乐意为一个停产的CPU做这种无用功的。
4.设备树启动
Linux-3.x以后的内核统一启用DeviceTree机制以后,所有的设备硬件信息描述就会放在arch/arm/boot/dts/路径下的xxx.dts文件中描述。这种dts(DeviceTreeSource)文件并不是C代码,而是具有相应句型格式的源文件。在编译内核时,我们可以使用makedtbs命令编译生成相应开发板的dtb(DeviceTreeBlob)文件。由于这种源文件并不是C程序,所以不是用gcc来编译,而是由其相应的编译工具dtc(DeviceTreeCompiler)来编译。
如下边我对AtmelSAMASAMA55DD4444开发板移植Linux内核的编译过程和结果:
很其实,这儿Linux内核uImage文件中只包含了Linux内核驱动相关的信息,而所有的设备硬件信息都在编译生成的at91-sama5d4_xplained.dtb设备树文件中。这也就意味着u-boot在启动时只有uImage是不够的,而是两个文件都须要。对于这些情况,u-boot在启动时须要这两个文件,同时bootm命令里还要指定它们加载到显存中的地址。如下所示:
5设备树和uImage合并
参考前面的事例我们可以看见,在这儿使用dtb文件会有一个很大的用处如何安装linux,即通过dtb文件将设备的硬件信息和Linux内核分离开了。这样也就意味着我们只须要编译一个Linux内核,之后加载不同的dtb文件,就可以为不同的硬件开发板服务了。比如在前面的事例中,我使用同一个内核uImage,假如我想在Atmel的SAMA5D4Xplained开发板上运行就只须要加载dtb文件at91-sama5d4_xplained.dtb即可;而假如我们想启动开发板SAMA5D3Xplained的话,只须要将DTB文件更新为at91-sama5d3_xplained.dtb即可,而不需更新uImage。这为今后的产品升级换代提供了很大的便利。
但嵌入式是一个软硬件高度订制的产品,我们通常极少使用这些特点。由于在生产时Linux系统内核要提供两个文件(uImage和dtb)并下载烧写,变得有点冗长,这时我们更多地是希望将dtb和uImage打包到一个image中烧写启动。这时侯可以分别通过Linux内核和u-boot来实现:
5.1Linux内核appendDTB
之所以Linux内核会提供这些方法是由于好多厂家都有自己的bootloader,然而这种bootloader并不都一定支持设备树,为了实现支持设备树启动,就引入了这些启动方法,正式编译出的zImage和编译出的设备树镜像文件拼成一个新的镜像,在内核的自解压代码中会辨识到,不会出现自解压时造成设备树被覆盖。2016年在本人上海消安做的一个LoRa物联网网段产品使用的Atmel的处理器AT91SAM9X35+Linux-4.1内核,在该内核代码中就是通过内核里支持的功能来合并uImage和dtb文件的。具体的实现方法是:
首先在内核makemenuconfig的“Bootoptions--->”选项里要选择:
在编译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/zImageguowenxue@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.binguowenxue@ubuntu-master: ~/linux-at91-linux4sam_5.3$ chmod a+x linuxrom-sam9x35ek.bin
这样,在u-boot里直接下载生成的uImage文件启动即可。
U-Boot>tftp22000000linuxrom-sam9x35ek.bin&&bootm22000000
5.2u-bootFITimage合并
近来接的法国CoherentPlus的一个NFC支付优盘项目,选用Atmel的CortexA5处理器SAMASAMA55DD4444,所使用的是Linux-4.9和U-Boot2014.07。同样尝试里面SAMSAM99XX3535的套路打包uImage和dtb文件并启动Linux内核时失败,U-boot启动时提示如下错误。虽然如今早已是9102年了,在这儿没有太延庆趣研究这些老的打包形式,而转向u-boot的全兴工作方法FITImage。
我们晓得,Linuxkernel在ARM构架中引入devicetree(全称是FlattenedDeviceTree,后续将会以FDT代称)的时侯,虽然怀揣了一个UnifyKernel的梦想----同一个Image,可以支持多个不同的平台。随着新的ARM64构架将FDT列为必选项,并将和体系结构有关的代码剥离以后,这个梦想早已接近实现。DeviceTree在ARM构架中普及以后,u-boot也马上跟进、大力支持,虽然,美好的Unifykernel的理想,须要bootloader的成全。为了支持基于devicetree的unifykernel,u-boot须要一种新的Image格式,这些格式须要具备如下能力:
Image中须要包含多个dtb文件;可以便捷的选择使用那个dtb文件bootkernel;
是不是这样就觉得跟Linux内核一样Niubility了?没错!要的就是这些觉得。综合前面的需求,u-boot推出了全新的image格式----FITuImagelinux学习视频,其中FIT是flattenedimagetree的简称。它借助了DeviceTreeSourcefiles(DTS)的句型,生成的image文件也和dtb文件类似(也称itb),下边是我们项目中的示例代码。:
guowenxue@ubuntu-master:~/sama5d4-sdk/linux-bsp/linux-at91$catlinuxrom-sama5d4.its
/* U-Boot uImage source file for "sama5d4_xplained" *//dts-v1/;/ { description = "U-Boot uImage source file for SAMA5D4 Xplained"; #address-cells = ; images { kernel@sama5d4 { description = "Linux kernel for SAMA5D4 Xplained"; data = /incbin/("arch/arm/boot/zImage"); type = "kernel"; arch = "arm"; os = "linux"; compression = "none"; load = ; entry = ; }; 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文件句型一样linux移植时需要编译设备树文件吗,上面的一些参数就是mkimage制做uImage时的一些参数。在编译生成Linux内核zImage和dtb文件以后,我们只须要使用mkimage命令就可以生成相应的itb文件了。其实,里面的文件遵守dts句型,那他的编译就须要dtc编译器,默认ubuntu并没有安装该命令,所以在使用之前还得安装相应的命令,好在ubuntu下都提供了,若果没有可以在u-boot或linux内核下去找:
guowenxue@ubuntu-master:~/sama5d4-sdk/linux-bsp/linux-at91$sudoapt-getinstallu-boot-toolsdevice-tree-compiler
guowenxue@ubuntu-master:~/sama5d4-sdk/linux-bsp/linux-at91$mkimage-flinuxrom-sama5d4.itslinuxrom-sama5d4.itb
FIT description: U-Boot uImage source file for SAMA5D4 XplainedCreated: Fri Aug 23 21:43:12 2019Image 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: 0x20008000Image 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: ARMDefault Configuration: 'conf@sama5d4'Configuration 0 (conf@sama5d4) Description: Boot Linux kernel with FDT blob Kernel: kernel@sama5d4 FDT: fdt@sama5d4
既然是全新的东西,u-boot默认并不一定支持。假如要U-boot支持FITImage启动的话,我们还得在u-boot的配置文件中添加它的支持,即加上CONFIG_FIT宏定义即可:
guowenxue@ubuntu-master:~/sama5d4-sdk/linux-bsp/u-boot-at91$viminclude/configs/sama5d4_xplained.h
/* add by guowenxue, 2019.08.22 */#define CONFIG_FIT 1
编译升级u-boot以后,我们则可以直接启动该itb文件了。启动过程如下所示:
6PS:
本文档主要是讲解Linux内核设备树的使用,并不涉及到DeviceTreeSource的句型和原理,假若有须要的请自行百度、参考Linux内核里的设备树文件学习。