设备树的规范

一、dts格式

参考学习(分享我学习过程中阅读的几篇文章):
设备树使用手册

设备树文件格式

(1) 语法:
Devicetree node格式:
[label:] node-name[@unit-address] {
    [properties definitions]
    [child nodes]
};

node-name[@unit-address]:节点名应以小写或大写字母开头,并应描述设备的一般类别。
名称的单元地址特定于节点所在的总线类型。它由表2.1中的字符集中的一个或多个ASCII
字符组成。单元地址必须匹配节点的reg属性中指定的第一个地址。如果节点没有reg属性,
则必须省略@unit-address,并且仅通过节点名将该节点与树中同一级别的其他节点区分
开来。特定总线的绑定可以为reg格式和单元地址指定附加的、更具体的要求。根节点没有
节点名或单元地址。它由一个正斜杠(/)标识。

在这里插入图片描述
在这里插入图片描述

Property格式1:
[label:] property-name = value;

Property格式2(没有值):
[label:] property-name;

Property取值只有3: 
arrays of cells(1个或多个32位数据, 64位数据使用232位数据表示), 
string(字符串), 
bytestring(1个或多个字节)

示例: 
a. Arrays of cells : cell就是一个32位的数据
interrupts = <17 0xc>;

b. 64bit数据使用2个cell来表示:
clock-frequency = <0x00000001 0x00000000>;

c. A null-terminated string (有结束符的字符串):
compatible = "simple-bus";

d. A bytestring(字节序列) :
local-mac-address = [00 00 12 34 56 78];  // 每个byte使用2个16进制数来表示
local-mac-address = [000012345678];       // 每个byte使用2个16进制数来表示

e. 可以是各种值的组合, 用逗号隔开:
compatible = "ns16550", "ns8250";
example = <0xf00f0000 19>, "a strange property format";
(2) DTS文件布局(layout):
/dts-v1/; /*设备树版本号*/
[memory reservations]    //指定保留内存,内核不会使用保留内存 格式为: /memreserve/ <address> <length>;
/ {
    [property definitions]
    [child nodes]
};
(3) 特殊的、默认的属性:
a. 根节点:
#address-cells   // 在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)
#size-cells      // 在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size)
compatible       // 定义一系列的字符串, 用来指定内核中哪个machine_desc可以支持本设备
                 // 即这个板子兼容哪些平台 
                 // uImage : smdk2410 smdk2440 mini2440     ==> machine_desc         
                 
model            // 咱这个板子是什么
                 // 比如有2款板子配置基本一致, 它们的compatible是一样的
                 // 那么就通过model来分辨这2款板子

b. /memory
device_type = "memory";
reg             // 用来指定内存的地址、大小

c. /chosen
bootargs        // 内核command line参数, 跟u-boot中设置的bootargs作用一样

d. /cpus
/cpus节点下有1个或多个cpu子节点, cpu子节点中用reg属性用来标明自己是哪一个cpu
所以 /cpus 中有以下2个属性:
#address-cells   // 在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)

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


e. /cpus/cpu*
device_type = "cpu";
reg             // 表明自己是哪一个cpu
(4) 引用其他节点:
a. phandle : // 节点中的phandle属性, 它的取值必须是唯一的(不要跟其他的phandle值一样)

pic@10000000 {
    phandle = <1>;
    interrupt-controller;
};

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

b. label:

PIC: pic@10000000 {
    interrupt-controller;
};

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

二、解析dtb文件

1、参考文档

官方文档:
https://www.devicetree.org/specifications/
内核文档:
Documentation/devicetree/booting-without-of.txt

在这里插入图片描述

DTB文件布局:
             ------------------------------
     base -> |  struct boot_param_header  |
             ------------------------------
             |      (alignment gap) (*)   |
             ------------------------------
             |      memory reserve map    |
             ------------------------------
             |      (alignment gap)       |
             ------------------------------
             |                            |
             |    device-tree structure   |
             |                            |
             ------------------------------
             |      (alignment gap)       |
             ------------------------------
             |                            |
             |     device-tree strings    |
             |                            |
      -----> ------------------------------
      |
      |
      --- (base + totalsize)

device-tree strings:存放属性的名字

  dtb文件用于在软件程序之间交换设备树数据。例如,在引导操作系统时,固件将dtb传递给操作系统内核。

  DTB格式将设备树数据编码为一个单一的、线性的、无指针的数据结构。它由一个小的报头,后面跟着三个可变大小的部分:内存预留块,结构块和字符串块。这些应该按照这个顺序出现在dtb中。因此,设备树结构作为一个整体,当按地址加载到内存中时,将类似于下图所示(较低的地址在图表的顶部,大端模式)。
在这里插入图片描述
  free space部分可能不存在,但在某些情况下,他们可能需要满足单个块的对齐约束。自上述格式出现以来,已经经历了几个版本,开头的ftd_header给出了版本相关信息,因此客户端程序可以确定设备树是否以兼容的格式编码。版本和设备树二进制文件的结构相关,与他的内容无关。

  dtb文件的头可通过以下C语言结构体进行解析,所有的报头字段都是32位整数,以大端模式存储:

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;
};
magic :这个字段的值应该是0xd00dfeed (big-endian)

totalsize :该字段应包含设备树数据结构的总大小(以字节为单位)。这个大小应该包含
结构的所有部分:报头,内存保留块,结构块和字符串块,以及块之间或最后一个块之后的
任何空闲空间间隙。

off_dt_struct :该字段应该包含结构块从报头开始的字节偏移量,通过该值索引到结构
块的位置

off_dt_strings :该字段应该包含字符串块从头信息开始的字节偏移量,通过该值找到字
符串块的位置

off_mem_rsvmap :该字段应该包内存保留块从头信息开始的字节偏移量,通过该值找到内
存保留块的位置

version :version该字段应包含设备树数据结构的版本。如果使用上边定义的结构,则版
本为17。DTSpec引导程序可以提供新版本的设备树,在这种情况下,该字段应包含在提供该
版本详细信息的新版本文档中定义的版本号。

last_comp_version :该字段应该包含设备树数据结构的最低版本,该版本使用的是向后
兼容的。因此,对于本文档(版本17)中定义的结构,该字段应该包含16,因为版本17是向后
兼容的版本16,而不是更早的版本。DTSpec引导程序应提供与16版本向后兼容的设备树,因
此该字段应始终包含16。

boot_cpuid_phys :系统启动CPU的物理ID。它应该与设备树中该CPU节点的 reg属性中给
出的物理ID相同。

size_dt_strings :该字段应包含dtb文件的字符串块段的长度(以字节为单位)。

size_dt_struct :该字段应包含dtb文件的结构块段的长度(以字节为单位)
Memory Reservation Block:内存保留块
	内存保留块向客户端程序提供物理内存中被保留的区域列表;也就是说,它不能用于一般
的内存分配,它用于保护重要的数据结构不被客户端程序覆盖。例如,在一些具有IOMMU的系
统上,由 DTSpec引导程序初始化的TCE表需要以这种方式进行保护。同样,在客户端程序运
行期间使用的任何引导程序代码或数据都需要保留。更具体地说,除非引导程序提供的其他信
息明确地指示它应这样做,否则客户机程序不得访问保留区域中的内存。然后,客户端程序可
以访问。

	内存保留块由一组64位的大端模式的整数对组成,每对整数由下面的C结构体来表示:
struct fdt_reserve_entry
{
	uint64_t address;
	uint64_t size;
};
	每对给出了保留内存区域的物理地址和大小(以字节为单位)。这些给定区域不得相互
重叠。保留块列表应终止于地址和大小都等于0的条目。注意,地址和大小值总是64位的。
在32位cpu 上,该值的高32位将被忽略。内存预留块中的每个元素都是uint64_t,因此内
存预留块作为一个整体,应位于从dtb文件开始的8字节对齐偏移处。
Structure Block:结构块
	结构块描述了设备树本身的结构和内容。它由一系列带有数据的标记组成,如下所述。
这些被组织成一个线性树结构。结构块中的每个标记,以及结构块本身,应位于dtb文件开
始的4字节对齐偏移处。
	结构块由一系列片段组成,每个片段都以一个标记开始,即一个大端32位整数。一些
标记后面跟着额外的数据,其格式由标记值决定。所有标记都应在32位边界对齐,这可能
需要在前一个标记数据之后插入填充字节(值为0x0)。标记的五种类型如下:

FDT_BEGIN_NODE (0x00000001) :标志着一个节点的开始,它后面应该有节点的名称作
为额外的数据。名称存储为一个以空结束的字符串,并应包括单位地址,如果有的话。节点
名后面跟着零填充字节(如果需要对齐的话),然后是下一个标记,它可以是除FDT_END 以外
的任何标记。

FDT_END_NODE (0x00000002) :标志着一个节点的结束,其后没有额外的数据;所以它紧
跟着下一个标记,这个标记可以是除 FDT_PROP之外的任何标记。

FDT_PROP (0x00000003):标志着在树中一个属性表示的开始。后面应该有描述该属性的
额外数据。该数据首先由属性的长度和名称组成,表示为以下C结构:
	struct 
	{
		uint32_t len;//表示属性值的长度,若为0表示属性为空
		uint32_t nameoff;//偏移量值,用于在字符串块中进行索引属性名称
						 //属性的名称在字符串块中是一个以NULL结尾的字符串
	}
	在此结构之后,该属性的值将以长度为len的字节字符串的形式给出。这个值后跟0填
充字节(如果需要)以对齐下个32位边界,然后是下一个标记,这个标记可以是除FDT_END之
外的任何标记。

FDT_NOP (0x00000004) :该标记被任何解析设备树的程序忽略,其没有额外的数据,所以
其后面紧跟着下一个标记,可以是任何有效的标记。属性或节点可以用FDT_NOP标记覆盖树中
的定义,从而在不移动的情况下将其从树中移除,在设备树中表示树的其他部分。

FDT_END (0x00000009) :标记结构块的结束。dtb文件只有一个FDT_END标记存在,它应
该是结构块中的最后一个标记。它没有额外的数据。

在这里插入图片描述

Strings Block:字符串块,存放属性的名字
	字符串块包含表示设备树中使用的所有属性名的字符串。在字符串块中,这些以空结
尾的字符串被简单地连接在一起,并通过从结构块得到偏移量值引用到字符串块索引到一个
字符串。字符串块没有对齐约束,可以出现在设备树blob开始的任何偏移位置。

  设备树结构表示为线性树:每个节点的表示都以FDT_BEGIN_NODE标记开始,并以 FDT_END_NODE 标记结束。 节点的属性和子节点(如果有)在 FDT_END_NODE 之前表示,因此 FDT_BEGIN_NODE 和 FDT_END_NODE 标记用于那些子节点嵌套在父节点中。结构块作为一个整体由根节点的表示组成(其中包含所有其他节点的表示)节点),后跟一个 FDT_END 标记,将结构块的结尾标记为一个整体。

2、示例分析

将该图放大查看!!!,结合1中的知识去理解。
请添加图片描述
注:本文章参考了《韦东山老师嵌入式课程》笔记,并结合了自己的实际开发经历以及网上他人的技术文章,综合整理得到。如有侵权,联系删除!水平有限,欢迎各位在评论区交流。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小嵌同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值