1 FDT 概念
FDT:Flatted Device Tree
DTC:Device Tree Compiler
DTS:Device Tree Source
DTB:Device Tree Binary
在 DTS 文件中按照指定格式结构配置设备信息,通过 DTC 编译后生成 DTB 文件。U-Boot 和 Linux kernel 运行时加载 DTB 信息到内存中,解析设备配置。
编译命令:dtc -I dts -O dtb -o xxx.dtb xxx.dts
2 dts 文件结构
/dts-v1/;
/ {
model = " ";
compatible = " ";
aliases {
...
};
memory@0 {
device_type = "memory";
reg = <0x0 0x40000000>;
};
chosen {
bootargs = "";
stdout-path = "serial0:115200n8";
};
device1 {
compatible = " ";
device1-subdevice {
};
};
};
dts 文件基本结构包括 node、property 和 value。root node 只能有一个,其余 node 有且只有一个 parent node;每个 node 有多个 property 和 value,并且用 node name 标识;value 的形式可能是空、数值、数组和字符串。
3 dtb 文件结构
dtb 文件内部信息排列 |
---|
dtb header |
memory reserve map |
device-tree structure |
device-tree string |
3.1 dtb header
struct fdt_header {
fdt32_t magic; /* magic word FDT_MAGIC */
fdt32_t totalsize; /* total size of DT block */
fdt32_t off_dt_struct; /* offset to structure */
fdt32_t off_dt_strings; /* offset to strings */
fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
fdt32_t version; /* format version */
fdt32_t last_comp_version; /* last compatible version */
/* version 2 fields below */
fdt32_t boot_cpuid_phys; /* Which physical CPU id we're
booting on */
/* version 3 fields below */
fdt32_t size_dt_strings; /* size of the strings block */
/* version 17 fields below */
fdt32_t size_dt_struct; /* size of the structure block */
};
文件头包括的信息有:幻数、文件大小、设备树结构信息偏移地址和大小、设备树参数西信息偏移地址和大小、以及文件格式版本号等。
3.2 memory reserve map
struct fdt_reserve_entry {
fdt64_t address;
fdt64_t size;
};
3.3 device-tree structure
struct fdt_node_header {
fdt32_t tag;
char name[0];
};
struct fdt_property {
fdt32_t tag;
fdt32_t len;
fdt32_t nameoff;
char data[0];
};
#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
#define FDT_END_NODE 0x2 /* End node */
#define FDT_PROP 0x3 /* Property: name off,
size, content */
#define FDT_NOP 0x4 /* nop */
#define FDT_END 0x9
dts 文件中 node 和 property 按照以上格式存储在 dtb 文件。tag 类型有以上五种,name 和 data 都是指针,所以 node 占用 8 bytes,property 占用 16 bytes。
node 结构的 name 记录 node name 的存储地址;property 的 nameoff 记录 property name 的存储地址,data 位置开始存储 property value 的值,len 记录 value 占用字节长度。
3.4 device-tree string
dts 文件中所有的 node name 和 property name 信息存储区域。
4 dtb 实际文件分析
4.1 dtb 文件反汇编
fdtdump -sd /zynq-zc702.dtb
/zynq-zc702.dtb: found fdt at offset 0
/dts-v1/;
// magic: 0xd00dfeed
// totalsize: 0x42c7 (17095)
// off_dt_struct: 0x38
// off_dt_strings: 0x3e08
// off_mem_rsvmap: 0x28
// version: 17
// last_comp_version: 16
// boot_cpuid_phys: 0x0
// size_dt_strings: 0x4bf
// size_dt_struct: 0x3dd0
// 0038: tag: 0x00000001 (FDT_BEGIN_NODE)
/ {
// 0040: tag: 0x00000003 (FDT_PROP)
// 3e08: string: #address-cells
// 004c: value
#address-cells = <0x00000001>;
// 0050: tag: 0x00000003 (FDT_PROP)
// 3e17: string: #size-cells
// 005c: value
#size-cells = <0x00000001>;
// 0060: tag: 0x00000003 (FDT_PROP)
// 3e23: string: compatible
// 006c: value
compatible = "xlnx,zynq-zc702", "xlnx,zynq-7000";
// 008c: tag: 0x00000003 (FDT_PROP)
// 3e2e: string: model
// 0098: value
model = "Xilinx ZC702 board";
// 00ac: tag: 0x00000001 (FDT_BEGIN_NODE)
cpus {
// 00b8: tag: 0x00000003 (FDT_PROP)
// 3e08: string: #address-cells
// 00c4: value
#address-cells = <0x00000001>;
// 00c8: tag: 0x00000003 (FDT_PROP)
// 3e17: string: #size-cells
// 00d4: value
#size-cells = <0x00000000>;
// 00d8: tag: 0x00000001 (FDT_BEGIN_NODE)
cpu@0 {
// 00e4: tag: 0x00000003 (FDT_PROP)
// 3e23: string: compatible
// 00f0: value
compatible = "arm,cortex-a9";
// 0100: tag: 0x00000003 (FDT_PROP)
// 3e34: string: device_type
// 010c: value
device_type = "cpu";
...
// 3e00: tag: 0x00000002 (FDT_END_NODE)
};
从 ftddump 结果可以看到文件头的信息,通过 off_dt_struct 和 off_mem_rsvmap 可以计算出文件头占用 40 bytes(0x28), reserve map 占用 16 bytes(0x10),device tree struct 的起始偏移地址为 0x38;根据最后一个 FDT_END_NODE 的地址可以推算出 dt_string 的开始地址为 0x3e08, dt_struct 的总大小为 0x3dd0,与 size_dt_struct 和 off_dt_strings 的信息一致。
在 0x38 地址找到第一个 root node,与上面的起始偏移信息完全吻合,根据 3.3 的分析可知下一个 node 地址应该是 0x40。在 0x40 地址找到一个 property,nameoff 指向 0x3e08(第一个 device-tree string),property name 为 #address-cells。
0x4c、0x6c、0x010c 都是存储 property value,0x4c 后面的 property 地址是 0x5c,因为0x4c 是一个数值<0x00000001>占用了 4 bytes;0x6c 后面的 property 地址是 0x8c,因为 0x6c 是一个字符串(“xlnx,zynq-zc702”, “xlnx,zynq-7000”)占用了不超过 32 bytes;0x010c 后面的 property 地址是 0x0110,因为 0x010c 是一个字符串(“cpu”)占用了不超过 4 bytes。根据 0x4c 和 0x6c 的对比可以得出 property value 确实直接从 property 的 data 位置开始存储,通过 0x6c 和 0x010c 的对比可以得出字符串存储内容是双引号内的字符并且做 4 bytes 对齐。
4.2 dts文件
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
compatible = "xlnx,zynq-7000";
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "arm,cortex-a9";
device_type = "cpu";
...
};
实际 dts 文件内容与 fdtdump 得到的结果一致。