4.2uboot对设备树的支持——dtb的修改原理

本节说明在uboot中修改dtb的原理

在uboot中,有一些命令支持对dtb文件进行修改

当我们想要修改dtb文件时,可以直接修改dts文件,然后编译dts文件生成新的dtb文件,再将新的dtb文件载入设备

或者,我们也可以在uboot中使用命令,直接修改dtb文件。修改完成后,再将新的dtb文件保存在板子上,以后启动时就可以使用这个新的dtb文件了。

实际上,在uboot中修改dtb的命令就是fdt。

fdt命令在最新的uboot版本中已经有支持了,但是1.1.6版本还不支持,所以需要修改uboot代码,增加支持fdt指令(从新版本中移植)。

4.2uboot对设备树的支持——dtb的修改原理_第1张图片

在移植之前,先了解一下在fdt指令修改dtb的原理。

在dtb文件中修改某个属性

以修改dtb文件中的某个属性的原理为例,开始说明fdt指令修改dtb的原理。

先来回顾一下dtb文件中,属性是怎么保存的

首先是表示属性开始的token FDT_PROP(0x00000003);然后是描述该属性信息的extra data(len+nameoff);最后是value,也就是属性值,属性值的长度就是len

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

 4.2uboot对设备树的支持——dtb的修改原理_第2张图片

那么,如何修改这个prop的值呢

假设,旧值长度len新值长度new_len,且new_len > len

那么,需要把原来的val所占的空间扩展一下,扩展为new_len;并且,在原来的val后面的数据,都需要往后移动一下,移动的长度为(new_len - len);移动之后,就可以把新的val写入了。

同时,属性值的长度也要更新为new_len

4.2uboot对设备树的支持——dtb的修改原理_第3张图片

 另外,头部信息也需要同步修改

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 */
};
  1. magic:不需要修改;
  2. totalsize:需要修改,总大小增加了(new_len - len)字节;
  3. off_dt_struct:不需要修改;
  4. off_dt_string:需要修改,偏移值增加了(new_len - len)字节;
  5. off_mem_rsvmap:不需要更改;
  6. version:不需要更改;
  7. last_comp_version:不需要更改;
  8. boot_cpuid_phys:不需要更改;
  9. size_dt_strings:不需要更改;
  10. size_dt_struct:需要更改,struct块的size增加了(new_len - len)字节;

也就是说,头部信息中有三项需要同步修改。

 dtb文件的布局:

4.2uboot对设备树的支持——dtb的修改原理_第4张图片

 总结一下,修改属性的值,老值为len,新值为new_len,且new_len > len,需要以下几步:

  1. 把原属性val所占空间从len字节扩展为new_len字节:
    把老值之后的所有内容向后移动(new_len - len)字节;
  2. 把新值写入val所占的newlen字节空间,更新属性值的长度信息;
  3. 修改dtb头部信息中structure block的长度: size_dt_struct;
  4. 修改dtb头部信息中string block的偏移值: off_dt_strings;
  5. 修改dtb头部信息中的总长度: totalsize;

在dtb文件中增加一个新的属性

了解了dtb文件中属性的修改原理之后,其实其他的操作也是类似的。

比如,在某个节点中增加一个新的属性

  1. 如果在string block中没有这个属性的名字,就在string block尾部添加一个新字符串:
    属性的名字;
    并且修改dtb头部信息中string block的长度:size_dt_strings;
    修改dtb头部信息中的总长度: totalsize;

  2. 找到属性所在节点, 在节点尾部扩展一块空间, 内容及长度为(12+len):
    TAG      // 4字节, 对应0x00000003
    len      // 4字节, 表示属性的val的长度
    nameoff  // 4字节, 表示属性名的offset
    val      // len字节, 用来存放val

  3. 修改dtb头部信息中structure block的长度: size_dt_struct;

  4. 修改dtb头部信息中string block的偏移值: off_dt_strings;

  5. 修改dtb头部信息中的总长度: totalsize;

代码

从uboot官方下载地址(Index of /pub/u-boot/)下载一个支持fdt指令的版本的uboot(u-boot-2018.11-rc2.tar.bz2)。

解压创建工程,在cmd目录下有一个fdt.c文件。

里面有构造一个fdt的指令。

并且还有fdt指令的用法说明。

4.2uboot对设备树的支持——dtb的修改原理_第5张图片

设置某个节点(path)的属性(prop)等于val为例,看一下代码上是怎么实现的。

fdt指令的入口函数是do_fdt

进入fdt,找到对应的设置属性的值的处理代码。

4.2uboot对设备树的支持——dtb的修改原理_第6张图片

处理流程如下:

  1. 找到对应节点的偏移值,即找到对应节点(fdt_path_offset);
  2. 找到节点中的对应属性(fdt_getprop);
  3. 若节点中已经存在该属性,将旧的属性值保存下来;
  4. 解析传入的新的属性值,将新的属性值转换成字节流(fdt_parse_prop);
  5. 设置新的属性值(fdt_setprop);

4.2uboot对设备树的支持——dtb的修改原理_第7张图片

先简单看一下fdt_parse_prop函数,它的作用是将传入的属性值转换成字节流

根据注释信息可以知道,传入的属性值可以是<>指定的十六进制数,也可以是[]指定的字节流,还可以是""指定的字符串

最终它们都要被转换成字节流。

4.2uboot对设备树的支持——dtb的修改原理_第8张图片

再来看一下fdt_setprop函数,它的作用是设置新的属性值

4.2uboot对设备树的支持——dtb的修改原理_第9张图片

fdt_setprop_placeholder函数中,调用fdt_resize_property_函数,重新规划dtb的大小

4.2uboot对设备树的支持——dtb的修改原理_第10张图片

调用关系如下。

fdt_setprop
	fdt_setprop_placeholder       // 为新值在DTB中腾出位置
			 fdt_get_property_w  // 得到老值的长度 oldlen
			 fdt_splice_struct_  // 腾空间
					fdt_splice_  // 使用memmove移动DTB数据, 移动(newlen-oldlen)
					fdt_set_size_dt_struct  // 修改DTB头部, size_dt_struct
					fdt_set_off_dt_strings  // 修改DTB头部, off_dt_strings
					
	memcpy(prop_data, val, len);  // 在DTB中存入新值

 

你可能感兴趣的:(设备树详细分析,开发语言,设备树)