设备树的通常操做方式是:开发人员根据开发需求编写dts文件,而后使用dtc将dts编译成dtb文件。html
dts文件是文本格式的文件,而dtb是二进制文件,在linux启动时被加载到内存中,接下来咱们须要来分析设备树dtb文件的格式。node
dtb做为二进制文件被加载到内存中,而后由内核读取并进行解析,若是对dtb文件的格式不了解,那么在看设备树解析相关的内核代码时将会步履维艰,而阅读源代码才是了解设备树最好的方式,因此,若是须要更透彻的了解设备树解析的细节,第一步就是须要了解设备树的格式。linux
注:本文部分参考:官方文档git
dtb的格式是这样的:github
但凡涉及到数据的记录,就必定会有一个总的描述部分,就像磁盘的超级块,书的目录,dtb固然也不例外,这个描述头部就是dtb的header部分,经过这个header部分,用户能够快速地了解到整个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 */ };
设备树的魔数,魔数其实就是一个用于识别的数字,表示设备树的开始,linux dtb的魔数为 0xd00dfeed.ui
这个设备树的size,也能够理解为所占用的实际内存空间。spa
offset to dt_struct,表示整个dtb中structure部分所在内存相对头部的偏移地址code
offset to dt_string,表示整个dtb中string部分所在内存相对头部的偏移地址
offset to memory reserve map,dtb中memory reserve map所在内存相对头部的偏移地址,
设备树的版本,截至目前的最新版本为17.
最新的兼容版本
这部分仅在版本2中存在,后续版本再也不使用。
表示整个dtb中string部分的大小
表示整个dtb中struct部分的大小
中间的alignment gap部分表示对齐间隙,它并不是是必须的,它是否被提供以及大小由具体的平台对数据对齐和的要求以及数据是否已经对齐来决定。
memory reserve map:描述保留的内存部分,这个map的数据结构是这样的:
{ uint64_t physical_address; uint64_t size; }
这部分存储了此结构的列表,整个部分的结尾由一个数据为0的结构来表示(即physical_address和size都为0,总共16字节)。
这一部分的数据并不是是节点中的memory子节点,而是在设备开始以前(也就是第一个花括号以前)定义的,例如:
/dts-v1/ /memreserve/ 0x10000000 0x100000 /*在结构提中的表示为 physical_address=0x10000000,size=0x100000 */ { ... }
这一部分的做用是告诉内核哪一些内存空间须要被保留而不该该被系统覆盖使用,由于在内核启动时经常须要动态申请大量的内存空间,只有提早进行注册,用户须要使用的内存才不会被系统征用而形成数据覆盖。
值得一提的是,对于设备树而言,即便不指定保留内存,系统也会默认为设备树保留相应的内存空间。
同时,这一部分须要64位(8字节)对齐。
device-tree structure:每一个节点都会被描述为一个struct,节点之间能够嵌套,所以也会有嵌套的struct。
structure的的结构是这样的:
device-tree strings:在dtb中有大量的重复字符串,好比"model","compatile"等等,为了节省空间,将这些字符串统一放在某个地址,须要使用的时候直接使用索引来查看。
须要注意的是,属性部分格式为key = value,key部分被放置在strings部分,而value部分的字符串并不会放在这一部分,而是直接放在structure中。
光说不练假把式,下面我就使用一个简单的示例来剖析dtb的文件格式。
下述示例仅仅是一个演示demo,不针对任何平台,为了演示方便,编写了一个很是简单的dts文件。
/dts-v1/; / {
compatible = "hd,test_dts", "hd,test_xxx"; #address-cells = <0x1>; #size-cells = <0x1>; model = "HD test dts"; chosen { stdout-path = "/ocp/serial@ffff"; }; memory@80000000 { device_type = "memory"; reg = <0x80000000 0x10000000>; }; led1:led@2000000 { compatible = "test_led"; #address-cells = <0x1>; #size-cells = <0x1>; reg = <0x200 0x4>; }; };
编译当前dts文件,获取对应的dtb文件。
鉴于dtb文件为二进制文件,普通编辑器打开显示乱码,咱们使用ultraEdit查看,它将数据以16进制形式显示:
整个dtb文件仍是比较简单的,图中的红色框出的部分为header部分的数据,能够看到:
第1个四字节对应magic,数据为 D00DFEED. 第2四字节对应totalsize,数据为 000001BC,能够由整张图片看出,这个dtb文件的大小由0x0~0x1bb,大小恰好是0x1bc 第3个四字节对应off_dt_struct,数据为00000038。 第4个四字节对应off_dt_strings,数据为00000174,能够由整张图片看到,从0x174开始恰好是字符串开始的地方 第5个四字节对应off_mem_rsvmap,数据为00000028 第6个四字节对应version,数据为00000011,十进制为17 第7个四字节对应last_comp_version,数据为00000010,十进制为16,表示兼容版本16 第8个四字节对应boot_cpuid_phys,数据为00000000,仅在版本2中使用,这里为0 第9个四字节对应size_dt_strings,数据为00000048,表示字符串总长。 第10个四字节对应size_dt_struct,数据为0000013c,表示struct部分总长度。
整个头部为40字节,16进制为0x28,从头部信息中off_mem_rsvmap部分能够获得,reserve memory起始地址为0x28,上文中提到,这一部分使用一个16字节的struct来描述,以一个全为0的struct结尾。
后16字节全为0,能够看出,这里并无设置reserve memory。
上文回顾:每个属性都是以 key = value的形式来描述,value部分可选。
偏移地址来到0x00000038(0x28+0x10),接下来8个字节为00000003,根据上述structure中的描述,这是OF_DT_PROP,即标示属性的开始。
接下来4字节为00000018,代表该属性的value部分size为24字节。
接下来4字节是当前属性的key在string 部分的偏移地址,这里是00000000,由头部信息中off_dt_strings能够获得,string部分的开始为00000174,偏移地址为0,因此对应字符串为"compatible".
以后就是value部分,这部分的数据是字符串,能够直接从图片右侧栏看出,总共24字节的字符串"hd,test_dts", "hd,test_xxx",由于字符串之间以0结尾,因此程序能够识别出这是两个字符串。
能够看出,到这里,compatible = "hd,test_dts", "hd,test_xxx";这个属性就被描述完了,对于属性的描述仍是很是简单的。
按照固有的规律,接下来就是对#address-cells = <0x1>的解析,而后是#size-cells = <0x1>...
而后就是递归的子节点chosen,memory@80000000等等都是按照上文中提到的structure解析规则来进行解析,最后以00000002结尾。
与根节点不一样的是,子节点有一个unit name,即chosen,memory@80000000这些名称,并不是节点中的.name属性。
而整个结构的结束由00000009来描述。
通常而言,在32位系统中,dtc在编译dts文件时会自动考虑对齐问题,因此对于设备树的对齐字节,咱们只须要有所了解便可,并不会常接触到。
好了,关于linux设备树dtb文件格式的讨论就到此为止啦,若是朋友们对于这个有什么疑问或者发现有文章中有什么错误,欢迎留言
关于linux设备树在内核启动时的解析能够参考个人另外两篇博客:
linux设备树的解析--dtb转换成device_node
linux设备树的解析--device_node转换成platform_deviec
原创博客,转载请注明出处!
祝各位早日实现项目丛中过,bug不沾身.