一. DTB文件简介

       
DTB文件是由DTS文件通过dtc命令编译生成的二进制文件。DTS文件不能直接被内核解析,需要编译成DTB文件才可以直接被内核识别并解析使用的。

二. DTB文件内容布局

                 从上图可以看出,DTB由四个部分组成,分别是struct fdt_header,memory reservation,
structure block,strings block,free space用于填充,使以上四个块的数据能满足四字节对齐,所以free
space并不总是会显示出来。下面会详细介绍DTB文件各个部分。

三. FDT Header介绍

        struct fdt_header的布局可以使用一个C语言结构体定义,占用的内存大小为40字节,如下
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; };
示例:
00000000 d0 0d fe ed 00 00 1a 77 00 00 00 38 00 00 18 60 |.......w...8...`|
00000010 00 00 00 28 00 00 00 11 00 00 00 10 00 00 00 00 |...(............|
00000020 00 00 02 17 00 00 18 28 |.......(........|
magic :该字段应该被设置成0xd00dfeed,字节序是大端字节序

totalsize :DTB文件的总大小,示例中为0x00001a77

off_dt_struct :structure block相对于DTB文件头的偏移的字节数,示例中为0x00000038

off_dt_strings :strings block相对于DTB文件头的偏移的字节数,示例中为0x00001860

off_mem_rsvmap :memory reservation相对于DTB文件头的字节数,示例中为0x00000028

version :DTB的版本号,示例中为0x00000011

last_comp_version :DTB的上一个版本,示例中为0x00000010

boot_cpuid_phys :保存系统CPU的ID号,示例中为0x00

size_dt_strings :strings block的大小,示例中为0x00000217

size_dt_struct :structure block的大小,示例中为0x00001828

四. Memory Reservation介绍

        memory
reservation指定的内存范围将会被保留,它不能被一般的内存分配函数使用,防止这块内存内的重要数据被内核破坏。memory
reservation也可以用C语言的结构体表示,如下
struct fdt_reserve_entry { uint64_t address; uint64_t size; };
        struct fdt_reserve_entry占用16字节大小,address和size都由u64数表示。

五. Structure Block介绍

        structure block由五个标识包含设备树的一些数据,形成一个树形结构,五个标识分别如下 

FDT_BEGIN_NODE (0x00000001) :表示一个设备节点的开始。接下来跟着设备节点的名字
,设备备节点名字如果有unit-address,需要加上去,节点名字最后以NULL结尾。如果结尾没有4字节对齐,需要填充0x00,保证4字节对齐。

FDT_END_NODE (0x00000002) :表示一个设备节点的结束。它的后面不用跟数据,通常是跟下一个标识,除了FDT_DROP。

示例:

DTS代码片段:
cpus { cpu@0 { compatible = "mips,mips24KEc"; }; };
DTB代码片段:
000000b0 00 00 00 01 63 70 75 73 00 00 00 00 |........cpus....| 000000c0 00 00
00 01 63 70 75 40 30 00 00 00 00 00 00 03 |....cpu@0.......| 000000d0 00 00 00
0f 00 00 00 1b 6d 69 70 73 2c 6d 69 70 |........mips,mip| 000000e0 73 32 34 4b
45 63 00 00 00 00 00 02 00 00 00 02 |s24KEc..........|
       
从上面的代码可知,包含了两个device_node,分别是cpus和cpu@0,cpu@0是cpus的子节点,所以代码中有两个FDT_BEGIN_NODE
(0x00000001),和FDT_END_NODE
(0x00000002)。FDT_BEGIN后跟着节点名称。从DTB的组织方式也可以看出structure block是以树状层次结构布局的。

FDT_DROP (0x00000003)
:表示一个属性的开始。后面需要跟一个属性名的长度和偏移量,再后面跟属性的值,属性的长度和偏移量可以用C语言结构体表示
struct { uint32_t len; uint32_t nameoff; }
        len表示此结构后跟的属性值的长度,NULL也包含在内,nameoff表示属性名在strings block中偏移量。示例如下
00000090 00 00 00 03 00 00 00 15 00 00 00 26 57 6f 6f 79 |...........&Wooy|
000000a0 61 20 49 4f 54 20 53 6d 61 72 74 20 37 36 32 30 |a IOT Smart 7620|
000000b0 00 00 00 00 |.... |
节选strings block:
00001860 23 61 64 64 72 65 73 73 2d 63 65 6c 6c 73 00 23 |#address-cells.#|
00001870 73 69 7a 65 2d 63 65 6c 6c 73 00 63 6f 6d 70 61 |size-cells.compa|
00001880 74 69 62 6c 65 00 6d 6f 64 65 6c 00 62 6f 6f 74 |tible.model.boot|
00001890 61 72 67 73 00 23 69 6e 74 65 72 72 75 70 74 2d |args.#interrupt-|
000018a0 63 65 6c 6c 73 00 69 6e 74 65 72 72 75 70 74 2d |cells.interrupt-|
000018b0 63 6f 6e 74 72 6f 6c 6c 65 72 00 6c 69 6e 75 78 |controller.linux|
000018c0 2c 70 68 61 6e 64 6c 65 00 72 65 67 00 72 61 6e |,phandle.reg.ran|
000018d0 67 65 73 00 69 6e 74 65 72 72 75 70 74 2d 70 61 |ges.interrupt-pa|
000018e0 72 65 6e 74 00 69 6e 74 65 72 72 75 70 74 73 00 |rent.interrupts.|
00 00 00 03 :表示FDT_DROP

00 00 00 15 :表示"Wooya IOT Smart 7620"的长度,记得结尾的NULL也计算在内

00 00 00 26 :表示"Wooya IOT Smart 7620"的属性名在strings block中的偏移,从节选的strings
block可以看出属性名是"model"。

tips :结尾多出的00 00 00是为了保证4字节对齐

FDT_NOP (0x00000004) :FDT_NOP程序在解析时将被忽略,此标识不跟任何数据,只跟下一个标识。

FDT_END (0x00000009) :FDT_END表示structure
block的结束,DTB文件只有一个FDT_END标识,并且是structure block的最后一个标识,所以后面跟的是strings block的开始。

根节点示例:

DTS代码片段:
/ { #address-cells = <0x1>; #size-cells = <0x1>; ........ ........ }
DTB代码片段:
00000030 00 00 00 01 00 00 00 00 |................| 00000040 00 00 00 03 00 00
00 04 00 00 00 00 00 00 00 01 |................| 00000050 00 00 00 03 00 00 00
04 00 00 00 0f 00 00 00 01 |................| ........ /* strings blcok start
*/ 00001860 23 61 64 64 72 65 73 73 2d 63 65 6c 6c 73 00 23 |#address-cells.#|
00001870 73 69 7a 65 2d 63 65 6c 6c 73 00 63 6f 6d 70 61 |size-cells.compa|
00001880 74 69 62 6c 65 00 6d 6f 64 65 6c 00 62 6f 6f 74 |tible.model.boot|
00001890 61 72 67 73 00 23 69 6e 74 65 72 72 75 70 74 2d |args.#interrupt-|
000018a0 63 65 6c 6c 73 00 69 6e 74 65 72 72 75 70 74 2d |cells.interrupt-|
000018b0 63 6f 6e 74 72 6f 6c 6c 65 72 00 6c 69 6e 75 78 |controller.linux|
000018c0 2c 70 68 61 6e 64 6c 65 00 72 65 67 00 72 61 6e |,phandle.reg.ran|
        以上DTB片段是根节点的,根节点比较特殊,根节点00 00 00 01后接着的是00 00 00 00,可以看出根节点节点名是空的,00
00 00 03后是属性值的长度为0x04,strings block中的偏移为0,值为00 00 00 01,表示#address-cells =
<0x1>;后面00 00 00 03则表示#size-cells = <0x1>。

六. Strings Block介绍

        string block包含了在设备树中出现的所有的属性名,所有的名字都是以NULL结尾,structure
block通过nameoff来引用其中的属性名。strings block的结尾不需要4字节对齐。

七. 总结

        本文介绍了DTB文件的四个块,分别是struct header,memory reservation,structure
block,strings
block,并介绍了他们的各自的组织方式。熟悉DTB的数据组织方式,是理解Linux内核解析DTB文件流程的基础,所以熟悉DTB非常的重要。

技术
下载桌面版
GitHub
Gitee
SourceForge
百度网盘(提取码:draw)
云服务器优惠
华为云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信