版权: 凌云物网智科实验室< www.iot-yun.com >
声明: 本文档由凌云物网智科实验室郭工编著!
作者: 郭文学< QQ: 281143292 guowenxue@gmail.com>
版本: 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格式,这种格式需要具备如下能力:
- Image中需要包含多个dtb文件;
- 可以方便的选择使用哪个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
编译升级u-boot之后,我们则可以直接启动该itb文件了。启动过程如下所示:
6 PS:
本文档主要是讲解Linux内核设备树的使用,并不涉及到Device Tree Source的语法和原理,如果有需要的请自行百度、参考Linux内核里的设备树文件学习。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/10080.html