作为uboot到kernel中DTS DTSI DTB等关系

ARM 169浏览


1、设备树(Device Tree)基本概念及作用

在内核源码中,存在大量对板级细节信息描述的代码。这些代码充斥在/arch/arm/plat->xxx和/arch/arm/mach-xxx目录,对内核而言,这些platform设备、resource、I2C_board_info、spi_board_info以及各种硬件的platform_data绝大多数纯属垃圾冗余代码。为了解决这一问题,ARM内核版本3.x之后引入了原先在Power PC等其他体系架构已经使用的Flattened Device Tree.
“A data structure by which bootloaders pass hardware layout to linux in a device-independent manner,simplifying hardware probing.”开源文档中对设备树的描述是,一种描述硬件资源的数据结构,它通过BootLoader将硬件资源传给内核,使得内核和硬件资源描述相对独立。
Device Tree可以描述的信息包括CPU的数量和类别、内存基地址和大小、总线和桥、外设连接、中断控制器和中断使用情况,GPIO控制器和GPIO使用情况、Clock控制器和Clock使用情况。
另外,设备树对于可热插拔的设备不进行具体描述,它只描述用于控制该热插拔设备的控制器。
设备树的主要优势:对于同一SOC的不同主板,只需要更换设备树文件.dtb即可实现不同主板的无差异支持,而无需更换内核文件。
注:要使得3.x之后的内核支持使用设备树,除了内核编译时需要打开相对应的选项外,BootLoader也需要支持将设备树的数据结构传给内核。

2、设备树的组成和使用

设备树包括DTC(device tree compiler),DTS(device tree source和DTB(device tree blob)。其对应关系如图1-1所示
DTS、DTSI由DTC转化成DTB。

2.1 DTS和DTSI(源文件)

.dts文件是一种ASCII文本对Device Tree的描述,放置在内核的/arch/arm/boot/dts目录。一般而言,一个.dts文件对应一个ARM的machine。
由于一个SOC可能有多个不同电路板,而每个电路板拥有一个.dts。这些dts势必会存在许多共同部分,为了减少代码的冗余,设备树讲这些共同部分提炼保存在.dtsi文件中,供不同的dts共同使用。.dtsi的使用方法,类似于C语言的头文件,在dts文件中需要进行include .dtsi文件。当然,dtsi本身也支持include另一个dtsi文件。

2.2 DTC(编译工具)

DTC为编译工具,它可以将.dts文件编译成.dtb文件。DTC的源码位于内核的scripts/dtc目录下,内核选中CONFIG_OF,编译内核的时候,主机可执行程序DTC就会被编译出来。即scripts/dtc/Makefile中

  1. hostprogs-y:=dtc
  2. always:=$(hostprogs-y)
    在内核的arch/arm/boot/dts/Makefile中,若选中某种SOC,则与其对应相关的所有dtb文件都将被编译出来。在Linux下,make dtbs可单独编译dtb.以下截取了TEGRA平台的一部分。
  3. ifeq($(CONFIG_OF),y)
  4. dtb-$(CONFIG_ARCH_TEGRA)+=tegra20-harmony.dtb
  5. tegra30-beaver.dtb
  6. tegra114-dalmore.dtb
  7. tegra124-ardbeg.dtb

2.3 DTB(二进制文件)

DTC编译.dts生成的二进制文件(.dtb),bootloader在引到内核时,会预先读取.dtb到内存,进而由内核解析。

2.4 BootLoader(bootloader支持)

Bootloader需要将设备树在内存中的地址传给内核。在ARM中通过bootm或bootz命令来进行传递。
bootm [kernel_addr] [initrd_address][dtb_address],其中kernel_addr为内核镜像的地址,init.rd为initrd的地址,dtb_address为dtb所在的地址。若initrd_address为空,则用“-”来代替。
DTS的基本语法示例,如图3-1 所示。

它包括一系列节点,以及描述节点的属性。

“/”为root节点。在一个.dts文件中,有且仅有一个root节点;在root节点下有“node1”,“node2”子节点,称root为“node1”和“node2”的parent节点,除了root节点外,每个节点有且仅有一个parent;其中子节点node1下还存在子节点“child-nodel1”和“child-node2”。

注:如果看过内核/arch/arm/boot/dts目录的读者看到这可能有一个疑问。在每个.dsti和.dts中都会存在一个“/”根节点,那么如果在一个设备树文件中include一个.dtsi文件,那么岂不是存在多个“/”根节点了么。其实不然,编译器DTC在对.dts进行编译生成dtb时,会对node进行合并操作,最终生成的dtb只有一个root node。Dtc会进行合并操作这一点从属性上也可以得到验证。这个稍后做讲解。
在节点的{}里面是描述该节点的属性(property),即设备的特性,它的值是多样化的:

  1. 它可以是string,
  2. 它也可以是32 bit unsigned integres
  3. 他也可以是binary data
  4. 他也可能是空
    BootLoader引导内核时,ARM寄存器R2会将.tdb的首地址传给内核,内核根据该地址,接卸.dtb中根节点的compatible属性,将该属性与内核中预先定义machine_desc机构体的dt_compat成员做匹配,得到最匹配的一个machine_desc。

DTB加载解析过程

先从uboot里的do_bootm出发,根据之前描述,DTB在内存中的地址通过bootm命令进行传递。在bootm中,它会根据所传进来的DTB所在内存做一系列操作,为内核解析DTB提供保证。

注:lmb为uboot下的一种内存管理机制,全称为logical memory blocks。用于管理镜像的内存。lmv所记录的内存信息最终会传递给kernel。