2.2设备树的规范(dts和dtb)——DTB格式

本节讲述设备树的dtb格式。

上节讲述了dts格式。回顾上节,在dts文件和dtsi文件中,可以使用C语言的define和include,使用方法和作用也同C语言相同。

编写dts文件后,需要使用dtc工具将dts文件编译成dtb文件。dtc工具可以检查dts文件是否存在语法或格式错误,如果发现语法或格式上有错误,那么就会提示修改这些错误。

在dts文件中,可以包含一个或多个dtsi文件,通过dtc工具将这些文件编译得到一个dtb文件。同时,可以在dts文件中重新定义覆盖dtsi文件中设置的节点属性。

本节内容来自于以下两个文档,可以查看这两个文档获取更多关于dtb文件的信息。

官方文档:
https://www.devicetree.org/specifications/

内核文档:
Documentation/devicetree/booting-without-of.txt

 下图是dtb文件的布局,从上往下(开始到结束)依次是:

  1. 头部(struct ftd_header):用来表明各个部分偏移地址整个文件的大小版本号等等;
  2. 内存的保留信息块(memory reservation block):存放dts文件中申明的需要预留内存的信息;
  3. 节点块(structure block):各个节点的信息将放在structure block中;
  4. 字符串块(strings block):存放字符串信息

 

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

头部(struct ftd_header)是一个结构体,其组成如下:

结构体成员的说明如下,注意magic是大端格式/大字节序(低地址放高字节,仅对存储数值有影响)存放的:

内存保留信息块(memory reservation block)也是一个结构体,其组成如下:

对上节的dts文件稍作修改,增加内存空间的定义

假设要将64M内存的最高1M留下自己使用,则在dts文件中添加下面这句。

/memreserve/ 0x33f00000 0x100000;

修改后的dts文件如下。

// SPDX-License-Identifier: GPL-2.0
/*
 * SAMSUNG SMDK2440 board device tree source
 *
 * Copyright (c) 2018 weidongshan@qq.com
 * dtc -I dtb -O dts -o jz2440.dts jz2440.dtb
 */
 
#define S3C2410_GPA(_nr)	((0<<16) + (_nr))
#define S3C2410_GPB(_nr)	((1<<16) + (_nr))
#define S3C2410_GPC(_nr)	((2<<16) + (_nr))
#define S3C2410_GPD(_nr)	((3<<16) + (_nr))
#define S3C2410_GPE(_nr)	((4<<16) + (_nr))
#define S3C2410_GPF(_nr)	((5<<16) + (_nr))
#define S3C2410_GPG(_nr)	((6<<16) + (_nr))
#define S3C2410_GPH(_nr)	((7<<16) + (_nr))
#define S3C2410_GPJ(_nr)	((8<<16) + (_nr))
#define S3C2410_GPK(_nr)	((9<<16) + (_nr))
#define S3C2410_GPL(_nr)	((10<<16) + (_nr))
#define S3C2410_GPM(_nr)	((11<<16) + (_nr))

/dts-v1/;

/memreserve/ 0x33f00000 0x100000;

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

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

	
/*
	cpus {
		cpu {
			compatible = "arm,arm926ej-s";
		};
	};
*/	
	chosen {
		bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
	};

	
	led {
		compatible = "jz2440_led";
		pin = <S3C2410_GPF(5)>;
	};
};

然后编译这个dts文件,将生成的dtb文件拉到windows下,再使用Hex Editor Neo/UE打开,分析dtb文件。

fdt_header

如前所述,首先是头部结构体fdt_header。

magic

fdt_header的第一个成员是magic,用于校验是否为dtb文件,按照大字节序排列,文件中为0xd00dfeed,即fdt_header.magic=0xd00dfeed,符合设备树的magic要求,说明这是一个dtb文件。

 totalsize

 然后是第二个成员totalsize,记录的是该dtb文件的大小,fdt_header.totalsize=0x1d1=465,也就是说dtb文件的总大小为465字节。

linux下查看dtb文件大小,确实为465字节。

off_dt_struct

之后是off_dt_struct,储存的是structure block相对于dtb起始位置的偏移

fdt_header.off_dt_struct=0x48=72。

 

off_dt_strings

off_dt_strings,储存的是string block相对于起始地址的偏移。

fdt_header.off_dt_strings=0x188=392。

off_mem_rsvmap

off_mem_rsvmap ,储存的是memory reservation block相对于起始位置的偏移。fdt_header.off_mem_rsvmap=0x28=40。

version

version,储存的是设备树数据结构的版本

fdt_header.version=0x11=17。

 

last_comp_version

last_comp_version,储存的是所用版本向后兼容的最低版本的设备树数据结构,如本版本使用的是17版本,17版本兼容16版本,但不兼容16版本之前的版本,则该成员应该为0x10=16。

 

boot_cpuid_phys

boot_cpuid_phys,储存的是系统引导CPU的物理ID,它的值应该与设备树文件中CPU节点下的reg属性值相等。

由于CPU节点被注释了,所以这里为全0。

fdt_header.boot_cpuid_phys=0。

 

size_dt_strings

 size_dt_strings,存储的是string block长度

fdt_header.size_dt_strings=0x49=73。

string block的偏移地址是0x188,加上长度0x49,刚好等于0x1D1,和dtb大小相等。

 

size_dt_struct

size_dt_struct,储存的是structure block长度

fdt_header.size_dt_struct=0x140=320。

根据off_dt_struct可以知道,structure block的偏移地址为0x48,0x48+0x140=0x188,所以从0x48开始到0x188结束,中间的部分都是structure block。

总结

最后总结一下jz2440.dtb文件中,fdt_header各个成员的值:

  1. fdt_header.magic=0xd00dfeed;
  2. fdt_header.totalsize=0x1d1;
  3. fdt_header.off_dt_struct=0x48;
  4. fdt_header.off_dt_strings=0x188;
  5. fdt_header.off_mem_rsvmap=0x28;
  6. fdt_header.version=0x11;
  7. fdt_header.last_comp_version=0x10;
  8. fdt_header.boot_cpuid_phys=0;
  9. fdt_header.size_dt_strings=0x49;
  10. fdt_header.size_dt_struct=0x140;

所以dtb文件的结构如下所示:

  1. 0 ~ 0x28 -- ftd_header
  2. 0x28 ~ 0x48 -- memory_reservation_block
  3. 0x48 ~ 0x188 -- structure block
  4. 0x188 ~ 0x1D1 -- string block

Memory Reservation Block

格式说明

memory reservation blcok,储存的是dts文件中,指定的需要保留的内存的物理地址和大小

在jz2440.dts文件中,指定了保留起始地址为0x33F00000,大小为0x100000的内存空间。

下面是memory reservation blcok的格式说明:

  1. memory reservation blcok,保存的也是一个C结构体,这个结构体只有两个成员addresssize,分别对应起始地址大小
  2. 不论CPU是32位还是64位,address 和 size都是 64位 的,且按大字节序排列,如果CPU是32位,那么高的32位将被丢弃,只使用低32bit
  3. 块的结尾固定是一个address和size全0的结构体;

实例分析

根据ftd_header信息可知,memory reservation blcok的起始地址为0x28,struct block的起始地址为0x48,所以0x28到0x48中间的部分都为memory reservation blcok。

jz2440的CPU是32bit的,所以高32位丢弃。

如图可见,第一个保留的内存块,address为0x33f00000,size为0x100000,与jz2440.dts文件中描述的一致;第二个保留的内存块信息为全0,说明memory reservation blcok到此结束。

Struct Block

结构

Struct Block是由一系列连续的片组成的,每个片的以一个32位token开始,token按照大字节序存储,某些token后面还会接着额外的数据(extra data)。所有的token都是32位对齐的,不满32位的会在前面补0。

以下是五种token:

  1. FDT_BEGIN_NODE(0x00000001) :FDT_BEGIN_NODE 表示节点的开始说明后面的内容都是节点信息 。FDT_BEGIN_NODE后接节点名字,节点名字是以NULL结尾字符串如果名字中包含地址,则该字符串中也应该包含地址
  2. FDT_END_NODE (0x00000002) FDT_END_NODE 表示节点的结束。FDT_END_NODE 没有额外的信息,所以它后面马上接着下一个除FDT_PROP之外的token
  3. FDT_PROP (0x00000003):FDT_PROP 表示属性的开始,后接描述该属性信息的extra data,它的extra data是按以下C结构体存储的

    struct {
        uint32_t len;        //以字节为单位记录了属性值的长度(长度可能为0,表示一个空值);
        uint32_t nameoff;    //表示属性名在string block中的偏移位置;
    }

    这两个成员的数据类型都是32bit的整型,按照大字节序存储。

    在这个结构体后面,存储的就是属性的值。属性值被储存为长度为len字符串。如有必要,这个值可以按32bit对齐存储(不足32bit补0)。

  4. FDT_NOP (0x00000004) :FDT_NOP 会 被解析程序忽略,这个token 没有extra data,所以它后面紧接下一个token。一个属性或节点可以通过FDT_NOP token来覆盖,从而将其从删除,而不需要修改树的其他部分。
  5. FDT_END (0x00000009):FDT_END 表示struct block结束,一个dtb文件中应该只包含一个FDT_END token,并且FDT_END token应该是 最后一个token。它没有extra data,因此它后面直接接string block的内容。

实例

下面为一个设备树节点的格式:

[label:] node-name[@unit-address] {
    [properties definitions]
    [child nodes]
};

以LED节点为例,说明一下设备节点的存储:

led {
	compatible = "jz2440_led";
	pin = <S3C2410_GPF(5)>;
};

其他节点的分析也与LED节点相似,以FDT_BEGIN_NODE+节点名称开始,然后是FDT_PROP描述节点的属性,然后是struct(len+nameoff)+val,该节点的属性描述完成后,使用FDT_END_NODE表示节点结束;最后,当所有节点描述完毕后,使用FDT_END结束struct block。

分析如下图所示。

dtb文件大小为465字节。

如果将两块内存分为两个节点,在节点中加入内存的起始地址来区分,则设备如下:

 编译后的dtb文件如下图:

  • 5
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值