A device tree is a tree data structure with nodes that describe the physical devices in a system.
设备树是一种数据结构,用来描述硬件设备,如此一来硬件信息就无需硬编码在代码中,这样就将设备驱动和实际硬件解耦开来,有了良好的可扩展性。操作系统可以在启动运行时读取相应的文件,必要时也可以修改。当然设备树也没有完全解决所有硬件配置的问题。
Flattened Device Tree
,Device Tree Source
,Device Tree Binary
Linux uses DT data for three major purposes:
Linux 中 ARM 的 dts 文件位于 arch/arm/boot/dts/
,通过相关的脚本可以在 dts 和 dtb 之间转换。
$ scripts/dtc/dtc -I dts -O dtb -o /path/my_tree.dtb /arch/arm/boot/dts/my_tree.dts
$ scripts/dtc/dtc -I dtb -O dts -o /path/my_tree.dts /path/my_tree.dtb
基本的语法可以表现为节点 node 以及其中的 key-value 键值对。
label: node@xxx {
key = value;
}
compatible: ", "
// 根据习惯,如果一个Node含有reg,那么其名称中必须含有unit-address,即reg中的第一个值。
Node: [@]
// 由 parent-node指定下面两个 property来帮助确定reg中的值长度。
Addressing: reg =
#address-cells: reg 中 base address 由多少个 cell (32bits values) 来表示
#size-cells: reg 中的 length 由多少个 cell (32bits values) 来表示
/{
compatible = "nvidia,harmony", "nvidia,tegra20"; // , "
#address-cells = <1>; // 在它的子节点的 reg 属性中, 使用多少个 u32 整数 (cell) 来描述地址 (base address)
#size-cells = <1>; // 在它的子节点的 reg 属性中, 使用多少个 u32 整数 (cell) 来描述大小 (size / length)
[memory reservations]
{
[property definitions]
[child node]
};
};
// [property definitions]
[label:] node-name[@unit-address] { // 根据习惯,Node含有reg,那么其名称中必须含有unit-address,即reg中的第一个值
[property definitions]
[child nodes]
};
// for example
memory@30000000 {
device_type = "memory";
reg = <0x30000000 0x4000000>;
};
// node reference by phandle
pic@10000000 {
phandle = <1>;
interrupt-controller;
};
another-device-node {
interrupt-parent = <1>; // 使用phandle值为1来引用上述节点
};
// node reference by label
PIC: pic@10000000 {
interrupt-controller;
};
another-device-node {
interrupt-parent = <&PIC>; // 使用label来引用上述节点,
// 使用lable时实际上也是使用phandle来引用,
// 在编译dts文件为dtb文件时, 编译器dtc会在dtb中插入phandle属性
};
常见的 Top-level module:
cpus: its each sub-nodes describing each CPU in the system.
memory : defines location and size of the RAM.
chosen : defines parameters chosen or defined by the system firmware at boot time.
In practice, one of its usage is to pass the kernel command line.
aliases : shortcuts to certain nodes.
One or more nodes defining the buses in the SoC
One or mode nodes defining on-board devices
/
:root node,device tree 从该节点开始。
property:每个 property 是一个 Key-value
键值对。
model
compatible
reg:node / device 的物理地址,reg =
aliases
…
"string-property = "a string";
< >
, cell-property = <0xbeef 123 0xabcd1234>;
[ ]
, binary-property = [0x01 0x23 0x45 0x67];
mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;
string-list = "red fish", "blue fish";
下面以 ARM
的 foundation v8
作为例子。
/* * ARM Ltd.
* ARMv8 Foundation model DTS */
/dts-v1/;
/memreserve/ 0x80000000 0x00010000;
/ {
model = "Foundation-v8A";
compatible = "arm,foundation-aarch64", "arm,vexpress";
interrupt-parent = <&gic>;
#address-cells = <2>;
#size-cells = <2>;
chosen { };
aliases {
serial0 = &v2m_serial0;
...
};
cpus {
#address-cells = <2>;
#size-cells = <0>;
cpu@0 {
device_type = "cpu";
compatible = "arm,armv8";
reg = <0x0 0x0>;
enable-method = "spin-table";
cpu-release-addr = <0x0 0x8000fff8>;
next-level-cache = <&L2_0>;
};
cpu@1 { ... };
cpu@2 { ... };
cpu@3 { ... };
L2_0: l2-cache0 {
compatible = "cache";
};
};
memory@80000000 {
device_type = "memory";
reg = <0x00000000 0x80000000 0 0x80000000>,
<0x00000008 0x80000000 0 0x80000000>;
};
gic: interrupt-controller@2c001000 {
compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
#interrupt-cells = <3>; // 所有以它作为中断接受者的设备都要用3个cell来描述中断信息。
#address-cells = <2>;
interrupt-controller; // 它是一个中断控制器
reg = <0x0 0x2c001000 0 0x1000>,
<0x0 0x2c002000 0 0x1000>,
<0x0 0x2c004000 0 0x2000>,
<0x0 0x2c006000 0 0x2000>;
interrupts = <1 9 0xf04>;
};
timer {
compatible = "arm,armv8-timer";
interrupts = <1 13 0xf08>,
<1 14 0xf08>,
<1 11 0xf08>,
<1 10 0xf08>;
clock-frequency = <100000000>;
};
pmu: { ... };
smb: { ... };
以上面的 CPU 节点为例,其拥有四个子节点和一个 l2-cache0
节点,其中 L2_0 是 l2-cache0
的 label。
cpus 节点中指定了 reg 中 address 为2个cell,构成一个u64,size为0,因此没有大小。
根据惯例,所以CPU@0的0为 unit-address
取自 reg 的第一个值 0x0 0x0,两个 cell。
...
The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
interrupts.
The 2nd cell contains the interrupt number for the interrupt type.
SPI interrupts are in the range [0-987]. PPI interrupts are in the
range [0-15].
The 3rd cell is the flags, encoded as follows:
bits[3:0] trigger type and level flags.
1 = low-to-high edge triggered
2 = high-to-low edge triggered (invalid for SPIs)
4 = active high level-sensitive
8 = active low level-sensitive (invalid for SPIs).
bits[15:8] PPI interrupt cpu mask. Each bit corresponds to each of
the 8 possible cpus attached to the GIC. A bit set to '1' indicated
the interrupt is wired to that CPU. Only valid for PPI interrupts.
Also note that the configurability of PPI interrupts is IMPLEMENTATION
DEFINED and as such not guaranteed to be present (most SoC available
in 2014 seem to ignore the setting of this flag and use the hardware
default value).
第一个域指明中断是 SPI 还是 PPI,0是 SPI,1是PPI
第二个是中断号
第三个是中断触发类型以及中断发往的 cpu_id
以timer为例
timer {
compatible = "arm,armv8-timer";
interrupts = <1 13 0xf08>,
<1 14 0xf08>,
<1 11 0xf08>,
<1 10 0xf08>;
clock-frequency = <100000000>;
};
上面的描述表明 timer 中断类型为PPI, 分别对应中断号10, 11, 13, 14, 并且对 4 个CPU与之相连, 且为 active low level-sentitive(0xf08)。
设备树源文件通过 dtc 编译得到 dtb。
struct ftd_header |
---|
free space |
memory reservation block |
free space |
structure block |
free space |
strings block |
free space |
DTB 的数据为大端
存放。
DTB分为四个部分:struct ftd_header
、memory reservation block
、structure block
、strings block
;
struct ftd_header:用来表明各个分部的偏移地址,整个文件的大小,版本号等;
memory reservation block:在设备树中使用 /memreservation/
定义的保留内存信息;
structure block:保存节点的信息,节点的结构;
strings block:保存属性的名字,单独作为字符串保存;
struct fdt_header {
uint32_t magic;
uint32_t totalsize;
uint32_t off_dt_struct;
uint32_t off_dt_strings;
uint32_t off_mem_rsvmap;
uint32_t version;
uint32_t last_comp_version;
uint32_t boot_cpuid_phys;
uint32_t size_dt_strings;
uint32_t size_dt_struct;
};
// 节点信息结构体
struct {
uint32_t len; // val 的长度
uint32_t nameoff; // 在string block的偏移
};
sudo apt-get install device-tree-compiler
两种文件格式可以互相转换。
dtc -I dts -O dtb -o /path/to/xxx.dtb /path/toxxx.dts
dtc -I dtb -O dts -o /path/to/xxx.dts /path/to/xxx.dtb
linux/Documentation/devicetree/usage-model.txt ↩︎
https://elixir.bootlin.com/linux/v4.3.6/source/Documentation/devicetree/bindings/arm/gic.txt ↩︎