【设备树笔记整理3】设备树的规范(dts和dtb)

1 dts 文件格式

1.1 DTS文件布局(layout):

cpp 复制代码
/dts-v1/;
[memory reservations] 
/ {
    [property definitions]
    [child nodes]
};

(1)第一行表示当前设备树文件的版本

(2)第二行用来定义保留的内存区域,该区域不给内核使用,例如:

cpp 复制代码
/memresersve/  0x33000000  0x10000

(3)第三行的 "/" 定义根节点

(4)根节点中包含根节点属性(property definitions)和子节点(child nodes)

[注意]: 大括号和属性都要在结尾处加 ";"

1.2 节点属性格式

(1)Property格式2(没有值):

cpp 复制代码
[label:] property-name;

(2)Property格式1:

cpp 复制代码
[label:] property-name = value;

(3)Property取值有3种情况:

① arrays of cells(1个或多个32位数据, 64位数据使用2个32位数据表示)

示例1:Arrays of cells : cell就是一个32位的数据

cpp 复制代码
interrupts = <17 0xc>;

示例2:64bit数据使用2个cell来表示:

cpp 复制代码
clock-frequency = <0x00000001 0x00000000>;

② string(字符串)

示例:A null-terminated string (有结束符的字符串):

cpp 复制代码
compatible = "simple-bus";

③ bytestring(1个或多个字节)

示例:A bytestring(字节序列) :

cpp 复制代码
local-mac-address = [00 00 12 34 56 78];  // 每个byte使用2个16进制数来表示
local-mac-address = [000012345678];       // 每个byte使用2个16进制数来表示(空格可以省略)

[补充]: Property的取值还可以是各种值的组合(该用法不常见), 用逗号隔开:

cpp 复制代码
compatible = "ns16550", "ns8250";
example = <0xf00f0000 19>, "a strange property format";

1.3 设备节点的格式

Devicetree node格式:

cpp 复制代码
[label:] node-name[@unit-address] {
    [properties definitions]
    [child nodes]
};

1.4 特殊的、默认的属性

(1)根结点的属性

cpp 复制代码
#address-cells   // 在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)
#size-cells      // 在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size)
compatible       // 定义一系列的字符串, 用来指定内核中哪个machine_desc可以支持本设备
                 // 即这个板子兼容哪些平台 
                 // uImage : smdk2410 smdk2440 mini2440     ==> machine_desc         
                 
model            // 咱这个板子是什么
                 // 比如有2款板子配置基本一致, 它们的compatible是一样的
                 // 那么就通过model来分辨这2款板子

[补充]: 关于 #address-cells 和 #size-cells

cpp 复制代码
/ {
	model = "SMDK24440";
	compatible = "samsung,smdk2440";

	#address-cells = <1>;
	#size-cells = <1>;
		
	memory@30000000 {
		device_type = "memory";
		reg =  <0x30000000 0x4000000>;
	};
}

由于#address-cells和#size-cells的值都为1,所以上述例子reg的值中0x30000000表示地址,而0x4000000就表示地址的长度。已知#address-cells和#size-cells,reg可以区分多组地址段:

cpp 复制代码
/ {
	model = "SMDK24440";
	compatible = "samsung,smdk2440";

	#address-cells = <1>;
	#size-cells = <1>;
		
	memory@30000000 {
		device_type = "memory";
		reg =  <0x30000000 0x4000000 0 4096>;
	};
}

上述例子中的reg表示两组数据,第一组是地址为0x30000000,长度为0x4000000的地址段;而第二组数据是地址为0,长度为4096的地址段。

(2)/memory 结点

cpp 复制代码
device_type = "memory";
reg             // 用来指定内存的地址、大小

(3)/chosen 结点

cpp 复制代码
bootargs        // 内核command line参数, 跟u-boot中设置的bootargs作用一样

(4)/cpus 结点

/cpus节点下有1个或多个cpu子节点, cpu子节点中用reg属性用来标明自己是哪一个cpu

所以 /cpus 中有以下2个属性:

cpp 复制代码
#address-cells   // 在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)

#size-cells      // 在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size)
                 // 必须设置为0

(5)/cpus/cpu* 结点(多个cpu中的每个cpu结点)

cpp 复制代码
device_type = "cpu";
reg             // 表明自己是哪一个cpu

1.5 引用其他节点

(1)phandle : // 节点中的phandle属性, 它的取值必须是唯一的(不要跟其他的phandle值一样)

cpp 复制代码
pic@10000000 {
    phandle = <1>;
    interrupt-controller;
};

another-device-node {
    interrupt-parent = <1>;   // 使用phandle值为1来引用上述节点
};

(2)label:

cpp 复制代码
PIC: pic@10000000 {
    interrupt-controller;
};

another-device-node {
    interrupt-parent = <&PIC>;   // 使用label来引用上述节点, 
                                 // 使用lable时实际上也是使用phandle来引用, 
                                 // 在编译dts文件为dtb文件时, 编译器dtc会在dtb中插入phandle属性
};

1.6 dtsi 文件

dtsi文件的格式和dts文件的格式一样,可以将公共的部分放到dtsi文件,并让dts文件包含该dtsi文件。在dts中包含dtsi文件的方法:

cpp 复制代码
#include "jz2440.dtsi"

下面举两个dtsi文件相关的例子:

(1)示例1:覆盖结点

① 文件:jz2440.dtsi

cpp 复制代码
/dtsi-v1/;

/ {
	model = "SMDK24440";
	compatible = "samsung,smdk2440";

	#address-cells = <1>;
	#size-cells = <1>;
		
	memory@30000000 {
		device_type = "memory";
		reg =  <0x30000000 0x4000000>;
	};
/*
	cpus {
		cpu {
			compatible = "arm,arm926ej-s";
		};
	};
*/	
	chosen {
		bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
	};

	
	led {
		compatible = "jz2440_led";
		reg = <5>;
	};
};

② 文件:mytree.dts

cpp 复制代码
/dts-v1/;

#include "jz2440.dtsi"

/ {
    led {
	    reg = <6>;
	};
};

[说明]: 在mytree.dts中包含jz2440.dtsi文件,然后按照jz2440.dtsi中led的结点路径,重写led结点,并修改其reg属性为6。这样在最终编译生成的dtb文件中,led的reg属性就为6。也就是说在dts文件中可以覆盖dtsi文件的结点和属性。

(2)示例2:标签引用

① 文件:jz2440.dtsi

cpp 复制代码
/dtsi-v1/;

/ {
	model = "SMDK24440";
	compatible = "samsung,smdk2440";

	#address-cells = <1>;
	#size-cells = <1>;
		
	memory@30000000 {
		device_type = "memory";
		reg =  <0x30000000 0x4000000>;
	};
/*
	cpus {
		cpu {
			compatible = "arm,arm926ej-s";
		};
	};
*/	
	chosen {
		bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
	};

	
	LED: led {
		compatible = "jz2440_led";
		reg = <5>;
	};
};

② 文件:mytree.dts

cpp 复制代码
/dts-v1/;

#include "jz2440.dtsi"

&LED {
    reg = <5>;
};

[说明]: 依旧让mytree.dts中包含jz2440.dtsi文件。但是这里通过led结点的标签LED来引用该结点,注意通过标签引用结点时不要从根路径开始写结点,否则会报错。

(3)dtc工具

  • dtc其实就是device-tree-compiler,也就是设备树文件dts的编译器。
  • dtc可以将dts和dtsi文件编译成dtb二进制文件,也可以将dtb文件反编译成dts文件。
  • dtc工具在Linux内核的scripts/dtc目录下,因此需要编译完内核源码后才会生成。

2 dtb 文件格式

2.1 dtb文件的布局

cpp 复制代码
             ------------------------------
     base -> |  struct boot_param_header  |
             ------------------------------
             |      (alignment gap) (*)   |
             ------------------------------
             |      memory reserve map    |
             ------------------------------
             |      (alignment gap)       |
             ------------------------------
             |                            |
             |    device-tree structure   |
             |                            |
             ------------------------------
             |      (alignment gap)       |
             ------------------------------
             |                            |
             |     device-tree strings    |
             |                            |
      -----> ------------------------------
      |
      |
      --- (base + totalsize)

2.2 dtb二进制文件的分析

2.3 dtb文件采用大端存储模式

(1)大端存储模式和小端存储模式

(2)补充

大端模式和小端模式是针对数值存储来说的,字符串存储则没有该规则。例如字符串"ab","b"总是存储在比"a"高的地址中。

2.4 查看二进制文件的工具

  • UltraEdit
  • Free-Hex-Editor-Neo

3 参考

(1)官方文档: Specifications - DeviceTree

(2)内核文档1: Documentation/devicetree/usage-model.txt

(3)内核文档2: Documentation/devicetree/booting-without-of.txt

相关推荐
学习向前冲10 分钟前
安装一键式重置密码插件(Linux)-CloudResetPwdAgent
linux·运维·服务器
石兴稳40 分钟前
Ceph client 写入osd 数据的两种方式librbd 和kernel rbd
linux·ceph
大G哥2 小时前
python 数据类型----可变数据类型
linux·服务器·开发语言·前端·python
BillKu2 小时前
Linux设置Nginx开机启动
linux·运维·nginx
baidu_375528813 小时前
光感传感器 芯片stk3171 linux驱动程序
linux·运维·服务器
飞腾开发者3 小时前
飞腾平台Arm NN软件栈安装使用指南
linux·运维·人工智能·机器学习·计算机视觉
夜暝3 小时前
Iotop使用
linux
鸠摩智首席音效师3 小时前
.NET Core 应用程序如何在 Linux 中创建 Systemd 服务 ?
linux·运维·.netcore
不是三毛没有半4 小时前
Centos 7 安装wget
linux
叫我龙翔4 小时前
【计网】实现reactor反应堆模型 --- 多线程方案优化 ,OTOL方案
linux·运维·网络