一、什么是设备树(Device Tree)
它是一种描述硬件资源的数据结构,可以通过 bootloader 将它传给内核,内核(Kernal)使用它对硬件进行初始化,好处是使得内核和硬件资源描述相对独立,不需要太多的硬编码。
二、设备树的相关名词
DTS(Device Tree Source):.dts文件是一种ASCII文本对Device Tree的描述,位于linux-x.xx/arch/arm64/boot/dts目录下。
DTC(Device Tree Compiler):DTC为编译工具,它可以将.dts文件编译成.dtb 文件,DTC 的源码位于 linux-x.xx/scripts/dtc目录下。
DTB(Device Tree Blob):DTC编译.dts 生成的二进制文件(.dtb)(实际上是字节码),bootloader 在加载内核时,也会同时把.dtb加载到内存,后面传递给内核使用。
三、DTS 文件格式
/*
* Device Tree Source for OMAP2 SoC
*
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
/include/ "skeleton.dtsi" /*需要引用的dts*/
/ {
compatible = "ti,omap2430", "ti,omap2420", "ti,omap2";
aliases {
serial0 = &uart1;
serial1 = &uart2;
serial2 = &uart3;
};
cpus {
cpu@0 {
compatible = "arm,arm1136jf-s";
};
};
soc {
compatible = "ti,omap-infra";
mpu {
compatible = "ti,omap2-mpu";
ti,hwmods = "mpu";
};
};
ocp {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges;
ti,hwmods = "l3_main";
intc: interrupt-controller@1 {
compatible = "ti,omap2-intc";
interrupt-controller;
#interrupt-cells = <1>;
};
uart1: serial@4806a000 {
compatible = "ti,omap2-uart";
ti,hwmods = "uart1";
clock-frequency = <48000000>;
};
uart2: serial@4806c000 {
compatible = "ti,omap2-uart";
ti,hwmods = "uart2";
clock-frequency = <48000000>;
};
uart3: serial@4806e000 {
compatible = "ti,omap2-uart";
ti,hwmods = "uart3";
clock-frequency = <48000000>;
};
};
};
“/“ 为 root 节点,在一个. dts 文件中,有且仅有一个 root 节点,/include/ "skeleton.dtsi"
和C代码中的 include 头文件的作用差不多,也就是把/include/ "skeleton.dtsi"
定义的 device tree 节点包含到 omap2.dtsi 中,虽然omap2.dtsi文件中也会有一个 “/“,但是 dtc 编译时,会把它们合并成一个。
/*
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/dts-v1/; /*dts的版本*/
/include/ "omap4.dtsi" /*需要引用的dts*/
/ {
model = "TI OMAP4 PandaBoard";
compatible = "ti,omap4-panda", "ti,omap4430", "ti,omap4";
memory {
device_type = "memory";
reg = <0x80000000 0x40000000>; /* 1 GB */
};
};
1)aliases node
aliases {
serial0 = &uart0;
serial1 = &uart1;
serial2 = &uart2;
};
aliases 节点定义了一些别名。为何要定义这个 node 呢?因为 Device tree 是树状结构,当要引用一个 node 的时候要指明相对于 root node 的 full path。例如
uart1: serial@4806a000 {
compatible = "ti,omap2-uart";
ti,hwmods = "uart1";
clock-frequency = <48000000>;
};
serial1 = &uart1 所以 serial1就是 serial@4806a000 的一个别名,uart1是一个 lable,也是 serial@4806a000,使用 lable 需要在前面加上 & 。例如
&uart1 {
status = "okay";
};
就是把serial@4806a000节点里面的 status 属性改成 okay
2)memory node
memory {
device_type = "memory";
reg = <0x80000000 0x40000000>; /* 1 GB */
};
对于 memory node,device_type 必须为 memory,memory device node 是所有设备树文件的必备节点,它定义了系统物理内存的 layout。
reg 描述了 memory-mapped IO register 的 offset 和 length。对于 memory node,定义了该 memory 的起始地址和长度,这里的0x80000000
是起始地址,0x40000000
是内存的大小(长度)。
#address-cells = <1>;
#size-cells = <1>;
为什么是 0 0x40000000 表示起始地址,因为 root 节点 #address-cells = <2>; 表示用两个 cell (32 位),同样的 #size-cells = <2> 表示用两个 cell (32 位)。
每个 node 用节点名字(node name)标识,节点名字的格式是 node-name@unit-address。如果该 node 没有 reg 属性,那么该节点名字中必须不能包括 @和 unit-address。unit-address 的具体格式是和设备挂在那个 bus 上相关。
所以上面 memory 的描述是,起始地址是 0x 80000000
3)chosen node
34 chosen {
35 stdout-path = "serial0:921600n8";
36 };
chosen node 主要用来描述由系统指定的 runtime parameter,它并没有描述任何硬件设备节点信息。原先通过 tag list 传递的一些 linux kernel 运行的参数,可以通过 chosen 节点来传递。如 command line 可以通过 bootargs 这个 property 来传递。如果存在 chosen node,它的 parent 节点必须为 “/” 根节点。
四、DTB文件格式
1、dtb文件结构
2、fdt_header
struct fdt_header {
uint32_t magic; /* magic word FDT_MAGIC */
uint32_t totalsize; /* total size of DT block */
uint32_t off_dt_struct; /* offset to structure */
uint32_t off_dt_strings; /* offset to strings */
uint32_t off_mem_rsvmap; /* offset to memory reserve map */
uint32_t version; /* format version */
uint32_t last_comp_version; /* last compatible version */
/* version 2 fields below */
uint32_t boot_cpuid_phys; /* Which physical CPU id we're
booting on */
/* version 3 fields below */
uint32_t size_dt_strings; /* size of the strings block */
/* version 17 fields below */
uint32_t size_dt_struct; /* size of the structure block */
};
off_dt_struct 是到dt_struct 结构块的偏移量(相对于文件起始位置),off_dt_strings 是到dt_strings字符串块的偏移量(相对于文件起始位置),off_mem_rsvmap 是到memory reserve map 区域的偏移量(相对于文件起始位置)。头部一共40字节,即off_mem_rsvmap 的值为40。
3、memory reserve map
该区域保存的数据会4字节对齐
4、dt_struct
结构块里面保存了dts 里面描述的设备信息,和dts 里面写的内容一致,只不过转成了另一种数据格式。节点的开始和结束,属性的开始用下面定义的标识。
#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
#define FDT_TAGSIZE sizeof(uint32_t)
#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
#define FDT_END_NODE 0x2 /* End node */
#define FDT_PROP 0x3 /* Property: content_size, name off(in_dt_string), content */
#define FDT_NOP 0x4 /* nop */
#define FDT_END 0x9
5、off_dt_strings
字符串块 保存的是dts中属性的名字,因为在不同的节点中会用到相同的属性名,为了减少保存重复的属性名字符串,所以把它们放在字符串块中,每个字符串是以\0为结束标识。
6、dtb文件生成与查看
使用dtc工具将dts转为dtb,命令如下:
mate@Vostro:~/Documents/.../arch/arm/boot/dts$ dtc -I dts omap4-panda.dts -O dtb -o omap4-panda.dtb
#或
mate@Vostro:~/Documents/.../arch/arm/boot/dts$ dtc -I dts -O dtb omap4-panda.dts -o omap4-panda.dtb
使用二进制文件查看工具hexdump查看编译后的dtb文件,如下:
mate@Vostro:~/Documents/linux-A3352W-4G-SPI-CAN-GPIO-IIC_20210622/linux-A3352W-4G-SPI-CAN-GPIO-IIC/arch/arm/boot/dts$ hexdump -C omap4-panda.dtb
00000000 d0 0d fe ed 00 00 06 3c 00 00 00 48 00 00 05 80 |.......<...H....|
00000010 00 00 00 28 00 00 00 11 00 00 00 10 00 00 00 00 |...(............|
00000020 00 00 00 bc 00 00 05 38 00 00 00 00 9d 00 00 00 |.......8........|
00000030 00 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 |................|
00000050 00 00 00 03 00 00 00 04 00 00 00 00 00 00 00 01 |................|
00000060 00 00 00 03 00 00 00 04 00 00 00 0f 00 00 00 01 |................|
00000070 00 00 00 03 00 00 00 24 00 00 00 1b 74 69 2c 6f |.......$....ti,o|
00000080 6d 61 70 34 2d 70 61 6e 64 61 00 74 69 2c 6f 6d |map4-panda.ti,om|
00000090 61 70 34 34 33 30 00 74 69 2c 6f 6d 61 70 34 00 |ap4430.ti,omap4.|
000000a0 00 00 00 03 00 00 00 04 00 00 00 26 00 00 00 01 |...........&....|
000000b0 00 00 00 03 00 00 00 14 00 00 00 37 54 49 20 4f |...........7TI O|
000000c0 4d 41 50 34 20 50 61 6e 64 61 42 6f 61 72 64 00 |MAP4 PandaBoard.|
000000d0 00 00 00 01 63 68 6f 73 65 6e 00 00 00 00 00 02 |....chosen......|
000000e0 00 00 00 01 61 6c 69 61 73 65 73 00 00 00 00 03 |....aliases.....|
000000f0 00 00 00 17 00 00 00 3d 2f 6f 63 70 2f 73 65 72 |.......=/ocp/ser|
00000100 69 61 6c 40 30 78 34 38 30 36 61 30 30 30 00 00 |ial@0x4806a000..|
00000110 00 00 00 03 00 00 00 17 00 00 00 45 2f 6f 63 70 |...........E/ocp|
00000120 2f 73 65 72 69 61 6c 40 30 78 34 38 30 36 63 30 |/serial@0x4806c0|
00000130 30 30 00 00 00 00 00 03 00 00 00 17 00 00 00 4d |00.............M|
00000140 2f 6f 63 70 2f 73 65 72 69 61 6c 40 30 78 34 38 |/ocp/serial@0x48|
00000150 30 32 30 30 30 30 00 00 00 00 00 03 00 00 00 17 |020000..........|
00000160 00 00 00 55 2f 6f 63 70 2f 73 65 72 69 61 6c 40 |...U/ocp/serial@|
00000170 30 78 34 38 30 36 65 30 30 30 00 00 00 00 00 02 |0x4806e000......|
00000180 00 00 00 01 6d 65 6d 6f 72 79 00 00 00 00 00 03 |....memory......|
00000190 00 00 00 07 00 00 00 5d 6d 65 6d 6f 72 79 00 00 |.......]memory..|
000001a0 00 00 00 03 00 00 00 08 00 00 00 69 80 00 00 00 |...........i....|
000001b0 40 00 00 00 00 00 00 02 00 00 00 01 63 70 75 73 |@...........cpus|
000001c0 00 00 00 00 00 00 00 01 63 70 75 40 30 00 00 00 |........cpu@0...|
000001d0 00 00 00 03 00 00 00 0e 00 00 00 1b 61 72 6d 2c |............arm,|
000001e0 63 6f 72 74 65 78 2d 61 39 00 00 00 00 00 00 02 |cortex-a9.......|
000001f0 00 00 00 01 63 70 75 40 31 00 00 00 00 00 00 03 |....cpu@1.......|
00000200 00 00 00 0e 00 00 00 1b 61 72 6d 2c 63 6f 72 74 |........arm,cort|
00000210 65 78 2d 61 39 00 00 00 00 00 00 02 00 00 00 02 |ex-a9...........|
00000220 00 00 00 01 73 6f 63 00 00 00 00 03 00 00 00 0e |....soc.........|
00000230 00 00 00 1b 74 69 2c 6f 6d 61 70 2d 69 6e 66 72 |....ti,omap-infr|
00000240 61 00 00 00 00 00 00 01 6d 70 75 00 00 00 00 03 |a.......mpu.....|
00000250 00 00 00 0d 00 00 00 1b 74 69 2c 6f 6d 61 70 34 |........ti,omap4|
00000260 2d 6d 70 75 00 00 00 00 00 00 00 03 00 00 00 04 |-mpu............|
00000270 00 00 00 6d 6d 70 75 00 00 00 00 02 00 00 00 01 |...mmpu.........|
00000280 64 73 70 00 00 00 00 03 00 00 00 0d 00 00 00 1b |dsp.............|
00000290 74 69 2c 6f 6d 61 70 33 2d 63 36 34 00 00 00 00 |ti,omap3-c64....|
000002a0 00 00 00 03 00 00 00 04 00 00 00 6d 64 73 70 00 |...........mdsp.|
000002b0 00 00 00 02 00 00 00 01 69 76 61 00 00 00 00 03 |........iva.....|
000002c0 00 00 00 09 00 00 00 1b 74 69 2c 69 76 61 68 64 |........ti,ivahd|
000002d0 00 00 00 00 00 00 00 03 00 00 00 04 00 00 00 6d |...............m|
000002e0 69 76 61 00 00 00 00 02 00 00 00 02 00 00 00 01 |iva.............|
000002f0 6f 63 70 00 00 00 00 03 00 00 00 1b 00 00 00 1b |ocp.............|
00000300 74 69 2c 6f 6d 61 70 34 2d 6c 33 2d 6e 6f 63 00 |ti,omap4-l3-noc.|
00000310 73 69 6d 70 6c 65 2d 62 75 73 00 00 00 00 00 03 |simple-bus......|
00000320 00 00 00 04 00 00 00 00 00 00 00 01 00 00 00 03 |................|
00000330 00 00 00 04 00 00 00 0f 00 00 00 01 00 00 00 03 |................|
00000340 00 00 00 00 00 00 00 77 00 00 00 03 00 00 00 1e |.......w........|
00000350 00 00 00 6d 6c 33 5f 6d 61 69 6e 5f 31 00 6c 33 |...ml3_main_1.l3|
00000360 5f 6d 61 69 6e 5f 32 00 6c 33 5f 6d 61 69 6e 5f |_main_2.l3_main_|
00000370 33 00 00 00 00 00 00 01 69 6e 74 65 72 72 75 70 |3.......interrup|
00000380 74 2d 63 6f 6e 74 72 6f 6c 6c 65 72 40 34 38 32 |t-controller@482|
00000390 34 31 30 30 30 00 00 00 00 00 00 03 00 00 00 12 |41000...........|
000003a0 00 00 00 1b 61 72 6d 2c 63 6f 72 74 65 78 2d 61 |....arm,cortex-a|
000003b0 39 2d 67 69 63 00 00 00 00 00 00 03 00 00 00 00 |9-gic...........|
000003c0 00 00 00 7e 00 00 00 03 00 00 00 04 00 00 00 93 |...~............|
000003d0 00 00 00 01 00 00 00 03 00 00 00 10 00 00 00 69 |...............i|
000003e0 48 24 10 00 00 00 10 00 48 24 01 00 00 00 01 00 |H$......H$......|
000003f0 00 00 00 03 00 00 00 04 00 00 00 a4 00 00 00 01 |................|
00000400 00 00 00 02 00 00 00 01 73 65 72 69 61 6c 40 30 |........serial@0|
00000410 78 34 38 30 36 61 30 30 30 00 00 00 00 00 00 03 |x4806a000.......|
00000420 00 00 00 0e 00 00 00 1b 74 69 2c 6f 6d 61 70 34 |........ti,omap4|
00000430 2d 75 61 72 74 00 00 00 00 00 00 03 00 00 00 06 |-uart...........|
00000440 00 00 00 6d 75 61 72 74 31 00 00 00 00 00 00 03 |...muart1.......|
00000450 00 00 00 04 00 00 00 ac 02 dc 6c 00 00 00 00 02 |..........l.....|
00000460 00 00 00 01 73 65 72 69 61 6c 40 30 78 34 38 30 |....serial@0x480|
00000470 36 63 30 30 30 00 00 00 00 00 00 03 00 00 00 0e |6c000...........|
00000480 00 00 00 1b 74 69 2c 6f 6d 61 70 34 2d 75 61 72 |....ti,omap4-uar|
00000490 74 00 00 00 00 00 00 03 00 00 00 06 00 00 00 6d |t..............m|
000004a0 75 61 72 74 32 00 00 00 00 00 00 03 00 00 00 04 |uart2...........|
000004b0 00 00 00 ac 02 dc 6c 00 00 00 00 02 00 00 00 01 |......l.........|
000004c0 73 65 72 69 61 6c 40 30 78 34 38 30 32 30 30 30 |serial@0x4802000|
000004d0 30 00 00 00 00 00 00 03 00 00 00 0e 00 00 00 1b |0...............|
000004e0 74 69 2c 6f 6d 61 70 34 2d 75 61 72 74 00 00 00 |ti,omap4-uart...|
000004f0 00 00 00 03 00 00 00 06 00 00 00 6d 75 61 72 74 |...........muart|
00000500 33 00 00 00 00 00 00 03 00 00 00 04 00 00 00 ac |3...............|
00000510 02 dc 6c 00 00 00 00 02 00 00 00 01 73 65 72 69 |..l.........seri|
00000520 61 6c 40 30 78 34 38 30 36 65 30 30 30 00 00 00 |al@0x4806e000...|
00000530 00 00 00 03 00 00 00 0e 00 00 00 1b 74 69 2c 6f |............ti,o|
00000540 6d 61 70 34 2d 75 61 72 74 00 00 00 00 00 00 03 |map4-uart.......|
00000550 00 00 00 06 00 00 00 6d 75 61 72 74 34 00 00 00 |.......muart4...|
00000560 00 00 00 03 00 00 00 04 00 00 00 ac 02 dc 6c 00 |..............l.|
00000570 00 00 00 02 00 00 00 02 00 00 00 02 00 00 00 09 |................|
00000580 23 61 64 64 72 65 73 73 2d 63 65 6c 6c 73 00 23 |#address-cells.#|
00000590 73 69 7a 65 2d 63 65 6c 6c 73 00 63 6f 6d 70 61 |size-cells.compa|
000005a0 74 69 62 6c 65 00 69 6e 74 65 72 72 75 70 74 2d |tible.interrupt-|
000005b0 70 61 72 65 6e 74 00 6d 6f 64 65 6c 00 73 65 72 |parent.model.ser|
000005c0 69 61 6c 30 00 73 65 72 69 61 6c 31 00 73 65 72 |ial0.serial1.ser|
000005d0 69 61 6c 32 00 73 65 72 69 61 6c 33 00 64 65 76 |ial2.serial3.dev|
000005e0 69 63 65 5f 74 79 70 65 00 72 65 67 00 74 69 2c |ice_type.reg.ti,|
000005f0 68 77 6d 6f 64 73 00 72 61 6e 67 65 73 00 69 6e |hwmods.ranges.in|
00000600 74 65 72 72 75 70 74 2d 63 6f 6e 74 72 6f 6c 6c |terrupt-controll|
00000610 65 72 00 23 69 6e 74 65 72 72 75 70 74 2d 63 65 |er.#interrupt-ce|
00000620 6c 6c 73 00 70 68 61 6e 64 6c 65 00 63 6c 6f 63 |lls.phandle.cloc|
00000630 6b 2d 66 72 65 71 75 65 6e 63 79 00 |k-frequency.|
0000063c
五、实例分析(根据dtb文件结构)
1、fdt_header
struct fdt_header {
uint32_t magic; /* magic word FDT_MAGIC */
uint32_t totalsize; /* total size of DT block */
uint32_t off_dt_struct; /* offset to structure */
uint32_t off_dt_strings; /* offset to strings */
uint32_t off_mem_rsvmap; /* offset to memory reserve map */
uint32_t version; /* format version */
uint32_t last_comp_version; /* last compatible version */
/* version 2 fields below */
uint32_t boot_cpuid_phys; /* Which physical CPU id we'rebooting on */
/* version 3 fields below */
uint32_t size_dt_strings; /* size of the strings block */
/* version 17 fields below */
uint32_t size_dt_struct; /* size of the structure block */
};
00000000 d0 0d fe ed 00 00 06 3c 00 00 00 48 00 00 05 80
/*magic_num*/ /*total = 1596*/ /*off_struct = 72*/ /*off_str = 1408*/
00000010 00 00 00 28 00 00 00 11 00 00 00 10 00 00 00 00
/*off_rsvmap = 40*/ /*version = 17*/ /*last_comp = 16*/ /*boot_cpuid = 0*/
00000020 00 00 00 bc 00 00 05 38
/*size_str = 188*/ /*size_dt_struct = 1336*/
2、memory reserve map
00000020 ----------------------- 00 00 00 00 9d 00 00 00 |.......8........|
00000030 00 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 00 00 00 00 00 00 00 00
3、dt_struct
(节点、属性等四字节对齐)
①:标志一个节点的开始(0x01,四字节对齐);
②:节点的名称(memory,四字节对齐);
③:标志一个属性的开始;
④:属性内容的长度(sizeof(“memory”) = 7);
⑤:属性名称在dt_string中的偏移量(偏移93字节:device_type);
⑥:属性的内容(memory,四字节对齐);
⑦:标志节点的结束(0x02,四字节对齐)。
#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
#define FDT_END_NODE 0x2 /* End node */
#define FDT_PROP 0x3 /* Property: content_size, name off(in_dt_string), content */
#define FDT_NOP 0x4 /* nop */
#define FDT_END 0x9
4、dt_string
(字符串紧密排列,相当于属性名称的库。利用dt_struct中的属性名称偏移量检索,至“\0”截止)
00000580 23 61 64 64 72 65 73 73 2d 63 65 6c 6c 73 00 23 |#address-cells.#|
00000590 73 69 7a 65 2d 63 65 6c 6c 73 00 63 6f 6d 70 61 |size-cells.compa|
000005a0 74 69 62 6c 65 00 69 6e 74 65 72 72 75 70 74 2d |tible.interrupt-|
000005b0 70 61 72 65 6e 74 00 6d 6f 64 65 6c 00 73 65 72 |parent.model.ser|
000005c0 69 61 6c 30 00 73 65 72 69 61 6c 31 00 73 65 72 |ial0.serial1.ser|
000005d0 69 61 6c 32 00 73 65 72 69 61 6c 33 00 64 65 76 |ial2.serial3.dev|
000005e0 69 63 65 5f 74 79 70 65 00 72 65 67 00 74 69 2c |ice_type.reg.ti,|
000005f0 68 77 6d 6f 64 73 00 72 61 6e 67 65 73 00 69 6e |hwmods.ranges.in|
00000600 74 65 72 72 75 70 74 2d 63 6f 6e 74 72 6f 6c 6c |terrupt-controll|
00000610 65 72 00 23 69 6e 74 65 72 72 75 70 74 2d 63 65 |er.#interrupt-ce|
00000620 6c 6c 73 00 70 68 61 6e 64 6c 65 00 63 6c 6f 63 |lls.phandle.cloc|
00000630 6b 2d 66 72 65 71 75 65 6e 63 79 00 |k-frequency.|