Linux设备树学习2 - DTB文件格式

一. DTB文件简介

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

二. DTB文件内容布局

Linux设备树学习2 - DTB文件格式_第1张图片

                 从上图可以看出,DTB由四个部分组成,分别是struct fdt_headermemory reservationstructure blockstrings 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非常的重要。

你可能感兴趣的:(Linux内核学习总结,linux)