第七章 DTS (Part I)
7.1 什么是DTS?
Linux DTS(Device Tree Source)是设备树的源文件,用于描述硬件的结构化数据格式。设备树的主要目的是将硬件描述从软件中分离出来,使得同一套软件可以在不同的硬件平台上运行,而无需修改代码。
设备树的概念最早出现在Open Firmware中,后来被PowerPC架构采用。随着嵌入式系统的普及,设备树逐渐被引入到ARM架构和其他嵌入式平台中。Linux内核从2.6版本开始支持设备树,并在后续版本中不断完善。
DTS文件被编译成DTB(二进制设备树)文件,并在引导程序运行时(通常是uboot)传递给Linux kernel, 之后Linux Kernel根据设备树描述的信息加载相应的外设及其驱动。
在嵌入式系统中,硬件配置通常是多样且复杂的。设备树提供了一种标准化的方法来描述硬件,使得操作系统和驱动程序可以更容易地适应不同的硬件平台。这不仅简化了开发过程,还提高了系统的可移植性和可维护性。
7.2 设备树基础
这里来看一个简单的DTS原文件, 以及如何编译及反编译。 首先创建一个example.dts文件, 内容如下:
/dts-v1/;
/ {
#address-cells = <2>;
#size-cells = <1>;
cpus {
#address-cells = <2>;
#size-cells = <1>;
cpu@0 {
reg = <0x0 0x0 0x0>;
};
};
memory@0 {
device_type = "memory";
reg = <0x0 0x80000000 0x20000000>;
};
serial@c81004c0 {
compatible = "neardi,demo_serial";
reg = <0x0 0xc81004c0 0x20>;
};
};
DTS的语法, 请参考下一章节。
7.2.1 安装DTS编译工具
在设备上面, 使用如下命令安装DTS编译工具:
sudo apt install device-tree-compiler
接下来编译example.dts文件, 使用如下命令:
neardi@LPA3588:~/dts$ ls
example.dts
neardi@LPA3588:~/dts$ dtc -I dts -O dtb example.dts -o example.dtb
neardi@LPA3588:~/dts$ ls -lh
total 8.0K
-rw-rw-r-- 1 neardi neardi 390 Aug 23 10:41 example.dtb
-rw-rw-r-- 1 neardi neardi 414 Aug 23 10:30 example.dts
这样就编译生成了example.dtb文件。
7.2.2 反编译DTB生成DTS文件
我们也可以使用dtc工具把DTB文件, 反编译成DTS源文件。 比如把刚才生成的example.dtb文件, 反编译生成test.dts, 如下:
neardi@LPA3588:~/dts$ dtc -I dtb -O dts example.dtb -o test.dts
neardi@LPA3588:~/dts$ ls -lh
total 12K
-rw-rw-r-- 1 neardi neardi 390 Aug 23 10:41 example.dtb
-rw-rw-r-- 1 neardi neardi 414 Aug 23 10:30 example.dts
-rw-rw-r-- 1 neardi neardi 348 Aug 23 10:45 test.dts
neardi@LPA3588:~/dts$ cat test.dts
/dts-v1/;
/ {
#address-cells = <0x02>;
#size-cells = <0x01>;
cpus {
#address-cells = <0x02>;
#size-cells = <0x01>;
cpu@0 {
reg = <0x00 0x00 0x00>;
};
};
memory@0 {
device_type = "memory";
reg = <0x00 0x80000000 0x20000000>;
};
serial@c81004c0 {
compatible = "neardi,demo_serial";
reg = <0x00 0xc81004c0 0x20>;
};
};
从上面的log看, 反编译的test.dts与源DTS文件example.dts相同。
7.3 Linux系统运行时DTS
在Linux系统起来后, 可以查看当前的设备树/proc/device-tree, 其内容如下:
neardi@LPA3588:/proc/device-tree$ ls cpu* -l
cpuinfo:
total 0
-r--r--r-- 1 root root 17 Aug 23 09:44 compatible
-r--r--r-- 1 root root 8 Aug 23 09:44 name
-r--r--r-- 1 root root 24 Aug 23 09:44 nvmem-cell-names
-r--r--r-- 1 root root 12 Aug 23 09:44 nvmem-cells
cpus:
total 0
-r--r--r-- 1 root root 4 Aug 23 09:44 '#address-cells'
drwxr-xr-x 2 root root 0 Aug 23 09:44 cpu@0
drwxr-xr-x 2 root root 0 Aug 23 09:44 cpu@100
drwxr-xr-x 2 root root 0 Aug 23 09:44 cpu@200
drwxr-xr-x 2 root root 0 Aug 23 09:44 cpu@300
drwxr-xr-x 2 root root 0 Aug 23 09:44 cpu@400
drwxr-xr-x 2 root root 0 Aug 23 09:44 cpu@500
drwxr-xr-x 2 root root 0 Aug 23 09:44 cpu@600
drwxr-xr-x 2 root root 0 Aug 23 09:44 cpu@700
drwxr-xr-x 5 root root 0 Aug 23 09:44 cpu-map
drwxr-xr-x 3 root root 0 Aug 23 09:44 idle-states
drwxr-xr-x 2 root root 0 Aug 23 09:44 l2-cache-b0
drwxr-xr-x 2 root root 0 Aug 23 09:44 l2-cache-b1
drwxr-xr-x 2 root root 0 Aug 23 09:44 l2-cache-b2
drwxr-xr-x 2 root root 0 Aug 23 09:44 l2-cache-b3
drwxr-xr-x 2 root root 0 Aug 23 09:44 l2-cache-l0
drwxr-xr-x 2 root root 0 Aug 23 09:44 l2-cache-l1
drwxr-xr-x 2 root root 0 Aug 23 09:44 l2-cache-l2
drwxr-xr-x 2 root root 0 Aug 23 09:44 l2-cache-l3
drwxr-xr-x 2 root root 0 Aug 23 09:44 l3-cache
-r--r--r-- 1 root root 5 Aug 23 09:44 name
-r--r--r-- 1 root root 4 Aug 23 09:44 '#size-cells'
查看某个属性值的具体内容, 比如:
neardi@LPA3588:/proc/device-tree/cpus/cpu@0$ cat compatible
arm,cortex-a55
neardi@LPA3588:/proc/device-tree/cpus/cpu@0$ hexdump reg
0000000 0000 0000
0000004
7.4 DTB
DTS源文件编译生成的DTB文件, 存储在哪里呢? kernel又是如何读取呢?
在Rockchip平台, 编译后生成的DTB如下路径#SDK/kernel/arch/arm64/boot/dts/rockchip:
SDK$ ls kernel/arch/arm64/boot/dts/rockchip/*.dtb
kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lkd3588-f0.dtb kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lpa3588-f1.dtb
kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lkd3588-f1.dtb kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lpa3588-f2.dtb
kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lkd3588-f2.dtb kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lpa3588-f3.dtb
kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lkd3588-f3.dtb kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lpa3588-f4.dtb
kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lkd3588-p0.dtb kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lpa3588-t0.dtb
kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lkd3588-t0.dtb kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lpb3588-f0.dtb
kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lkd3588-t1.dtb kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lpb3588-t0.dtb
kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lpa3588-f0.dtb
之后, DTB文件和kernel文件一起合并生成boot.img文件, 烧录时boot.img文件存储在Flash(eMMC/Nand等)里。
当u-boot启动时, bootm会加载kernel并把dtb参数告知kernel, 比如:
bootm <kernel_addr> - <dtb_addr>
Rockchip设备启动时, 有如下Log:
## Loading fdt from FIT Image at e92bba40 ...
Using 'conf' configuration
Trying 'fdt' fdt subimage
Description: unavailable
Type: Flat Device Tree
Compression: uncompressed
Data Start: 0xe92bc240
Data Size: 169170 Bytes = 165.2 KiB
Architecture: AArch64
Load Address: 0x08300000
Hash algo: sha256
Hash value: d3a2702f6d0844c585721587f06d740806983d6458ec1d8f56221e403e2b3472
Verifying Hash Integrity ... sha256+ OK
Using fdt from load-in fdt
Loading fdt from 0xe92bc240 to 0x08300000
Booting using the fdt blob at 0x08300000
Loading Kernel Image from 0xe92e5840 to 0x00400000 ... OK
kernel loaded at 0x00400000, end = 0x02913a00
'reserved-memory' cma: addr=10000000 size=10000000
'reserved-memory' demo_memory@20000000: addr=20000000 size=1000000
'reserved-memory' ramoops@110000: addr=110000 size=f0000
Using Device Tree in place at 0000000008300000, end 000000000832c4d1
can't get otp device, ret=-19
Adding bank: 0x00200000 - 0x08400000 (size: 0x08200000)
Adding bank: 0x09400000 - 0xf0000000 (size: 0xe6c00000)
Adding bank: 0x100000000 - 0x200000000 (size: 0x100000000)
Total: 1574.941 ms
Starting kernel ...
其中这行就是把dtb文件从flash里加载到memory: 0x08300000
Loading fdt from 0xe92bc240 to 0x08300000
DTB文件的sha256 hash value与启动log里的Hash value是一致的, 如:
SDK$ sha256sum kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lkd3588-f0.dtb
d3a2702f6d0844c585721587f06d740806983d6458ec1d8f56221e403e2b3472 kernel/arch/arm64/boot/dts/rockchip/rk3588-neardi-linux-lkd3588-f0.dtb
有兴趣的同学, 可以查看kernel/arch/arm64/kernel/setup.c代码, 这里开始加载DTB文件。